Showing preview only (3,212K chars total). Download the full file or copy to clipboard to get everything.
Repository: admin-ch/CovidCertificate-App-Android
Branch: main
Commit: 2578da419092
Files: 643
Total size: 2.9 MB
Directory structure:
gitextract_e7z7c8gy/
├── .github/
│ ├── actions/
│ │ └── gradle_docker/
│ │ ├── action.yml
│ │ └── main.sh
│ └── workflows/
│ ├── appcenter_verifier_abn.yml
│ ├── appcenter_verifier_dev.yml
│ ├── appcenter_verifier_prod.yml
│ ├── appcenter_verifier_prodfdroid.yml
│ ├── appcenter_wallet_abn.yml
│ ├── appcenter_wallet_dev.yml
│ ├── appcenter_wallet_prod.yml
│ ├── appcenter_wallet_prodfdroid.yml
│ ├── browserstack_wallet_abn.yml
│ ├── build.yml
│ └── gradle-wrapper-validation.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── REPRODUCIBLE_BUILDS.md
├── apkdiff.py
├── build.gradle
├── buildAndCompare.sh
├── common/
│ ├── .gitignore
│ ├── build.gradle
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src/
│ ├── abn/
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── common/
│ │ └── debug/
│ │ └── DebugFragment.kt
│ ├── dev/
│ │ ├── java/
│ │ │ └── ch/
│ │ │ └── admin/
│ │ │ └── bag/
│ │ │ └── covidcertificate/
│ │ │ └── common/
│ │ │ └── debug/
│ │ │ ├── DebugFragment.kt
│ │ │ └── DebugSecureStorage.kt
│ │ └── res/
│ │ └── xml/
│ │ └── network_security_config.xml
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ └── impressum/
│ │ │ ├── de/
│ │ │ │ ├── impressum.html
│ │ │ │ └── licence.html
│ │ │ ├── en/
│ │ │ │ ├── impressum.html
│ │ │ │ └── licence.html
│ │ │ ├── fonts/
│ │ │ │ ├── Inter-Bold.otf
│ │ │ │ └── Inter-Light.otf
│ │ │ ├── fr/
│ │ │ │ ├── impressum.html
│ │ │ │ └── licence.html
│ │ │ ├── it/
│ │ │ │ ├── impressum.html
│ │ │ │ └── licence.html
│ │ │ └── rm/
│ │ │ ├── impressum.html
│ │ │ └── licence.html
│ │ ├── java/
│ │ │ └── ch/
│ │ │ └── admin/
│ │ │ └── bag/
│ │ │ └── covidcertificate/
│ │ │ └── common/
│ │ │ ├── BaseActivity.kt
│ │ │ ├── browserstack/
│ │ │ │ ├── AirplaneMode.kt
│ │ │ │ ├── BadNetwork.kt
│ │ │ │ ├── Normal.kt
│ │ │ │ └── Onboarding.kt
│ │ │ ├── config/
│ │ │ │ ├── CertificateRenewalInfoDetailModel.kt
│ │ │ │ ├── CertificateRenewalInfoModel.kt
│ │ │ │ ├── CertificateRenewalType.kt
│ │ │ │ ├── CheckModesInfosModel.kt
│ │ │ │ ├── ConfigModel.kt
│ │ │ │ ├── ConfigViewModel.kt
│ │ │ │ ├── CovidCertificateNewsItem.kt
│ │ │ │ ├── EolBannerInfoModel.kt
│ │ │ │ ├── FaqEntryModel.kt
│ │ │ │ ├── FaqIntroSection.kt
│ │ │ │ ├── FaqModel.kt
│ │ │ │ ├── ForeignRulesHintModel.kt
│ │ │ │ ├── InfoBoxModel.kt
│ │ │ │ ├── InfoCovidCertificateNews.kt
│ │ │ │ ├── RefreshButtonInfoModel.kt
│ │ │ │ ├── VaccinationBookingInfoModel.kt
│ │ │ │ └── VaccinationHintModel.kt
│ │ │ ├── data/
│ │ │ │ └── ConfigSecureStorage.kt
│ │ │ ├── dialog/
│ │ │ │ └── InfoDialogFragment.kt
│ │ │ ├── exception/
│ │ │ │ ├── HttpIOException.kt
│ │ │ │ └── TimeDeviationException.kt
│ │ │ ├── extensions/
│ │ │ │ ├── ContextExtensions.kt
│ │ │ │ ├── DccCertExtensions.kt
│ │ │ │ ├── LifecycleOwnerExtensions.kt
│ │ │ │ ├── OkHttpExtensions.kt
│ │ │ │ └── WindowExtensions.kt
│ │ │ ├── faq/
│ │ │ │ ├── FaqAdapter.kt
│ │ │ │ ├── FaqFragment.kt
│ │ │ │ ├── FaqItem.kt
│ │ │ │ ├── FaqViewHolder.kt
│ │ │ │ └── model/
│ │ │ │ └── Header.kt
│ │ │ ├── html/
│ │ │ │ ├── BuildInfo.kt
│ │ │ │ └── ImprintFragment.kt
│ │ │ ├── net/
│ │ │ │ ├── ConfigRepository.kt
│ │ │ │ └── ConfigService.kt
│ │ │ ├── onboarding/
│ │ │ │ ├── BaseOnboardingActivity.kt
│ │ │ │ └── SimpleOnboardingPagerAdapter.kt
│ │ │ ├── qr/
│ │ │ │ ├── CameraPermissionExplanationDialog.kt
│ │ │ │ ├── KittlerBinarizer.kt
│ │ │ │ ├── QRCodeReaderHelper.kt
│ │ │ │ ├── QrScanFragment.kt
│ │ │ │ └── QrScannerState.kt
│ │ │ ├── settings/
│ │ │ │ └── SettingsFragment.kt
│ │ │ ├── util/
│ │ │ │ ├── AssetUtil.kt
│ │ │ │ ├── CutOutEdgeTreatment.kt
│ │ │ │ ├── EnvironmentUtil.kt
│ │ │ │ ├── ErrorCodeUtil.kt
│ │ │ │ ├── ErrorHelper.kt
│ │ │ │ ├── ErrorState.kt
│ │ │ │ ├── HorizontalMarginItemDecoration.kt
│ │ │ │ ├── LocaleUtil.kt
│ │ │ │ ├── SingleLiveEvent.java
│ │ │ │ ├── StringUtil.kt
│ │ │ │ ├── UiUtil.kt
│ │ │ │ ├── UlTagHandler.java
│ │ │ │ └── UrlUtil.java
│ │ │ └── views/
│ │ │ ├── MarginItemDecoration.kt
│ │ │ ├── ViewExtensions.kt
│ │ │ └── WindowInsetsLayout.kt
│ │ └── res/
│ │ ├── anim/
│ │ │ ├── fragment_open_enter.xml
│ │ │ ├── fragment_open_exit.xml
│ │ │ ├── slide_enter.xml
│ │ │ ├── slide_exit.xml
│ │ │ ├── slide_pop_enter.xml
│ │ │ └── slide_pop_exit.xml
│ │ ├── color/
│ │ │ ├── selector_black_or_white.xml
│ │ │ ├── selector_blue_or_white.xml
│ │ │ ├── selector_grey_or_blue.xml
│ │ │ ├── selector_grey_or_white.xml
│ │ │ ├── selector_transparent_or_blue.xml
│ │ │ ├── selector_white_or_black.xml
│ │ │ ├── selector_white_or_blue.xml
│ │ │ └── text_radio_checkable.xml
│ │ ├── drawable/
│ │ │ ├── bg_button_default.xml
│ │ │ ├── bg_button_red.xml
│ │ │ ├── bg_button_white.xml
│ │ │ ├── bg_corners_top_left.xml
│ │ │ ├── bg_dialog.xml
│ │ │ ├── bg_pill.xml
│ │ │ ├── bg_rect_rounded_sheet.xml
│ │ │ ├── bg_rect_rounded_small.xml
│ │ │ ├── bg_rect_rounded_small_blue_ripple.xml
│ │ │ ├── bg_rect_rounded_small_checkable.xml
│ │ │ ├── bg_rect_rounded_small_red.xml
│ │ │ ├── btn_radio_checkable.xml
│ │ │ ├── dot_black.xml
│ │ │ ├── dot_grey.xml
│ │ │ ├── dot_white.xml
│ │ │ ├── header_bottom.xml
│ │ │ ├── header_collapsed_shadow.xml
│ │ │ ├── ic_1g.xml
│ │ │ ├── ic_2g.xml
│ │ │ ├── ic_2g_green.xml
│ │ │ ├── ic_2g_grey.xml
│ │ │ ├── ic_2g_plus.xml
│ │ │ ├── ic_3g.xml
│ │ │ ├── ic_arrow_contract.xml
│ │ │ ├── ic_arrow_expand.xml
│ │ │ ├── ic_arrow_forward.xml
│ │ │ ├── ic_bund_small.xml
│ │ │ ├── ic_bundwappen_big.xml
│ │ │ ├── ic_call.xml
│ │ │ ├── ic_cam_off.xml
│ │ │ ├── ic_camera_switch.xml
│ │ │ ├── ic_check_filled.xml
│ │ │ ├── ic_check_green.xml
│ │ │ ├── ic_check_grey.xml
│ │ │ ├── ic_check_large.xml
│ │ │ ├── ic_checkbox_empty.xml
│ │ │ ├── ic_checkbox_filled.xml
│ │ │ ├── ic_close.xml
│ │ │ ├── ic_close_red.xml
│ │ │ ├── ic_dot.xml
│ │ │ ├── ic_double_check.xml
│ │ │ ├── ic_error.xml
│ │ │ ├── ic_error_blue.xml
│ │ │ ├── ic_error_grey.xml
│ │ │ ├── ic_error_large.xml
│ │ │ ├── ic_error_orange.xml
│ │ │ ├── ic_error_triangle.xml
│ │ │ ├── ic_expire_i.xml
│ │ │ ├── ic_faq.xml
│ │ │ ├── ic_header_slim.xml
│ │ │ ├── ic_how_it_works_image.xml
│ │ │ ├── ic_info.xml
│ │ │ ├── ic_info_alert.xml
│ │ │ ├── ic_info_blue.xml
│ │ │ ├── ic_info_outline.xml
│ │ │ ├── ic_invalid_grey.xml
│ │ │ ├── ic_invalid_red.xml
│ │ │ ├── ic_light_off.xml
│ │ │ ├── ic_light_off_blue.xml
│ │ │ ├── ic_light_on.xml
│ │ │ ├── ic_light_on_black.xml
│ │ │ ├── ic_link_external.xml
│ │ │ ├── ic_load.xml
│ │ │ ├── ic_no1g.xml
│ │ │ ├── ic_no2g.xml
│ │ │ ├── ic_no3g.xml
│ │ │ ├── ic_no_2_g_plus_height.xml
│ │ │ ├── ic_no_connection.xml
│ │ │ ├── ic_no_connection_large.xml
│ │ │ ├── ic_notification.xml
│ │ │ ├── ic_notification_filled.xml
│ │ │ ├── ic_offline.xml
│ │ │ ├── ic_offline_large.xml
│ │ │ ├── ic_offline_orange.xml
│ │ │ ├── ic_one.xml
│ │ │ ├── ic_phone.xml
│ │ │ ├── ic_plus.xml
│ │ │ ├── ic_plus_green.xml
│ │ │ ├── ic_privacy.xml
│ │ │ ├── ic_privacy_grey.xml
│ │ │ ├── ic_process_error.xml
│ │ │ ├── ic_process_error_grey.xml
│ │ │ ├── ic_process_error_large.xml
│ │ │ ├── ic_qr_certificate_light.xml
│ │ │ ├── ic_qr_certificate_light_no.xml
│ │ │ ├── ic_question_outline.xml
│ │ │ ├── ic_retry.xml
│ │ │ ├── ic_scanner_alert.xml
│ │ │ ├── ic_scanner_alert_white.xml
│ │ │ ├── ic_settings.xml
│ │ │ ├── ic_t.xml
│ │ │ ├── ic_three.xml
│ │ │ ├── ic_timeerror.xml
│ │ │ ├── ic_timeerror_large.xml
│ │ │ ├── ic_timeerror_orange.xml
│ │ │ ├── ic_timelapse.xml
│ │ │ ├── ic_timelapse_blue.xml
│ │ │ ├── ic_timelapse_red.xml
│ │ │ ├── ic_travel.xml
│ │ │ ├── ic_two.xml
│ │ │ ├── ic_zoom_off.xml
│ │ │ ├── ic_zoom_off_white.xml
│ │ │ ├── ic_zoom_on.xml
│ │ │ ├── ic_zoom_on_black.xml
│ │ │ ├── illu_onboarding_data_protection.xml
│ │ │ ├── line_dashed_grey.xml
│ │ │ ├── qr_scanner_bottom_left.xml
│ │ │ ├── qr_scanner_bottom_right.xml
│ │ │ ├── qr_scanner_top_left.xml
│ │ │ ├── qr_scanner_top_right.xml
│ │ │ ├── ripple_rect.xml
│ │ │ ├── ripple_rounded.xml
│ │ │ ├── ripple_rounded_button.xml
│ │ │ ├── ripple_rounded_rect.xml
│ │ │ ├── ripple_rounded_rect_banner.xml
│ │ │ ├── ripple_rounded_rect_small.xml
│ │ │ ├── tab_selector.xml
│ │ │ └── tab_selector_white.xml
│ │ ├── font/
│ │ │ └── inter.xml
│ │ ├── layout/
│ │ │ ├── activity_onboarding.xml
│ │ │ ├── dialog_camera_permission_explanation.xml
│ │ │ ├── dialog_fragment_info_box.xml
│ │ │ ├── fragment_debug.xml
│ │ │ ├── fragment_faq.xml
│ │ │ ├── fragment_html.xml
│ │ │ ├── fragment_settings.xml
│ │ │ ├── item_error_status.xml
│ │ │ ├── item_faq_header.xml
│ │ │ ├── item_faq_intro_section.xml
│ │ │ ├── item_faq_question.xml
│ │ │ ├── item_header.xml
│ │ │ └── item_language_option.xml
│ │ ├── menu/
│ │ │ └── imprint.xml
│ │ ├── values/
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-de/
│ │ │ └── strings.xml
│ │ ├── values-fr/
│ │ │ └── strings.xml
│ │ ├── values-it/
│ │ │ └── strings.xml
│ │ ├── values-rm/
│ │ │ └── strings.xml
│ │ └── xml/
│ │ └── network_security_config.xml
│ └── prod/
│ └── java/
│ └── ch/
│ └── admin/
│ └── bag/
│ └── covidcertificate/
│ └── common/
│ └── debug/
│ └── DebugFragment.kt
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── loadConfigs.sh
├── settings.gradle
├── verifier/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ ├── src/
│ │ ├── abn/
│ │ │ └── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ ├── dev/
│ │ │ └── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ ├── java/
│ │ │ └── ch/
│ │ │ └── admin/
│ │ │ └── bag/
│ │ │ └── covidcertificate/
│ │ │ └── verifier/
│ │ │ ├── HomeFragment.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainApplication.kt
│ │ │ ├── data/
│ │ │ │ └── VerifierSecureStorage.kt
│ │ │ ├── extensions/
│ │ │ │ └── ContextExtensions.kt
│ │ │ ├── faq/
│ │ │ │ └── VerifierFaqFragment.kt
│ │ │ ├── modes/
│ │ │ │ ├── ChooseModeDialogFragment.kt
│ │ │ │ └── ModesAndConfigViewModel.kt
│ │ │ ├── news/
│ │ │ │ └── InfoCertificateNewsFragment.kt
│ │ │ ├── pager/
│ │ │ │ ├── HomescreenPageAdapter.kt
│ │ │ │ └── HomescreenPagerFragment.kt
│ │ │ ├── qr/
│ │ │ │ └── VerifierQrScanFragment.kt
│ │ │ ├── updateboarding/
│ │ │ │ ├── UpdateboardingActivity.kt
│ │ │ │ ├── UpdateboardingAgbFragment.kt
│ │ │ │ └── UpdateboardingCertificateLightFragment.kt
│ │ │ ├── verification/
│ │ │ │ ├── VerificationAdapter.kt
│ │ │ │ ├── VerificationFragment.kt
│ │ │ │ ├── VerificationItem.kt
│ │ │ │ ├── VerificationStateUtil.kt
│ │ │ │ ├── VerificationViewHolder.kt
│ │ │ │ └── VerificationViewModel.kt
│ │ │ └── zebra/
│ │ │ ├── ZebraActionBroadcastReceiver.kt
│ │ │ ├── ZebraDataWedgeApiUtil.kt
│ │ │ └── ZebraResultActionBroadcastReceiver.kt
│ │ ├── play/
│ │ │ ├── listings/
│ │ │ │ ├── de-DE/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ ├── en-US/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ ├── fr-FR/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ └── it-IT/
│ │ │ │ ├── full-description.txt
│ │ │ │ ├── short-description.txt
│ │ │ │ └── title.txt
│ │ │ └── release-notes/
│ │ │ └── en-US/
│ │ │ └── default.txt
│ │ └── res/
│ │ ├── drawable/
│ │ │ ├── bg_bubble_bottom_left.xml
│ │ │ ├── ic_faq_image.xml
│ │ │ ├── ic_header_2g_off.xml
│ │ │ ├── ic_header_2g_on.xml
│ │ │ ├── ic_header_plus_off.xml
│ │ │ ├── ic_header_plus_on.xml
│ │ │ ├── ic_illu_home_1.xml
│ │ │ ├── ic_illu_home_2.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_settings.xml
│ │ │ └── illu_updateboarding_certificate_light.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── dialog_fragment_choose_mode.xml
│ │ │ ├── dialog_fragment_info_certificate_news.xml
│ │ │ ├── fragment_home.xml
│ │ │ ├── fragment_home_screen_pager.xml
│ │ │ ├── fragment_qr_scan.xml
│ │ │ ├── fragment_updateboarding_agb.xml
│ │ │ ├── fragment_updateboarding_certificate_light.xml
│ │ │ ├── fragment_verification.xml
│ │ │ ├── item_certificate_news.xml
│ │ │ ├── item_mode_button.xml
│ │ │ ├── item_mode_info.xml
│ │ │ ├── item_progress_indicator.xml
│ │ │ ├── item_verification_header_icon.xml
│ │ │ ├── item_verification_status.xml
│ │ │ └── item_verification_status_info.xml
│ │ └── mipmap-anydpi-v26/
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ └── testKeystore
└── wallet/
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── src/
│ ├── abn/
│ │ ├── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── wallet/
│ │ └── debug/
│ │ └── WalletDebugFragment.kt
│ ├── androidTest/
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── wallet/
│ │ ├── EspressoUtil.kt
│ │ ├── LoadconfigTest.kt
│ │ ├── NestedScrollViewScrollTo.kt
│ │ ├── OnboardingTest.kt
│ │ ├── RecyclerViewNotEmptyAssertion.kt
│ │ ├── ScanCountTest.kt
│ │ ├── TransferCodeTest.kt
│ │ └── WaitUntilVisibleAction.kt
│ ├── dev/
│ │ ├── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── wallet/
│ │ └── debug/
│ │ ├── DebugCertificateItem.kt
│ │ ├── DebugCertificatesListAdapter.kt
│ │ ├── DebugCertificatesListViewHolder.kt
│ │ └── WalletDebugFragment.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ ├── java/
│ │ │ └── ch/
│ │ │ └── admin/
│ │ │ └── bag/
│ │ │ └── covidcertificate/
│ │ │ └── wallet/
│ │ │ ├── CertificatesAndConfigViewModel.kt
│ │ │ ├── DeeplinkViewModel.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainApplication.kt
│ │ │ ├── add/
│ │ │ │ └── CertificateAddFragment.kt
│ │ │ ├── data/
│ │ │ │ ├── CertificateStorage.kt
│ │ │ │ ├── WalletDataItem.kt
│ │ │ │ ├── WalletDataSecureStorage.kt
│ │ │ │ ├── WalletSecureStorage.kt
│ │ │ │ └── adapter/
│ │ │ │ └── InstantJsonAdapter.kt
│ │ │ ├── detail/
│ │ │ │ ├── CertificateDetailAdapter.kt
│ │ │ │ ├── CertificateDetailFragment.kt
│ │ │ │ ├── CertificateDetailItem.kt
│ │ │ │ ├── CertificateDetailItemListBuilder.kt
│ │ │ │ └── CertificateDetailViewHolder.kt
│ │ │ ├── dialog/
│ │ │ │ ├── CertificateBannerInfoDialogFragment.kt
│ │ │ │ ├── ModeInfoDialogFragment.kt
│ │ │ │ └── RefreshButtonInfoDialogFragment.kt
│ │ │ ├── faq/
│ │ │ │ └── WalletFaqFragment.kt
│ │ │ ├── homescreen/
│ │ │ │ ├── HomeFragment.kt
│ │ │ │ └── pager/
│ │ │ │ ├── CertificatePagerFragment.kt
│ │ │ │ ├── CertificatesPagerAdapter.kt
│ │ │ │ ├── PagerDiffUtil.kt
│ │ │ │ ├── StatefulWalletItem.kt
│ │ │ │ ├── TransferCodePagerFragment.kt
│ │ │ │ └── WalletItem.kt
│ │ │ ├── howto/
│ │ │ │ └── HowToScanFragment.kt
│ │ │ ├── light/
│ │ │ │ ├── CertificateLightConversionFragment.kt
│ │ │ │ ├── CertificateLightDetailFragment.kt
│ │ │ │ ├── CertificateLightErrorCodes.kt
│ │ │ │ ├── CertificateLightPagerFragment.kt
│ │ │ │ ├── CertificateLightViewModel.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── CertificateLightConversionResponse.kt
│ │ │ │ │ └── CertificateLightConversionState.kt
│ │ │ │ └── net/
│ │ │ │ ├── CertificateLightRepository.kt
│ │ │ │ ├── CertificateLightRequestBody.kt
│ │ │ │ ├── CertificateLightResponse.kt
│ │ │ │ └── CertificateLightService.kt
│ │ │ ├── list/
│ │ │ │ ├── CertificatesListFragment.kt
│ │ │ │ ├── CertificatesListTouchHelper.kt
│ │ │ │ ├── WalletDataListAdapter.kt
│ │ │ │ ├── WalletDataListItem.kt
│ │ │ │ └── WalletDataListViewHolder.kt
│ │ │ ├── networking/
│ │ │ │ └── interceptor/
│ │ │ │ └── AcceptLanguageHeaderInterceptor.kt
│ │ │ ├── onboarding/
│ │ │ │ ├── OnboardingActivity.kt
│ │ │ │ ├── OnboardingAgbFragment.kt
│ │ │ │ ├── OnboardingContentFragment.kt
│ │ │ │ ├── OnboardingIntroFragment.kt
│ │ │ │ ├── OnboardingPreInfoFragment.kt
│ │ │ │ ├── agbupdate/
│ │ │ │ │ └── UpdateboardingAgbFragment.kt
│ │ │ │ ├── certificatelight/
│ │ │ │ │ └── UpdateboardingCertificateLightFragment.kt
│ │ │ │ └── validity/
│ │ │ │ ├── UpdateboardingValidity1Fragment.kt
│ │ │ │ ├── UpdateboardingValidity2Fragment.kt
│ │ │ │ ├── UpdateboardingValidity3Fragment.kt
│ │ │ │ └── UpdateboardingValidity4Fragment.kt
│ │ │ ├── pdf/
│ │ │ │ ├── PdfViewModel.kt
│ │ │ │ ├── export/
│ │ │ │ │ ├── PdfExportFragment.kt
│ │ │ │ │ ├── PdfExportShareContract.kt
│ │ │ │ │ └── PdfExportState.kt
│ │ │ │ └── net/
│ │ │ │ ├── PdfExportRepository.kt
│ │ │ │ ├── PdfExportRequestBody.kt
│ │ │ │ ├── PdfExportResponse.kt
│ │ │ │ └── PdfExportService.kt
│ │ │ ├── qr/
│ │ │ │ ├── VerifierInfoDialogFragment.kt
│ │ │ │ └── WalletQrScanFragment.kt
│ │ │ ├── ratconversion/
│ │ │ │ └── RatConversionFragment.kt
│ │ │ ├── renewal/
│ │ │ │ ├── QrCodeRenewalErrorCodes.kt
│ │ │ │ ├── QrCodeRenewalFragment.kt
│ │ │ │ ├── QrCodeRenewalViewModel.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── QrCodeRenewalResponse.kt
│ │ │ │ │ └── QrCodeRenewalViewState.kt
│ │ │ │ └── net/
│ │ │ │ ├── QrCodeRenewalBody.kt
│ │ │ │ ├── QrCodeRenewalRepository.kt
│ │ │ │ └── QrCodeRenewalService.kt
│ │ │ ├── transfercode/
│ │ │ │ ├── TransferCodeCreationFragment.kt
│ │ │ │ ├── TransferCodeCreationViewModel.kt
│ │ │ │ ├── TransferCodeDetailFragment.kt
│ │ │ │ ├── TransferCodeErrorCodes.kt
│ │ │ │ ├── TransferCodeHowToFragment.kt
│ │ │ │ ├── TransferCodeIntroFragment.kt
│ │ │ │ ├── TransferCodeViewModel.kt
│ │ │ │ ├── logic/
│ │ │ │ │ ├── Luhn.kt
│ │ │ │ │ └── TransferCodeCrypto.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── ConvertedCertificate.kt
│ │ │ │ │ ├── TransferCodeConversionState.kt
│ │ │ │ │ ├── TransferCodeCreationResponse.kt
│ │ │ │ │ ├── TransferCodeCreationState.kt
│ │ │ │ │ └── TransferCodeModel.kt
│ │ │ │ ├── net/
│ │ │ │ │ ├── CovidCert.kt
│ │ │ │ │ ├── CovidCertDelivery.kt
│ │ │ │ │ ├── DeliveryRegistration.kt
│ │ │ │ │ ├── DeliveryRepository.kt
│ │ │ │ │ ├── DeliveryService.kt
│ │ │ │ │ └── RequestDeliveryPayload.kt
│ │ │ │ ├── view/
│ │ │ │ │ ├── TransferCodeBubbleView.kt
│ │ │ │ │ ├── TransferCodeView.kt
│ │ │ │ │ └── TransferCodeWaitingView.kt
│ │ │ │ └── worker/
│ │ │ │ └── TransferWorker.kt
│ │ │ ├── travel/
│ │ │ │ ├── ForeignValidityFragment.kt
│ │ │ │ ├── ForeignValidityViewModel.kt
│ │ │ │ └── ForeignValidityViewState.kt
│ │ │ ├── util/
│ │ │ │ ├── BitmapUtil.kt
│ │ │ │ ├── ModeValidityStateUtil.kt
│ │ │ │ ├── NotificationUtil.kt
│ │ │ │ ├── QrCode.kt
│ │ │ │ └── VerificationStateUtil.kt
│ │ │ └── vaccination/
│ │ │ ├── appointment/
│ │ │ │ └── VaccinationAppointmentFragment.kt
│ │ │ └── hint/
│ │ │ └── VaccinationHintViewModel.kt
│ │ ├── play/
│ │ │ ├── listings/
│ │ │ │ ├── de-DE/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ ├── en-US/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ ├── fr-FR/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ └── it-IT/
│ │ │ │ ├── full-description.txt
│ │ │ │ ├── short-description.txt
│ │ │ │ └── title.txt
│ │ │ └── release-notes/
│ │ │ └── en-US/
│ │ │ └── default.txt
│ │ └── res/
│ │ ├── color/
│ │ │ └── text_color_button.xml
│ │ ├── drawable/
│ │ │ ├── bg_add_certificate_option.xml
│ │ │ ├── bg_border.xml
│ │ │ ├── bg_bordered_button.xml
│ │ │ ├── bg_bubble_bottom_left.xml
│ │ │ ├── bg_certificate_bottom.xml
│ │ │ ├── bg_certificate_bubble.xml
│ │ │ ├── bg_certificate_bubble_bundesrot.xml
│ │ │ ├── bg_certificate_bubble_ripple.xml
│ │ │ ├── bg_certificate_detail_note.xml
│ │ │ ├── bg_certificate_top.xml
│ │ │ ├── bg_info_banner.xml
│ │ │ ├── bg_type_bubble_small.xml
│ │ │ ├── circle_white.xml
│ │ │ ├── cutout_left.xml
│ │ │ ├── cutout_right.xml
│ │ │ ├── ic_add_certificate.xml
│ │ │ ├── ic_arrow_right.xml
│ │ │ ├── ic_calendar.xml
│ │ │ ├── ic_certificate_light.xml
│ │ │ ├── ic_check_mark.xml
│ │ │ ├── ic_cloud.xml
│ │ │ ├── ic_corner_offline.xml
│ │ │ ├── ic_corner_process_error.xml
│ │ │ ├── ic_covid_check_app.xml
│ │ │ ├── ic_data_protection.xml
│ │ │ ├── ic_drag.xml
│ │ │ ├── ic_exchange.xml
│ │ │ ├── ic_expire_1.xml
│ │ │ ├── ic_expire_2.xml
│ │ │ ├── ic_expire_3.xml
│ │ │ ├── ic_expire_4.xml
│ │ │ ├── ic_expire_5.xml
│ │ │ ├── ic_expire_6.xml
│ │ │ ├── ic_expire_7.xml
│ │ │ ├── ic_faq_image.xml
│ │ │ ├── ic_flag_ch.xml
│ │ │ ├── ic_header_slim.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_launcher_foreground.xml
│ │ │ ├── ic_list.xml
│ │ │ ├── ic_online.xml
│ │ │ ├── ic_pdf.xml
│ │ │ ├── ic_pen_write.xml
│ │ │ ├── ic_qr_certificate.xml
│ │ │ ├── ic_qrcode.xml
│ │ │ ├── ic_qrcode_add.xml
│ │ │ ├── ic_qrcode_icon_placeholder.xml
│ │ │ ├── ic_qrcode_scan.xml
│ │ │ ├── ic_scan_code.xml
│ │ │ ├── ic_transfer_code_list_failed.xml
│ │ │ ├── ic_transfer_code_list_valid.xml
│ │ │ ├── ic_transfer_notification.xml
│ │ │ ├── ic_validation.xml
│ │ │ ├── icon_ag.xml
│ │ │ ├── icon_ai.xml
│ │ │ ├── icon_ar.xml
│ │ │ ├── icon_be.xml
│ │ │ ├── icon_bl.xml
│ │ │ ├── icon_bs.xml
│ │ │ ├── icon_fr.xml
│ │ │ ├── icon_ge.xml
│ │ │ ├── icon_gl.xml
│ │ │ ├── icon_gr.xml
│ │ │ ├── icon_ju.xml
│ │ │ ├── icon_lu.xml
│ │ │ ├── icon_ne.xml
│ │ │ ├── icon_nw.xml
│ │ │ ├── icon_ow.xml
│ │ │ ├── icon_sg.xml
│ │ │ ├── icon_sh.xml
│ │ │ ├── icon_so.xml
│ │ │ ├── icon_sz.xml
│ │ │ ├── icon_tg.xml
│ │ │ ├── icon_ti.xml
│ │ │ ├── icon_ur.xml
│ │ │ ├── icon_vd.xml
│ │ │ ├── icon_vs.xml
│ │ │ ├── icon_zg.xml
│ │ │ ├── icon_zh.xml
│ │ │ ├── illu_add_certificate.xml
│ │ │ ├── illu_faq_transfer_code.xml
│ │ │ ├── illu_home_empty_state.xml
│ │ │ ├── illu_how_it_works.xml
│ │ │ ├── illu_how_to_scan.xml
│ │ │ ├── illu_onboarding_covid_certificate.xml
│ │ │ ├── illu_onboarding_hero.xml
│ │ │ ├── illu_onboarding_privacy.xml
│ │ │ ├── illu_transfer_code_failed.xml
│ │ │ ├── illu_transfer_code_intro.xml
│ │ │ ├── illu_transfer_code_waiting_phone.xml
│ │ │ ├── illu_transfer_code_waiting_ripple.xml
│ │ │ ├── illu_transfer_code_waiting_shadow.xml
│ │ │ ├── illu_updateboarding_certificate_light.xml
│ │ │ └── oval_horizontal.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── dialog_fragment_certificate_banner_info.xml
│ │ │ ├── dialog_fragment_mode_info.xml
│ │ │ ├── dialog_fragment_refresh_button_info.xml
│ │ │ ├── fragment_certificate_add.xml
│ │ │ ├── fragment_certificate_detail.xml
│ │ │ ├── fragment_certificate_light_conversion.xml
│ │ │ ├── fragment_certificate_light_detail.xml
│ │ │ ├── fragment_certificate_light_pager.xml
│ │ │ ├── fragment_certificate_pager.xml
│ │ │ ├── fragment_certificates_list.xml
│ │ │ ├── fragment_foreign_validity.xml
│ │ │ ├── fragment_home.xml
│ │ │ ├── fragment_how_to_scan.xml
│ │ │ ├── fragment_onboarding_agb.xml
│ │ │ ├── fragment_onboarding_content.xml
│ │ │ ├── fragment_onboarding_intro.xml
│ │ │ ├── fragment_onboarding_pre_info.xml
│ │ │ ├── fragment_pdf_export.xml
│ │ │ ├── fragment_qr_code_renewal.xml
│ │ │ ├── fragment_qr_scan.xml
│ │ │ ├── fragment_rat_conversion.xml
│ │ │ ├── fragment_transfer_code_creation.xml
│ │ │ ├── fragment_transfer_code_detail.xml
│ │ │ ├── fragment_transfer_code_howto.xml
│ │ │ ├── fragment_transfer_code_intro.xml
│ │ │ ├── fragment_transfer_code_pager.xml
│ │ │ ├── fragment_updateboarding_agb.xml
│ │ │ ├── fragment_updateboarding_certificate_light.xml
│ │ │ ├── fragment_updateboarding_validity_1.xml
│ │ │ ├── fragment_updateboarding_validity_2.xml
│ │ │ ├── fragment_updateboarding_validity_3.xml
│ │ │ ├── fragment_updateboarding_validity_4.xml
│ │ │ ├── fragment_vaccination_appointment.xml
│ │ │ ├── item_certificate_list.xml
│ │ │ ├── item_debug_certificate_list.xml
│ │ │ ├── item_detail_divider.xml
│ │ │ ├── item_detail_mode.xml
│ │ │ ├── item_detail_mode_refresh.xml
│ │ │ ├── item_detail_modes_list.xml
│ │ │ ├── item_detail_title.xml
│ │ │ ├── item_detail_value.xml
│ │ │ ├── item_detail_value_without_label.xml
│ │ │ ├── item_foreign_rules_check_hint.xml
│ │ │ ├── item_header_not_empty.xml
│ │ │ ├── item_icon_text_info.xml
│ │ │ ├── item_mode_list_info.xml
│ │ │ ├── item_transfer_code_list.xml
│ │ │ ├── item_vaccination_appointment_canton.xml
│ │ │ ├── partial_certificate_detail_banners.xml
│ │ │ ├── partial_home_add_certificate_options.xml
│ │ │ ├── view_transfer_code.xml
│ │ │ ├── view_transfer_code_bubble.xml
│ │ │ └── view_transfer_code_waiting.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── xml/
│ │ └── filepaths.xml
│ ├── prod/
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── wallet/
│ │ └── debug/
│ │ └── WalletDebugFragment.kt
│ └── test/
│ └── java/
│ └── ch/
│ └── admin/
│ └── bag/
│ └── covidcertificate/
│ └── wallet/
│ └── transfercode/
│ ├── LuhnTest.kt
│ └── LuhnTestWrongCodes.kt
└── testKeystore
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/actions/gradle_docker/action.yml
================================================
name: 'GradleDocker'
description: 'Run gradle inside Docker image'
inputs:
gradle-cmd: # id of input
description: 'gradleCmd to run'
required: true
runs:
using: 'docker'
image: '../../../Dockerfile'
entrypoint: '.github/actions/gradle_docker/main.sh'
args:
- ${{ inputs.gradle-cmd }}
================================================
FILE: .github/actions/gradle_docker/main.sh
================================================
#!/bin/sh
cd $GITHUB_WORKSPACE
gradle $1
================================================
FILE: .github/workflows/appcenter_verifier_abn.yml
================================================
name: Build Verifier ABN
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: verifier:assembleAbnRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_VERIFIER_APP_ABN}}
token: ${{secrets.APPCENTER_API_TOKEN}}
group: public
file: verifier/build/outputs/apk/abn/release/verifier-abn-release.apk
================================================
FILE: .github/workflows/appcenter_verifier_dev.yml
================================================
name: Build Verifier DEV
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: verifier:assembleDevRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_VERIFIER_APP_DEV}}
token: ${{secrets.APPCENTER_API_TOKEN}}
group: public
file: verifier/build/outputs/apk/dev/release/verifier-dev-release.apk
================================================
FILE: .github/workflows/appcenter_verifier_prod.yml
================================================
name: Build Verifier PROD
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: verifier:assembleProdRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_VERIFIER_APP}}
token: ${{secrets.APPCENTER_API_TOKEN}}
group: public
file: verifier/build/outputs/apk/prod/release/verifier-prod-release.apk
- name: Upload APK
uses: actions/upload-artifact@v1.0.0
with:
name: verifier.apk
path: verifier/build/outputs/apk/prod/release/verifier-prod-release.apk
================================================
FILE: .github/workflows/appcenter_verifier_prodfdroid.yml
================================================
name: Build Verifier PRODFDROID
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: verifier:assembleProdRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}} -PminSdkVersion=24
- name: upload artefact to App Center
uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_VERIFIER_APP}}
token: ${{secrets.APPCENTER_API_TOKEN}}
group: public
file: verifier/build/outputs/apk/prod/release/verifier-prod-release.apk
- name: Upload APK
uses: actions/upload-artifact@v1.0.0
with:
name: verifier.apk
path: verifier/build/outputs/apk/prod/release/verifier-prod-release.apk
================================================
FILE: .github/workflows/appcenter_wallet_abn.yml
================================================
name: Build Wallet ABN
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: wallet:assembleAbnRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_WALLET_APP_ABN}}
token: ${{secrets.APPCENTER_API_TOKEN}}
group: public
file: wallet/build/outputs/apk/abn/release/wallet-abn-release.apk
================================================
FILE: .github/workflows/appcenter_wallet_dev.yml
================================================
name: Build Wallet DEV
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: wallet:assembleDevRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_WALLET_APP_DEV}}
token: ${{secrets.APPCENTER_API_TOKEN}}
group: public
file: wallet/build/outputs/apk/dev/release/wallet-dev-release.apk
================================================
FILE: .github/workflows/appcenter_wallet_prod.yml
================================================
name: Build Wallet PROD
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: wallet:assembleProdRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_WALLET_APP}}
token: ${{secrets.APPCENTER_API_TOKEN}}
group: public
file: wallet/build/outputs/apk/prod/release/wallet-prod-release.apk
- name: Upload APK
uses: actions/upload-artifact@v1.0.0
with:
name: wallet.apk
path: wallet/build/outputs/apk/prod/release/wallet-prod-release.apk
================================================
FILE: .github/workflows/appcenter_wallet_prodfdroid.yml
================================================
name: Build Wallet PRODFDROID
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: wallet:assembleProdRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}} -PminSdkVersion=24
- name: upload artefact to App Center
uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_WALLET_APP}}
token: ${{secrets.APPCENTER_API_TOKEN}}
group: public
file: wallet/build/outputs/apk/prod/release/wallet-prod-release.apk
- name: Upload APK
uses: actions/upload-artifact@v1.0.0
with:
name: wallet.apk
path: wallet/build/outputs/apk/prod/release/wallet-prod-release.apk
================================================
FILE: .github/workflows/browserstack_wallet_abn.yml
================================================
name: Build Browserstack UI-Test Wallet
on:
push:
branches:
- main
- 'release/**'
pull_request:
branches:
- main
- 'release/**'
jobs:
build-app-and-upload:
runs-on: ubuntu-latest
outputs:
app-url: ${{ steps.upload-app.outputs.app-url }}
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: wallet:assembleAbnRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to Browserstack
run: curl -u ${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESSKEY }} -X Post "https://api-cloud.browserstack.com/app-automate/espresso/v2/app" -F "file=@wallet/build/outputs/apk/abn/release/wallet-abn-release.apk" >> appResponse.json
- id: upload-app
run: echo ::set-output name=app-url::$(jq -r '.app_url' appResponse.json)
build-test-suite-and-upload:
runs-on: ubuntu-latest
outputs:
test-url: ${{ steps.upload-test-suite.outputs.test-url }}
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build with Gradle
uses: ./.github/actions/gradle_docker
with:
gradle-cmd: wallet:assembleAbnDebugAndroidTest -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to Browserstack
run: curl -u ${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESSKEY }} -X Post "https://api-cloud.browserstack.com/app-automate/espresso/v2/test-suite" -F "file=@wallet/build/outputs/apk/androidTest/abn/debug/wallet-abn-debug-androidTest.apk" >> testSuiteResponse.json
- id: upload-test-suite
run: echo ::set-output name=test-url::$(jq -r '.test_suite_url' testSuiteResponse.json)
run-tests:
runs-on: ubuntu-latest
needs: [build-app-and-upload, build-test-suite-and-upload]
steps:
- name: run normal tests
run: curl -u ${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESSKEY }} -X Post "https://api-cloud.browserstack.com/app-automate/espresso/v2/build" -d '{"app":"${{ needs.build-app-and-upload.outputs.app-url }}", "testSuite":"${{ needs.build-test-suite-and-upload.outputs.test-url }}", "devices":["Samsung Galaxy S10e-9.0"] , "language":"de", "annotation":["ch.admin.bag.covidcertificate.common.browserstack.Normal"] }' -H "Content-Type:application/json"
- name: run airplane tests
run: curl -u ${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESSKEY }} -X Post "https://api-cloud.browserstack.com/app-automate/espresso/v2/build" -d '{"app":"${{ needs.build-app-and-upload.outputs.app-url }}", "testSuite":"${{ needs.build-test-suite-and-upload.outputs.test-url }}", "devices":["Samsung Galaxy S10e-9.0"] , "networkProfile":"airplane-mode", "annotation":["ch.admin.bag.covidcertificate.common.browserstack.AirplaneMode"] }' -H "Content-Type:application/json"
- name: run bad network tests
run: curl -u ${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESSKEY }} -X Post "https://api-cloud.browserstack.com/app-automate/espresso/v2/build" -d '{"app":"${{ needs.build-app-and-upload.outputs.app-url }}", "testSuite":"${{ needs.build-test-suite-and-upload.outputs.test-url }}", "devices":["Samsung Galaxy S10e-9.0"] , "networkProfile":"2g-gprs-lossy", "annotation":["ch.admin.bag.covidcertificate.common.browserstack.BadNetwork"] }' -H "Content-Type:application/json"
- name: run onboarding test
run: curl -u ${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESSKEY }} -X Post "https://api-cloud.browserstack.com/app-automate/espresso/v2/build" -d '{"app":"${{ needs.build-app-and-upload.outputs.app-url }}", "testSuite":"${{ needs.build-test-suite-and-upload.outputs.test-url }}", "devices":["Samsung Galaxy S10e-9.0"] , "language":"de", "annotation":["ch.admin.bag.covidcertificate.common.browserstack.Onboarding"] }' -H "Content-Type:application/json"
- name: run onboarding test offline
run: curl -u ${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESSKEY }} -X Post "https://api-cloud.browserstack.com/app-automate/espresso/v2/build" -d '{"app":"${{ needs.build-app-and-upload.outputs.app-url }}", "testSuite":"${{ needs.build-test-suite-and-upload.outputs.test-url }}", "devices":["Samsung Galaxy S10e-9.0"] , "language":"de", "networkProfile":"airplane-mode", "annotation":["ch.admin.bag.covidcertificate.common.browserstack.Onboarding"] }' -H "Content-Type:application/json"
================================================
FILE: .github/workflows/build.yml
================================================
name: Build
on:
push:
branches:
- main
- 'release/**'
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Cache SonarCloud packages
uses: actions/cache@v1
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Gradle packages
uses: actions/cache@v1
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: ${{ runner.os }}-gradle
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: ./gradlew sonarqube
================================================
FILE: .github/workflows/gradle-wrapper-validation.yml
================================================
name: Validate Gradle Wrapper
on: [push, pull_request]
jobs:
validation:
name: Validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- uses: gradle/wrapper-validation-action@84d7e182ae7c7a37f200c184f64038fb0e62dd7d
================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.aar
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
.idea/misc.xml
.idea/
.DS_Store
================================================
FILE: Dockerfile
================================================
FROM gradle:7.2-jdk11
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip
ENV ANDROID_BUILD_TOOLS_VERSION 30.0.1
ENV ANDROID_HOME /usr/local/android-sdk-linux
ENV ANDROID_VERSION 30
ENV PATH ${PATH}:${ANDROID_HOME}/cmdline-tools/bin:${ANDROID_HOME}/platform-tools
RUN mkdir "$ANDROID_HOME" .android && \
cd "$ANDROID_HOME" && \
curl -o sdk.zip $ANDROID_SDK_URL && \
unzip sdk.zip && \
rm sdk.zip
RUN yes | ${ANDROID_HOME}/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_HOME --licenses
RUN $ANDROID_HOME/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_HOME --update
RUN $ANDROID_HOME/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_HOME "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \
"platforms;android-${ANDROID_VERSION}" \
"platform-tools"
RUN apt-get update
RUN apt-get install -y imagemagick
RUN convert -version
================================================
FILE: LICENSE
================================================
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
================================================
FILE: README.md
================================================
# COVID Certificate Apps - Android
[](https://github.com/admin-ch/CovidCertificate-App-Android/blob/main/LICENSE)


This project is released by the the [Federal Office of Information Technology, Systems and Telecommunication FOITT](https://www.bit.admin.ch/)
on behalf of the [Federal Office of Public Health FOPH](https://www.bag.admin.ch/).
The app design, UX and implementation was done by [Ubique](https://www.ubique.ch?app=github).
## COVID Certificate App
COVID Certificate is the official app for storing and presenting COVID certificates issued in Switzerland.
The certificates are kept and checked locally on the user's phone.
<p align="verticalcenter">
<a href='https://play.google.com/store/apps/details?id=ch.admin.bag.covidcertificate.wallet'>
<img alt='Get it on Google Play' src='badges/googleplay.png' width="30%"/>
</a>
<a href='https://f-droid.org/packages/ch.admin.bag.covidcertificate.wallet'>
<img alt='Get it on F-Droid' src='badges/fdroid.png' width="30%"/>
</a>
<a href='https://appgallery.huawei.com/#/app/C104434571'>
<img alt='Explore it on AppGallery' src='badges/appgallery.png' width="30%"/>
</a>
</p>
<p align="center">
<img src="wallet/src/main/play/listings/en-US/graphics/phone-screenshots/EN_01.png" width="15%">
<img src="wallet/src/main/play/listings/en-US/graphics/phone-screenshots/EN_02.png" width="15%">
<img src="wallet/src/main/play/listings/en-US/graphics/phone-screenshots/EN_03.png" width="15%">
<img src="wallet/src/main/play/listings/en-US/graphics/phone-screenshots/EN_04.png" width="15%">
<img src="wallet/src/main/play/listings/en-US/graphics/phone-screenshots/EN_05.png" width="15%">
</p>
## COVID Certificate Check App
COVID Certificate Check is the official app for checking COVID certificates in Switzerland.
The validation is executed locally on the phone and no information of the scanned certificates is saved.
<p align="verticalcenter">
<a href='https://play.google.com/store/apps/details?id=ch.admin.bag.covidcertificate.verifier'>
<img alt='Get it on Google Play' src='badges/googleplay.png' width="30%"/>
</a>
<a href='https://f-droid.org/packages/ch.admin.bag.covidcertificate.verifier'>
<img alt='Get it on F-Droid' src='badges/fdroid.png' width="30%"/>
</a>
<a href='https://appgallery.huawei.com/#/app/C104435637'>
<img alt='Explore it on AppGallery' src='badges/appgallery.png' width="30%"/>
</a>
</p>
<p align="center">
<img src="verifier/src/main/play/listings/en-US/graphics/phone-screenshots/EN_01.png" width="20%">
<img src="verifier/src/main/play/listings/en-US/graphics/phone-screenshots/EN_02.png" width="20%">
<img src="verifier/src/main/play/listings/en-US/graphics/phone-screenshots/EN_03.png" width="20%">
</p>
## Contribution Guide
This project is truly open-source and we welcome any feedback on the code regarding both the implementation and security aspects.
Bugs or potential problems should be reported using Github issues.
We welcome all pull requests that improve the quality of the source code.
Please note that the app will be available with approved translations in English, German, French, Italian, Rumantsch.
## Repositories
* Android App: [CovidCertificate-App-Android](https://github.com/admin-ch/CovidCertificate-App-Android)
* Android SDK: [CovidCertificate-SDK-Android](https://github.com/admin-ch/CovidCertificate-SDK-Android)
* iOS App: [CovidCertificate-App-iOS](https://github.com/admin-ch/CovidCertificate-App-iOS)
* For all others, see the [Github organisation](https://github.com/admin-ch/)
## Installation and Building
The apps require at least Android 6 (Marshmallow).
To build the project you need at least Java 11 and Android Studio 2020.3.1.
You can also build the apps directly:
```sh
$ ./gradlew verifier:assembleProdRelease
$ ./gradlew wallet:assembleProdRelease
```
Note that in order for that to work, you must have set up your own keystore.
The APK is generated under `app/build/outputs/apk/prod/release/app-prod-release.apk` where `app` is one of: `verifier`, `wallet`.
## Reproducible builds
To verify that the app distributed on the Play Store was built by the source code published here, please see the instructions
in [REPRODUCIBLE_BUILDS.md](REPRODUCIBLE_BUILDS.md).
## License
This project is licensed under the terms of the MPL 2 license. See the [LICENSE](LICENSE) file for details.
================================================
FILE: REPRODUCIBLE_BUILDS.md
================================================
# Reproducible Builds
This document outlines how you can reproduce the Android app.
The instructions below are for the wallet app.
To reproduce the verifier app, change **all** occurences of `wallet` to `verifier`.
## Prerequisites
1. Make sure you have both [Docker](https://www.docker.com/) and `git` installed.
2. Clone the repository
3. Checkout the tag (or branch or commit) that corresponds to the version of your app (e.g., 1.0.0)
```shell
git clone https://github.com/admin-ch/CovidCertificate-App-Android.git ~/CovidCertificate-App-Android
cd ~/CovidCertificate-App-Android
git tag # List all available tags
git checkout v2.7.0-2700-wallet
```
## Verifying the app
### Step 1: Check your app version and build timestamp
1. Open the app
2. Click on the `i` button in the top-right corner
3. Check the app version in the top right corner
4. Check the build timestamp in the bottom right corner, which is the number before the slash (e.g., 1622186583268), and record its value to be used later
### Step 2: Extract the APK from your device
1. Make sure you have `adb` installed
2. Connect your phone to your computer
3. Extract the APK from the phone:
```shell
adb pull `adb shell pm path ch.admin.bag.covidcertificate.wallet | cut -d':' -f2` wallet-store.apk
```
If you want to check the version of the APK you are pulling from your device:
```shell
adb shell dumpsys package ch.admin.bag.covidcertificate.wallet | grep versionName=| cut -d '=' -f 2
```
### Step 3: Reproduce it
TLDR: Run the script and follow its instructions:
```shell
./buildAndCompare.sh wallet-store.apk
```
The script will do the following:
1. Build a Docker image with the required Android tools
2. (Optionally) Generate a dummy key store for signing
3. Build the app from source in the Docker container
4. Compare the APK pulled from your phone with the APK built from source
To manually compare to files you can run:
```shell
python3 apkdiff.py wallet-built.apk wallet-store.apk
```
================================================
FILE: apkdiff.py
================================================
# Taken from https://github.com/DrKLO/Telegram/blob/master/apkdiff.py on June 4th, 2020
import sys
from zipfile import ZipFile
def compareFiles(first, second):
while True:
firstBytes = first.read(4096);
secondBytes = second.read(4096);
if firstBytes != secondBytes:
return False
if firstBytes == b"":
break
return True
def compare(first, second):
FILES_TO_IGNORE = ["META-INF/MANIFEST.MF", "META-INF/CERT.RSA", "META-INF/CERT.SF"]
firstZip = ZipFile(first, 'r')
secondZip = ZipFile(second, 'r')
firstList = list(filter(lambda firstInfo: firstInfo.filename not in FILES_TO_IGNORE, firstZip.infolist()))
secondList = list(filter(lambda secondInfo: secondInfo.filename not in FILES_TO_IGNORE, secondZip.infolist()))
apksAreTheSame = True
if len(firstList) != len(secondList):
print("APKs has different amount of files (%d != %d)" % (len(firstList), len(secondList)))
apksAreTheSame = False
for firstInfo in firstList:
found = False
for secondInfo in secondList:
if firstInfo.filename == secondInfo.filename:
found = True
firstFile = firstZip.open(firstInfo, 'r')
secondFile = secondZip.open(secondInfo, 'r')
if compareFiles(firstFile, secondFile) != True:
print("APK file %s does not match" % firstInfo.filename)
apksAreTheSame = False
secondList.remove(secondInfo)
break
if found == False:
print("file %s not found in second APK" % firstInfo.filename)
apksAreTheSame = False
if len(secondList) != 0:
for secondInfo in secondList:
print("file %s not found in first APK" % secondInfo.filename)
apksAreTheSame = False
return apksAreTheSame
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: apkdiff <pathToFirstApk> <pathToSecondApk>")
sys.exit(1)
if sys.argv[1] == sys.argv[2] or compare(sys.argv[1], sys.argv[2]) == True:
print("APKs are the same!")
else:
print("APKs are different!")
================================================
FILE: build.gradle
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.6.10"
repositories {
google()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'com.android.tools:r8:3.0.73'
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath 'ch.ubique.gradle:ubdiag-android:7.0.2'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: buildAndCompare.sh
================================================
#!/bin/bash
# Script to automate the building and comparing of the CovidCertificate apps
#
# The first and only argument should be the path to the APK to be compared.
set -eu
if [[ $# -ne 1 ]]; then
echo "Pass the path/to/store.apk as an argument!"
exit 1
fi
referenceApk=$1
echo "Which app would you like to build ('wallet' or 'verifier')?"
read appName
echo "Do you want to provide a keystore [Yn]?"
read willProvideKeystore
case "$willProvideKeystore" in
# Case 1: Autogenerate a dummy keystore
[nN][oO]|[nN])
echo "[WARNING] Auto-generating a dummy keystore with default credentials. Do NOT use the resulting APK!"
rm -f "$appName"/insecure.keystore
# Generate a keystore with default credentials that is only valid 1 day
keytool -genkeypair -storepass password -keypass password -alias keyAlias -keyalg RSA -keystore "$appName"/insecure.keystore -dname 'CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown' -validity 1
keystoreFile="$appName"/insecure.keystore
keystorePassword=password
keyAlias=keyAlias
keyAliasPassword=password
;;
# Case 2: Let the user choose a keystore
*)
echo "Please enter the keystore filename (e.g. wallet/build.keystore):"
read keystoreFile
echo "Please enter the keystore password:"
read -s keystorePassword
echo "Please enter the keyAlias:"
read keyAlias
echo "Please enter the keyAlias password:"
read -s keyAliasPassword
;;
esac
echo "Please enter the build timestamp (e.g. 1622186583268):"
read buildTimestamp
# This is necessary because Ubique's gradle plugin will automatically set the branch.
# Here we want to override it in order to reproduce the build.
echo "Please enter the branch off which the release was build (e.g. release/version-1.0.0):"
read buildBranch
echo "Please enter the git tag (e.g. v2.7.0-2700-wallet) or branch (e.g. release/version-1.0.0) to be reproduced."
echo "This is what will be checked out and reproduced."
read tree
echo "Do you want to build the F-Droid version [yN]?"
read fdroid
# Set gradle task to be run
case "$fdroid" in
[yY][eE][sS]|[yY])
overrideMinSdk="-PminSdkVersion=24"
;;
*)
overrideMinSdk=""
;;
esac
echo "Building apk from source..."
# Clean up any existing images
docker images -a | grep "covidcertificate-builder" | awk '{print $3}' | xargs -r docker rmi
# Build a fresh container image
docker build -t covidcertificate-builder .
# Prepare the build command (for readability)
currentPath=`pwd`
buildCommand=$(cat <<EOF
git clone https://github.com/admin-ch/CovidCertificate-App-Android.git;
cd CovidCertificate-App-Android;
git checkout $tree;
gradle $appName:assembleProdRelease -PkeystorePassword='$keystorePassword' -PkeyAlias=$keyAlias -PkeyAliasPassword='$keyAliasPassword' -PkeystoreFile=/home/covidcertificate/external/$keystoreFile -PbuildTimestamp=$buildTimestamp -Pbranch=$buildBranch $overrideMinSdk;
cp $appName/build/outputs/apk/prod/release/$appName-prod-release.apk /home/covidcertificate/external/$appName-built.apk
EOF
)
buildCommand=$(echo -n "$buildCommand" | tr '\n' ' ' ) # replace newlines by spaces
# Build the app in the container
docker run --rm -v "$currentPath":/home/covidcertificate/external -w /home/covidcertificate covidcertificate-builder /bin/bash -c -eux "$buildCommand"
# Remove dummy keystore again
rm -f "$appName"/insecure.keystore
echo "Comparing the APK built from source with the reference APK..."
python3 apkdiff.py $appName-built.apk $referenceApk
================================================
FILE: common/.gitignore
================================================
/build
================================================
FILE: common/build.gradle
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'com.google.devtools.ksp' version '1.6.10-1.0.4'
}
ext.readPropertyWithDefault = { paramName, defaultValue ->
if (project.hasProperty(paramName)) {
return project.getProperties().get(paramName)
} else {
Properties properties = new Properties()
if (project.rootProject.file('local.properties').exists()) {
properties.load(project.rootProject.file('local.properties').newDataInputStream())
}
if (properties.getProperty(paramName) != null) {
return properties.getProperty(paramName)
} else {
return defaultValue
}
}
}
android {
compileSdkVersion 31
defaultConfig {
minSdkVersion readPropertyWithDefault('minSdkVersion', '23').toInteger()
targetSdkVersion 31
// Stops the Gradle plugin’s automatic rasterization of vectors
generatedDensities = []
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
productFlavors {
dev {}
abn {}
prod {}
}
flavorDimensions "version"
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
api 'ch.admin.bag.covidcertificate:sdk-android:2.3.3'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
api 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.security:security-crypto:1.0.0'
ksp 'com.squareup.moshi:moshi-kotlin-codegen:1.13.0'
// ZXing base QR code scanner
api 'ch.ubique.android:qrscanner-zxing:1.0.0-dev-6'
api 'com.augustcellars.cose:cose-java:1.1.0'
api 'org.bouncycastle:bcprov-jdk15on:1.69'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
================================================
FILE: common/consumer-rules.pro
================================================
================================================
FILE: common/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: common/src/abn/java/ch/admin/bag/covidcertificate/common/debug/DebugFragment.kt
================================================
package ch.admin.bag.covidcertificate.common.debug
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
import android.content.Context
import androidx.fragment.app.Fragment
open class DebugFragment : Fragment() {
companion object {
fun newInstance(): DebugFragment = DebugFragment()
const val EXISTS = false
fun initDebug(context: Context) {}
}
}
================================================
FILE: common/src/dev/java/ch/admin/bag/covidcertificate/common/debug/DebugFragment.kt
================================================
package ch.admin.bag.covidcertificate.common.debug
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import ch.admin.bag.covidcertificate.common.databinding.FragmentDebugBinding
import ch.admin.bag.covidcertificate.sdk.android.net.CertificatePinning
open class DebugFragment : Fragment() {
companion object {
fun newInstance(): DebugFragment = DebugFragment()
const val EXISTS = true
fun initDebug(context: Context) {
CertificatePinning.enabled = DebugSecureStorage.getInstance(context).isCertPinningEnabled
}
}
private var _binding: FragmentDebugBinding? = null
protected val binding get() = _binding!!
private lateinit var debugSecureStorage: DebugSecureStorage
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
debugSecureStorage = DebugSecureStorage.getInstance(requireContext())
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentDebugBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.toolbar.setNavigationOnClickListener {
parentFragmentManager.popBackStack()
}
binding.buttonToggleCertificatePinning.apply {
isChecked = debugSecureStorage.isCertPinningEnabled
setOnCheckedChangeListener { _, isChecked ->
debugSecureStorage.isCertPinningEnabled = isChecked
CertificatePinning.enabled = isChecked
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
================================================
FILE: common/src/dev/java/ch/admin/bag/covidcertificate/common/debug/DebugSecureStorage.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.debug
import android.content.Context
import ch.admin.bag.covidcertificate.sdk.android.net.CertificatePinning
import ch.admin.bag.covidcertificate.sdk.android.utils.EncryptedSharedPreferencesUtil
import ch.admin.bag.covidcertificate.sdk.android.utils.SingletonHolder
class DebugSecureStorage private constructor(context: Context) {
companion object : SingletonHolder<DebugSecureStorage, Context>(::DebugSecureStorage) {
private const val PREFERENCES = "DebugSecureStorage"
private const val KEY_CERT_PINNING_ENABLED = "KEY_CERT_PINNING_ENABLED"
}
private val prefs = EncryptedSharedPreferencesUtil.initializeSharedPreferences(context, PREFERENCES)
var isCertPinningEnabled: Boolean
get() = prefs.getBoolean(KEY_CERT_PINNING_ENABLED, CertificatePinning.enabled)
set(value) = prefs.edit().putBoolean(KEY_CERT_PINNING_ENABLED, value).apply()
}
================================================
FILE: common/src/dev/res/xml/network_security_config.xml
================================================
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2020 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<!-- Trust user added CAs in dev app only -->
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
================================================
FILE: common/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ch.admin.bag.covidcertificate.common">
</manifest>
================================================
FILE: common/src/main/assets/impressum/de/impressum.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="de"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<header>
<div class="title">{APP_NAME}</div>
<div class="version">Version {VERSION}</div>
</header>
<div class="container impressum">
<h1>Herausgeber</h1>
<picture>
<img src="../img/bundeslogo-bag.svg" class="bag-logo" alt="Bundesamt für Gesundheit BAG">
</picture>
<h2>Weitere Informationen</h2>
<a class="link" href="https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/covid-zertifikat.html">
<img src="../img/ic-link-external.svg" alt="">
www.bag.admin.ch
</a>
<h2>Infoline Coronavirus</h2>
<a class="link" href="tel:+41584630000">
<img src="../img/ic-call.svg" alt="">
+41 58 463 00 00
</a>
<h2>Rechtliches</h2>
<a class="link" href="{LAW_LINK}">
<img src="../img/ic-link-external.svg" alt="">
Datenschutzerklärung & Nutzungsbedingungen
</a>
<div class="divider"></div>
<h1>Umsetzung & Betrieb</h1>
<picture>
<img src="../img/bundeslogo-bit.svg" class="bag-logo" alt="Bundesamt für Informatik und Telekommunikation BIT">
</picture>
<h2>Technische Hilfe</h2>
<a class="link" href="tel:+41584660799">
<img src="../img/ic-call.svg" alt="">
+41 58 466 07 99
</a>
<div class="divider"></div>
<h1>In Zusammenarbeit mit</h1>
<picture>
<img src="../img/logo-ubique.svg" class="logo logo-ubique" alt="">
</picture>
<a class="link" href="https://www.ubique.ch/?app={PARAM_APP_IDENTIFIER}">
<img src="../img/ic-link-external.svg" alt="">
www.ubique.ch
</a>
<div class="divider"></div>
<h1>Quellcode</h1>
<p class="text">Der Quellcode des Covid Certificate Systems (inkl. dieser App) ist Open-Source und kann auf GitHub eingesehen werden.</p>
<a class="link" href="https://www.github.com/admin-ch">
<img src="../img/ic-link-external.svg" alt="">
www.github.com/admin-ch
</a>
<div class="divider"></div>
<a href="ccert://licence.html" class="licence-link">
<h1>Lizenzen</h1>
<picture>
<img src="../img/ic-arrow.svg" alt="">
</picture>
</a>
</div>
<footer>
<div class="build">{BUILD}</div>
</footer>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/de/licence.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="de"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<div class="container licence">
<h1>Lizenzen</h1>
<div class="list1">
<ul>
<li>This app uses <strong>COVID Certificate Android SDK</strong> licensed under the <strong>MPL 2</strong> license</li>
<li>This app uses <strong>androidx</strong>, <strong>kotlinx</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Material Components For Android</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>ZXing</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Retrofit</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Moshi</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Jackson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Gson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>hcert-kotlin</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>dgc-java</strong> licensed under the <strong>MIT license</strong> license</li>
<li>This app uses <strong>COSE-JAVA</strong> licensed under the <strong>BSD-3-Clause</strong> license</li>
<li>This app uses <strong>Java JWT</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Bouncy Castle</strong> licensed under the <strong>MIT</strong> license</li>
<li>This app uses <strong>Fira Code Font</strong> licensed under the <strong>OFL-1.1</strong> license</li>
</ul>
</div>
<div class="licence-list">
<ul>
<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 license</a></li>
<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause license</a></li>
<li><a href="https://opensource.org/licenses/MIT">MIT license</a></li>
<li><a href="https://opensource.org/licenses/MPL-2.0">MPL 2 license</a></li>
<li><a href="https://opensource.org/licenses/OFL-1.1">SIL OPEN FONT LICENSE</a></li>
</ul>
</div>
</div>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/en/impressum.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="en"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<header>
<div class="title">{APP_NAME}</div>
<div class="version">Version {VERSION}</div>
</header>
<div class="container impressum">
<h1>Published by</h1>
<picture>
<img src="../img/bundeslogo-bag.svg" class="bag-logo" alt="Federal Office of Public Health FOPH">
</picture>
<h2>Further information</h2>
<a class="link" href="https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/covid-zertifikat.html">
<img src="../img/ic-link-external.svg" alt="">
www.bag.admin.ch
</a>
<h2>Infoline Coronavirus</h2>
<a class="link" href="tel:+41584630000">
<img src="../img/ic-call.svg" alt="">
+41 58 463 00 00
</a>
<h2>Legal</h2>
<a class="link" href="{LAW_LINK}">
<img src="../img/ic-link-external.svg" alt="">
Data Protection Statement & Conditions of Use
</a>
<div class="divider"></div>
<h1>Development & operation</h1>
<picture>
<img src="../img/bundeslogo-bit.svg" class="bag-logo" alt="Federal Office of Information Technology, Systems and Telecommunication FOITT">
</picture>
<h2>Technical support</h2>
<a class="link" href="tel:+41584660799">
<img src="../img/ic-call.svg" alt="">
+41 58 466 07 99
</a>
<div class="divider"></div>
<h1>In collaboration with</h1>
<picture>
<img src="../img/logo-ubique.svg" class="logo logo-ubique" alt="">
</picture>
<a class="link" href="https://www.ubique.ch/?app={PARAM_APP_IDENTIFIER}">
<img src="../img/ic-link-external.svg" alt="">
www.ubique.ch
</a>
<div class="divider"></div>
<h1>Source code</h1>
<p class="text">The source code of the COVID Certificate System (including this app) is open source and can be viewed on GitHub.</p>
<a class="link" href="https://www.github.com/admin-ch">
<img src="../img/ic-link-external.svg" alt="">
www.github.com/admin-ch
</a>
<div class="divider"></div>
<a href="ccert://licence.html" class="licence-link">
<h1>Licences</h1>
<picture>
<img src="../img/ic-arrow.svg" alt="">
</picture>
</a>
</div>
<footer>
<div class="build">{BUILD}</div>
</footer>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/en/licence.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="en"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<div class="container licence">
<h1>Licences</h1>
<div class="list1">
<ul>
<li>This app uses <strong>COVID Certificate Android SDK</strong> licensed under the <strong>MPL 2</strong> license</li>
<li>This app uses <strong>androidx</strong>, <strong>kotlinx</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Material Components For Android</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>ZXing</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Retrofit</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Moshi</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Jackson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Gson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>hcert-kotlin</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>dgc-java</strong> licensed under the <strong>MIT license</strong> license</li>
<li>This app uses <strong>COSE-JAVA</strong> licensed under the <strong>BSD-3-Clause</strong> license</li>
<li>This app uses <strong>Java JWT</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Bouncy Castle</strong> licensed under the <strong>MIT</strong> license</li>
<li>This app uses <strong>Fira Code Font</strong> licensed under the <strong>OFL-1.1</strong> license</li>
</ul>
</div>
<div class="licence-list">
<ul>
<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 license</a></li>
<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause license</a></li>
<li><a href="https://opensource.org/licenses/MIT">MIT license</a></li>
<li><a href="https://opensource.org/licenses/MPL-2.0">MPL 2 license</a></li>
<li><a href="https://opensource.org/licenses/OFL-1.1">SIL OPEN FONT LICENSE</a></li>
</ul>
</div>
</div>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/fr/impressum.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="fr"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<header>
<div class="title">{APP_NAME}</div>
<div class="version">Version {VERSION}</div>
</header>
<div class="container impressum">
<h1>Éditeur</h1>
<picture>
<img src="../img/bundeslogo-bag.svg" class="bag-logo" alt="Office fédéral de la santé publique OFSP">
</picture>
<h2>Informations complémentaires</h2>
<a class="link" href="https://www.bag.admin.ch/bag/fr/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/covid-zertifikat.html">
<img src="../img/ic-link-external.svg" alt="">
www.bag.admin.ch
</a>
<h2>Infoline Coronavirus</h2>
<a class="link" href="tel:+41584630000">
<img src="../img/ic-call.svg" alt="">
+41 58 463 00 00
</a>
<h2>Indications légales</h2>
<a class="link" href="{LAW_LINK}">
<img src="../img/ic-link-external.svg" alt="">
Déclaration de confidentialité & condition d'utilisation
</a>
<div class="divider"></div>
<h1>Mise en application & opération</h1>
<picture>
<img src="../img/bundeslogo-bit.svg" class="bag-logo" alt="Office fédéral de l'informatique et de la télécommunication OFIT">
</picture>
<h2>Assistance technique</h2>
<a class="link" href="tel:+41584660799">
<img src="../img/ic-call.svg" alt="">
+41 58 466 07 99
</a>
<div class="divider"></div>
<h1>En collaboration avec</h1>
<picture>
<img src="../img/logo-ubique.svg" class="logo logo-ubique" alt="">
</picture>
<a class="link" href="https://www.ubique.ch/?app={PARAM_APP_IDENTIFIER}">
<img src="../img/ic-link-external.svg" alt="">
www.ubique.ch
</a>
<div class="divider"></div>
<h1>Code source</h1>
<p class="text">Le code source du système de Certificat COVID (y compris cette application) est open source et peut être trouvé sur GitHub.</p>
<a class="link" href="https://www.github.com/admin-ch">
<img src="../img/ic-link-external.svg" alt="">
www.github.com/admin-ch
</a>
<div class="divider"></div>
<a href="ccert://licence.html" class="licence-link">
<h1>Licences</h1>
<picture>
<img src="../img/ic-arrow.svg" alt="">
</picture>
</a>
</div>
<footer>
<div class="build">{BUILD}</div>
</footer>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/fr/licence.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="fr"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<div class="container licence">
<h1>Licences</h1>
<div class="list1">
<ul>
<li>This app uses <strong>COVID Certificate Android SDK</strong> licensed under the <strong>MPL 2</strong> license</li>
<li>This app uses <strong>androidx</strong>, <strong>kotlinx</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Material Components For Android</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>ZXing</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Retrofit</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Moshi</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Jackson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Gson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>hcert-kotlin</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>dgc-java</strong> licensed under the <strong>MIT license</strong> license</li>
<li>This app uses <strong>COSE-JAVA</strong> licensed under the <strong>BSD-3-Clause</strong> license</li>
<li>This app uses <strong>Java JWT</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Bouncy Castle</strong> licensed under the <strong>MIT</strong> license</li>
<li>This app uses <strong>Fira Code Font</strong> licensed under the <strong>OFL-1.1</strong> license</li>
</ul>
</div>
<div class="licence-list">
<ul>
<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 license</a></li>
<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause license</a></li>
<li><a href="https://opensource.org/licenses/MIT">MIT license</a></li>
<li><a href="https://opensource.org/licenses/MPL-2.0">MPL 2 license</a></li>
<li><a href="https://opensource.org/licenses/OFL-1.1">SIL OPEN FONT LICENSE</a></li>
</ul>
</div>
</div>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/it/impressum.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="it"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<header>
<div class="title">{APP_NAME}</div>
<div class="version">Version {VERSION}</div>
</header>
<div class="container impressum">
<h1>Editore</h1>
<picture>
<img src="../img/bundeslogo-bag.svg" class="bag-logo" alt="Ufficio federale della sanità pubblica UFSP">
</picture>
<h2>Ulteriori informazioni</h2>
<a class="link" href="https://www.bag.admin.ch/bag/it/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/covid-zertifikat.html">
<img src="../img/ic-link-external.svg" alt="">
www.bag.admin.ch
</a>
<h2>Infoline Coronavirus</h2>
<a class="link" href="tel:+41584630000">
<img src="../img/ic-call.svg" alt="">
+41 58 463 00 00
</a>
<h2>Note legali</h2>
<a class="link" href="{LAW_LINK}">
<img src="../img/ic-link-external.svg" alt="">
Informativa sulla protezione dei dati & condizioni d'uso
</a>
<div class="divider"></div>
<h1>Realizzazione & operazione</h1>
<picture>
<img src="../img/bundeslogo-bit.svg" class="bag-logo" alt="Ufficio federale dell'informatica e della telecomunicazione UFIT">
</picture>
<h2>Supporto tecnico</h2>
<a class="link" href="tel:+41584660799">
<img src="../img/ic-call.svg" alt="">
+41 58 466 07 99
</a>
<div class="divider"></div>
<h1>In collaborazione con</h1>
<picture>
<img src="../img/logo-ubique.svg" class="logo logo-ubique" alt="">
</picture>
<a class="link" href="https://www.ubique.ch/?app={PARAM_APP_IDENTIFIER}">
<img src="../img/ic-link-external.svg" alt="">
www.ubique.ch
</a>
<div class="divider"></div>
<h1>Codice sorgente</h1>
<p class="text">Il codice sorgente del Covid Certificate System (compresa questa applicazione) è open source e può essere trovato su GitHub.</p>
<a class="link" href="https://www.github.com/admin-ch">
<img src="../img/ic-link-external.svg" alt="">
www.github.com/admin-ch
</a>
<div class="divider"></div>
<a href="ccert://licence.html" class="licence-link">
<h1>Licenze</h1>
<picture>
<img src="../img/ic-arrow.svg" alt="">
</picture>
</a>
</div>
<footer>
<div class="build">{BUILD}</div>
</footer>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/it/licence.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="it"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<div class="container licence">
<h1>Licenze</h1>
<div class="list1">
<ul>
<li>This app uses <strong>COVID Certificate Android SDK</strong> licensed under the <strong>MPL 2</strong> license</li>
<li>This app uses <strong>androidx</strong>, <strong>kotlinx</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Material Components For Android</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>ZXing</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Retrofit</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Moshi</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Jackson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Gson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>hcert-kotlin</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>dgc-java</strong> licensed under the <strong>MIT license</strong> license</li>
<li>This app uses <strong>COSE-JAVA</strong> licensed under the <strong>BSD-3-Clause</strong> license</li>
<li>This app uses <strong>Java JWT</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Bouncy Castle</strong> licensed under the <strong>MIT</strong> license</li>
<li>This app uses <strong>Fira Code Font</strong> licensed under the <strong>OFL-1.1</strong> license</li>
</ul>
</div>
<div class="licence-list">
<ul>
<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 license</a></li>
<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause license</a></li>
<li><a href="https://opensource.org/licenses/MIT">MIT license</a></li>
<li><a href="https://opensource.org/licenses/MPL-2.0">MPL 2 license</a></li>
<li><a href="https://opensource.org/licenses/OFL-1.1">SIL OPEN FONT LICENSE</a></li>
</ul>
</div>
</div>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/rm/impressum.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="rm"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<header>
<div class="title">{APP_NAME}</div>
<div class="version">Version {VERSION}</div>
</header>
<div class="container impressum">
<h1>Editur</h1>
<picture>
<img src="../img/bundeslogo-bag.svg" class="bag-logo" alt="Uffizi federal da sanadad publica UFSP">
</picture>
<h2>Ulteriuras infurmaziuns</h2>
<a class="link" href="https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/covid-zertifikat.html">
<img src="../img/ic-link-external.svg" alt="">
www.bag.admin.ch
</a>
<h2>Infoline Coronavirus</h2>
<a class="link" href="tel:+41584630000">
<img src="../img/ic-call.svg" alt="">
+41 58 463 00 00
</a>
<h2>Indicaziuns giuridicas</h2>
<a class="link" href="{LAW_LINK}">
<img src="../img/ic-link-external.svg" alt="">
Basas legalas
</a>
<div class="divider"></div>
<h1>Realisaziun & gestiun</h1>
<picture>
<img src="../img/bundeslogo-bit.svg" class="bag-logo" alt="Uffizi federal d'informatica e da telecommunicaziun UFIT">
</picture>
<h2>Agid tecnic</h2>
<a class="link" href="tel:+41584660799">
<img src="../img/ic-call.svg" alt="">
+41 58 466 07 99
</a>
<div class="divider"></div>
<h1>En collavuraziun cun</h1>
<picture>
<img src="../img/logo-ubique.svg" class="logo logo-ubique" alt="">
</picture>
<a class="link" href="https://www.ubique.ch/?app={PARAM_APP_IDENTIFIER}">
<img src="../img/ic-link-external.svg" alt="">
www.ubique.ch
</a>
<div class="divider"></div>
<h1>Code da funtauna</h1>
<p class="text">Il code da funtauna dal COVID Certificate System (incl. questa app) è open source e po vegnir consultà/chattà sin GitHub.</p>
<a class="link" href="https://www.github.com/admin-ch">
<img src="../img/ic-link-external.svg" alt="">
www.github.com/admin-ch
</a>
<div class="divider"></div>
<a href="ccert://licence.html" class="licence-link">
<h1>Licenzas</h1>
<picture>
<img src="../img/ic-arrow.svg" alt="">
</picture>
</a>
</div>
<footer>
<div class="build">{BUILD}</div>
</footer>
</body>
</html>
================================================
FILE: common/src/main/assets/impressum/rm/licence.html
================================================
<!--
~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
~
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
~
~ SPDX-License-Identifier: MPL-2.0
-->
<!DOCTYPE html>
<html lang="rm"><head>
<title> </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../scss/main.min.css">
</head><body>
<div class="container licence">
<h1>Licenzas</h1>
<div class="list1">
<ul>
<li>This app uses <strong>COVID Certificate Android SDK</strong> licensed under the <strong>MPL 2</strong> license</li>
<li>This app uses <strong>androidx</strong>, <strong>kotlinx</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Material Components For Android</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>ZXing</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Retrofit</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Moshi</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Jackson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Gson</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>hcert-kotlin</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>dgc-java</strong> licensed under the <strong>MIT license</strong> license</li>
<li>This app uses <strong>COSE-JAVA</strong> licensed under the <strong>BSD-3-Clause</strong> license</li>
<li>This app uses <strong>Java JWT</strong> licensed under the <strong>Apache 2.0</strong> license</li>
<li>This app uses <strong>Bouncy Castle</strong> licensed under the <strong>MIT</strong> license</li>
<li>This app uses <strong>Fira Code Font</strong> licensed under the <strong>OFL-1.1</strong> license</li>
</ul>
</div>
<div class="licence-list">
<ul>
<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 license</a></li>
<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause license</a></li>
<li><a href="https://opensource.org/licenses/MIT">MIT license</a></li>
<li><a href="https://opensource.org/licenses/MPL-2.0">MPL 2 license</a></li>
<li><a href="https://opensource.org/licenses/OFL-1.1">SIL OPEN FONT LICENSE</a></li>
</ul>
</div>
</div>
</body>
</html>
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/BaseActivity.kt
================================================
package ch.admin.bag.covidcertificate.common
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import ch.admin.bag.covidcertificate.common.data.ConfigSecureStorage
import ch.admin.bag.covidcertificate.common.util.LocaleUtil
open class BaseActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
val language = ConfigSecureStorage.getInstance(newBase).getUserLanguage()
super.attachBaseContext(LocaleUtil.updateLanguage(newBase, language))
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/browserstack/AirplaneMode.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.browserstack
annotation class AirplaneMode
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/browserstack/BadNetwork.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.browserstack
annotation class BadNetwork
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/browserstack/Normal.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.browserstack
annotation class Normal
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/browserstack/Onboarding.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.browserstack
annotation class Onboarding
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/CertificateRenewalInfoDetailModel.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class CertificateRenewalInfoDetailModel(
val iconAndroid: String,
val text: String
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/CertificateRenewalInfoModel.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class CertificateRenewalInfoModel(
val heading: String,
val infos: List<CertificateRenewalInfoDetailModel>,
val faqLinkText: String,
val faqLinkUrl: String,
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/CertificateRenewalType.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = false)
enum class CertificateRenewalType {
INFO, EXPIRED, RENEWED
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/CheckModesInfosModel.kt
================================================
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
//for verifier
@JsonClass(generateAdapter = true)
data class CheckModesInfosModel(
val infos: Map<String, CheckModeInfoModel>?,
val unselected: VerifierInfos?
)
@JsonClass(generateAdapter = true)
data class VerifierInfos(
val infos: List<VerifierInfoDetails>?
)
@JsonClass(generateAdapter = true)
data class VerifierInfoDetails(
val iconAndroid: String,
val iconIos: String,
val text: String
)
//for wallet
@JsonClass(generateAdapter = true)
data class CheckModesInfoModel(
val title: String?,
val modes: Map<String, WalletModeModel>?,
)
@JsonClass(generateAdapter = true)
data class WalletModeModel(
val ok: WalletModeDetails,
val notOk: WalletModeDetails
)
@JsonClass(generateAdapter = true)
data class WalletModeDetails(
val iconAndroid: String,
val iconIos: String,
val text: String
)
@JsonClass(generateAdapter = true)
data class CheckModeInfoModel(
val title: String,
val hexColor: String,
val infos: List<CheckModeInfoEntry>
)
data class CheckModeInfoModelWithId(
val id: String,
val title: String,
val hexColor: String,
val infos: List<CheckModeInfoEntry>
)
@JsonClass(generateAdapter = true)
data class CheckModeInfoEntry(
val iconAndroid: String,
val text: String
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/ConfigModel.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import ch.admin.bag.covidcertificate.common.faq.model.Faq
import ch.admin.bag.covidcertificate.common.faq.model.Header
import ch.admin.bag.covidcertificate.common.faq.model.Question
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class ConfigModel(
val forceUpdate: Boolean,
val infoBox: Map<String, InfoBoxModel>?,
val questions: Map<String, FaqModel>?,
val works: Map<String, FaqModel>?,
val transferQuestions: Map<String, FaqModel>?,
val transferWorks: Map<String, FaqModel>?,
val androidTransferCheckIntervalMs: Long?,
val androidTransferCheckBackoffMs: Long?,
val lightCertificateActive: Boolean?,
val pdfGenerationActive: Boolean?,
val vaccinationHints: Map<String, List<VaccinationHintModel>>?,
val vaccinationBookingInfo: Map<String, VaccinationBookingInfoModel>?,
val showVaccinationHintHomescreen: Boolean?,
val showVaccinationHintDetail: Boolean?,
val showVaccinationHintTransfer: Boolean?,
val timeshiftDetectionEnabled: Boolean?,
val checkModesInfos: Map<String, CheckModesInfosModel>?,
val checkModesInfo: Map<String, CheckModesInfoModel>?,
val checkModeReselectAfterHours: Int?,
val lightCertDurationInHours: Int?,
val refreshButtonDisabled: Boolean?,
val refreshButtonInfo: Map<String, RefreshButtonInfoModel>?,
val eolBannerInfo: Map<String, Map<String, EolBannerInfoModel>>?,
val foreignRulesCheckEnabled: Boolean?,
val foreignRulesLinkText: Map<String, String>?,
val foreignRulesLinkUrl: Map<String, String>?,
val foreignRulesHints: Map<String, List<ForeignRulesHintModel>>?,
val showRatConversionForm: Boolean?,
val ratConversionFormUrl: String?,
val certRenewalInfo: Map<String, Map<CertificateRenewalType, CertificateRenewalInfoModel>>?,
val showValidityState: Boolean?,
val covidCertificateNewsText: Map<String, String>?,
val infoCovidCertificateNews: Map<String, InfoCovidCertificateNews>?,
) {
fun getInfoBox(languageKey: String?): InfoBoxModel? = infoBox?.get(languageKey)
fun getQuestionsFaqs(languageKey: String): FaqModel? = questions?.get(languageKey)
fun getWorksFaqs(languageKey: String): FaqModel? = works?.get(languageKey)
fun getTransferQuestionsFaqs(languageKey: String): FaqModel? = transferQuestions?.get(languageKey)
fun getTransferWorksFaqs(languageKey: String): FaqModel? = transferWorks?.get(languageKey)
fun getVaccinationHints(languageKey: String): List<VaccinationHintModel>? = vaccinationHints?.get(languageKey)
fun getCheckModesInfos(languageKey: String): Map<String, CheckModeInfoModel>? = checkModesInfos?.get(languageKey)?.infos
fun getUnselectedModesInfos(languageKey: String): VerifierInfos? = checkModesInfos?.get(languageKey)?.unselected
fun getRefreshButtonInfo(languageKey: String): RefreshButtonInfoModel? = refreshButtonInfo?.get(languageKey)
fun getEolBannerInfo(languageKey: String): Map<String, EolBannerInfoModel>? = eolBannerInfo?.get(languageKey)
fun getForeignRulesLinkText(languageKey: String): String? = foreignRulesLinkText?.get(languageKey)
fun getForeignRulesLinkUrl(languageKey: String): String? = foreignRulesLinkUrl?.get(languageKey)
fun getForeignRulesHints(languageKey: String): List<ForeignRulesHintModel>? = foreignRulesHints?.get(languageKey)
fun getCertRenewalInfo(languageKey: String): Map<CertificateRenewalType, CertificateRenewalInfoModel>? =
certRenewalInfo?.get(languageKey)
fun getCheckModes(languageKey: String): Map<String, WalletModeModel>? = checkModesInfo?.get(languageKey)?.modes
fun getInfoModeTitle(languageKey: String): String? = checkModesInfo?.get(languageKey)?.title
fun getVaccinationBookingInfo(languageKey: String): VaccinationBookingInfoModel? = vaccinationBookingInfo?.get(languageKey)
fun getCovidCertificateNewsText(languageKey: String): String? = covidCertificateNewsText?.get(languageKey)
fun getInfoCovidCertificateNews(languageKey: String): InfoCovidCertificateNews? = infoCovidCertificateNews?.get(languageKey)
fun generateFaqItems(languageKey: String): List<Faq> {
val itemsList = mutableListOf<Faq>()
getQuestionsFaqs(languageKey)?.let { questionModel ->
val questionItems = questionModel.faqEntries
itemsList.add(Header(questionModel.faqIconAndroid, questionModel.faqTitle, questionModel.faqSubTitle))
questionItems?.let {
itemsList.addAll(it.map { faqEntry ->
Question(
faqEntry.title,
faqEntry.text,
linkTitle = faqEntry.linkTitle,
linkUrl = faqEntry.linkUrl
)
})
}
}
getWorksFaqs(languageKey)?.let { worksModel ->
val questionItems = worksModel.faqEntries
itemsList.add(Header(worksModel.faqIconAndroid, worksModel.faqTitle, worksModel.faqSubTitle))
questionItems?.let {
itemsList.addAll(it.map { faqEntry ->
Question(
faqEntry.title,
faqEntry.text,
linkTitle = faqEntry.linkTitle,
linkUrl = faqEntry.linkUrl
)
})
}
}
return itemsList
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/ConfigViewModel.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import ch.admin.bag.covidcertificate.common.config.ConfigModel
import ch.admin.bag.covidcertificate.common.net.ConfigRepository
import ch.admin.bag.covidcertificate.common.net.ConfigSpec
import kotlinx.coroutines.launch
abstract class ConfigViewModel(application: Application) : AndroidViewModel(application) {
private val configMutableLiveData = MutableLiveData<ConfigModel>(ConfigRepository.getCurrentConfig(application))
val configLiveData: LiveData<ConfigModel> = configMutableLiveData
fun loadConfig(baseUrl: String, versionName: String, buildTime: String) {
val configSpec = ConfigSpec(getApplication(), baseUrl, versionName, buildTime)
val configRepository = ConfigRepository.getInstance(configSpec)
viewModelScope.launch {
configRepository.loadConfig(getApplication())?.let { config ->
configMutableLiveData.postValue(config) }
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/CovidCertificateNewsItem.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
import java.io.Serializable
@JsonClass(generateAdapter = true)
data class CovidCertificateNewsItem(
val iconAndroid: String?,
val text: String?,
) : Serializable
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/EolBannerInfoModel.kt
================================================
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
import java.io.Serializable
@JsonClass(generateAdapter = true)
data class EolBannerInfoModel(
val homescreenHexColor: String,
val homescreenTitle: String,
val detailHexColor: String,
val detailTitle: String,
val detailText: String?,
val detailMoreInfo: String,
val popupTitle: String,
val popupText1: String?,
val popupBoldText: String?,
val popupText2: String?,
val popupLinkText: String,
val popupLinkUrl: String,
) : Serializable
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/FaqEntryModel.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class FaqEntryModel(
val title: String,
val text: String,
val iconAndroid: String?,
val linkTitle: String?,
val linkUrl: String?
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/FaqIntroSection.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class FaqIntroSection(
val iconAndroid: String,
val text: String
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/FaqModel.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class FaqModel(
val faqTitle: String,
val faqSubTitle: String?,
val faqIntroSections: List<FaqIntroSection>?,
val faqIconAndroid: String?,
val faqEntries: List<FaqEntryModel>?
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/ForeignRulesHintModel.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class ForeignRulesHintModel(
val iconAndroid: String,
val text: String,
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/InfoBoxModel.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import android.os.Parcel
import android.os.Parcelable
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class InfoBoxModel(
val title: String,
val msg: String,
val url: String?,
val urlTitle: String?,
val isDismissible: Boolean,
val infoId: Long
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString() ?: throw IllegalArgumentException("No title specified for InfoDialog"),
parcel.readString() ?: throw IllegalArgumentException("No message specified for InfoDialog"),
parcel.readString(),
parcel.readString(),
parcel.readByte() != 0.toByte(),
parcel.readLong()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(title)
parcel.writeString(msg)
parcel.writeString(url)
parcel.writeString(urlTitle)
parcel.writeByte(if (isDismissible) 1 else 0)
parcel.writeLong(infoId)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<InfoBoxModel> {
override fun createFromParcel(parcel: Parcel): InfoBoxModel {
return InfoBoxModel(parcel)
}
override fun newArray(size: Int): Array<InfoBoxModel?> {
return arrayOfNulls(size)
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/InfoCovidCertificateNews.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
import java.io.Serializable
@JsonClass(generateAdapter = true)
data class InfoCovidCertificateNews(
val title: String?,
val newsItems: List<CovidCertificateNewsItem>,
) : Serializable
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/RefreshButtonInfoModel.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
import java.io.Serializable
@JsonClass(generateAdapter = true)
data class RefreshButtonInfoModel(
val title: String,
val text1: String,
val text2: String,
val fatTitle: String,
val text3: String,
val linkText: String,
val linkUrl: String,
) : Serializable
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/VaccinationBookingInfoModel.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class VaccinationBookingInfoModel(
val title: String,
val text: String,
val info: String,
val impfcheckTitle: String?,
val impfcheckText: String?,
val impfcheckButton: String?,
val impfcheckUrl: String?
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/config/VaccinationHintModel.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.config
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class VaccinationHintModel(
val title: String,
val text: String,
)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/data/ConfigSecureStorage.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.data
import android.content.Context
import androidx.core.content.edit
import ch.admin.bag.covidcertificate.common.config.ConfigModel
import ch.admin.bag.covidcertificate.sdk.android.utils.EncryptedSharedPreferencesUtil
import ch.admin.bag.covidcertificate.sdk.android.utils.SingletonHolder
import com.squareup.moshi.Moshi
class ConfigSecureStorage private constructor(context: Context) {
companion object : SingletonHolder<ConfigSecureStorage, Context>(::ConfigSecureStorage) {
private const val PREFERENCES = "ConfigSecureStorage"
private const val KEY_CONFIG = "ConfigKey"
private const val KEY_CONFIG_LAST_SUCCESS = "LastSuccessTimestampKey"
private const val KEY_CONFIG_LAST_SUCCESS_APP_AND_OS_VERSION = "LastSuccessVersionKey"
private const val KEY_CONFIG_SHOWN_INFO_BOX_ID = "LastShownInfoBoxId"
private const val KEY_USER_LANGUAGE = "UserLanguage"
private const val KEY_ZOOM_ACTIVE = "KEY_ZOOM_ACTIVE"
private const val KEY_REFRESH_BUTTON_DISABLED_TIMESTAMP = "KEY_REFRESH_BUTTON_DISABLED_TIMESTAMP"
private val moshi = Moshi.Builder().build()
private val configModelAdapter = moshi.adapter(ConfigModel::class.java)
}
private val prefs = EncryptedSharedPreferencesUtil.initializeSharedPreferences(context, PREFERENCES)
fun updateConfigData(config: ConfigModel, timestamp: Long, appVersion: String) {
val editor = prefs.edit()
editor.putLong(KEY_CONFIG_LAST_SUCCESS, timestamp)
editor.putString(KEY_CONFIG_LAST_SUCCESS_APP_AND_OS_VERSION, appVersion)
editor.putString(KEY_CONFIG, configModelAdapter.toJson(config))
editor.apply()
}
fun getConfig(): ConfigModel? =
prefs.getString(KEY_CONFIG, null)
?.let { configModelAdapter.fromJson(it) }
fun getConfigLastSuccessTimestamp(): Long =
prefs.getLong(KEY_CONFIG_LAST_SUCCESS, 0)
fun getConfigLastSuccessAppAndOSVersion(): String? =
prefs.getString(KEY_CONFIG_LAST_SUCCESS_APP_AND_OS_VERSION, null)
fun setLastShownInfoBoxId(infoBoxId: Long) = prefs.edit().putLong(KEY_CONFIG_SHOWN_INFO_BOX_ID, infoBoxId).apply()
fun getLastShownInfoBoxId(): Long = prefs.getLong(KEY_CONFIG_SHOWN_INFO_BOX_ID, 0L)
fun setUserLanguage(language: String?) = prefs.edit { putString(KEY_USER_LANGUAGE, language) }
fun getUserLanguage(): String? = prefs.getString(KEY_USER_LANGUAGE, null)
fun getZoomOn(): Boolean = prefs.getBoolean(KEY_ZOOM_ACTIVE, false)
fun setZoomOn(isZoomActive: Boolean) = prefs.edit().putBoolean(KEY_ZOOM_ACTIVE, isZoomActive).apply()
fun getRefreshButtonDisabledTimestamp(): Long = prefs.getLong(KEY_REFRESH_BUTTON_DISABLED_TIMESTAMP, -1L)
fun setRefreshButtonDisabledTimestamp(timestamp: Long) = prefs.edit { putLong(KEY_REFRESH_BUTTON_DISABLED_TIMESTAMP, timestamp) }
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/dialog/InfoDialogFragment.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import ch.admin.bag.covidcertificate.common.R
import ch.admin.bag.covidcertificate.common.config.InfoBoxModel
import ch.admin.bag.covidcertificate.common.databinding.DialogFragmentInfoBoxBinding
import ch.admin.bag.covidcertificate.common.util.UrlUtil
class InfoDialogFragment : DialogFragment() {
companion object {
private const val ARG_INFO_BOX_MODEL = "ARG_INFO_BOX_MODEL"
fun newInstance(infoBoxModel: InfoBoxModel): InfoDialogFragment = InfoDialogFragment().apply {
arguments = bundleOf(ARG_INFO_BOX_MODEL to infoBoxModel)
}
}
private lateinit var infoBoxModel: InfoBoxModel
private var _binding: DialogFragmentInfoBoxBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.CovidCertificate_InfoDialog)
infoBoxModel = requireArguments().getParcelable(ARG_INFO_BOX_MODEL)
?: throw IllegalStateException("No infoBox information supplied to DialogFragment!")
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = DialogFragmentInfoBoxBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setupInfo()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun setupInfo() {
binding.infoDialogTitle.text = infoBoxModel.title
binding.infoDialogText.text = infoBoxModel.msg
val hasCustomButton = !infoBoxModel.urlTitle.isNullOrEmpty()
binding.infoDialogUrlButton.apply {
text = infoBoxModel.urlTitle
isVisible = hasCustomButton
infoBoxModel.url?.let { url ->
setOnClickListener { UrlUtil.openUrl(context, url) }
}
}
binding.infoDialogCloseButton.apply {
text = getString(if (hasCustomButton) R.string.accessibility_close_button else R.string.ok_button)
setOnClickListener { if (this@InfoDialogFragment.isVisible) dismiss() }
}
}
fun getInfoId(): Long = infoBoxModel.infoId
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/exception/HttpIOException.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.exception
import retrofit2.Response
import java.io.IOException
/**
* A replacement for the retrofit2.HttpException that inherits from IOException instead of RuntimeException.
* Since other request related exceptions (e.g. UnknownHost or SocketTimeout) extend IOException, this makes it easier to catch all
* networking related exceptions in a single catch-clause.
*/
class HttpIOException(val response: Response<*>) : IOException(response.message()) {
val code = response.code()
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/exception/TimeDeviationException.kt
================================================
package ch.admin.bag.covidcertificate.common.exception
class TimeDeviationException : IllegalStateException()
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/ContextExtensions.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.extensions
import android.content.Context
import android.content.res.Configuration
import java.util.*
fun Context.getDrawableIdentifier(drawableName: String) = resources.getIdentifier(drawableName, "drawable", packageName)
fun Context.updateLocale(languageKey: String? = null): Context {
val config = Configuration()
val locale = languageKey?.let {
Locale(languageKey, "CH")
} ?: Locale.getDefault()
config.setLocale(locale)
return createConfigurationContext(config)
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/DccCertExtensions.kt
================================================
package ch.admin.bag.covidcertificate.common.extensions
import ch.admin.bag.covidcertificate.sdk.core.extensions.firstPositiveResult
import ch.admin.bag.covidcertificate.sdk.core.extensions.vaccineDate
import ch.admin.bag.covidcertificate.sdk.core.extensions.validFromDate
import ch.admin.bag.covidcertificate.sdk.core.models.healthcert.eu.DccCert
import java.time.LocalDateTime
fun DccCert.getDate(): LocalDateTime? {
var date = this.vaccinations?.firstOrNull()?.vaccineDate()
if (date == null) {
date = this.pastInfections?.firstOrNull()?.firstPositiveResult()
}
if (date == null) {
date = this.tests?.firstOrNull()?.validFromDate()
}
return date
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/LifecycleOwnerExtensions.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.extensions
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
/**
* Collect a flow, starting and cancelling the collection automatically when the lifecycle changes from or to STARTED
*/
fun <T> LifecycleOwner.collectWhenStarted(flow: Flow<T>, block: suspend (T) -> Unit) {
lifecycleScope.launch {
flow.flowWithLifecycle(lifecycle).collect {
block(it)
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/OkHttpExtensions.kt
================================================
package ch.admin.bag.covidcertificate.common.extensions
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
fun OkHttpClient.Builder.setTimeouts(seconds: Long = 10): OkHttpClient.Builder {
connectTimeout(seconds, TimeUnit.SECONDS)
writeTimeout(seconds, TimeUnit.SECONDS)
readTimeout(seconds, TimeUnit.SECONDS)
return this
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/WindowExtensions.kt
================================================
package ch.admin.bag.covidcertificate.common.extensions
import android.view.Window
import android.view.WindowManager
fun Window.overrideScreenBrightness(override: Boolean) {
if (override) {
addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
val layoutParams: WindowManager.LayoutParams = attributes
layoutParams.screenBrightness = if (override) 1f else -1f
attributes = layoutParams
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/faq/FaqAdapter.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.faq
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import ch.admin.bag.covidcertificate.common.faq.model.Faq
import ch.admin.bag.covidcertificate.common.faq.model.Header
import ch.admin.bag.covidcertificate.common.faq.model.IntroSection
import ch.admin.bag.covidcertificate.common.faq.model.Question
class FaqAdapter(val onLinkClickListener: ((String) -> Unit)? = null) : RecyclerView.Adapter<FaqViewHolder>() {
private val items = mutableListOf<FaqItem>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FaqViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
0 -> FaqViewHolder(inflater.inflate(HeaderItem.layoutResource, parent, false))
1 -> FaqViewHolder(inflater.inflate(QuestionItem.layoutResource, parent, false))
2 -> FaqViewHolder(inflater.inflate(IntroSectionItem.layoutResource, parent, false))
else -> throw IllegalStateException("Unknown viewType $viewType in FaqAdapter")
}
}
override fun onBindViewHolder(holder: FaqViewHolder, position: Int) {
holder.bindItem(items[position]) { notifyItemChanged(position) }
}
override fun getItemCount(): Int = items.size
override fun getItemViewType(position: Int): Int {
return when (items[position]) {
is HeaderItem -> 0
is QuestionItem -> 1
is IntroSectionItem -> 2
}
}
fun setItems(items: List<Faq>) {
this.items.clear()
val newItems = items.mapNotNull {
when (it) {
is Header -> HeaderItem(it)
is Question -> QuestionItem(it, onLinkClickListener)
is IntroSection -> IntroSectionItem(it)
else -> null
}
}
this.items.addAll(newItems)
notifyDataSetChanged()
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/faq/FaqFragment.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.faq
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import ch.admin.bag.covidcertificate.common.databinding.FragmentFaqBinding
import ch.admin.bag.covidcertificate.common.faq.model.Faq
import ch.admin.bag.covidcertificate.common.util.UrlUtil
import ch.admin.bag.covidcertificate.common.views.hideAnimated
abstract class FaqFragment : Fragment() {
private var _binding: FragmentFaqBinding? = null
private val binding get() = _binding!!
lateinit var toolbar: Toolbar
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = FragmentFaqBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar = binding.certificatesOverviewToolbar
toolbar.setNavigationOnClickListener {
parentFragmentManager.popBackStack()
}
setupFaqProvider()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
protected abstract fun setupFaqProvider()
protected fun setupFaqList(items: List<Faq>) {
binding.faqLoadingView.hideAnimated()
val recyclerView = binding.faqRecyclerView
(recyclerView.itemAnimator as SimpleItemAnimator?)?.supportsChangeAnimations = false
val layoutManager = LinearLayoutManager(recyclerView.context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = layoutManager
val adapter = FaqAdapter { url: String ->
context?.let { UrlUtil.openUrl(it, url) }
}
recyclerView.adapter = adapter
adapter.setItems(items)
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/faq/FaqItem.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.faq
import android.view.View
import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible
import ch.admin.bag.covidcertificate.common.R
import ch.admin.bag.covidcertificate.common.databinding.ItemFaqHeaderBinding
import ch.admin.bag.covidcertificate.common.databinding.ItemFaqIntroSectionBinding
import ch.admin.bag.covidcertificate.common.databinding.ItemFaqQuestionBinding
import ch.admin.bag.covidcertificate.common.extensions.getDrawableIdentifier
import ch.admin.bag.covidcertificate.common.faq.model.Header
import ch.admin.bag.covidcertificate.common.faq.model.IntroSection
import ch.admin.bag.covidcertificate.common.faq.model.Question
sealed class FaqItem {
abstract fun bindView(view: View, onItemClickListener: (() -> Unit)? = null)
}
data class HeaderItem(val header: Header) : FaqItem() {
companion object {
val layoutResource = R.layout.item_faq_header
}
override fun bindView(view: View, onItemClickListener: (() -> Unit)?) {
val binding = ItemFaqHeaderBinding.bind(view)
binding.itemFaqHeaderTitle.text = header.title
binding.itemFaqHeaderText.apply {
text = header.subtitle
isVisible = header.subtitle != null
}
val drawableId = header.iconName?.let { iconName ->
view.context.getDrawableIdentifier(iconName)
} ?: 0
binding.itemFaqHeaderIllu.apply {
setImageResource(drawableId)
isVisible = drawableId != 0
}
}
}
data class QuestionItem(
val question: Question,
val onLinkClickListener: ((String) -> Unit)? = null,
) : FaqItem() {
companion object {
val layoutResource = R.layout.item_faq_question
}
override fun bindView(view: View, onItemClickListener: (() -> Unit)?) {
val binding = ItemFaqQuestionBinding.bind(view)
binding.root.setOnClickListener {
question.isSelected = !question.isSelected
view.doOnPreDraw { onItemClickListener?.invoke() }
}
binding.itemFaqQuestionTitle.text = question.question
binding.itemFaqQuestionAnswer.apply {
text = question.answer
isVisible = question.isSelected
}
val hasLink = !question.linkTitle.isNullOrEmpty() && !question.linkUrl.isNullOrEmpty()
(hasLink && question.isSelected).let { visible ->
binding.itemFaqQuestionLinkLabel.isVisible = visible
binding.itemFaqQuestionLink.isVisible = visible
}
if (hasLink) {
binding.itemFaqQuestionLinkLabel.text = question.linkTitle
binding.itemFaqQuestionLink.setOnClickListener { onLinkClickListener?.invoke(question.linkUrl!!) }
} else {
binding.itemFaqQuestionLink.setOnClickListener(null)
}
binding.itemFaqQuestionChevron.setImageResource(if (question.isSelected) R.drawable.ic_arrow_contract else R.drawable.ic_arrow_expand)
}
}
data class IntroSectionItem(val introSection: IntroSection) : FaqItem() {
companion object {
val layoutResource = R.layout.item_faq_intro_section
}
override fun bindView(view: View, onItemClickListener: (() -> Unit)?) {
val binding = ItemFaqIntroSectionBinding.bind(view)
val drawableId = view.context.getDrawableIdentifier(introSection.iconName)
binding.faqIntroSectionIcon.setImageResource(drawableId)
binding.faqIntroSectionText.text = introSection.text
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/faq/FaqViewHolder.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.faq
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class FaqViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItem(item: FaqItem, onItemClickListener: (() -> Unit)? = null) = item.bindView(itemView, onItemClickListener)
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/faq/model/Header.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.faq.model
import java.io.Serializable
interface Faq
data class Header(val iconName: String?, val title: String, val subtitle: String?) : Faq, Serializable
data class Question(
val question: String,
val answer: String,
var isSelected: Boolean = false,
val linkTitle: String? = null,
val linkUrl: String? = null,
) : Faq, Serializable
data class IntroSection(val iconName: String, val text: String) : Faq, Serializable
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/html/BuildInfo.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.html
import java.io.Serializable
data class BuildInfo(
val appName: String,
val versionName: String,
val buildTime: Long,
val flavor: String,
val agbUrl: String,
val appIdentifier: String,
) : Serializable
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/html/ImprintFragment.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.html
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import ch.admin.bag.covidcertificate.common.R
import ch.admin.bag.covidcertificate.common.databinding.FragmentHtmlBinding
import ch.admin.bag.covidcertificate.common.settings.SettingsFragment
import ch.admin.bag.covidcertificate.common.util.AssetUtil
import ch.admin.bag.covidcertificate.common.util.UrlUtil
import ch.admin.bag.covidcertificate.common.views.hideAnimated
import java.util.*
class ImprintFragment : Fragment() {
companion object {
private const val COVID_CERT_IMPRESSUM_PREFIX = "ccert://"
private const val ARG_BASE_URL = "ARG_BASE_URL"
private const val ARG_BUILD_INFO = "ARG_BUILD_INFO"
private const val ARG_DATA = "ARG_DATA"
private const val ARG_TITLE = "ARG_TITLE"
private const val ARG_SETTINGS = "ARG_SETTINGS"
fun newInstance(
titleRes: Int,
buildInfo: BuildInfo,
baseUrl: String? = null,
data: String? = null,
showSettings: Boolean = true
): ImprintFragment {
val fragment = ImprintFragment()
fragment.arguments = Bundle().apply {
putString(ARG_BASE_URL, baseUrl)
putSerializable(ARG_BUILD_INFO, buildInfo)
putString(ARG_DATA, data)
putInt(ARG_TITLE, titleRes)
putBoolean(ARG_SETTINGS, showSettings)
}
return fragment
}
}
private var _binding: FragmentHtmlBinding? = null
private val binding get() = _binding!!
private lateinit var baseUrl: String
private var data: String? = null
private var buildInfo: BuildInfo? = null
@StringRes
private var titleRes = 0
private var showSettings = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireArguments().apply {
baseUrl = getString(ARG_BASE_URL) ?: AssetUtil.getImpressumBaseUrl(requireContext())
buildInfo = getSerializable(ARG_BUILD_INFO) as? BuildInfo?
data = getString(ARG_DATA) ?: buildInfo?.let { AssetUtil.getImpressumHtml(requireContext(), it) }
titleRes = getInt(ARG_TITLE)
showSettings = getBoolean(ARG_SETTINGS)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = FragmentHtmlBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = binding.htmlToolbar
toolbar.setTitle(titleRes)
toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
if (showSettings) {
toolbar.inflateMenu(R.menu.imprint)
toolbar.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.menu_settings -> parentFragmentManager.commit {
setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
replace(id, SettingsFragment.newInstance())
addToBackStack(SettingsFragment::class.java.canonicalName)
}
else -> throw UnsupportedOperationException()
}
true
}
}
val web = binding.htmlWebview
val loadingSpinner = binding.loadingSpinner
web.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
loadingSpinner.hideAnimated()
super.onPageFinished(view, url)
}
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
if (baseUrl == url) return true
if (url.toLowerCase(Locale.ENGLISH).startsWith(COVID_CERT_IMPRESSUM_PREFIX)) {
val buildInfo = buildInfo ?: throw IllegalStateException("No BuildInfo supplied for imprint")
val strippedUrl = url.substring(COVID_CERT_IMPRESSUM_PREFIX.length)
val htmlFragment = newInstance(
R.string.impressum_title,
buildInfo,
baseUrl,
AssetUtil.loadImpressumHtmlFile(view.context, strippedUrl, buildInfo),
false
)
parentFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter,
R.anim.slide_pop_exit
)
.replace(id, htmlFragment)
.addToBackStack(ImprintFragment::class.java.canonicalName)
.commit()
return true
}
UrlUtil.openUrl(context, url)
return true
}
}
if (data != null) {
data?.let { web.loadDataWithBaseURL(baseUrl, it, "text/html", "UTF-8", null) }
} else {
web.loadUrl(baseUrl)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/net/ConfigRepository.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.net
import android.content.Context
import android.os.Build
import ch.admin.bag.covidcertificate.common.BuildConfig
import ch.admin.bag.covidcertificate.common.config.ConfigModel
import ch.admin.bag.covidcertificate.common.data.ConfigSecureStorage
import ch.admin.bag.covidcertificate.common.extensions.setTimeouts
import ch.admin.bag.covidcertificate.common.util.AssetUtil
import ch.admin.bag.covidcertificate.sdk.android.CovidCertificateSdk
import ch.admin.bag.covidcertificate.sdk.android.data.Config
import ch.admin.bag.covidcertificate.sdk.android.net.CertificatePinning
import ch.admin.bag.covidcertificate.sdk.android.net.interceptor.JwsInterceptor
import ch.admin.bag.covidcertificate.sdk.android.net.interceptor.UserAgentInterceptor
import ch.admin.bag.covidcertificate.sdk.android.utils.SingletonHolder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Cache
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.HttpException
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
class ConfigRepository private constructor(private val configSpec: ConfigSpec) {
companion object : SingletonHolder<ConfigRepository, ConfigSpec>(::ConfigRepository) {
private const val APP_VERSION_PREFIX_ANDROID = "android-"
private const val OS_VERSION_PREFIX_ANDROID = "android"
private const val MIN_LOAD_WAIT_TIME = 60 * 60 * 1000L // 1h
private const val MAX_AGE_STATUS_VALID_CACHED_CONFIG = 48 * 60 * 60 * 1000L // 48h
fun getCurrentConfig(context: Context) =
ConfigSecureStorage.getInstance(context).getConfig() ?: AssetUtil.loadDefaultConfig(context)
}
private val configService: ConfigService
private val storage = ConfigSecureStorage.getInstance(configSpec.context)
init {
val rootCa = CovidCertificateSdk.getRootCa(configSpec.context)
val expectedCommonName = CovidCertificateSdk.getExpectedCommonName()
val okHttpBuilder = OkHttpClient.Builder()
.certificatePinner(CertificatePinning.pinner)
.addInterceptor(JwsInterceptor(rootCa, expectedCommonName))
.addInterceptor(UserAgentInterceptor(Config.userAgent))
.setTimeouts()
val cacheSize = 5 * 1024 * 1024 // 5 MB
val cache = Cache(configSpec.context.cacheDir, cacheSize.toLong())
okHttpBuilder.cache(cache)
if (BuildConfig.DEBUG) {
val httpInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
okHttpBuilder.addInterceptor(httpInterceptor)
}
configService = Retrofit.Builder()
.baseUrl(configSpec.baseUrl)
.client(okHttpBuilder.build())
.addConverterFactory(MoshiConverterFactory.create())
.build()
.create(ConfigService::class.java)
}
suspend fun loadConfig(context: Context): ConfigModel? {
val requestTimeStamp = System.currentTimeMillis()
val appVersion = APP_VERSION_PREFIX_ANDROID + configSpec.versionName
val osVersion = OS_VERSION_PREFIX_ANDROID + Build.VERSION.SDK_INT
val buildNumber = configSpec.buildTime
val versionString = "appversion=$appVersion&osversion=$osVersion&buildnr=$buildNumber"
var config =
if (storage.getConfigLastSuccessTimestamp() + MIN_LOAD_WAIT_TIME <= System.currentTimeMillis() || versionString != storage.getConfigLastSuccessAppAndOSVersion()) {
try {
val response = withContext(Dispatchers.IO) { configService.getConfig(appVersion, osVersion, buildNumber) }
if (!response.isSuccessful) throw HttpException(response)
response.body()?.let { storage.updateConfigData(it, requestTimeStamp, versionString) }
response.body()
} catch (e: Exception) {
e.printStackTrace()
null
}
} else null
if (config == null) config = storage.getConfig()
if (config == null) config = AssetUtil.loadDefaultConfig(context)
// If the config has disabled the refresh button check if it's the first time and if so, store the timestamp
if (config?.refreshButtonDisabled == true) {
if (storage.getRefreshButtonDisabledTimestamp() < 0L) {
storage.setRefreshButtonDisabledTimestamp(System.currentTimeMillis())
}
} else {
storage.setRefreshButtonDisabledTimestamp(-1L)
}
return config
}
}
class ConfigSpec(val context: Context, val baseUrl: String, val versionName: String, val buildTime: String)
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/net/ConfigService.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.net
import ch.admin.bag.covidcertificate.common.config.ConfigModel
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.Query
interface ConfigService {
@Headers("Accept: application/json+jws")
@GET("config")
suspend fun getConfig(
@Query("appversion") appVersion: String,
@Query("osversion") osVersion: String,
@Query("buildnr") buildNumber: String
): Response<ConfigModel>
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/onboarding/BaseOnboardingActivity.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.onboarding
import android.os.Bundle
import androidx.viewpager2.adapter.FragmentStateAdapter
import ch.admin.bag.covidcertificate.common.BaseActivity
import ch.admin.bag.covidcertificate.common.R
import ch.admin.bag.covidcertificate.common.databinding.ActivityOnboardingBinding
abstract class BaseOnboardingActivity : BaseActivity() {
companion object {
const val EXTRA_ONBOARDING_TYPE = "EXTRA_ONBOARDING_TYPE"
}
private lateinit var binding: ActivityOnboardingBinding
private lateinit var pagerAdapter: FragmentStateAdapter
protected abstract fun getPagerAdapter(): FragmentStateAdapter?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pagerAdapter = getPagerAdapter()
if (pagerAdapter == null) {
finish()
return
}
binding = ActivityOnboardingBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.viewPager.isUserInputEnabled = false
this.pagerAdapter = pagerAdapter
binding.viewPager.adapter = pagerAdapter
}
fun continueToNextPage() {
val currentItem: Int = binding.viewPager.currentItem
if (currentItem < pagerAdapter.itemCount - 1) {
binding.viewPager.setCurrentItem(currentItem + 1, true)
} else {
setResult(RESULT_OK)
finish()
overridePendingTransition(R.anim.fragment_open_enter, R.anim.fragment_open_exit)
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/onboarding/SimpleOnboardingPagerAdapter.kt
================================================
/*
* Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.onboarding
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
class SimpleOnboardingPagerAdapter(
fragmentActivity: FragmentActivity,
private vararg val fragmentProviders: FragmentProvider
) : FragmentStateAdapter(fragmentActivity) {
override fun createFragment(position: Int) = fragmentProviders[position].provide()
override fun getItemCount() = fragmentProviders.size
fun interface FragmentProvider {
fun provide(): Fragment
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/qr/CameraPermissionExplanationDialog.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.qr
import android.content.Context
import android.graphics.Paint
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import ch.admin.bag.covidcertificate.common.R
class CameraPermissionExplanationDialog(context: Context) : AlertDialog(context) {
private var grantCameraAccessClickListener: View.OnClickListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.dialog_camera_permission_explanation)
window?.apply {
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
setBackgroundDrawableResource(R.drawable.bg_dialog)
}
findViewById<TextView>(R.id.camera_permission_dialog_ok_button)?.apply {
paintFlags = paintFlags or Paint.UNDERLINE_TEXT_FLAG
setOnClickListener { v: View? ->
dismiss()
grantCameraAccessClickListener?.onClick(v)
}
}
findViewById<View>(R.id.camera_permission_dialog_close_button)?.setOnClickListener { _ -> cancel() }
}
fun setGrantCameraAccessClickListener(listener: View.OnClickListener?) {
grantCameraAccessClickListener = listener
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/qr/KittlerBinarizer.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.qr
import com.google.zxing.LuminanceSource
import com.google.zxing.common.BitMatrix
import com.google.zxing.common.GlobalHistogramBinarizer
import okhttp3.internal.and
import kotlin.math.abs
/*
*Use a Kittler Binarizer described here //http://www.iztok-jr-fister.eu/static/publications/39.pdf
*
* */
class KittlerBinarizer(luminanceSource: LuminanceSource) : GlobalHistogramBinarizer(luminanceSource) {
override fun getBlackMatrix(): BitMatrix {
val source = luminanceSource
val width = source.width
val height = source.height
var count = 0
var totalcount = 0
val matrix = BitMatrix(width, height)
val localLuminanceSource = source.matrix
val threshold = estimateThreshold(localLuminanceSource, width, height)
(0 until height).forEach { y ->
val offset = y * width
(0 until width).forEach { x ->
totalcount++
val pixel = localLuminanceSource[offset + x] and 0xFF
if (pixel < threshold) {
count++
matrix.set(x, y)
}
}
}
return matrix
}
private fun estimateThreshold(localLuminanceSource: ByteArray, width: Int, height: Int): Int {
var E = 0L
var EF = 0L
(1 until height - 1).forEach { y ->
val offset = y * width
(1 until width - 1).forEach { x ->
val grey = localLuminanceSource[offset + x] and 0xFF
val grey1 = localLuminanceSource[offset + x - 1] and 0xFF
val grey2 = localLuminanceSource[offset + x + 1] and 0xFF
val grey3 = localLuminanceSource[offset + x - width] and 0xFF
val grey4 = localLuminanceSource[offset + x + width] and 0xFF
val Ex = abs(grey1 - grey2)
val Ey = abs(grey3 - grey4)
val exy = Math.max(Ex, Ey)
E += exy
EF += exy * grey
}
}
if (E == 0L) {
return 128
}
return ((EF / E) - 1L).toInt()
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/qr/QRCodeReaderHelper.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.qr
import android.app.ActivityManager
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.pdf.PdfRenderer
import android.os.ParcelFileDescriptor
import com.google.zxing.*
import com.google.zxing.common.GlobalHistogramBinarizer
import com.google.zxing.common.HybridBinarizer
import java.io.File
import java.lang.Integer.min
import kotlin.math.roundToInt
object QRCodeReaderHelper {
private val hints = mapOf(
DecodeHintType.POSSIBLE_FORMATS to arrayListOf(BarcodeFormat.QR_CODE),
DecodeHintType.TRY_HARDER to true,
)
private const val PDF_PAGE_LIMIT = 5
private val reader = MultiFormatReader().apply { setHints(hints) }
fun decodeQrCode(bitmap: Bitmap): String? {
val intArray = IntArray(bitmap.width * bitmap.height)
bitmap.getPixels(intArray, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
val source: LuminanceSource = RGBLuminanceSource(bitmap.width, bitmap.height, intArray)
// First try with hybrid binarizer, then with global histogram binarizer. Same as in the camera scanner
return decodeQrCodeWithBinarizer(HybridBinarizer(source))
?: decodeQrCodeWithBinarizer(GlobalHistogramBinarizer(source))
}
fun pdfToBitmaps(context: Context, pdfFile: File): Sequence<Bitmap> = sequence {
try {
PdfRenderer(ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY)).use { renderer ->
val pageCount = renderer.pageCount
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memoryInfo = ActivityManager.MemoryInfo().also { activityManager.getMemoryInfo(it) }
for (scale in listOf(1f, context.resources.displayMetrics.densityDpi / 72f, 3f, 5f, 7f, 9f)) {
for (i in 0 until min(pageCount, PDF_PAGE_LIMIT)) {
renderer.openPage(i).use { page ->
// PDF width/height are given in "points pt" such that 1 pt = 1/72 inch
// => 72 "dots-per-inch dpi" <==> scale = 1
val pixelWidth = (scale * page.width).roundToInt()
val pixelHeight = (scale * page.height).roundToInt()
if (scale == 1f || !memoryInfo.lowMemory) {
// Only yield the scaled bitmap if the system is not considered to be in low memory mode
// On some devices, this bitmap can get up to 50MB due to the large scale factor
page.renderToBitmap(pixelWidth, pixelHeight).use {
yield(it)
}
}
}
}
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
private fun decodeQrCodeWithBinarizer(binarizer: Binarizer): String? {
val binaryBitmap = BinaryBitmap(binarizer)
try {
val result: Result = reader.decodeWithState(binaryBitmap)
return result.text
} catch (e: NotFoundException) {
e.printStackTrace()
} catch (e: ChecksumException) {
e.printStackTrace()
} catch (e: FormatException) {
e.printStackTrace()
}
return null
}
private fun PdfRenderer.Page.renderToBitmap(pixelWidth: Int, pixelHeight: Int): Bitmap {
val bitmap = Bitmap.createBitmap(pixelWidth, pixelHeight, Bitmap.Config.ARGB_8888)
// Make sure the bitmap's background is not transparent (which can cause issues for QR code detection)
bitmap.eraseColor(Color.WHITE)
// Draw the page onto the bitmap. Internally, this will scale the page to fit the bitmap (unless transform != null).
this.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT)
// reduce pixel size to avoid memory issues down the line
val bitmapR = bitmap.copy(Bitmap.Config.RGB_565, false)
bitmap.recycle()
return bitmapR
}
private inline fun Bitmap.use(block: (Bitmap) -> Unit) {
try {
block(this)
} finally {
this.recycle()
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/qr/QrScanFragment.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.qr
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
import android.os.Bundle
import android.view.View
import android.widget.ImageButton
import android.widget.TextView
import androidx.annotation.ColorRes
import androidx.appcompat.widget.Toolbar
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.liveData
import ch.admin.bag.covidcertificate.common.R
import ch.admin.bag.covidcertificate.common.data.ConfigSecureStorage
import ch.admin.bag.covidcertificate.common.util.ErrorHelper
import ch.admin.bag.covidcertificate.common.util.ErrorState
import ch.admin.bag.covidcertificate.sdk.core.models.state.StateError
import ch.ubique.qrscanner.scanner.ErrorCodes
import ch.ubique.qrscanner.scanner.ScanningMode
import ch.ubique.qrscanner.state.DecodingState
import ch.ubique.qrscanner.util.CameraUtil
import ch.ubique.qrscanner.view.QrScannerView
import ch.ubique.qrscanner.zxing.decoder.GlobalHistogramImageDecoder
import ch.ubique.qrscanner.zxing.decoder.HybridImageDecoder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
abstract class QrScanFragment : Fragment() {
companion object {
private const val STATE_IS_TORCH_ON = "STATE_IS_TORCH_ON"
private const val PERMISSION_REQUEST_CAMERA = 13
private const val MIN_ERROR_VISIBILITY = 1000L
private const val QR_CODE_ERROR_WRONG_FORMAT = "Q|YWF"
private const val QR_CODE_ERROR_READ_FAILED = "Q|IRF"
}
// These need to be set by implementing classes during onCreateView. That's why they are not private.
protected lateinit var toolbar: Toolbar
protected lateinit var flashButton: ImageButton
protected lateinit var errorView: View
protected lateinit var errorCodeView: TextView
protected lateinit var zoomButton: ImageButton
protected lateinit var invalidCodeText: TextView
protected lateinit var viewFinderTopLeftIndicator: View
protected lateinit var viewFinderTopRightIndicator: View
protected lateinit var viewFinderBottomLeftIndicator: View
protected lateinit var viewFinderBottomRightIndicator: View
protected lateinit var qrCodeScanner: QrScannerView
protected lateinit var cutOut: View
abstract val viewFinderColor: Int
abstract val viewFinderErrorColor: Int
abstract val torchOnDrawable: Int
abstract val torchOffDrawable: Int
abstract val zoomOnDrawable: Int
abstract val zoomOffDrawable: Int
private var lastUIErrorUpdate = 0L
private var cameraPermissionState = CameraPermissionState.REQUESTING
private val secureStorage by lazy { ConfigSecureStorage.getInstance(requireContext()) }
private var cameraPermissionExplanationDialog: CameraPermissionExplanationDialog? = null
private var isTorchOn: Boolean = false
private val autoFocusClockLiveData = liveData(Dispatchers.IO) {
while (currentCoroutineContext().isActive) {
emit(Unit)
delay(3 * 1000L)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
isTorchOn = savedInstanceState?.getBoolean(STATE_IS_TORCH_ON, isTorchOn) ?: isTorchOn
toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
// Wait for the views to be properly laid out
qrCodeScanner.post {
initializeCamera()
}
}
override fun onResume() {
super.onResume()
// Check permission in onResume to automatically handle the user returning from the system settings.
// Be careful to avoid popup loops, since our fragment is resumed whenever the user returns from the dialog!
checkCameraPermission()
setFlashAndButtonStyle()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(STATE_IS_TORCH_ON, isTorchOn)
}
abstract fun decodeQrCodeData(qrCodeData: String, onDecodeSuccess: () -> Unit, onDecodeError: (StateError) -> Unit)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
if (requestCode == PERMISSION_REQUEST_CAMERA) {
val isGranted = grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
cameraPermissionState = if (isGranted) CameraPermissionState.GRANTED else CameraPermissionState.DENIED
refreshView()
}
}
protected fun activateCamera() {
if (!isAdded || !qrCodeScanner.isAttachedToWindow) return
autoFocusClockLiveData.observe(viewLifecycleOwner) {
autoFocus()
}
qrCodeScanner.activateCamera()
}
protected fun deactivateCamera() {
if (!isAdded || !qrCodeScanner.isAttachedToWindow) return
autoFocusClockLiveData.removeObservers(viewLifecycleOwner)
qrCodeScanner.deactivateCamera()
}
@SuppressLint("ClickableViewAccessibility")
private fun initializeCamera() {
if (!isAdded || !qrCodeScanner.isAttachedToWindow) return
setScannerCallback()
qrCodeScanner.setImageDecoders(GlobalHistogramImageDecoder(), HybridImageDecoder())
qrCodeScanner.setScanningMode(ScanningMode.PARALLEL)
qrCodeScanner.setFocusOnTap(true)
qrCodeScanner.setCameraStateCallback { isActive ->
if (isActive) {
qrCodeScanner.isVisible = true
setupZoomButton()
setupFlashButton()
} else {
qrCodeScanner.isVisible = false
}
}
}
protected fun setScannerCallback() {
qrCodeScanner.setScannerCallback { state ->
when (state) {
is DecodingState.NotFound -> view?.post { updateQrCodeScannerState(QrScannerState.NO_CODE_FOUND) }
is DecodingState.Decoded -> {
val qrCodeData = state.content
decodeQrCodeData(
qrCodeData,
onDecodeSuccess = {
// Once successfully decoded, clear the analyzer from stopping more frames being
// analyzed and possibly decoded successfully
qrCodeScanner.setScannerCallback(null)
view?.post { updateQrCodeScannerState(QrScannerState.VALID) }
},
onDecodeError = { error ->
view?.post { handleInvalidQRCodeExceptions(error) }
}
)
}
is DecodingState.Error -> {
val stateError = if (state.errorCode == ErrorCodes.INPUT_WRONG_FORMAT) {
StateError(QR_CODE_ERROR_WRONG_FORMAT)
} else {
StateError(QR_CODE_ERROR_READ_FAILED)
}
handleInvalidQRCodeExceptions(stateError)
}
}
}
}
private fun setZoom() {
if (secureStorage.getZoomOn()) {
qrCodeScanner.setLinearZoom(1f)
} else {
qrCodeScanner.setLinearZoom(0f)
}
}
private fun autoFocus() {
val centerX = cutOut.left + cutOut.width / 2.0f
val centerY = cutOut.top + cutOut.height / 2.0f
qrCodeScanner.startAutofocus(centerX, centerY)
}
private fun checkCameraPermission() {
val isGranted = CameraUtil.hasCameraPermission(requireContext())
if (isGranted) {
cameraPermissionState = CameraPermissionState.GRANTED
}
// Do not request the permission again if the last time we tried the user denied it.
// I.e. don't show the popup but the error view
else if (cameraPermissionState != CameraPermissionState.DENIED) {
cameraPermissionState = CameraPermissionState.REQUESTING
}
refreshView()
}
private fun refreshView() {
when (cameraPermissionState) {
CameraPermissionState.GRANTED -> {
errorView.isVisible = false
}
CameraPermissionState.REQUESTING -> {
errorView.isVisible = false
showCameraPermissionExplanationDialog()
}
CameraPermissionState.CANCELLED, CameraPermissionState.DENIED -> {
errorView.isVisible = true
ErrorHelper.updateErrorView(errorView, ErrorState.CAMERA_ACCESS_DENIED, null, context)
}
}
updateQrCodeScannerState(QrScannerState.NO_CODE_FOUND)
}
private fun showCameraPermissionExplanationDialog() {
if (cameraPermissionExplanationDialog?.isShowing == true) {
return
}
cameraPermissionExplanationDialog = CameraPermissionExplanationDialog(requireContext()).apply {
setOnCancelListener {
cameraPermissionState = CameraPermissionState.CANCELLED
refreshView()
}
setGrantCameraAccessClickListener {
requestPermissions(arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CAMERA)
}
setOnDismissListener {
cameraPermissionExplanationDialog = null
}
show()
}
}
private fun setupFlashButton() {
val cameraInfo = qrCodeScanner.getCameraInfo() ?: return
if (cameraInfo.hasFlashUnit()) {
flashButton.isVisible = true
setFlashAndButtonStyle()
} else {
flashButton.isVisible = false
}
flashButton.setOnClickListener {
isTorchOn = !flashButton.isSelected
setFlashAndButtonStyle()
}
zoomButton.setOnClickListener {
secureStorage.setZoomOn(!secureStorage.getZoomOn())
setupZoomButton()
}
}
private fun setFlashAndButtonStyle() {
qrCodeScanner.setFlash(isTorchOn)
val drawableId = if (isTorchOn) torchOnDrawable else torchOffDrawable
flashButton.isSelected = isTorchOn
flashButton.setImageResource(drawableId)
}
private fun setupZoomButton() {
if (requireContext().packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS)) {
zoomButton.isVisible = false
} else {
val isZoomOn = secureStorage.getZoomOn()
zoomButton.isVisible = true
val drawableId = if (isZoomOn) zoomOnDrawable else zoomOffDrawable
zoomButton.isSelected = isZoomOn
zoomButton.setImageResource(drawableId)
setZoom()
}
}
private fun handleInvalidQRCodeExceptions(error: StateError?) {
updateQrCodeScannerState(QrScannerState.INVALID_FORMAT, error?.code)
}
private fun updateQrCodeScannerState(qrScannerState: QrScannerState, errorCode: String? = null) {
if (!isAdded) return
val currentTime = System.currentTimeMillis()
if (lastUIErrorUpdate > currentTime - MIN_ERROR_VISIBILITY && qrScannerState == QrScannerState.NO_CODE_FOUND) {
return
}
lastUIErrorUpdate = currentTime
var color: Int = viewFinderColor
when (qrScannerState) {
QrScannerState.VALID, QrScannerState.NO_CODE_FOUND -> {
invalidCodeText.isVisible = false
errorCodeView.isVisible = false
}
QrScannerState.INVALID_FORMAT -> {
invalidCodeText.isVisible = true
errorCodeView.isVisible = errorCode != null
errorCodeView.text = errorCode
color = viewFinderErrorColor
}
}
setIndicatorColor(viewFinderTopLeftIndicator, color)
setIndicatorColor(viewFinderTopRightIndicator, color)
setIndicatorColor(viewFinderBottomLeftIndicator, color)
setIndicatorColor(viewFinderBottomRightIndicator, color)
}
private fun setIndicatorColor(indicator: View, @ColorRes color: Int) {
val drawable = indicator.background as LayerDrawable
val stroke = drawable.findDrawableByLayerId(R.id.indicator) as GradientDrawable
stroke.setStroke(
resources.getDimensionPixelSize(R.dimen.qr_scanner_indicator_stroke_width),
resources.getColor(color, null)
)
}
enum class CameraPermissionState {
GRANTED,
REQUESTING,
CANCELLED,
DENIED,
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/qr/QrScannerState.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.qr
enum class QrScannerState {
NO_CODE_FOUND,
VALID,
INVALID_FORMAT,
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/settings/SettingsFragment.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.settings
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import ch.admin.bag.covidcertificate.common.R
import ch.admin.bag.covidcertificate.common.data.ConfigSecureStorage
import ch.admin.bag.covidcertificate.common.databinding.FragmentSettingsBinding
import ch.admin.bag.covidcertificate.common.databinding.ItemLanguageOptionBinding
import ch.admin.bag.covidcertificate.common.util.LocaleUtil.DEFAULT_COUNTRY
import java.util.*
class SettingsFragment : Fragment() {
companion object {
fun newInstance(): SettingsFragment {
return SettingsFragment()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return FragmentSettingsBinding.inflate(inflater, container, false).apply {
settingsToolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
val currentLanguage = getString(R.string.language_key)
listOf("de", "fr", "it", "rm", "en").forEach { language ->
ItemLanguageOptionBinding.inflate(inflater, languageList, true).apply {
val locale = Locale(language, DEFAULT_COUNTRY)
radiobutton.text = locale.getDisplayLanguage(locale).capitalize(locale)
radiobutton.isChecked = locale.language == currentLanguage
radiobutton.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
updateLanguage(language)
}
}
}
}
}.root
}
private fun updateLanguage(language: String) {
ConfigSecureStorage.getInstance(requireContext()).setUserLanguage(language)
requireActivity().recreate()
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/AssetUtil.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util
import android.content.Context
import ch.admin.bag.covidcertificate.common.R
import ch.admin.bag.covidcertificate.common.config.ConfigModel
import ch.admin.bag.covidcertificate.common.html.BuildInfo
import com.squareup.moshi.Moshi
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.text.SimpleDateFormat
import java.util.*
object AssetUtil {
private const val PREFIX_ASSET_FILE = "file:///android_asset/"
private const val FOLDER_NAME_IMPRESSUM = "impressum/"
private const val FILE_NAME_IMPRESSUM = "impressum.html"
private const val FOLDER_NAME_DISCLAIMER = "disclaimer/"
private const val DISCLAIMER_FALLBACK_LANGUAGE = "de"
private const val FILE_NAME_DATA_PROTECTION_STATEMENT = "data_protection_statement.html"
private const val FILE_NAME_TERMS_OF_USE = "terms_of_use.html"
private const val REPLACE_STRING_APP_NAME = "{APP_NAME}"
private const val REPLACE_STRING_VERSION = "{VERSION}"
private const val REPLACE_STRING_APPVERSION = "{APPVERSION}"
private const val REPLACE_STRING_RELEASEDATE = "{RELEASEDATE}"
private const val REPLACE_STRING_BUILDNR = "{BUILD}"
private const val REPLACE_STRING_LAW_LINK = "{LAW_LINK}"
private const val REPLACE_STRING_APP_IDENTIFIER = "{PARAM_APP_IDENTIFIER}"
private val RELEASE_DATE_FORMAT = SimpleDateFormat("dd.MM.yyyy").apply { timeZone = TimeZone.getTimeZone("Europe/Zurich") }
private const val ASSET_FILENAME_DEFAULT_CONFIG = "faq/config.json"
private fun loadAssetJson(context: Context, filename: String): String? {
return try {
val inputStream = context.assets.open(filename)
val json = inputStream.bufferedReader().use { it.readText() }
inputStream.close()
json
} catch (ex: IOException) {
ex.printStackTrace()
null
}
}
fun loadDefaultConfig(context: Context): ConfigModel? = loadAssetJson(context, ASSET_FILENAME_DEFAULT_CONFIG)?.let {
Moshi.Builder().build().adapter(ConfigModel::class.java).fromJson(it)
}
fun getImpressumBaseUrl(context: Context): String {
return PREFIX_ASSET_FILE + getFolderNameImpressum(context)
}
private fun getFolderNameImpressum(context: Context): String {
return FOLDER_NAME_IMPRESSUM + context.getString(R.string.language_key) + "/"
}
fun getImpressumHtml(context: Context, buildInfo: BuildInfo): String? {
return loadImpressumHtmlFile(context, FILE_NAME_IMPRESSUM, buildInfo)
}
fun loadImpressumHtmlFile(
context: Context,
filename: String,
buildInfo: BuildInfo,
): String? {
return try {
val html = StringBuilder()
BufferedReader(InputStreamReader(context.assets.open(getFolderNameImpressum(context) + filename))).use { reader ->
var line: String?
while (reader.readLine().also { line = it } != null) {
html.append(line)
}
}
var impressum = html.toString()
val buildString = "${buildInfo.buildTime} / ${buildInfo.flavor}"
impressum = impressum.replace(REPLACE_STRING_VERSION, buildInfo.versionName)
impressum = impressum.replace(REPLACE_STRING_APPVERSION, buildInfo.versionName)
impressum = impressum.replace(REPLACE_STRING_RELEASEDATE, RELEASE_DATE_FORMAT.format(buildInfo.buildTime))
impressum = impressum.replace(REPLACE_STRING_BUILDNR, buildString)
impressum = impressum.replace(REPLACE_STRING_APP_NAME, buildInfo.appName)
impressum = impressum.replace(REPLACE_STRING_LAW_LINK, buildInfo.agbUrl)
impressum = impressum.replace(REPLACE_STRING_APP_IDENTIFIER, buildInfo.appIdentifier)
impressum
} catch (e: IOException) {
e.printStackTrace()
""
}
}
private fun getFolderNameDisclaimer(context: Context): String {
return FOLDER_NAME_DISCLAIMER + context.getString(R.string.language_key) + "/"
}
private fun getDefaultLanguageFolderNameDisclaimer(): String {
return "$FOLDER_NAME_DISCLAIMER$DISCLAIMER_FALLBACK_LANGUAGE/"
}
fun getTermsOfUse(context: Context): String {
var htmlString = loadHtml(
context,
getFolderNameDisclaimer(context) + FILE_NAME_TERMS_OF_USE
)
if (htmlString == null) htmlString = loadHtml(
context,
getDefaultLanguageFolderNameDisclaimer() + FILE_NAME_TERMS_OF_USE
)
if (htmlString == null) htmlString = ""
return replaceUlTags(htmlString)
}
fun getDataProtection(context: Context): String {
var htmlString = loadHtml(
context,
getFolderNameDisclaimer(context) + FILE_NAME_DATA_PROTECTION_STATEMENT
)
if (htmlString == null) htmlString = loadHtml(
context,
getDefaultLanguageFolderNameDisclaimer() + FILE_NAME_DATA_PROTECTION_STATEMENT
)
if (htmlString == null) htmlString = ""
return replaceUlTags(htmlString)
}
private fun replaceUlTags(htmlString: String): String {
return htmlString.replace("<ul>", "<myul>").replace("</ul>", "</myul>").replace("<li>", "<myli>")
.replace("</li>", "</myli>")
}
private fun loadHtml(context: Context, path: String): String? {
return try {
val html = StringBuilder()
BufferedReader(InputStreamReader(context.assets.open(path))).use { reader ->
var line: String?
while (reader.readLine().also { line = it } != null) {
html.append(line)
}
}
html.toString()
} catch (e: IOException) {
e.printStackTrace()
null
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/CutOutEdgeTreatment.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util
import com.google.android.material.shape.EdgeTreatment
import com.google.android.material.shape.ShapePath
import kotlin.math.atan
import kotlin.math.sqrt
class CutOutEdgeTreatment(
private val radius: Float,
private val positionPercentage: Float
) : EdgeTreatment() {
override fun getEdgePath(length: Float, center: Float, interpolation: Float, shapePath: ShapePath) {
val cutOutCenter = length * positionPercentage
// Calculate the vertical offset of the cut out. When interpolating the edge, the offset is the perpendicular distance
// from the cut out center to the edge. When fully interpolated (1.0), the offset will be zero, while at zero interpolation
// the offset will be equal to the radius
val verticalOffset = (1.0f - interpolation) * radius
val verticalOffsetRatio = verticalOffset / radius
if (verticalOffsetRatio >= 1.0f) {
// The vertical offset is so high that there would be no cut out drawn in the edge, so just draw a straight line along the edge
shapePath.lineTo(length, 0.0f)
return
}
// Calculate the X distance between the center and edge of the cut out, taking the interpolation value into consideration
// When fully interpolated (1.0) the radiusX is the same as the radius
val squaredRadius = radius * radius
val verticalOffsetSquared = verticalOffset * verticalOffset
val radiusX = sqrt((squaredRadius - verticalOffsetSquared).toDouble()).toFloat()
// Draw a line from the start of the edge to the start of the cut out
val cutOutStartX = cutOutCenter - radiusX
shapePath.lineTo(cutOutStartX, 0.0f)
// Calculate the arc of the cut out
val cornerRadiusArcLength = Math.toDegrees(atan((radiusX / verticalOffset).toDouble())).toFloat()
val cutoutArcOffset = 90.0f - cornerRadiusArcLength
// Draw the cut out arc
shapePath.addArc(
cutOutCenter - radius,
-radius - verticalOffset,
cutOutCenter + radius,
radius - verticalOffset,
180.0f - cutoutArcOffset,
cutoutArcOffset * 2.0f - 180.0f
)
// Draw a line from the end of the cut out to the end of the edge
shapePath.lineTo(length, 0.0f)
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/EnvironmentUtil.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util
import ch.admin.bag.covidcertificate.common.BuildConfig
import ch.admin.bag.covidcertificate.sdk.android.SdkEnvironment
object EnvironmentUtil {
fun getSdkEnvironment(flavor: String = BuildConfig.FLAVOR) = when (flavor) {
"dev" -> SdkEnvironment.DEV
"abn" -> SdkEnvironment.ABN
"prod" -> SdkEnvironment.PROD
else -> throw IllegalArgumentException("Unknown environment $flavor")
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/ErrorCodeUtil.kt
================================================
package ch.admin.bag.covidcertificate.common.util
import ch.admin.bag.covidcertificate.sdk.core.models.state.CheckNationalRulesState
import ch.admin.bag.covidcertificate.sdk.core.models.state.CheckRevocationState
import ch.admin.bag.covidcertificate.sdk.core.models.state.CheckSignatureState
import ch.admin.bag.covidcertificate.sdk.core.models.state.VerificationState
fun VerificationState.getInvalidErrorCode(errorDelimiter: String = ", ", showNationalErrors: Boolean = false): String {
val errorCodes = mutableListOf<String>()
if (this !is VerificationState.INVALID) return ""
val signatureState = signatureState
if (signatureState is CheckSignatureState.INVALID) {
errorCodes.add(signatureState.signatureErrorCode)
}
val revocationState = revocationState
if (revocationState is CheckRevocationState.INVALID) {
errorCodes.add(revocationState.revocationErrorCode)
}
val nationalRulesState = nationalRulesState
if (showNationalErrors && nationalRulesState is CheckNationalRulesState.INVALID) {
nationalRulesState.nationalRulesError?.errorCode?.let { errorCodes.add(it) }
}
return errorCodes.joinToString(errorDelimiter)
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/ErrorHelper.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.graphics.Paint
import android.net.Uri
import android.provider.Settings
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import ch.admin.bag.covidcertificate.common.R
object ErrorHelper {
@JvmOverloads
fun updateErrorView(
errorView: View,
errorState: ErrorState,
customButtonClickAction: Runnable?,
context: Context?,
showButton: Boolean = true
) {
errorView.findViewById<TextView>(R.id.error_status_title).setText(errorState.titleResId)
errorView.findViewById<TextView>(R.id.error_status_text).setText(errorState.textResId)
errorView.findViewById<ImageView>(R.id.error_status_image)
.setImageDrawable(ContextCompat.getDrawable(errorView.context, errorState.imageResId))
val buttonView = errorView.findViewById<TextView>(R.id.error_status_button)
if (showButton) {
buttonView.visibility = View.VISIBLE
buttonView.setText(errorState.actionResId)
buttonView.paintFlags = buttonView.paintFlags or Paint.UNDERLINE_TEXT_FLAG
buttonView.setOnClickListener { executeErrorAction(errorState, customButtonClickAction, context) }
} else {
buttonView.visibility = View.GONE
}
}
private fun executeErrorAction(errorState: ErrorState, customButtonClickAction: Runnable?, context: Context?) {
customButtonClickAction?.run()
when (errorState) {
ErrorState.CAMERA_ACCESS_DENIED -> openApplicationSettings(context)
else -> {}
}
}
private fun openApplicationSettings(context: Context?) {
val context = context ?: return
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", context.packageName, null)
intent.data = uri
try {
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, "Could not open settings", Toast.LENGTH_LONG).show()
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/ErrorState.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import ch.admin.bag.covidcertificate.common.R
enum class ErrorState(
@field:StringRes @param:StringRes val titleResId: Int,
@field:StringRes @param:StringRes val textResId: Int,
@field:StringRes @param:StringRes val actionResId: Int,
@field:DrawableRes @param:DrawableRes val imageResId: Int
) {
NETWORK(
R.string.error_network_title,
R.string.error_network_text,
R.string.error_action_retry,
R.drawable.ic_error_triangle
),
CAMERA_ACCESS_DENIED(
R.string.error_camera_permission_title,
R.string.error_camera_permission_text,
R.string.error_action_change_settings,
R.drawable.ic_cam_off
),
NO_VALID_QR_CODE(
R.string.error_title,
R.string.qr_scanner_error,
R.string.ok_button,
R.drawable.ic_error_triangle
);
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/HorizontalMarginItemDecoration.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util
import android.content.Context
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class HorizontalMarginItemDecoration(context: Context, val horizontalMarginInPx: Int) :
RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
) {
outRect.right = horizontalMarginInPx
outRect.left = horizontalMarginInPx
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/LocaleUtil.kt
================================================
package ch.admin.bag.covidcertificate.common.util
import android.content.Context
import android.content.res.Configuration
import ch.admin.bag.covidcertificate.common.R
import java.util.*
object LocaleUtil {
const val DEFAULT_COUNTRY = "CH"
fun isSystemLangNotEnglish(context: Context): Boolean {
return context.getString(R.string.language_key) != "en"
}
fun updateLanguage(context: Context, language: String?): Context {
if (!language.isNullOrEmpty()) {
val config = Configuration()
config.setLocale(Locale(language, DEFAULT_COUNTRY))
return context.createConfigurationContext(config)
}
return context
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/SingleLiveEvent.java
================================================
/*
* Copyright 2017 Google Inc.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.admin.bag.covidcertificate.common.util;
import android.util.Log;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*/
public class SingleLiveEvent<T> extends MutableLiveData<T> {
private static final String TAG = "SingleLiveEvent";
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(LifecycleOwner owner, final Observer<? super T> observer) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
}
// Observe the internal MutableLiveData
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/StringUtil.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util
import android.graphics.Typeface
import android.text.SpannableString
import android.text.style.StyleSpan
import ch.admin.bag.covidcertificate.sdk.android.extensions.DEFAULT_DISPLAY_DATE_FORMATTER
import ch.admin.bag.covidcertificate.sdk.android.extensions.DEFAULT_DISPLAY_DATE_TIME_FORMATTER
import java.time.LocalDateTime
fun String.makeBold(): SpannableString = SpannableString(this).apply {
setSpan(StyleSpan(Typeface.BOLD), 0, this@makeBold.length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
}
fun String.makeSubStringBold(
subString: String,
startIndex: Int = 0,
ignoreCase: Boolean = true
): SpannableString =
SpannableString(this).apply {
val indexSubString = this@makeSubStringBold.indexOf(subString, startIndex, ignoreCase)
if (indexSubString >= 0) {
setSpan(
StyleSpan(Typeface.BOLD),
indexSubString,
indexSubString + subString.length,
SpannableString.SPAN_INCLUSIVE_EXCLUSIVE
)
}
}
fun String.makeSubStringsBold(
subStrings: List<String>,
startIndex: Int = 0,
ignoreCase: Boolean = true
): SpannableString = SpannableString(this).apply {
subStrings.forEach {
val indexSubString = this@makeSubStringsBold.indexOf(it, startIndex, ignoreCase)
if (indexSubString >= 0) {
setSpan(
StyleSpan(Typeface.BOLD),
indexSubString,
indexSubString + it.length,
SpannableString.SPAN_INCLUSIVE_EXCLUSIVE
)
}
}
}
fun String.addBoldDate(dateReplacementString: String, date: LocalDateTime): SpannableString {
val dateString = date.format(DEFAULT_DISPLAY_DATE_FORMATTER)
return SpannableString(this.replace(dateReplacementString, dateString)).apply {
val indexSubString = this.indexOf(dateString)
if (indexSubString >= 0) {
setSpan(
StyleSpan(Typeface.BOLD),
indexSubString,
indexSubString + dateString.length,
SpannableString.SPAN_INCLUSIVE_EXCLUSIVE
)
}
}
}
fun String.addBoldDateTime(dateTimeReplacementString: String, dateTime: LocalDateTime): SpannableString {
val dateTimeString = dateTime.format(DEFAULT_DISPLAY_DATE_TIME_FORMATTER)
return SpannableString(this.replace(dateTimeReplacementString, dateTimeString)).apply {
val indexSubString = this.indexOf(dateTimeString)
if (indexSubString >= 0) {
setSpan(
StyleSpan(Typeface.BOLD),
indexSubString,
indexSubString + dateTimeString.length,
SpannableString.SPAN_INCLUSIVE_EXCLUSIVE
)
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/UiUtil.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util
import android.view.Window
import android.view.WindowManager
private val allowlistedFlavours = listOf("abn", "dev")
fun Window.setSecureFlagToBlockScreenshots(flavor: String) {
if (!allowlistedFlavours.contains(flavor)) {
setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/UlTagHandler.java
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util;
import android.text.Editable;
import android.text.Html;
import android.text.Spanned;
import android.text.style.BulletSpan;
import org.xml.sax.XMLReader;
public class UlTagHandler implements Html.TagHandler {
private static final int indent = 30;
public static String LI = "myli";
@Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equals(LI)) {
if (opening) {
if (output.length() > 0 && output.charAt(output.length() - 1) != '\n') {
output.append("\n");
}
start(output, new Ul());
} else {
if (output.charAt(output.length() - 1) != '\n') {
output.append("\n");
}
BulletSpan newBullet = new BulletSpan(indent);
end(output, Ul.class, newBullet);
}
}
}
private static void start(Editable text, Object mark) {
int len = text.length();
text.setSpan(mark, len, len, Spanned.SPAN_MARK_MARK);
}
private static void end(Editable text, Class<?> kind, Object... replaces) {
int len = text.length();
Object obj = getLast(text, kind);
int where = text.getSpanStart(obj);
text.removeSpan(obj);
if (where != len) {
for (Object replace : replaces) {
text.setSpan(replace, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private static Object getLast(Spanned text, Class<?> kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
}
return objs[objs.length - 1];
}
private static class Ul { }
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/UrlUtil.java
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.util;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
public class UrlUtil {
public static void openUrl(Context context, String url) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, "No browser installed", Toast.LENGTH_LONG).show();
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/views/MarginItemDecoration.kt
================================================
package ch.admin.bag.covidcertificate.common.views
import android.content.Context
import android.graphics.Rect
import android.view.View
import androidx.annotation.DimenRes
import androidx.recyclerview.widget.RecyclerView
class VerticalMarginItemDecoration(context: Context, @DimenRes marginRes: Int) : RecyclerView.ItemDecoration() {
private val marginPx = context.resources.getDimensionPixelSize(marginRes)
override fun getItemOffsets(
outRect: Rect, view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
with(outRect) {
if (parent.getChildAdapterPosition(view) != 0) {
top = marginPx
}
}
}
}
================================================
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/views/ViewExtensions.kt
================================================
/*
* Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package ch.admin.bag.covidcertificate.common.views
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.res.ColorStateList
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import ch.admin.bag.covidcertificate.common.R
import ch.admin.bag.covidcertificate.common.util.CutOutEdgeTreatment
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
fun View.showAnimated(
duration: Long = resources.getInteger(android.R.integer.config_shortAnimTime).toLong(),
fade: Boolean = true
) {
animation?.cancel()
if (visibility == View.VISIBLE) return
visibility = View.VISIBLE
alpha = if (fade) 0f else 1f
animate()
.setDuration(duration)
.alpha(1f)
.setInterpolator(DecelerateInterpolator())
.setListener(null)
gitextract_e7z7c8gy/
├── .github/
│ ├── actions/
│ │ └── gradle_docker/
│ │ ├── action.yml
│ │ └── main.sh
│ └── workflows/
│ ├── appcenter_verifier_abn.yml
│ ├── appcenter_verifier_dev.yml
│ ├── appcenter_verifier_prod.yml
│ ├── appcenter_verifier_prodfdroid.yml
│ ├── appcenter_wallet_abn.yml
│ ├── appcenter_wallet_dev.yml
│ ├── appcenter_wallet_prod.yml
│ ├── appcenter_wallet_prodfdroid.yml
│ ├── browserstack_wallet_abn.yml
│ ├── build.yml
│ └── gradle-wrapper-validation.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── REPRODUCIBLE_BUILDS.md
├── apkdiff.py
├── build.gradle
├── buildAndCompare.sh
├── common/
│ ├── .gitignore
│ ├── build.gradle
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src/
│ ├── abn/
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── common/
│ │ └── debug/
│ │ └── DebugFragment.kt
│ ├── dev/
│ │ ├── java/
│ │ │ └── ch/
│ │ │ └── admin/
│ │ │ └── bag/
│ │ │ └── covidcertificate/
│ │ │ └── common/
│ │ │ └── debug/
│ │ │ ├── DebugFragment.kt
│ │ │ └── DebugSecureStorage.kt
│ │ └── res/
│ │ └── xml/
│ │ └── network_security_config.xml
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ └── impressum/
│ │ │ ├── de/
│ │ │ │ ├── impressum.html
│ │ │ │ └── licence.html
│ │ │ ├── en/
│ │ │ │ ├── impressum.html
│ │ │ │ └── licence.html
│ │ │ ├── fonts/
│ │ │ │ ├── Inter-Bold.otf
│ │ │ │ └── Inter-Light.otf
│ │ │ ├── fr/
│ │ │ │ ├── impressum.html
│ │ │ │ └── licence.html
│ │ │ ├── it/
│ │ │ │ ├── impressum.html
│ │ │ │ └── licence.html
│ │ │ └── rm/
│ │ │ ├── impressum.html
│ │ │ └── licence.html
│ │ ├── java/
│ │ │ └── ch/
│ │ │ └── admin/
│ │ │ └── bag/
│ │ │ └── covidcertificate/
│ │ │ └── common/
│ │ │ ├── BaseActivity.kt
│ │ │ ├── browserstack/
│ │ │ │ ├── AirplaneMode.kt
│ │ │ │ ├── BadNetwork.kt
│ │ │ │ ├── Normal.kt
│ │ │ │ └── Onboarding.kt
│ │ │ ├── config/
│ │ │ │ ├── CertificateRenewalInfoDetailModel.kt
│ │ │ │ ├── CertificateRenewalInfoModel.kt
│ │ │ │ ├── CertificateRenewalType.kt
│ │ │ │ ├── CheckModesInfosModel.kt
│ │ │ │ ├── ConfigModel.kt
│ │ │ │ ├── ConfigViewModel.kt
│ │ │ │ ├── CovidCertificateNewsItem.kt
│ │ │ │ ├── EolBannerInfoModel.kt
│ │ │ │ ├── FaqEntryModel.kt
│ │ │ │ ├── FaqIntroSection.kt
│ │ │ │ ├── FaqModel.kt
│ │ │ │ ├── ForeignRulesHintModel.kt
│ │ │ │ ├── InfoBoxModel.kt
│ │ │ │ ├── InfoCovidCertificateNews.kt
│ │ │ │ ├── RefreshButtonInfoModel.kt
│ │ │ │ ├── VaccinationBookingInfoModel.kt
│ │ │ │ └── VaccinationHintModel.kt
│ │ │ ├── data/
│ │ │ │ └── ConfigSecureStorage.kt
│ │ │ ├── dialog/
│ │ │ │ └── InfoDialogFragment.kt
│ │ │ ├── exception/
│ │ │ │ ├── HttpIOException.kt
│ │ │ │ └── TimeDeviationException.kt
│ │ │ ├── extensions/
│ │ │ │ ├── ContextExtensions.kt
│ │ │ │ ├── DccCertExtensions.kt
│ │ │ │ ├── LifecycleOwnerExtensions.kt
│ │ │ │ ├── OkHttpExtensions.kt
│ │ │ │ └── WindowExtensions.kt
│ │ │ ├── faq/
│ │ │ │ ├── FaqAdapter.kt
│ │ │ │ ├── FaqFragment.kt
│ │ │ │ ├── FaqItem.kt
│ │ │ │ ├── FaqViewHolder.kt
│ │ │ │ └── model/
│ │ │ │ └── Header.kt
│ │ │ ├── html/
│ │ │ │ ├── BuildInfo.kt
│ │ │ │ └── ImprintFragment.kt
│ │ │ ├── net/
│ │ │ │ ├── ConfigRepository.kt
│ │ │ │ └── ConfigService.kt
│ │ │ ├── onboarding/
│ │ │ │ ├── BaseOnboardingActivity.kt
│ │ │ │ └── SimpleOnboardingPagerAdapter.kt
│ │ │ ├── qr/
│ │ │ │ ├── CameraPermissionExplanationDialog.kt
│ │ │ │ ├── KittlerBinarizer.kt
│ │ │ │ ├── QRCodeReaderHelper.kt
│ │ │ │ ├── QrScanFragment.kt
│ │ │ │ └── QrScannerState.kt
│ │ │ ├── settings/
│ │ │ │ └── SettingsFragment.kt
│ │ │ ├── util/
│ │ │ │ ├── AssetUtil.kt
│ │ │ │ ├── CutOutEdgeTreatment.kt
│ │ │ │ ├── EnvironmentUtil.kt
│ │ │ │ ├── ErrorCodeUtil.kt
│ │ │ │ ├── ErrorHelper.kt
│ │ │ │ ├── ErrorState.kt
│ │ │ │ ├── HorizontalMarginItemDecoration.kt
│ │ │ │ ├── LocaleUtil.kt
│ │ │ │ ├── SingleLiveEvent.java
│ │ │ │ ├── StringUtil.kt
│ │ │ │ ├── UiUtil.kt
│ │ │ │ ├── UlTagHandler.java
│ │ │ │ └── UrlUtil.java
│ │ │ └── views/
│ │ │ ├── MarginItemDecoration.kt
│ │ │ ├── ViewExtensions.kt
│ │ │ └── WindowInsetsLayout.kt
│ │ └── res/
│ │ ├── anim/
│ │ │ ├── fragment_open_enter.xml
│ │ │ ├── fragment_open_exit.xml
│ │ │ ├── slide_enter.xml
│ │ │ ├── slide_exit.xml
│ │ │ ├── slide_pop_enter.xml
│ │ │ └── slide_pop_exit.xml
│ │ ├── color/
│ │ │ ├── selector_black_or_white.xml
│ │ │ ├── selector_blue_or_white.xml
│ │ │ ├── selector_grey_or_blue.xml
│ │ │ ├── selector_grey_or_white.xml
│ │ │ ├── selector_transparent_or_blue.xml
│ │ │ ├── selector_white_or_black.xml
│ │ │ ├── selector_white_or_blue.xml
│ │ │ └── text_radio_checkable.xml
│ │ ├── drawable/
│ │ │ ├── bg_button_default.xml
│ │ │ ├── bg_button_red.xml
│ │ │ ├── bg_button_white.xml
│ │ │ ├── bg_corners_top_left.xml
│ │ │ ├── bg_dialog.xml
│ │ │ ├── bg_pill.xml
│ │ │ ├── bg_rect_rounded_sheet.xml
│ │ │ ├── bg_rect_rounded_small.xml
│ │ │ ├── bg_rect_rounded_small_blue_ripple.xml
│ │ │ ├── bg_rect_rounded_small_checkable.xml
│ │ │ ├── bg_rect_rounded_small_red.xml
│ │ │ ├── btn_radio_checkable.xml
│ │ │ ├── dot_black.xml
│ │ │ ├── dot_grey.xml
│ │ │ ├── dot_white.xml
│ │ │ ├── header_bottom.xml
│ │ │ ├── header_collapsed_shadow.xml
│ │ │ ├── ic_1g.xml
│ │ │ ├── ic_2g.xml
│ │ │ ├── ic_2g_green.xml
│ │ │ ├── ic_2g_grey.xml
│ │ │ ├── ic_2g_plus.xml
│ │ │ ├── ic_3g.xml
│ │ │ ├── ic_arrow_contract.xml
│ │ │ ├── ic_arrow_expand.xml
│ │ │ ├── ic_arrow_forward.xml
│ │ │ ├── ic_bund_small.xml
│ │ │ ├── ic_bundwappen_big.xml
│ │ │ ├── ic_call.xml
│ │ │ ├── ic_cam_off.xml
│ │ │ ├── ic_camera_switch.xml
│ │ │ ├── ic_check_filled.xml
│ │ │ ├── ic_check_green.xml
│ │ │ ├── ic_check_grey.xml
│ │ │ ├── ic_check_large.xml
│ │ │ ├── ic_checkbox_empty.xml
│ │ │ ├── ic_checkbox_filled.xml
│ │ │ ├── ic_close.xml
│ │ │ ├── ic_close_red.xml
│ │ │ ├── ic_dot.xml
│ │ │ ├── ic_double_check.xml
│ │ │ ├── ic_error.xml
│ │ │ ├── ic_error_blue.xml
│ │ │ ├── ic_error_grey.xml
│ │ │ ├── ic_error_large.xml
│ │ │ ├── ic_error_orange.xml
│ │ │ ├── ic_error_triangle.xml
│ │ │ ├── ic_expire_i.xml
│ │ │ ├── ic_faq.xml
│ │ │ ├── ic_header_slim.xml
│ │ │ ├── ic_how_it_works_image.xml
│ │ │ ├── ic_info.xml
│ │ │ ├── ic_info_alert.xml
│ │ │ ├── ic_info_blue.xml
│ │ │ ├── ic_info_outline.xml
│ │ │ ├── ic_invalid_grey.xml
│ │ │ ├── ic_invalid_red.xml
│ │ │ ├── ic_light_off.xml
│ │ │ ├── ic_light_off_blue.xml
│ │ │ ├── ic_light_on.xml
│ │ │ ├── ic_light_on_black.xml
│ │ │ ├── ic_link_external.xml
│ │ │ ├── ic_load.xml
│ │ │ ├── ic_no1g.xml
│ │ │ ├── ic_no2g.xml
│ │ │ ├── ic_no3g.xml
│ │ │ ├── ic_no_2_g_plus_height.xml
│ │ │ ├── ic_no_connection.xml
│ │ │ ├── ic_no_connection_large.xml
│ │ │ ├── ic_notification.xml
│ │ │ ├── ic_notification_filled.xml
│ │ │ ├── ic_offline.xml
│ │ │ ├── ic_offline_large.xml
│ │ │ ├── ic_offline_orange.xml
│ │ │ ├── ic_one.xml
│ │ │ ├── ic_phone.xml
│ │ │ ├── ic_plus.xml
│ │ │ ├── ic_plus_green.xml
│ │ │ ├── ic_privacy.xml
│ │ │ ├── ic_privacy_grey.xml
│ │ │ ├── ic_process_error.xml
│ │ │ ├── ic_process_error_grey.xml
│ │ │ ├── ic_process_error_large.xml
│ │ │ ├── ic_qr_certificate_light.xml
│ │ │ ├── ic_qr_certificate_light_no.xml
│ │ │ ├── ic_question_outline.xml
│ │ │ ├── ic_retry.xml
│ │ │ ├── ic_scanner_alert.xml
│ │ │ ├── ic_scanner_alert_white.xml
│ │ │ ├── ic_settings.xml
│ │ │ ├── ic_t.xml
│ │ │ ├── ic_three.xml
│ │ │ ├── ic_timeerror.xml
│ │ │ ├── ic_timeerror_large.xml
│ │ │ ├── ic_timeerror_orange.xml
│ │ │ ├── ic_timelapse.xml
│ │ │ ├── ic_timelapse_blue.xml
│ │ │ ├── ic_timelapse_red.xml
│ │ │ ├── ic_travel.xml
│ │ │ ├── ic_two.xml
│ │ │ ├── ic_zoom_off.xml
│ │ │ ├── ic_zoom_off_white.xml
│ │ │ ├── ic_zoom_on.xml
│ │ │ ├── ic_zoom_on_black.xml
│ │ │ ├── illu_onboarding_data_protection.xml
│ │ │ ├── line_dashed_grey.xml
│ │ │ ├── qr_scanner_bottom_left.xml
│ │ │ ├── qr_scanner_bottom_right.xml
│ │ │ ├── qr_scanner_top_left.xml
│ │ │ ├── qr_scanner_top_right.xml
│ │ │ ├── ripple_rect.xml
│ │ │ ├── ripple_rounded.xml
│ │ │ ├── ripple_rounded_button.xml
│ │ │ ├── ripple_rounded_rect.xml
│ │ │ ├── ripple_rounded_rect_banner.xml
│ │ │ ├── ripple_rounded_rect_small.xml
│ │ │ ├── tab_selector.xml
│ │ │ └── tab_selector_white.xml
│ │ ├── font/
│ │ │ └── inter.xml
│ │ ├── layout/
│ │ │ ├── activity_onboarding.xml
│ │ │ ├── dialog_camera_permission_explanation.xml
│ │ │ ├── dialog_fragment_info_box.xml
│ │ │ ├── fragment_debug.xml
│ │ │ ├── fragment_faq.xml
│ │ │ ├── fragment_html.xml
│ │ │ ├── fragment_settings.xml
│ │ │ ├── item_error_status.xml
│ │ │ ├── item_faq_header.xml
│ │ │ ├── item_faq_intro_section.xml
│ │ │ ├── item_faq_question.xml
│ │ │ ├── item_header.xml
│ │ │ └── item_language_option.xml
│ │ ├── menu/
│ │ │ └── imprint.xml
│ │ ├── values/
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-de/
│ │ │ └── strings.xml
│ │ ├── values-fr/
│ │ │ └── strings.xml
│ │ ├── values-it/
│ │ │ └── strings.xml
│ │ ├── values-rm/
│ │ │ └── strings.xml
│ │ └── xml/
│ │ └── network_security_config.xml
│ └── prod/
│ └── java/
│ └── ch/
│ └── admin/
│ └── bag/
│ └── covidcertificate/
│ └── common/
│ └── debug/
│ └── DebugFragment.kt
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── loadConfigs.sh
├── settings.gradle
├── verifier/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ ├── src/
│ │ ├── abn/
│ │ │ └── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ ├── dev/
│ │ │ └── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ ├── java/
│ │ │ └── ch/
│ │ │ └── admin/
│ │ │ └── bag/
│ │ │ └── covidcertificate/
│ │ │ └── verifier/
│ │ │ ├── HomeFragment.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainApplication.kt
│ │ │ ├── data/
│ │ │ │ └── VerifierSecureStorage.kt
│ │ │ ├── extensions/
│ │ │ │ └── ContextExtensions.kt
│ │ │ ├── faq/
│ │ │ │ └── VerifierFaqFragment.kt
│ │ │ ├── modes/
│ │ │ │ ├── ChooseModeDialogFragment.kt
│ │ │ │ └── ModesAndConfigViewModel.kt
│ │ │ ├── news/
│ │ │ │ └── InfoCertificateNewsFragment.kt
│ │ │ ├── pager/
│ │ │ │ ├── HomescreenPageAdapter.kt
│ │ │ │ └── HomescreenPagerFragment.kt
│ │ │ ├── qr/
│ │ │ │ └── VerifierQrScanFragment.kt
│ │ │ ├── updateboarding/
│ │ │ │ ├── UpdateboardingActivity.kt
│ │ │ │ ├── UpdateboardingAgbFragment.kt
│ │ │ │ └── UpdateboardingCertificateLightFragment.kt
│ │ │ ├── verification/
│ │ │ │ ├── VerificationAdapter.kt
│ │ │ │ ├── VerificationFragment.kt
│ │ │ │ ├── VerificationItem.kt
│ │ │ │ ├── VerificationStateUtil.kt
│ │ │ │ ├── VerificationViewHolder.kt
│ │ │ │ └── VerificationViewModel.kt
│ │ │ └── zebra/
│ │ │ ├── ZebraActionBroadcastReceiver.kt
│ │ │ ├── ZebraDataWedgeApiUtil.kt
│ │ │ └── ZebraResultActionBroadcastReceiver.kt
│ │ ├── play/
│ │ │ ├── listings/
│ │ │ │ ├── de-DE/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ ├── en-US/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ ├── fr-FR/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ └── it-IT/
│ │ │ │ ├── full-description.txt
│ │ │ │ ├── short-description.txt
│ │ │ │ └── title.txt
│ │ │ └── release-notes/
│ │ │ └── en-US/
│ │ │ └── default.txt
│ │ └── res/
│ │ ├── drawable/
│ │ │ ├── bg_bubble_bottom_left.xml
│ │ │ ├── ic_faq_image.xml
│ │ │ ├── ic_header_2g_off.xml
│ │ │ ├── ic_header_2g_on.xml
│ │ │ ├── ic_header_plus_off.xml
│ │ │ ├── ic_header_plus_on.xml
│ │ │ ├── ic_illu_home_1.xml
│ │ │ ├── ic_illu_home_2.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_settings.xml
│ │ │ └── illu_updateboarding_certificate_light.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── dialog_fragment_choose_mode.xml
│ │ │ ├── dialog_fragment_info_certificate_news.xml
│ │ │ ├── fragment_home.xml
│ │ │ ├── fragment_home_screen_pager.xml
│ │ │ ├── fragment_qr_scan.xml
│ │ │ ├── fragment_updateboarding_agb.xml
│ │ │ ├── fragment_updateboarding_certificate_light.xml
│ │ │ ├── fragment_verification.xml
│ │ │ ├── item_certificate_news.xml
│ │ │ ├── item_mode_button.xml
│ │ │ ├── item_mode_info.xml
│ │ │ ├── item_progress_indicator.xml
│ │ │ ├── item_verification_header_icon.xml
│ │ │ ├── item_verification_status.xml
│ │ │ └── item_verification_status_info.xml
│ │ └── mipmap-anydpi-v26/
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ └── testKeystore
└── wallet/
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── src/
│ ├── abn/
│ │ ├── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── wallet/
│ │ └── debug/
│ │ └── WalletDebugFragment.kt
│ ├── androidTest/
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── wallet/
│ │ ├── EspressoUtil.kt
│ │ ├── LoadconfigTest.kt
│ │ ├── NestedScrollViewScrollTo.kt
│ │ ├── OnboardingTest.kt
│ │ ├── RecyclerViewNotEmptyAssertion.kt
│ │ ├── ScanCountTest.kt
│ │ ├── TransferCodeTest.kt
│ │ └── WaitUntilVisibleAction.kt
│ ├── dev/
│ │ ├── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── wallet/
│ │ └── debug/
│ │ ├── DebugCertificateItem.kt
│ │ ├── DebugCertificatesListAdapter.kt
│ │ ├── DebugCertificatesListViewHolder.kt
│ │ └── WalletDebugFragment.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ └── faq/
│ │ │ └── config.json
│ │ ├── java/
│ │ │ └── ch/
│ │ │ └── admin/
│ │ │ └── bag/
│ │ │ └── covidcertificate/
│ │ │ └── wallet/
│ │ │ ├── CertificatesAndConfigViewModel.kt
│ │ │ ├── DeeplinkViewModel.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainApplication.kt
│ │ │ ├── add/
│ │ │ │ └── CertificateAddFragment.kt
│ │ │ ├── data/
│ │ │ │ ├── CertificateStorage.kt
│ │ │ │ ├── WalletDataItem.kt
│ │ │ │ ├── WalletDataSecureStorage.kt
│ │ │ │ ├── WalletSecureStorage.kt
│ │ │ │ └── adapter/
│ │ │ │ └── InstantJsonAdapter.kt
│ │ │ ├── detail/
│ │ │ │ ├── CertificateDetailAdapter.kt
│ │ │ │ ├── CertificateDetailFragment.kt
│ │ │ │ ├── CertificateDetailItem.kt
│ │ │ │ ├── CertificateDetailItemListBuilder.kt
│ │ │ │ └── CertificateDetailViewHolder.kt
│ │ │ ├── dialog/
│ │ │ │ ├── CertificateBannerInfoDialogFragment.kt
│ │ │ │ ├── ModeInfoDialogFragment.kt
│ │ │ │ └── RefreshButtonInfoDialogFragment.kt
│ │ │ ├── faq/
│ │ │ │ └── WalletFaqFragment.kt
│ │ │ ├── homescreen/
│ │ │ │ ├── HomeFragment.kt
│ │ │ │ └── pager/
│ │ │ │ ├── CertificatePagerFragment.kt
│ │ │ │ ├── CertificatesPagerAdapter.kt
│ │ │ │ ├── PagerDiffUtil.kt
│ │ │ │ ├── StatefulWalletItem.kt
│ │ │ │ ├── TransferCodePagerFragment.kt
│ │ │ │ └── WalletItem.kt
│ │ │ ├── howto/
│ │ │ │ └── HowToScanFragment.kt
│ │ │ ├── light/
│ │ │ │ ├── CertificateLightConversionFragment.kt
│ │ │ │ ├── CertificateLightDetailFragment.kt
│ │ │ │ ├── CertificateLightErrorCodes.kt
│ │ │ │ ├── CertificateLightPagerFragment.kt
│ │ │ │ ├── CertificateLightViewModel.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── CertificateLightConversionResponse.kt
│ │ │ │ │ └── CertificateLightConversionState.kt
│ │ │ │ └── net/
│ │ │ │ ├── CertificateLightRepository.kt
│ │ │ │ ├── CertificateLightRequestBody.kt
│ │ │ │ ├── CertificateLightResponse.kt
│ │ │ │ └── CertificateLightService.kt
│ │ │ ├── list/
│ │ │ │ ├── CertificatesListFragment.kt
│ │ │ │ ├── CertificatesListTouchHelper.kt
│ │ │ │ ├── WalletDataListAdapter.kt
│ │ │ │ ├── WalletDataListItem.kt
│ │ │ │ └── WalletDataListViewHolder.kt
│ │ │ ├── networking/
│ │ │ │ └── interceptor/
│ │ │ │ └── AcceptLanguageHeaderInterceptor.kt
│ │ │ ├── onboarding/
│ │ │ │ ├── OnboardingActivity.kt
│ │ │ │ ├── OnboardingAgbFragment.kt
│ │ │ │ ├── OnboardingContentFragment.kt
│ │ │ │ ├── OnboardingIntroFragment.kt
│ │ │ │ ├── OnboardingPreInfoFragment.kt
│ │ │ │ ├── agbupdate/
│ │ │ │ │ └── UpdateboardingAgbFragment.kt
│ │ │ │ ├── certificatelight/
│ │ │ │ │ └── UpdateboardingCertificateLightFragment.kt
│ │ │ │ └── validity/
│ │ │ │ ├── UpdateboardingValidity1Fragment.kt
│ │ │ │ ├── UpdateboardingValidity2Fragment.kt
│ │ │ │ ├── UpdateboardingValidity3Fragment.kt
│ │ │ │ └── UpdateboardingValidity4Fragment.kt
│ │ │ ├── pdf/
│ │ │ │ ├── PdfViewModel.kt
│ │ │ │ ├── export/
│ │ │ │ │ ├── PdfExportFragment.kt
│ │ │ │ │ ├── PdfExportShareContract.kt
│ │ │ │ │ └── PdfExportState.kt
│ │ │ │ └── net/
│ │ │ │ ├── PdfExportRepository.kt
│ │ │ │ ├── PdfExportRequestBody.kt
│ │ │ │ ├── PdfExportResponse.kt
│ │ │ │ └── PdfExportService.kt
│ │ │ ├── qr/
│ │ │ │ ├── VerifierInfoDialogFragment.kt
│ │ │ │ └── WalletQrScanFragment.kt
│ │ │ ├── ratconversion/
│ │ │ │ └── RatConversionFragment.kt
│ │ │ ├── renewal/
│ │ │ │ ├── QrCodeRenewalErrorCodes.kt
│ │ │ │ ├── QrCodeRenewalFragment.kt
│ │ │ │ ├── QrCodeRenewalViewModel.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── QrCodeRenewalResponse.kt
│ │ │ │ │ └── QrCodeRenewalViewState.kt
│ │ │ │ └── net/
│ │ │ │ ├── QrCodeRenewalBody.kt
│ │ │ │ ├── QrCodeRenewalRepository.kt
│ │ │ │ └── QrCodeRenewalService.kt
│ │ │ ├── transfercode/
│ │ │ │ ├── TransferCodeCreationFragment.kt
│ │ │ │ ├── TransferCodeCreationViewModel.kt
│ │ │ │ ├── TransferCodeDetailFragment.kt
│ │ │ │ ├── TransferCodeErrorCodes.kt
│ │ │ │ ├── TransferCodeHowToFragment.kt
│ │ │ │ ├── TransferCodeIntroFragment.kt
│ │ │ │ ├── TransferCodeViewModel.kt
│ │ │ │ ├── logic/
│ │ │ │ │ ├── Luhn.kt
│ │ │ │ │ └── TransferCodeCrypto.kt
│ │ │ │ ├── model/
│ │ │ │ │ ├── ConvertedCertificate.kt
│ │ │ │ │ ├── TransferCodeConversionState.kt
│ │ │ │ │ ├── TransferCodeCreationResponse.kt
│ │ │ │ │ ├── TransferCodeCreationState.kt
│ │ │ │ │ └── TransferCodeModel.kt
│ │ │ │ ├── net/
│ │ │ │ │ ├── CovidCert.kt
│ │ │ │ │ ├── CovidCertDelivery.kt
│ │ │ │ │ ├── DeliveryRegistration.kt
│ │ │ │ │ ├── DeliveryRepository.kt
│ │ │ │ │ ├── DeliveryService.kt
│ │ │ │ │ └── RequestDeliveryPayload.kt
│ │ │ │ ├── view/
│ │ │ │ │ ├── TransferCodeBubbleView.kt
│ │ │ │ │ ├── TransferCodeView.kt
│ │ │ │ │ └── TransferCodeWaitingView.kt
│ │ │ │ └── worker/
│ │ │ │ └── TransferWorker.kt
│ │ │ ├── travel/
│ │ │ │ ├── ForeignValidityFragment.kt
│ │ │ │ ├── ForeignValidityViewModel.kt
│ │ │ │ └── ForeignValidityViewState.kt
│ │ │ ├── util/
│ │ │ │ ├── BitmapUtil.kt
│ │ │ │ ├── ModeValidityStateUtil.kt
│ │ │ │ ├── NotificationUtil.kt
│ │ │ │ ├── QrCode.kt
│ │ │ │ └── VerificationStateUtil.kt
│ │ │ └── vaccination/
│ │ │ ├── appointment/
│ │ │ │ └── VaccinationAppointmentFragment.kt
│ │ │ └── hint/
│ │ │ └── VaccinationHintViewModel.kt
│ │ ├── play/
│ │ │ ├── listings/
│ │ │ │ ├── de-DE/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ ├── en-US/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ ├── fr-FR/
│ │ │ │ │ ├── full-description.txt
│ │ │ │ │ ├── short-description.txt
│ │ │ │ │ └── title.txt
│ │ │ │ └── it-IT/
│ │ │ │ ├── full-description.txt
│ │ │ │ ├── short-description.txt
│ │ │ │ └── title.txt
│ │ │ └── release-notes/
│ │ │ └── en-US/
│ │ │ └── default.txt
│ │ └── res/
│ │ ├── color/
│ │ │ └── text_color_button.xml
│ │ ├── drawable/
│ │ │ ├── bg_add_certificate_option.xml
│ │ │ ├── bg_border.xml
│ │ │ ├── bg_bordered_button.xml
│ │ │ ├── bg_bubble_bottom_left.xml
│ │ │ ├── bg_certificate_bottom.xml
│ │ │ ├── bg_certificate_bubble.xml
│ │ │ ├── bg_certificate_bubble_bundesrot.xml
│ │ │ ├── bg_certificate_bubble_ripple.xml
│ │ │ ├── bg_certificate_detail_note.xml
│ │ │ ├── bg_certificate_top.xml
│ │ │ ├── bg_info_banner.xml
│ │ │ ├── bg_type_bubble_small.xml
│ │ │ ├── circle_white.xml
│ │ │ ├── cutout_left.xml
│ │ │ ├── cutout_right.xml
│ │ │ ├── ic_add_certificate.xml
│ │ │ ├── ic_arrow_right.xml
│ │ │ ├── ic_calendar.xml
│ │ │ ├── ic_certificate_light.xml
│ │ │ ├── ic_check_mark.xml
│ │ │ ├── ic_cloud.xml
│ │ │ ├── ic_corner_offline.xml
│ │ │ ├── ic_corner_process_error.xml
│ │ │ ├── ic_covid_check_app.xml
│ │ │ ├── ic_data_protection.xml
│ │ │ ├── ic_drag.xml
│ │ │ ├── ic_exchange.xml
│ │ │ ├── ic_expire_1.xml
│ │ │ ├── ic_expire_2.xml
│ │ │ ├── ic_expire_3.xml
│ │ │ ├── ic_expire_4.xml
│ │ │ ├── ic_expire_5.xml
│ │ │ ├── ic_expire_6.xml
│ │ │ ├── ic_expire_7.xml
│ │ │ ├── ic_faq_image.xml
│ │ │ ├── ic_flag_ch.xml
│ │ │ ├── ic_header_slim.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_launcher_foreground.xml
│ │ │ ├── ic_list.xml
│ │ │ ├── ic_online.xml
│ │ │ ├── ic_pdf.xml
│ │ │ ├── ic_pen_write.xml
│ │ │ ├── ic_qr_certificate.xml
│ │ │ ├── ic_qrcode.xml
│ │ │ ├── ic_qrcode_add.xml
│ │ │ ├── ic_qrcode_icon_placeholder.xml
│ │ │ ├── ic_qrcode_scan.xml
│ │ │ ├── ic_scan_code.xml
│ │ │ ├── ic_transfer_code_list_failed.xml
│ │ │ ├── ic_transfer_code_list_valid.xml
│ │ │ ├── ic_transfer_notification.xml
│ │ │ ├── ic_validation.xml
│ │ │ ├── icon_ag.xml
│ │ │ ├── icon_ai.xml
│ │ │ ├── icon_ar.xml
│ │ │ ├── icon_be.xml
│ │ │ ├── icon_bl.xml
│ │ │ ├── icon_bs.xml
│ │ │ ├── icon_fr.xml
│ │ │ ├── icon_ge.xml
│ │ │ ├── icon_gl.xml
│ │ │ ├── icon_gr.xml
│ │ │ ├── icon_ju.xml
│ │ │ ├── icon_lu.xml
│ │ │ ├── icon_ne.xml
│ │ │ ├── icon_nw.xml
│ │ │ ├── icon_ow.xml
│ │ │ ├── icon_sg.xml
│ │ │ ├── icon_sh.xml
│ │ │ ├── icon_so.xml
│ │ │ ├── icon_sz.xml
│ │ │ ├── icon_tg.xml
│ │ │ ├── icon_ti.xml
│ │ │ ├── icon_ur.xml
│ │ │ ├── icon_vd.xml
│ │ │ ├── icon_vs.xml
│ │ │ ├── icon_zg.xml
│ │ │ ├── icon_zh.xml
│ │ │ ├── illu_add_certificate.xml
│ │ │ ├── illu_faq_transfer_code.xml
│ │ │ ├── illu_home_empty_state.xml
│ │ │ ├── illu_how_it_works.xml
│ │ │ ├── illu_how_to_scan.xml
│ │ │ ├── illu_onboarding_covid_certificate.xml
│ │ │ ├── illu_onboarding_hero.xml
│ │ │ ├── illu_onboarding_privacy.xml
│ │ │ ├── illu_transfer_code_failed.xml
│ │ │ ├── illu_transfer_code_intro.xml
│ │ │ ├── illu_transfer_code_waiting_phone.xml
│ │ │ ├── illu_transfer_code_waiting_ripple.xml
│ │ │ ├── illu_transfer_code_waiting_shadow.xml
│ │ │ ├── illu_updateboarding_certificate_light.xml
│ │ │ └── oval_horizontal.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── dialog_fragment_certificate_banner_info.xml
│ │ │ ├── dialog_fragment_mode_info.xml
│ │ │ ├── dialog_fragment_refresh_button_info.xml
│ │ │ ├── fragment_certificate_add.xml
│ │ │ ├── fragment_certificate_detail.xml
│ │ │ ├── fragment_certificate_light_conversion.xml
│ │ │ ├── fragment_certificate_light_detail.xml
│ │ │ ├── fragment_certificate_light_pager.xml
│ │ │ ├── fragment_certificate_pager.xml
│ │ │ ├── fragment_certificates_list.xml
│ │ │ ├── fragment_foreign_validity.xml
│ │ │ ├── fragment_home.xml
│ │ │ ├── fragment_how_to_scan.xml
│ │ │ ├── fragment_onboarding_agb.xml
│ │ │ ├── fragment_onboarding_content.xml
│ │ │ ├── fragment_onboarding_intro.xml
│ │ │ ├── fragment_onboarding_pre_info.xml
│ │ │ ├── fragment_pdf_export.xml
│ │ │ ├── fragment_qr_code_renewal.xml
│ │ │ ├── fragment_qr_scan.xml
│ │ │ ├── fragment_rat_conversion.xml
│ │ │ ├── fragment_transfer_code_creation.xml
│ │ │ ├── fragment_transfer_code_detail.xml
│ │ │ ├── fragment_transfer_code_howto.xml
│ │ │ ├── fragment_transfer_code_intro.xml
│ │ │ ├── fragment_transfer_code_pager.xml
│ │ │ ├── fragment_updateboarding_agb.xml
│ │ │ ├── fragment_updateboarding_certificate_light.xml
│ │ │ ├── fragment_updateboarding_validity_1.xml
│ │ │ ├── fragment_updateboarding_validity_2.xml
│ │ │ ├── fragment_updateboarding_validity_3.xml
│ │ │ ├── fragment_updateboarding_validity_4.xml
│ │ │ ├── fragment_vaccination_appointment.xml
│ │ │ ├── item_certificate_list.xml
│ │ │ ├── item_debug_certificate_list.xml
│ │ │ ├── item_detail_divider.xml
│ │ │ ├── item_detail_mode.xml
│ │ │ ├── item_detail_mode_refresh.xml
│ │ │ ├── item_detail_modes_list.xml
│ │ │ ├── item_detail_title.xml
│ │ │ ├── item_detail_value.xml
│ │ │ ├── item_detail_value_without_label.xml
│ │ │ ├── item_foreign_rules_check_hint.xml
│ │ │ ├── item_header_not_empty.xml
│ │ │ ├── item_icon_text_info.xml
│ │ │ ├── item_mode_list_info.xml
│ │ │ ├── item_transfer_code_list.xml
│ │ │ ├── item_vaccination_appointment_canton.xml
│ │ │ ├── partial_certificate_detail_banners.xml
│ │ │ ├── partial_home_add_certificate_options.xml
│ │ │ ├── view_transfer_code.xml
│ │ │ ├── view_transfer_code_bubble.xml
│ │ │ └── view_transfer_code_waiting.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── xml/
│ │ └── filepaths.xml
│ ├── prod/
│ │ └── java/
│ │ └── ch/
│ │ └── admin/
│ │ └── bag/
│ │ └── covidcertificate/
│ │ └── wallet/
│ │ └── debug/
│ │ └── WalletDebugFragment.kt
│ └── test/
│ └── java/
│ └── ch/
│ └── admin/
│ └── bag/
│ └── covidcertificate/
│ └── wallet/
│ └── transfercode/
│ ├── LuhnTest.kt
│ └── LuhnTestWrongCodes.kt
└── testKeystore
SYMBOL INDEX (14 symbols across 4 files)
FILE: apkdiff.py
function compareFiles (line 7) | def compareFiles(first, second):
function compare (line 19) | def compare(first, second):
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/SingleLiveEvent.java
class SingleLiveEvent (line 38) | public class SingleLiveEvent<T> extends MutableLiveData<T> {
method observe (line 44) | @MainThread
method setValue (line 62) | @MainThread
method call (line 71) | @MainThread
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/UlTagHandler.java
class UlTagHandler (line 20) | public class UlTagHandler implements Html.TagHandler {
method handleTag (line 25) | @Override
method start (line 43) | private static void start(Editable text, Object mark) {
method end (line 48) | private static void end(Editable text, Class<?> kind, Object... replac...
method getLast (line 60) | private static Object getLast(Spanned text, Class<?> kind) {
class Ul (line 68) | private static class Ul { }
FILE: common/src/main/java/ch/admin/bag/covidcertificate/common/util/UrlUtil.java
class UrlUtil (line 19) | public class UrlUtil {
method openUrl (line 21) | public static void openUrl(Context context, String url) {
Condensed preview — 643 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,302K chars).
[
{
"path": ".github/actions/gradle_docker/action.yml",
"chars": 306,
"preview": "name: 'GradleDocker'\ndescription: 'Run gradle inside Docker image'\ninputs:\n gradle-cmd: # id of input\n description:"
},
{
"path": ".github/actions/gradle_docker/main.sh",
"chars": 41,
"preview": "#!/bin/sh\n\ncd $GITHUB_WORKSPACE\ngradle $1"
},
{
"path": ".github/workflows/appcenter_verifier_abn.yml",
"chars": 889,
"preview": "name: Build Verifier ABN\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n branches:\n "
},
{
"path": ".github/workflows/appcenter_verifier_dev.yml",
"chars": 917,
"preview": "name: Build Verifier DEV\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n branches:\n "
},
{
"path": ".github/workflows/appcenter_verifier_prod.yml",
"chars": 1112,
"preview": "name: Build Verifier PROD\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n branches:\n "
},
{
"path": ".github/workflows/appcenter_verifier_prodfdroid.yml",
"chars": 1137,
"preview": "name: Build Verifier PRODFDROID\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n branche"
},
{
"path": ".github/workflows/appcenter_wallet_abn.yml",
"chars": 879,
"preview": "name: Build Wallet ABN\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n branches:\n "
},
{
"path": ".github/workflows/appcenter_wallet_dev.yml",
"chars": 907,
"preview": "name: Build Wallet DEV\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n branches:\n "
},
{
"path": ".github/workflows/appcenter_wallet_prod.yml",
"chars": 1096,
"preview": "name: Build Wallet PROD\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n branches:\n "
},
{
"path": ".github/workflows/appcenter_wallet_prodfdroid.yml",
"chars": 1121,
"preview": "name: Build Wallet PRODFDROID\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n branches:"
},
{
"path": ".github/workflows/browserstack_wallet_abn.yml",
"chars": 4730,
"preview": "name: Build Browserstack UI-Test Wallet\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\n "
},
{
"path": ".github/workflows/build.yml",
"chars": 1089,
"preview": "name: Build\n\non:\n push:\n branches:\n - main\n - 'release/**'\n pull_request:\njobs:\n build:\n name: Build\n"
},
{
"path": ".github/workflows/gradle-wrapper-validation.yml",
"chars": 299,
"preview": "name: Validate Gradle Wrapper\n\non: [push, pull_request]\n\njobs:\n validation:\n name: Validation\n runs-on: ubuntu-la"
},
{
"path": ".gitignore",
"chars": 1471,
"preview": "# Built application files\n*.apk\n*.aar\n*.ap_\n*.aab\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Ge"
},
{
"path": "Dockerfile",
"chars": 900,
"preview": "FROM gradle:7.2-jdk11\n\nENV ANDROID_SDK_URL https://dl.google.com/android/repository/commandlinetools-linux-7583922_lates"
},
{
"path": "LICENSE",
"chars": 16725,
"preview": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\""
},
{
"path": "README.md",
"chars": 4686,
"preview": "# COVID Certificate Apps - Android\n\n["
},
{
"path": "REPRODUCIBLE_BUILDS.md",
"chars": 1992,
"preview": "# Reproducible Builds\n\nThis document outlines how you can reproduce the Android app.\n\nThe instructions below are for the"
},
{
"path": "apkdiff.py",
"chars": 1916,
"preview": "\n# Taken from https://github.com/DrKLO/Telegram/blob/master/apkdiff.py on June 4th, 2020\n\nimport sys\nfrom zipfile import"
},
{
"path": "build.gradle",
"chars": 998,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "buildAndCompare.sh",
"chars": 3481,
"preview": "#!/bin/bash\n\n# Script to automate the building and comparing of the CovidCertificate apps\n#\n# The first and only argumen"
},
{
"path": "common/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "common/build.gradle",
"chars": 2649,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/consumer-rules.pro",
"chars": 0,
"preview": ""
},
{
"path": "common/proguard-rules.pro",
"chars": 750,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "common/src/abn/java/ch/admin/bag/covidcertificate/common/debug/DebugFragment.kt",
"chars": 626,
"preview": "package ch.admin.bag.covidcertificate.common.debug\n/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>"
},
{
"path": "common/src/dev/java/ch/admin/bag/covidcertificate/common/debug/DebugFragment.kt",
"chars": 2025,
"preview": "package ch.admin.bag.covidcertificate.common.debug\n/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>"
},
{
"path": "common/src/dev/java/ch/admin/bag/covidcertificate/common/debug/DebugSecureStorage.kt",
"chars": 1222,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/dev/res/xml/network_security_config.xml",
"chars": 635,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ~ Copyright (c) 2020 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ T"
},
{
"path": "common/src/main/AndroidManifest.xml",
"chars": 500,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/assets/impressum/de/impressum.html",
"chars": 2895,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/de/licence.html",
"chars": 2684,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/en/impressum.html",
"chars": 2928,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/en/licence.html",
"chars": 2684,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/fr/impressum.html",
"chars": 2978,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/fr/licence.html",
"chars": 2684,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/it/impressum.html",
"chars": 2962,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/it/licence.html",
"chars": 2683,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/rm/impressum.html",
"chars": 2897,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/assets/impressum/rm/licence.html",
"chars": 2684,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/BaseActivity.kt",
"chars": 509,
"preview": "package ch.admin.bag.covidcertificate.common\n\nimport android.content.Context\nimport androidx.appcompat.app.AppCompatActi"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/browserstack/AirplaneMode.kt",
"chars": 409,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/browserstack/BadNetwork.kt",
"chars": 407,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/browserstack/Normal.kt",
"chars": 403,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/browserstack/Onboarding.kt",
"chars": 407,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/CertificateRenewalInfoDetailModel.kt",
"chars": 536,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/CertificateRenewalInfoModel.kt",
"chars": 612,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/CertificateRenewalType.kt",
"chars": 507,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/CheckModesInfosModel.kt",
"chars": 1296,
"preview": "package ch.admin.bag.covidcertificate.common.config\n\nimport com.squareup.moshi.JsonClass\n\n//for verifier\n@JsonClass(gene"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/ConfigModel.kt",
"chars": 5237,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/ConfigViewModel.kt",
"chars": 1430,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/CovidCertificateNewsItem.kt",
"chars": 574,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/EolBannerInfoModel.kt",
"chars": 535,
"preview": "package ch.admin.bag.covidcertificate.common.config\n\nimport com.squareup.moshi.JsonClass\nimport java.io.Serializable\n\n@J"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/FaqEntryModel.kt",
"chars": 585,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/FaqIntroSection.kt",
"chars": 519,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/FaqModel.kt",
"chars": 632,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/ForeignRulesHintModel.kt",
"chars": 525,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/InfoBoxModel.kt",
"chars": 1569,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/InfoCovidCertificateNews.kt",
"chars": 595,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/RefreshButtonInfoModel.kt",
"chars": 672,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/VaccinationBookingInfoModel.kt",
"chars": 662,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/config/VaccinationHintModel.kt",
"chars": 519,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/data/ConfigSecureStorage.kt",
"chars": 3065,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/dialog/InfoDialogFragment.kt",
"chars": 2665,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/exception/HttpIOException.kt",
"chars": 852,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/exception/TimeDeviationException.kt",
"chars": 110,
"preview": "package ch.admin.bag.covidcertificate.common.exception\n\nclass TimeDeviationException : IllegalStateException()"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/ContextExtensions.kt",
"chars": 845,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/DccCertExtensions.kt",
"chars": 663,
"preview": "package ch.admin.bag.covidcertificate.common.extensions\n\nimport ch.admin.bag.covidcertificate.sdk.core.extensions.firstP"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/LifecycleOwnerExtensions.kt",
"chars": 917,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/OkHttpExtensions.kt",
"chars": 342,
"preview": "package ch.admin.bag.covidcertificate.common.extensions\n\nimport okhttp3.OkHttpClient\nimport java.util.concurrent.TimeUni"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/extensions/WindowExtensions.kt",
"chars": 472,
"preview": "package ch.admin.bag.covidcertificate.common.extensions\n\nimport android.view.Window\nimport android.view.WindowManager\n\nf"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/faq/FaqAdapter.kt",
"chars": 2086,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/faq/FaqFragment.kt",
"chars": 2227,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/faq/FaqItem.kt",
"chars": 3496,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/faq/FaqViewHolder.kt",
"chars": 639,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/faq/model/Header.kt",
"chars": 791,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/html/BuildInfo.kt",
"chars": 579,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/html/ImprintFragment.kt",
"chars": 5031,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/net/ConfigRepository.kt",
"chars": 4626,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/net/ConfigService.kt",
"chars": 805,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/onboarding/BaseOnboardingActivity.kt",
"chars": 1712,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/onboarding/SimpleOnboardingPagerAdapter.kt",
"chars": 897,
"preview": "/*\n * Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/qr/CameraPermissionExplanationDialog.kt",
"chars": 1575,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/qr/KittlerBinarizer.kt",
"chars": 2118,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/qr/QRCodeReaderHelper.kt",
"chars": 4061,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/qr/QrScanFragment.kt",
"chars": 11322,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/qr/QrScannerState.kt",
"chars": 439,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/settings/SettingsFragment.kt",
"chars": 2027,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/AssetUtil.kt",
"chars": 5538,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/CutOutEdgeTreatment.kt",
"chars": 2471,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/EnvironmentUtil.kt",
"chars": 765,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/ErrorCodeUtil.kt",
"chars": 1145,
"preview": "package ch.admin.bag.covidcertificate.common.util\n\nimport ch.admin.bag.covidcertificate.sdk.core.models.state.CheckNatio"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/ErrorHelper.kt",
"chars": 2398,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/ErrorState.kt",
"chars": 1192,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/HorizontalMarginItemDecoration.kt",
"chars": 830,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/LocaleUtil.kt",
"chars": 633,
"preview": "package ch.admin.bag.covidcertificate.common.util\n\nimport android.content.Context\nimport android.content.res.Configurati"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/SingleLiveEvent.java",
"chars": 2260,
"preview": "/*\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/StringUtil.kt",
"chars": 2738,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/UiUtil.kt",
"chars": 691,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/UlTagHandler.java",
"chars": 1864,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/util/UrlUtil.java",
"chars": 878,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/views/MarginItemDecoration.kt",
"chars": 631,
"preview": "package ch.admin.bag.covidcertificate.common.views\n\nimport android.content.Context\nimport android.graphics.Rect\nimport a"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/views/ViewExtensions.kt",
"chars": 4246,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/java/ch/admin/bag/covidcertificate/common/views/WindowInsetsLayout.kt",
"chars": 2951,
"preview": "/*\n * Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n *\n * This Source Code Form is subject to the term"
},
{
"path": "common/src/main/res/anim/fragment_open_enter.xml",
"chars": 1136,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/anim/fragment_open_exit.xml",
"chars": 1130,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/anim/slide_enter.xml",
"chars": 807,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/anim/slide_exit.xml",
"chars": 626,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/anim/slide_pop_enter.xml",
"chars": 625,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/anim/slide_pop_exit.xml",
"chars": 809,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/color/selector_black_or_white.xml",
"chars": 559,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/color/selector_blue_or_white.xml",
"chars": 558,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/color/selector_grey_or_blue.xml",
"chars": 556,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/color/selector_grey_or_white.xml",
"chars": 557,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/color/selector_transparent_or_blue.xml",
"chars": 571,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/color/selector_white_or_black.xml",
"chars": 559,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/color/selector_white_or_blue.xml",
"chars": 558,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/color/text_radio_checkable.xml",
"chars": 623,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_button_default.xml",
"chars": 863,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_button_red.xml",
"chars": 866,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_button_white.xml",
"chars": 880,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_corners_top_left.xml",
"chars": 632,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_dialog.xml",
"chars": 668,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_pill.xml",
"chars": 548,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ T"
},
{
"path": "common/src/main/res/drawable/bg_rect_rounded_sheet.xml",
"chars": 566,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_rect_rounded_small.xml",
"chars": 568,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_rect_rounded_small_blue_ripple.xml",
"chars": 861,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_rect_rounded_small_checkable.xml",
"chars": 1074,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/bg_rect_rounded_small_red.xml",
"chars": 570,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/btn_radio_checkable.xml",
"chars": 720,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/dot_black.xml",
"chars": 643,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/dot_grey.xml",
"chars": 642,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/dot_white.xml",
"chars": 643,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/header_bottom.xml",
"chars": 705,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/header_collapsed_shadow.xml",
"chars": 611,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ "
},
{
"path": "common/src/main/res/drawable/ic_1g.xml",
"chars": 1381,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"33dp\"\n android:height=\"32dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_2g.xml",
"chars": 1989,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"33dp\"\n android:height=\"32dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_2g_green.xml",
"chars": 1937,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_2g_grey.xml",
"chars": 1961,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"33dp\"\n android:height=\"32dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_2g_plus.xml",
"chars": 2361,
"preview": "<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_3g.xml",
"chars": 2565,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"33dp\"\n android:height=\"32dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_arrow_contract.xml",
"chars": 723,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_arrow_expand.xml",
"chars": 722,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_arrow_forward.xml",
"chars": 521,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_bund_small.xml",
"chars": 1083,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_bundwappen_big.xml",
"chars": 1085,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_call.xml",
"chars": 644,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_cam_off.xml",
"chars": 927,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_camera_switch.xml",
"chars": 971,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_check_filled.xml",
"chars": 724,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_check_green.xml",
"chars": 900,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_check_grey.xml",
"chars": 899,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_check_large.xml",
"chars": 1071,
"preview": "<!--\r\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\r\n ~\r\n ~ This Source Code Form is subject to "
},
{
"path": "common/src/main/res/drawable/ic_checkbox_empty.xml",
"chars": 460,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_checkbox_filled.xml",
"chars": 569,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_close.xml",
"chars": 689,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_close_red.xml",
"chars": 1058,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_dot.xml",
"chars": 666,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_double_check.xml",
"chars": 861,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"25dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_error.xml",
"chars": 981,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_error_blue.xml",
"chars": 565,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_error_grey.xml",
"chars": 952,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_error_large.xml",
"chars": 1000,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_error_orange.xml",
"chars": 954,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_error_triangle.xml",
"chars": 1166,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_expire_i.xml",
"chars": 762,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"25dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_faq.xml",
"chars": 1328,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_header_slim.xml",
"chars": 757,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_how_it_works_image.xml",
"chars": 3992,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_info.xml",
"chars": 1034,
"preview": "<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_info_alert.xml",
"chars": 620,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"25dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_info_blue.xml",
"chars": 980,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_info_outline.xml",
"chars": 1049,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_invalid_grey.xml",
"chars": 904,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_invalid_red.xml",
"chars": 903,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_light_off.xml",
"chars": 1387,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_light_off_blue.xml",
"chars": 1361,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_light_on.xml",
"chars": 2081,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_light_on_black.xml",
"chars": 2081,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_link_external.xml",
"chars": 1338,
"preview": "<!--\r\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\r\n ~\r\n ~ This Source Code Form is subject to "
},
{
"path": "common/src/main/res/drawable/ic_load.xml",
"chars": 1163,
"preview": "<!--\r\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\r\n ~\r\n ~ This Source Code Form is subject to "
},
{
"path": "common/src/main/res/drawable/ic_no1g.xml",
"chars": 1326,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"33dp\"\n android:height=\"32dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_no2g.xml",
"chars": 1747,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"33dp\"\n android:height=\"32dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_no3g.xml",
"chars": 2312,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"33dp\"\n android:height=\"32dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_no_2_g_plus_height.xml",
"chars": 3097,
"preview": "<!--\n ~ Copyright (c) 2022 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_no_connection.xml",
"chars": 1777,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_no_connection_large.xml",
"chars": 1782,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_notification.xml",
"chars": 1050,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_notification_filled.xml",
"chars": 820,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_offline.xml",
"chars": 1218,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_offline_large.xml",
"chars": 1190,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_offline_orange.xml",
"chars": 1218,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_one.xml",
"chars": 975,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_phone.xml",
"chars": 742,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"25dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_plus.xml",
"chars": 466,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_plus_green.xml",
"chars": 467,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "common/src/main/res/drawable/ic_privacy.xml",
"chars": 1716,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_privacy_grey.xml",
"chars": 1720,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_process_error.xml",
"chars": 1423,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
},
{
"path": "common/src/main/res/drawable/ic_process_error_grey.xml",
"chars": 1394,
"preview": "<!--\n ~ Copyright (c) 2021 Ubique Innovation AG <https://www.ubique.ch>\n ~\n ~ This Source Code Form is subject to the"
}
]
// ... and 443 more files (download for full content)
About this extraction
This page contains the full source code of the admin-ch/CovidCertificate-App-Android GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 643 files (2.9 MB), approximately 799.7k tokens, and a symbol index with 14 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.