├── .editorconfig ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md ├── PULL_REQUEST_TEMPLATE.md ├── stale.yml └── workflows │ └── cid.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app ├── android-base │ ├── build.gradle.kts │ ├── lint-baseline.xml │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ ├── AuthHelper.kt │ │ │ ├── Bridge.kt │ │ │ ├── HomeActivity.kt │ │ │ ├── LinkReceiverActivity.kt │ │ │ └── RobotScouter.kt │ │ ├── play │ │ ├── contact-email.txt │ │ ├── contact-website.txt │ │ ├── default-language.txt │ │ ├── listings │ │ │ ├── en-US │ │ │ │ ├── full-description.txt │ │ │ │ ├── graphics │ │ │ │ │ ├── feature-graphic │ │ │ │ │ │ └── feature.png │ │ │ │ │ ├── icon │ │ │ │ │ │ └── logo.png │ │ │ │ │ ├── large-tablet-screenshots │ │ │ │ │ │ ├── 1) Home.png │ │ │ │ │ │ └── 2) Home 2.png │ │ │ │ │ └── phone-screenshots │ │ │ │ │ │ ├── 1) Home.png │ │ │ │ │ │ ├── 2) Dark Mode.png │ │ │ │ │ │ ├── 3) Scout Editor.png │ │ │ │ │ │ ├── 4) Scout Editor 2.png │ │ │ │ │ │ ├── 5) Template Editor.png │ │ │ │ │ │ ├── 6) Template Editor 2.png │ │ │ │ │ │ └── 7) Team Details Editor.png │ │ │ │ ├── short-description.txt │ │ │ │ ├── title.txt │ │ │ │ └── video-url.txt │ │ │ └── fr-FR │ │ │ │ ├── full-description.txt │ │ │ │ └── short-description.txt │ │ └── release-notes │ │ │ └── en-US │ │ │ └── default.txt │ │ └── res │ │ ├── anim │ │ ├── fade_in.xml │ │ ├── fade_out.xml │ │ ├── pop_fade_in.xml │ │ ├── pop_fade_in_right.xml │ │ └── pop_fade_out.xml │ │ ├── drawable-anydpi-v25 │ │ ├── ic_shortcut_add_scout.xml │ │ ├── ic_shortcut_export_all_teams.xml │ │ ├── shortcut_add_scout.xml │ │ ├── shortcut_background.xml │ │ └── shortcut_export_all_teams.xml │ │ ├── drawable-anydpi-v26 │ │ ├── shortcut_add_scout.xml │ │ └── shortcut_export_all_teams.xml │ │ ├── drawable │ │ ├── ic_auto_fix_colorable_24dp.xml │ │ ├── ic_content_paste_colorable_24dp.xml │ │ ├── ic_delete_colorable_24dp.xml │ │ ├── ic_donate_grey_24dp.xml │ │ ├── ic_import_export_colorable_24dp.xml │ │ ├── ic_people_colorable_24dp.xml │ │ ├── ic_settings_colorable_24dp.xml │ │ └── ic_sign_in_colorable_24dp.xml │ │ ├── layout-large-land │ │ └── activity_home.xml │ │ ├── layout-large │ │ └── activity_home_base.xml │ │ ├── layout-xlarge │ │ └── activity_home.xml │ │ ├── layout │ │ ├── activity_home.xml │ │ ├── activity_home_content.xml │ │ └── activity_link_receiver.xml │ │ ├── menu │ │ ├── home_bottom_navigation.xml │ │ ├── home_drawer.xml │ │ └── home_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values │ │ └── strings.xml │ │ └── xml-v25 │ │ └── shortcuts.xml └── server │ ├── .firebaserc │ ├── firebase.json │ ├── firestore.rules │ ├── functions │ ├── build.gradle.kts │ ├── src │ │ └── main │ │ │ └── kotlin │ │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── server │ │ │ ├── Index.kt │ │ │ ├── functions │ │ │ ├── Cleanup.kt │ │ │ ├── ClientApi.kt │ │ │ ├── Init.kt │ │ │ ├── Log.kt │ │ │ ├── TeamPopulation.kt │ │ │ ├── Templates.kt │ │ │ └── Transfer.kt │ │ │ └── utils │ │ │ ├── Constants.kt │ │ │ ├── Database.kt │ │ │ ├── Js.kt │ │ │ ├── Metrics.kt │ │ │ └── types │ │ │ ├── Auth.kt │ │ │ ├── Firestore.kt │ │ │ ├── Functions.kt │ │ │ ├── Https.kt │ │ │ ├── Pubsub.kt │ │ │ └── Superagent.kt │ └── upload │ │ ├── .gitignore │ │ ├── common │ │ └── package.json │ │ ├── package-lock.json │ │ └── package.json │ ├── storage.rules │ └── web │ ├── .well-known │ └── assetlinks.json │ ├── 404.html │ └── index.html ├── assets ├── demo.gif └── logo.svg ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ ├── kotlin │ ├── Config.kt │ ├── Projects.kt │ └── com │ │ └── supercilex │ │ └── robotscouter │ │ └── build │ │ ├── RobotScouterBuildPlugin.kt │ │ ├── internal │ │ ├── Constants.kt │ │ ├── External.kt │ │ └── Io.kt │ │ └── tasks │ │ ├── DeployServer.kt │ │ ├── GenerateChangelog.kt │ │ ├── RebuildSecrets.kt │ │ ├── Setup.kt │ │ └── UpdateTranslations.kt │ └── resources │ └── META-INF │ └── gradle-plugins │ └── com.supercilex.robotscouter.build.properties ├── ci-dummies ├── config.xml ├── google-services.json ├── keystore.jks ├── keystore.properties └── upload-keystore.properties ├── feature ├── autoscout │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── feature │ │ │ └── autoscout │ │ │ └── AutoScoutFragment.kt │ │ └── res │ │ └── layout │ │ └── fragment_auto_scout.xml ├── exports │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── feature │ │ │ └── exports │ │ │ ├── ExportNotificationManager.kt │ │ │ ├── ExportService.kt │ │ │ ├── SpreadsheetCache.kt │ │ │ ├── Spreadsheets.kt │ │ │ └── TemplateExporter.kt │ │ └── res │ │ ├── drawable-anydpi-v23 │ │ ├── ic_done_white_24dp.xml │ │ ├── ic_launch_white_24dp.xml │ │ └── ic_share_white_24dp.xml │ │ ├── drawable-hdpi │ │ ├── ic_done_white_24dp.png │ │ ├── ic_launch_white_24dp.png │ │ └── ic_share_white_24dp.png │ │ ├── drawable-xhdpi │ │ ├── ic_done_white_24dp.png │ │ ├── ic_launch_white_24dp.png │ │ └── ic_share_white_24dp.png │ │ ├── drawable-xxhdpi │ │ ├── ic_done_white_24dp.png │ │ ├── ic_launch_white_24dp.png │ │ └── ic_share_white_24dp.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_done_white_24dp.png │ │ ├── ic_launch_white_24dp.png │ │ └── ic_share_white_24dp.png │ │ ├── drawable │ │ ├── ic_done_white_24dp.png │ │ ├── ic_launch_white_24dp.png │ │ └── ic_share_white_24dp.png │ │ ├── values-fr │ │ └── strings.xml │ │ └── values │ │ └── strings.xml ├── scouts │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── feature │ │ │ └── scouts │ │ │ ├── ActivityScoutListFragment.kt │ │ │ ├── AppBarViewHolderBase.kt │ │ │ ├── IntegratedScoutListFragment.kt │ │ │ ├── ScoutAdapter.kt │ │ │ ├── ScoutFragment.kt │ │ │ ├── ScoutListActivity.kt │ │ │ ├── ScoutListFragmentBase.kt │ │ │ ├── ScoutPagerAdapter.kt │ │ │ ├── ScoutTemplateSelectorDialog.kt │ │ │ ├── TabletScoutListContainer.kt │ │ │ ├── TabletScoutListFragment.kt │ │ │ ├── TemplateSelectionListener.kt │ │ │ └── viewholder │ │ │ ├── EditTextViewHolder.kt │ │ │ └── SpinnerViewHolder.kt │ │ └── res │ │ ├── drawable │ │ ├── ic_assignment_grey_96dp.xml │ │ └── ic_cloud_grey_96dp.xml │ │ ├── layout │ │ ├── activity_scout_list.xml │ │ ├── fragment_scout_list.xml │ │ ├── fragment_scout_list_toolbar.xml │ │ ├── fragment_scout_metric_list.xml │ │ ├── scout_checkbox.xml │ │ ├── scout_counter.xml │ │ ├── scout_header.xml │ │ ├── scout_notes.xml │ │ ├── scout_spinner.xml │ │ └── scout_stopwatch.xml │ │ ├── menu │ │ ├── integrated_scout_list_menu.xml │ │ ├── scout_list_menu.xml │ │ └── scout_options.xml │ │ ├── values-fr │ │ └── strings.xml │ │ └── values │ │ └── strings.xml ├── settings │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── feature │ │ │ └── settings │ │ │ ├── DefaultTemplatePreference.kt │ │ │ ├── LicensesFragment.kt │ │ │ ├── SettingsActivity.kt │ │ │ ├── SettingsFragment.kt │ │ │ └── SettingsViewModel.kt │ │ └── res │ │ ├── drawable │ │ ├── ic_link_colorable_24dp.xml │ │ ├── ic_lock_outline_colorable_24dp.xml │ │ ├── ic_replay_colorable_24dp.xml │ │ ├── ic_sign_out_colorable_24dp.xml │ │ └── ic_weather_night_colorable_24dp.xml │ │ ├── layout │ │ ├── activity_settings.xml │ │ └── fragment_licenses.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── app_preferences.xml ├── teams │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── feature │ │ │ └── teams │ │ │ ├── NewTeamDialog.kt │ │ │ ├── Selection.kt │ │ │ ├── SnackbarSelectionObserver.kt │ │ │ ├── TeamListAdapter.kt │ │ │ ├── TeamListFragment.kt │ │ │ ├── TeamListHolder.kt │ │ │ ├── TeamMenuHelper.kt │ │ │ ├── TeamTemplateSelectorDialog.kt │ │ │ ├── TeamViewHolder.kt │ │ │ └── Tutorials.kt │ │ └── res │ │ ├── drawable │ │ ├── ic_check_circle_grey_56dp.xml │ │ └── ic_group_grey_96dp.xml │ │ ├── layout │ │ ├── dialog_new_team.xml │ │ ├── fragment_team_list.xml │ │ └── team_list_row_layout.xml │ │ ├── menu │ │ └── team_options.xml │ │ ├── values-fr │ │ └── strings.xml │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml ├── templates │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── feature │ │ │ └── templates │ │ │ ├── AddMetricDialog.kt │ │ │ ├── DeleteTemplateDialog.kt │ │ │ ├── NewTemplateDialog.kt │ │ │ ├── TemplateAdapter.kt │ │ │ ├── TemplateFragment.kt │ │ │ ├── TemplateItemTouchCallback.kt │ │ │ ├── TemplateListFragment.kt │ │ │ ├── TemplatePagerAdapter.kt │ │ │ ├── TemplateSharer.kt │ │ │ └── viewholder │ │ │ ├── CheckboxTemplateViewHolder.kt │ │ │ ├── CounterTemplateViewHolder.kt │ │ │ ├── EditTextTemplateViewHolder.kt │ │ │ ├── HeaderTemplateViewHolder.kt │ │ │ ├── MetricTemplateViewHolder.kt │ │ │ ├── SpinnerTemplateViewHolder.kt │ │ │ ├── StopwatchTemplateViewHolder.kt │ │ │ └── TemplateViewHolder.kt │ │ └── res │ │ ├── drawable │ │ ├── ic_default_24dp.xml │ │ ├── ic_delete_black_24dp.xml │ │ ├── ic_header_spinner.xml │ │ ├── ic_metric_checkbox.xml │ │ ├── ic_metric_counter.xml │ │ ├── ic_metric_header.xml │ │ ├── ic_metric_notes.xml │ │ ├── ic_metric_stopwatch.xml │ │ ├── ic_reorder_black_24dp.xml │ │ └── ic_ruler_grey_96dp.xml │ │ ├── layout │ │ ├── dialog_add_metric.xml │ │ ├── fragment_template_list.xml │ │ ├── fragment_template_metric_list.xml │ │ ├── scout_template_base_reorder.xml │ │ ├── scout_template_checkbox.xml │ │ ├── scout_template_counter.xml │ │ ├── scout_template_header.xml │ │ ├── scout_template_notes.xml │ │ ├── scout_template_spinner.xml │ │ ├── scout_template_spinner_item.xml │ │ └── scout_template_stopwatch.xml │ │ ├── menu │ │ ├── template_list_menu.xml │ │ └── template_options.xml │ │ ├── values-fr │ │ └── strings.xml │ │ └── values │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml └── trash │ ├── build.gradle.kts │ └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── supercilex │ │ └── robotscouter │ │ └── feature │ │ └── trash │ │ ├── Data.kt │ │ ├── EmptyTrashDialog.kt │ │ ├── SelectableScrollView.kt │ │ ├── Selection.kt │ │ ├── TrashActivity.kt │ │ ├── TrashFragment.kt │ │ ├── TrashListAdapter.kt │ │ └── TrashViewHolder.kt │ └── res │ ├── drawable │ ├── ic_close_colorable_24dp.xml │ ├── ic_delete_empty_96dp.xml │ ├── ic_restore_colorable_24dp.xml │ ├── trash_item_divider.xml │ └── trash_list_item.xml │ ├── layout │ ├── activity_trash.xml │ ├── fragment_trash.xml │ └── trash_item.xml │ ├── menu │ └── trash_options.xml │ ├── values-fr │ └── strings.xml │ └── values │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── common │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── kotlin │ │ └── com │ │ └── supercilex │ │ └── robotscouter │ │ └── common │ │ ├── Collections.kt │ │ └── Constants.kt ├── core-data │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── core │ │ │ └── data │ │ │ ├── Analytics.kt │ │ │ ├── Args.kt │ │ │ ├── CachingSharer.kt │ │ │ ├── Cleanup.kt │ │ │ ├── Constants.kt │ │ │ ├── Database.kt │ │ │ ├── Io.kt │ │ │ ├── Jobs.kt │ │ │ ├── Links.kt │ │ │ ├── ListenerRegistrationLifecycleOwner.kt │ │ │ ├── Notifications.kt │ │ │ ├── Prefs.kt │ │ │ ├── RemoteConfig.kt │ │ │ ├── SimpleViewModelBase.kt │ │ │ ├── SingleLiveEvent.kt │ │ │ ├── TaskLogging.kt │ │ │ ├── ViewModelBase.kt │ │ │ ├── client │ │ │ ├── UploadTeamMedia.kt │ │ │ └── WorkerBase.kt │ │ │ ├── model │ │ │ ├── Metrics.kt │ │ │ ├── Scouts.kt │ │ │ ├── ScoutsHolder.kt │ │ │ ├── TeamCache.kt │ │ │ ├── TeamHolder.kt │ │ │ ├── Teams.kt │ │ │ ├── Templates.kt │ │ │ └── Users.kt │ │ │ └── remote │ │ │ ├── TeamMediaApi.kt │ │ │ └── TeamMediaUploader.kt │ │ └── res │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values │ │ └── strings.xml │ │ ├── xml-v29 │ │ └── file_paths.xml │ │ └── xml │ │ ├── file_paths.xml │ │ └── remote_config_defaults.xml ├── core-model │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── supercilex │ │ └── robotscouter │ │ └── core │ │ └── model │ │ ├── Metric.kt │ │ ├── MetricType.kt │ │ ├── OrderedModel.kt │ │ ├── OrderedRemoteModel.kt │ │ ├── Scout.kt │ │ ├── Team.kt │ │ └── TemplateType.kt ├── core-ui │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── core │ │ │ └── ui │ │ │ ├── Animations.kt │ │ │ ├── CardMetric.kt │ │ │ ├── ContentLoading.kt │ │ │ ├── Contexts.kt │ │ │ ├── Dialogs.kt │ │ │ ├── Drawables.kt │ │ │ ├── Flow.kt │ │ │ ├── Keyboard.kt │ │ │ ├── Lifecycle.kt │ │ │ ├── Permissions.kt │ │ │ ├── RecyclerView.kt │ │ │ ├── Selection.kt │ │ │ ├── Snackbars.kt │ │ │ ├── StateHolder.kt │ │ │ ├── Ui.kt │ │ │ └── views │ │ │ ├── CardMetricConstraintLayout.kt │ │ │ ├── CardMetricFlexboxLayout.kt │ │ │ ├── CardMetricLinearLayout.kt │ │ │ ├── ContentLoadingHint.kt │ │ │ ├── ContentLoadingProgressBar.kt │ │ │ ├── SupportVectorDrawablesImageButton.kt │ │ │ └── UnscrollableViewPager.kt │ │ └── res │ │ ├── color │ │ ├── enablable_button_icon.xml │ │ └── list_item.xml │ │ ├── drawable-anydpi-v23 │ │ ├── ic_logo.xml │ │ └── launch_screen.xml │ │ ├── drawable-hdpi │ │ └── ic_logo.png │ │ ├── drawable-xhdpi │ │ └── ic_logo.png │ │ ├── drawable-xxhdpi │ │ └── ic_logo.png │ │ ├── drawable-xxxhdpi │ │ └── ic_logo.png │ │ ├── drawable │ │ ├── card_item_background.xml │ │ ├── ic_logo.png │ │ └── launch_screen.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-large-port │ │ └── dimens.xml │ │ ├── values-large │ │ └── dimens.xml │ │ ├── values-night-v27 │ │ └── styles.xml │ │ ├── values-night │ │ └── colors.xml │ │ ├── values-normal-land │ │ └── dimens.xml │ │ ├── values-v17 │ │ └── styles.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-v26 │ │ └── styles.xml │ │ ├── values-v27 │ │ └── styles.xml │ │ ├── values-xlarge-land │ │ └── dimens.xml │ │ ├── values-xlarge-port │ │ └── dimens.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml ├── core │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── core │ │ │ ├── Async.kt │ │ │ ├── Connectivity.kt │ │ │ ├── Constants.kt │ │ │ ├── Logging.kt │ │ │ ├── Properties.kt │ │ │ ├── RobotScouter.kt │ │ │ └── Toasts.kt │ │ └── resources │ │ └── META-INF │ │ └── services │ │ └── kotlinx.coroutines.CoroutineExceptionHandler ├── shared-scouting │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── supercilex │ │ │ └── robotscouter │ │ │ └── shared │ │ │ └── scouting │ │ │ ├── CounterValueDialog.kt │ │ │ ├── MetricListAdapterBase.kt │ │ │ ├── MetricListFragment.kt │ │ │ ├── MetricListHolder.kt │ │ │ ├── MetricViewHolderBase.kt │ │ │ ├── TabNameDialog.kt │ │ │ ├── TabPagerAdapterBase.kt │ │ │ ├── ValueDialogBase.kt │ │ │ └── viewholder │ │ │ ├── CheckboxViewHolder.kt │ │ │ ├── CounterViewHolder.kt │ │ │ ├── HeaderViewHolder.kt │ │ │ └── StopwatchViewHolder.kt │ │ └── res │ │ ├── color │ │ ├── activatable_button_text.xml │ │ └── button_outline.xml │ │ ├── drawable-anydpi-v21 │ │ └── button_outline_colored.xml │ │ ├── drawable-v21 │ │ ├── ic_timer_off_to_on_24dp.xml │ │ └── ic_timer_on_to_off_24dp.xml │ │ ├── drawable │ │ ├── button_outline_colored.xml │ │ ├── ic_remove_colorable_24dp.xml │ │ ├── ic_timer_off_24dp.xml │ │ └── ic_timer_on_24dp.xml │ │ ├── layout │ │ ├── dialog_value.xml │ │ ├── scout_base_checkbox.xml │ │ ├── scout_base_counter.xml │ │ ├── scout_base_stopwatch.xml │ │ └── scout_stopwatch_cycle_item.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-v17 │ │ └── styles.xml │ │ └── values │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml └── shared │ ├── build.gradle.kts │ └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── supercilex │ │ └── robotscouter │ │ └── shared │ │ ├── CardListHelper.kt │ │ ├── CustomTabs.kt │ │ ├── MovableFragmentStatePagerAdapter.kt │ │ ├── RatingDialog.kt │ │ ├── SharedLifecycleResource.kt │ │ ├── ShouldUploadMediaToTbaDialog.kt │ │ ├── TeamDetailsDialog.kt │ │ ├── TeamMediaCreator.kt │ │ ├── TeamSharer.kt │ │ ├── TemplateSelectorDialog.kt │ │ ├── Ui.kt │ │ └── client │ │ ├── AccountMergeService.kt │ │ ├── AppIndexing.kt │ │ └── Auth.kt │ └── res │ ├── anim │ ├── slide_in_left.xml │ └── slide_out_right.xml │ ├── color │ └── empty_icon_list_item.xml │ ├── drawable │ ├── ic_add_a_photo_colorable_24dp.xml │ ├── ic_add_colorable_24dp.xml │ ├── ic_check_grey_24dp.xml │ ├── ic_content_paste_grey_96dp.xml │ ├── ic_delete_forever_colorable_24dp.xml │ ├── ic_info_colorable_24dp.xml │ ├── ic_launch_accent_24dp.xml │ ├── ic_mode_edit_colorable_24dp.xml │ ├── ic_person_grey_96dp.xml │ ├── ic_share_colorable_24dp.xml │ └── ic_star_accent_24dp.xml │ ├── layout │ ├── dialog_should_upload_media.xml │ ├── dialog_team_details.xml │ └── dialog_template_selector.xml │ ├── values-fr │ └── strings.xml │ └── values │ ├── font_certs.xml │ └── strings.xml ├── secrets.tar.enc └── settings.gradle.kts /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @SUPERCILEX 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: SUPERCILEX 2 | ko_fi: supercilex 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Help improve Robot Scouter 4 | --- 5 | 6 | 15 | 16 | ### Step 1: Describe your environment 17 | 18 | 22 | 23 | ### Step 2: Describe the problem: 24 | 25 | #### Steps to reproduce: 26 | 27 | 1. _____ 28 | 2. _____ 29 | 3. _____ 30 | 31 | #### Observed Results: 32 | 33 | 34 | 35 | #### Expected Results: 36 | 37 | 38 | 39 | #### Other Relevant Bits: 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea 4 | --- 5 | 6 | 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 12 | 15 | 16 | **Describe the solution you'd like** 17 | 18 | 21 | 22 | **Describe alternatives you've considered** 23 | 24 | 27 | 28 | **Additional context** 29 | 30 | 33 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Hey there! So you want to contribute to Robot Scouter? 2 | Before you file this pull request, follow these steps: 3 | 4 | - Read [the contribution guidelines](https://github.com/SUPERCILEX/Robot-Scouter/blob/master/.github/CONTRIBUTING.md). 5 | - If this has been discussed in an issue, make sure to mention the issue number here. 6 | If not, go file an issue about this to make sure this is a desirable change. 7 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | daysUntilStale: 7 2 | daysUntilClose: 7 3 | exemptLabels: 4 | - feature 5 | - enhancement 6 | - bug 7 | - refactor 8 | staleLabel: stale 9 | markComment: > 10 | This issue has been automatically marked as stale because it has not had 11 | recent activity. It will be closed if no further activity occurs. Thank you 12 | for your contributions. 13 | closeComment: false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea/* 4 | build 5 | !**/src/**/build 6 | node_modules 7 | .firebase 8 | /local.properties 9 | 10 | *keystore.properties 11 | google-*.json 12 | *.jks 13 | config.xml 14 | !ci-dummies/*.* 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Want to contribute? Awesome! 2 | 3 | ### Before you contribute 4 | 5 | Before we can use your code, you must sign the 6 | [SUPERCILEX Individual Contributor License Agreement](https://cla-assistant.io/SUPERCILEX/Robot-Scouter), 7 | which you can do online. The CLA is necessary mainly because you own the copyright to your changes, 8 | even after your contribution becomes part of our codebase, so we need your permission to use and 9 | distribute your code. We also need to be sure of various other things—for instance that you'll tell 10 | us if you know that your code infringes on other people's patents. You don't have to sign the CLA 11 | until after you've submitted your code for review and a member has approved it, but you must do it 12 | before we can put your code into our codebase. 13 | 14 | ### Adding new features 15 | 16 | Before you start working on a larger contribution, you should get in touch with us first through the 17 | issue tracker with your idea so that we can help out and possibly guide you. Coordinating up front 18 | makes it much easier to avoid frustration later on. 19 | 20 | If this has been discussed in an issue, make sure to mention the issue number. If not, go file an 21 | issue about this to make sure this is a desirable change. 22 | 23 | ### Code reviews 24 | 25 | All submissions, including submissions by project members, require review. We adhere to the 26 | [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html). 27 | -------------------------------------------------------------------------------- /app/android-base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Keeps line numbers and file name obfuscation 2 | -renamesourcefileattribute SourceFile 3 | -keepattributes SourceFile,LineNumberTable 4 | -printconfiguration build/outputs/mapping/configuration.txt 5 | 6 | # Keep bridges 7 | -keep class com.supercilex.robotscouter.Bridge 8 | -keep @com.supercilex.robotscouter.Bridge class * { 9 | *** Companion; 10 | } 11 | 12 | # Ignore Kotlin errors TODO https://youtrack.jetbrains.com/issue/KT-23172 13 | -dontwarn com.supercilex.robotscouter.** 14 | -dontwarn kotlinx.** 15 | 16 | # Remove logging 17 | -assumenosideeffects class android.util.Log { 18 | public static boolean isLoggable(java.lang.String, int); 19 | public static int v(...); 20 | public static int i(...); 21 | public static int w(...); 22 | public static int d(...); 23 | public static int e(...); 24 | } 25 | 26 | # Apache POI - TODO remove once the next POIA version comes out 27 | -keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellProtection { *; } 28 | -keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellProtectionImpl { *; } 29 | -keep class org.openxmlformats.schemas.spreadsheetml.x2006.main.STBorderStyle$Enum { *; } 30 | -------------------------------------------------------------------------------- /app/android-base/src/debug/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Robot Scouter DEBUG 4 | 5 | -------------------------------------------------------------------------------- /app/android-base/src/main/play/contact-email.txt: -------------------------------------------------------------------------------- 1 | robotscouter@supercilex.com 2 | -------------------------------------------------------------------------------- /app/android-base/src/main/play/contact-website.txt: -------------------------------------------------------------------------------- 1 | https://github.com/SUPERCILEX/Robot-Scouter/ 2 | -------------------------------------------------------------------------------- /app/android-base/src/main/play/default-language.txt: -------------------------------------------------------------------------------- 1 | en-US 2 | -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/feature-graphic/feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/feature-graphic/feature.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/icon/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/icon/logo.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/large-tablet-screenshots/1) Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/large-tablet-screenshots/1) Home.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/large-tablet-screenshots/2) Home 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/large-tablet-screenshots/2) Home 2.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/1) Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/1) Home.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/2) Dark Mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/2) Dark Mode.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/3) Scout Editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/3) Scout Editor.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/4) Scout Editor 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/4) Scout Editor 2.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/5) Template Editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/5) Template Editor.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/6) Template Editor 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/6) Template Editor 2.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/7) Team Details Editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/play/listings/en-US/graphics/phone-screenshots/7) Team Details Editor.png -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/short-description.txt: -------------------------------------------------------------------------------- 1 | Easy, efficient, and collaborative FIRST robot scouting 2 | -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Robot Scouter - FRC Scouting 2 | -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/en-US/video-url.txt: -------------------------------------------------------------------------------- 1 | https://www.youtube.com/watch?v=0tYVCfPrdGY 2 | -------------------------------------------------------------------------------- /app/android-base/src/main/play/listings/fr-FR/short-description.txt: -------------------------------------------------------------------------------- 1 | Repérage de robot FIRST facile, efficace et collaboratif 2 | -------------------------------------------------------------------------------- /app/android-base/src/main/play/release-notes/en-US/default.txt: -------------------------------------------------------------------------------- 1 | Robot Scouter v3.0 is out!!! v3.0 completely reworks Robot Scouter's user experience, supports trashing teams and templates instead of permanently deleting them, includes rewritten internals to make future development easier, and fixes swaths of bugs. 2 | 3 | For more details, see the full release notes: https://github.com/SUPERCILEX/Robot-Scouter/releases. 4 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/anim/pop_fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 11 | 12 | 16 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/anim/pop_fade_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/anim/pop_fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable-anydpi-v25/ic_shortcut_add_scout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable-anydpi-v25/ic_shortcut_export_all_teams.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable-anydpi-v25/shortcut_add_scout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable-anydpi-v25/shortcut_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable-anydpi-v25/shortcut_export_all_teams.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable-anydpi-v26/shortcut_add_scout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable-anydpi-v26/shortcut_export_all_teams.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable/ic_auto_fix_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable/ic_content_paste_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable/ic_delete_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable/ic_donate_grey_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable/ic_import_export_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable/ic_people_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable/ic_settings_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/drawable/ic_sign_in_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/layout-large-land/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/layout-xlarge/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/layout/activity_link_receiver.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/menu/home_bottom_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 14 | 15 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/menu/home_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 27 | 28 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/menu/home_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap/ic_launcher.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/android-base/src/main/res/mipmap/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/app/android-base/src/main/res/mipmap/ic_launcher_round.png -------------------------------------------------------------------------------- /app/server/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "robot-scouter-app" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/server/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "firestore": { 3 | "rules": "firestore.rules" 4 | }, 5 | "storage": { 6 | "rules": "storage.rules" 7 | }, 8 | "hosting": { 9 | "public": "web", 10 | "ignore": [ 11 | "firebase.json", 12 | "**/.*", 13 | "**/node_modules/**" 14 | ] 15 | }, 16 | "functions": { 17 | "source": "functions/upload" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/server/functions/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.js") 3 | } 4 | 5 | kotlin { 6 | target { 7 | useCommonJs() 8 | nodejs() 9 | } 10 | } 11 | 12 | dependencies { 13 | implementation(project(":library:common")) 14 | 15 | implementation(Config.Libs.Kotlin.js) 16 | implementation(Config.Libs.Kotlin.coroutinesJs) 17 | } 18 | 19 | tasks.named("clean") { 20 | delete("upload/index.js", "upload/common/index.js") 21 | delete("upload/node_modules") 22 | } 23 | -------------------------------------------------------------------------------- /app/server/functions/src/main/kotlin/com/supercilex/robotscouter/server/utils/Js.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.server.utils 2 | 3 | import kotlin.js.Json 4 | 5 | fun Json.toMap(): Map { 6 | @Suppress("SENSELESS_COMPARISON") // JavaScrip sucks 7 | if (this == null) return emptyMap() 8 | 9 | val map: MutableMap = mutableMapOf() 10 | for (key: String in js("Object").keys(this)) { 11 | @Suppress("UNCHECKED_CAST") // Trust the client 12 | map[key] = this[key] as T 13 | } 14 | return map 15 | } 16 | -------------------------------------------------------------------------------- /app/server/functions/src/main/kotlin/com/supercilex/robotscouter/server/utils/types/Https.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress( 2 | "INTERFACE_WITH_SUPERCLASS", 3 | "OVERRIDING_FINAL_MEMBER", 4 | "RETURN_TYPE_MISMATCH_ON_OVERRIDE", 5 | "CONFLICTING_OVERLOADS", 6 | "unused" 7 | ) 8 | 9 | package com.supercilex.robotscouter.server.utils.types 10 | 11 | import kotlin.js.Promise 12 | 13 | @Suppress("FunctionName", "UNUSED_PARAMETER", "UNUSED_VARIABLE") // Fake class 14 | fun HttpsError(code: String, message: String? = null, details: Any? = null): Throwable { 15 | val functions = functions 16 | return js("new functions.https.HttpsError(code, message, details)") 17 | } 18 | 19 | external class Https { 20 | fun onCall(handler: (data: T, context: CallableContext) -> Promise<*>?): dynamic = definedExternally 21 | } 22 | 23 | external interface CallableContext { 24 | val auth: AuthContext? get() = definedExternally 25 | val instanceIdToken: String? get() = definedExternally 26 | } 27 | -------------------------------------------------------------------------------- /app/server/functions/src/main/kotlin/com/supercilex/robotscouter/server/utils/types/Pubsub.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress( 2 | "INTERFACE_WITH_SUPERCLASS", 3 | "OVERRIDING_FINAL_MEMBER", 4 | "RETURN_TYPE_MISMATCH_ON_OVERRIDE", 5 | "CONFLICTING_OVERLOADS", 6 | "unused" 7 | ) 8 | 9 | package com.supercilex.robotscouter.server.utils.types 10 | 11 | import kotlin.js.Json 12 | import kotlin.js.Promise 13 | 14 | external class Pubsub { 15 | fun topic(topic: String): TopicBuilder = definedExternally 16 | } 17 | 18 | external class TopicBuilder { 19 | fun onPublish(handler: (message: Message, context: EventContext) -> Promise<*>?): dynamic = definedExternally 20 | } 21 | 22 | external class Message(data: Any) { 23 | val data: String = definedExternally 24 | val json: Json = definedExternally 25 | } 26 | -------------------------------------------------------------------------------- /app/server/functions/upload/.gitignore: -------------------------------------------------------------------------------- 1 | index.js 2 | -------------------------------------------------------------------------------- /app/server/functions/upload/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Robot-Scouter-common-server", 3 | "version": "1.0.0", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /app/server/functions/upload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "dependencies": { 5 | "@google-cloud/firestore": "3.7.1", 6 | "Robot-Scouter-common-server": "file:./common", 7 | "firebase-admin": "8.10.0", 8 | "firebase-functions": "3.3.0", 9 | "kotlin": "1.3.70", 10 | "kotlinx-coroutines-core": "1.3.4", 11 | "lru-cache": "5.1.1", 12 | "moment": "2.24.0", 13 | "superagent": "5.2.2" 14 | }, 15 | "private": true, 16 | "engines": { 17 | "node": "8" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/server/storage.rules: -------------------------------------------------------------------------------- 1 | service firebase.storage { 2 | match /b/robot-scouter-app.appspot.com/o { 3 | match /share_team_template.html { 4 | allow read; 5 | } 6 | 7 | match /share_template_template.html { 8 | allow read; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/server/web/.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "relation": [ 4 | "delegate_permission/common.handle_all_urls" 5 | ], 6 | "target": { 7 | "namespace": "android_app", 8 | "package_name": "com.supercilex.robotscouter", 9 | "sha256_cert_fingerprints": [ 10 | "AB:0B:BC:EB:D1:4C:73:91:3E:A5:6B:04:80:88:87:30:A2:9F:7C:BD:E9:6D:16:D4:AA:90:90:DB:A5:08:69:DD" 11 | ] 12 | } 13 | }, 14 | { 15 | "relation": [ 16 | "delegate_permission/common.get_login_creds" 17 | ], 18 | "target": { 19 | "namespace": "web", 20 | "site": "https://supercilex.github.io" 21 | } 22 | }, 23 | { 24 | "relation": [ 25 | "delegate_permission/common.get_login_creds" 26 | ], 27 | "target": { 28 | "namespace": "android_app", 29 | "package_name": "com.supercilex.robotscouter", 30 | "sha256_cert_fingerprints": [ 31 | "AB:0B:BC:EB:D1:4C:73:91:3E:A5:6B:04:80:88:87:30:A2:9F:7C:BD:E9:6D:16:D4:AA:90:90:DB:A5:08:69:DD" 32 | ] 33 | } 34 | } 35 | ] 36 | -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/assets/demo.gif -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | repositories { 2 | google() 3 | jcenter() 4 | } 5 | 6 | plugins { 7 | `kotlin-dsl` 8 | } 9 | 10 | tasks.withType().configureEach { 11 | enableStricterValidation.set(true) 12 | } 13 | 14 | dependencies { 15 | implementation("org.ajoberstar.grgit:grgit-gradle:4.0.1") 16 | implementation("com.google.cloud:google-cloud-pubsub:1.102.0") 17 | 18 | // TODO remove when GPP 2.7 ships 19 | implementation("com.google.guava:guava:28.1-jre") 20 | } 21 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Projects.kt: -------------------------------------------------------------------------------- 1 | import com.supercilex.robotscouter.build.internal.isCi 2 | import com.supercilex.robotscouter.build.internal.isRelease 3 | import org.gradle.api.Project 4 | 5 | val Project.buildTags: List 6 | get() = listOf( 7 | if (isCi) "CI" else "Local", 8 | if (isRelease) "Release" else "Debug", 9 | if (isReleaseBuild) "ProdBuild" else "DevBuild" 10 | ) 11 | 12 | val Project.isReleaseBuild: Boolean get() = hasProperty("relBuild") || isCi 13 | 14 | internal fun Project.child(name: String): Project = subprojects.first { it.name == name } 15 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/supercilex/robotscouter/build/internal/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.build.internal 2 | 3 | import child 4 | import org.gradle.api.Task 5 | import org.gradle.api.file.RegularFile 6 | 7 | internal val isCi = System.getenv("CI") != null 8 | internal val isRelease = System.getenv("ROBOT_SCOUTER_RELEASE") != null 9 | 10 | internal val Task.secrets: Secrets 11 | get() { 12 | val rootProject = project.rootProject 13 | val android = rootProject.child("android-base").layout.projectDirectory 14 | 15 | val ci = listOf( 16 | android.file("upload-keystore.jks"), 17 | android.file("upload-keystore.properties"), 18 | android.file("google-services.json"), 19 | android.file("google-play-auto-publisher.json"), 20 | rootProject.child("core-data").layout.projectDirectory 21 | .file("src/main/res/values/config.xml") 22 | ) 23 | val full = ci + listOf( 24 | android.file("keystore.jks"), 25 | android.file("keystore.properties") 26 | ) 27 | 28 | return Secrets(full, ci) 29 | } 30 | 31 | internal data class Secrets(val full: List, val ci: List) 32 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/supercilex/robotscouter/build/internal/External.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.build.internal 2 | 3 | import org.eclipse.jgit.util.io.NullOutputStream 4 | import org.gradle.process.ExecSpec 5 | 6 | internal fun ExecSpec.redactLogs() { 7 | standardOutput = NullOutputStream.INSTANCE 8 | errorOutput = NullOutputStream.INSTANCE 9 | } 10 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/com/supercilex/robotscouter/build/internal/Io.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.build.internal 2 | 3 | import java.io.File 4 | 5 | internal fun File.orNull() = takeIf { exists() } 6 | -------------------------------------------------------------------------------- /buildSrc/src/main/resources/META-INF/gradle-plugins/com.supercilex.robotscouter.build.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.supercilex.robotscouter.build.RobotScouterBuildPlugin 2 | -------------------------------------------------------------------------------- /ci-dummies/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TODO 5 | TODO 6 | TODO 7 | TODO 8 | TODO 9 | TODO 10 | TODO.firebaseapp.com 11 | Client-ID TODO 12 | TODO 13 | 14 | -------------------------------------------------------------------------------- /ci-dummies/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/ci-dummies/keystore.jks -------------------------------------------------------------------------------- /ci-dummies/keystore.properties: -------------------------------------------------------------------------------- 1 | keyAlias=keystore 2 | keyPassword=000000 3 | storeFile=keystore.jks 4 | storePassword=000000 5 | -------------------------------------------------------------------------------- /ci-dummies/upload-keystore.properties: -------------------------------------------------------------------------------- 1 | keyAlias=keystore 2 | keyPassword=000000 3 | storeFile=keystore.jks 4 | storePassword=000000 5 | -------------------------------------------------------------------------------- /feature/autoscout/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":app:android-base")) 3 | implementation(project(":library:shared")) 4 | 5 | implementation(Config.Libs.PlayServices.nearby) 6 | } 7 | -------------------------------------------------------------------------------- /feature/autoscout/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 24 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /feature/autoscout/src/main/java/com/supercilex/robotscouter/feature/autoscout/AutoScoutFragment.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.autoscout 2 | 3 | import androidx.fragment.app.FragmentManager 4 | import com.supercilex.robotscouter.AutoScoutFragmentCompanion 5 | import com.supercilex.robotscouter.AutoScoutFragmentCompanion.Companion.TAG 6 | import com.supercilex.robotscouter.Bridge 7 | import com.supercilex.robotscouter.core.ui.FragmentBase 8 | 9 | @Bridge 10 | internal class AutoScoutFragment : FragmentBase(R.layout.fragment_auto_scout) { 11 | companion object : AutoScoutFragmentCompanion { 12 | override fun getInstance(manager: FragmentManager) = 13 | manager.findFragmentByTag(TAG) as AutoScoutFragment? ?: AutoScoutFragment() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /feature/autoscout/src/main/res/layout/fragment_auto_scout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /feature/exports/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":app:android-base")) 3 | implementation(project(":library:shared")) 4 | 5 | implementation(Config.Libs.Misc.poi) 6 | implementation(Config.Libs.Misc.poiProguard) 7 | implementation(Config.Libs.Misc.gson) 8 | } 9 | -------------------------------------------------------------------------------- /feature/exports/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-anydpi-v23/ic_done_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-anydpi-v23/ic_launch_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-anydpi-v23/ic_share_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-hdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-hdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-hdpi/ic_launch_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-hdpi/ic_launch_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-hdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-hdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xhdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xhdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xhdpi/ic_launch_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xhdpi/ic_launch_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xxhdpi/ic_launch_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xxhdpi/ic_launch_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xxxhdpi/ic_launch_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xxxhdpi/ic_launch_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable/ic_done_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable/ic_launch_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable/ic_launch_white_24dp.png -------------------------------------------------------------------------------- /feature/exports/src/main/res/drawable/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/feature/exports/src/main/res/drawable/ic_share_white_24dp.png -------------------------------------------------------------------------------- /feature/scouts/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":app:android-base")) 3 | implementation(project(":library:shared-scouting")) 4 | 5 | implementation(Config.Libs.Jetpack.palette) 6 | } 7 | -------------------------------------------------------------------------------- /feature/scouts/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/ScoutTemplateSelectorDialog.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.scouts 2 | 3 | import androidx.fragment.app.FragmentManager 4 | import com.supercilex.robotscouter.shared.TemplateSelectorDialog 5 | 6 | internal class ScoutTemplateSelectorDialog : TemplateSelectorDialog() { 7 | override fun onItemSelected(id: String) { 8 | super.onItemSelected(id) 9 | (requireParentFragment() as TemplateSelectionListener).onTemplateSelected(id) 10 | } 11 | 12 | companion object { 13 | private const val TAG = "ScoutTemplateSelector" 14 | 15 | fun show(manager: FragmentManager) = ScoutTemplateSelectorDialog().show(manager, TAG) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/TabletScoutListContainer.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.scouts 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.commitNow 6 | import com.supercilex.robotscouter.Bridge 7 | import com.supercilex.robotscouter.TabletScoutListFragmentCompanion 8 | import com.supercilex.robotscouter.core.ui.FragmentBase 9 | 10 | @Bridge 11 | internal class TabletScoutListContainer : FragmentBase(R.layout.activity_scout_list) { 12 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 13 | if (savedInstanceState == null) { 14 | childFragmentManager.commitNow { 15 | add(R.id.scoutList, 16 | TabletScoutListFragment.newInstance(requireArguments()), 17 | TabletScoutListFragment.TAG) 18 | } 19 | } 20 | } 21 | 22 | companion object : TabletScoutListFragmentCompanion { 23 | override fun newInstance(args: Bundle) = 24 | TabletScoutListContainer().apply { arguments = args } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /feature/scouts/src/main/java/com/supercilex/robotscouter/feature/scouts/TemplateSelectionListener.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.scouts 2 | 3 | interface TemplateSelectionListener { 4 | fun onTemplateSelected(id: String) 5 | } 6 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/drawable/ic_assignment_grey_96dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/activity_scout_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/fragment_scout_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/fragment_scout_metric_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/scout_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/scout_counter.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/scout_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/scout_notes.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/scout_spinner.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/layout/scout_stopwatch.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/menu/integrated_scout_list_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/menu/scout_list_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 20 | 21 | 26 | 27 | 32 | 33 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/menu/scout_options.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hors connexion. Les données seront sauvegardées 4 | Les repérages ajoutés apparaissent ici 5 | 6 | Nouveau repérage 7 | Ajouter un repérage 8 | Ajouter un média 9 | Modifier le modèle de repérage suivant 10 | Envoyer à une nouvelle fenêtre 11 | Accès refusé : modèle possédé par un autre utilisateur 12 | 13 | Modifier le nom du match 14 | Supprimer le repérage 15 | Repérage supprimé 16 | 17 | -------------------------------------------------------------------------------- /feature/scouts/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Offline. Data will be saved 4 | Scouts you add appear here 5 | 6 | New scout 7 | Add scout 8 | Add media 9 | Edit next scout template 10 | Move to new window 11 | 12 | Access denied: template owned by another user 13 | 14 | 15 | Edit match name 16 | Delete scout 17 | Scout deleted 18 | 19 | -------------------------------------------------------------------------------- /feature/settings/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":app:android-base")) 3 | implementation(project(":library:shared")) 4 | 5 | implementation(Config.Libs.Jetpack.pref) 6 | implementation(Config.Libs.Misc.licenses) 7 | } 8 | -------------------------------------------------------------------------------- /feature/settings/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /feature/settings/src/main/java/com/supercilex/robotscouter/feature/settings/SettingsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.settings 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.SavedStateHandle 6 | import androidx.lifecycle.viewModelScope 7 | import com.bumptech.glide.Glide 8 | import com.supercilex.robotscouter.core.CrashLogger 9 | import com.supercilex.robotscouter.core.RobotScouter 10 | import com.supercilex.robotscouter.core.data.SimpleViewModelBase 11 | import com.supercilex.robotscouter.core.data.cleanup 12 | import com.supercilex.robotscouter.shared.client.idpSignOut 13 | import kotlinx.coroutines.launch 14 | 15 | internal class SettingsViewModel(state: SavedStateHandle) : SimpleViewModelBase(state) { 16 | private val _signOutListener = MutableLiveData() 17 | val signOutListener: LiveData = _signOutListener 18 | 19 | fun signOut() { 20 | cleanup() 21 | Glide.get(RobotScouter).clearMemory() 22 | 23 | viewModelScope.launch { 24 | try { 25 | idpSignOut() 26 | _signOutListener.value = null 27 | } catch (e: Exception) { 28 | _signOutListener.value = e 29 | CrashLogger.onFailure(e) 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /feature/settings/src/main/res/drawable/ic_link_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/settings/src/main/res/drawable/ic_lock_outline_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /feature/settings/src/main/res/drawable/ic_replay_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/settings/src/main/res/drawable/ic_sign_out_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/settings/src/main/res/drawable/ic_weather_night_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/settings/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /feature/settings/src/main/res/layout/fragment_licenses.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /feature/settings/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /feature/teams/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":app:android-base")) 3 | implementation(project(":library:shared")) 4 | 5 | implementation(Config.Libs.Misc.glideRv) 6 | implementation(Config.Libs.Misc.mttp) 7 | } 8 | -------------------------------------------------------------------------------- /feature/teams/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /feature/teams/src/main/java/com/supercilex/robotscouter/feature/teams/TeamListHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.teams 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.SavedStateHandle 5 | import com.supercilex.robotscouter.core.data.SimpleViewModelBase 6 | import com.supercilex.robotscouter.core.data.TEAM_KEY 7 | import com.supercilex.robotscouter.core.data.teams 8 | import com.supercilex.robotscouter.core.model.Team 9 | 10 | internal class TeamListHolder(state: SavedStateHandle) : SimpleViewModelBase(state) { 11 | private val _selectedTeamIdListener = state.getLiveData(TEAM_KEY) 12 | val selectedTeamIdListener: LiveData get() = _selectedTeamIdListener 13 | 14 | override fun onCreate() { 15 | teams.keepAlive = true 16 | } 17 | 18 | fun selectTeam(team: Team?) { 19 | _selectedTeamIdListener.value = team 20 | } 21 | 22 | override fun onCleared() { 23 | super.onCleared() 24 | teams.keepAlive = false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /feature/teams/src/main/java/com/supercilex/robotscouter/feature/teams/TeamTemplateSelectorDialog.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.teams 2 | 3 | import androidx.fragment.app.FragmentManager 4 | import com.supercilex.robotscouter.TeamSelectionListener 5 | import com.supercilex.robotscouter.core.data.getScoutBundle 6 | import com.supercilex.robotscouter.core.data.getTeam 7 | import com.supercilex.robotscouter.core.data.toBundle 8 | import com.supercilex.robotscouter.core.model.Team 9 | import com.supercilex.robotscouter.core.ui.show 10 | import com.supercilex.robotscouter.shared.TemplateSelectorDialog 11 | 12 | internal class TeamTemplateSelectorDialog : TemplateSelectorDialog() { 13 | override fun onItemSelected(id: String) { 14 | super.onItemSelected(id) 15 | (context as TeamSelectionListener) 16 | .onTeamSelected(getScoutBundle(requireArguments().getTeam(), true, id)) 17 | } 18 | 19 | companion object { 20 | private const val TAG = "TeamTemplateSelector" 21 | 22 | fun show(manager: FragmentManager, team: Team) = 23 | TeamTemplateSelectorDialog().show(manager, TAG, team.toBundle()) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /feature/teams/src/main/res/drawable/ic_check_circle_grey_56dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/teams/src/main/res/drawable/ic_group_grey_96dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/teams/src/main/res/layout/dialog_new_team.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /feature/teams/src/main/res/layout/fragment_team_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /feature/teams/src/main/res/menu/team_options.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | 19 | 20 | 25 | 26 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /feature/teams/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Numéro de l\'équipe 4 | Exporter les équipes 5 | Les équipes ajoutées apparaissent ici 6 | 7 | Inconnu 8 | 9 | Équipe supprimée 10 | %s équipes supprimées 11 | 12 | 13 | Tout sélectionner 14 | Plusieurs équipes sélectionnées 15 | 16 | Créer votre première équipe 17 | Se connecter 18 | Se connecter vous permet de synchroniser vos données avec d\'autres appareils à travers votre équipe. 19 | 20 | -------------------------------------------------------------------------------- /feature/teams/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Team number 4 | Export teams 5 | Teams you add appear here 6 | 7 | Unknown 8 | 9 | Team deleted 10 | %s teams deleted 11 | 12 | 13 | Select all 14 | Multiple teams selected 15 | 16 | Create your first team 17 | Sign in 18 | 19 | Signing in lets you easily sync data with other devices across your team. 20 | 21 | 22 | -------------------------------------------------------------------------------- /feature/teams/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":app:android-base")) 3 | implementation(project(":library:shared-scouting")) 4 | 5 | implementation(Config.Libs.Firebase.links) 6 | } 7 | -------------------------------------------------------------------------------- /feature/templates/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/NewTemplateDialog.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.templates 2 | 3 | import android.content.DialogInterface 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AlertDialog 6 | import androidx.fragment.app.FragmentManager 7 | import com.supercilex.robotscouter.core.data.model.addTemplate 8 | import com.supercilex.robotscouter.core.model.TemplateType 9 | import com.supercilex.robotscouter.core.ui.DialogFragmentBase 10 | import com.supercilex.robotscouter.R as RC 11 | 12 | internal class NewTemplateDialog : DialogFragmentBase(), DialogInterface.OnClickListener { 13 | override fun onCreateDialog(savedInstanceState: Bundle?) = AlertDialog.Builder(requireContext()) 14 | .setTitle(R.string.template_new_title) 15 | .setItems(RC.array.template_new_options, this) 16 | .setNegativeButton(android.R.string.cancel, null) 17 | .create() 18 | 19 | override fun onClick(dialog: DialogInterface, which: Int) { 20 | (requireParentFragment() as TemplateListFragment) 21 | .onTemplateCreated(addTemplate(TemplateType.valueOf(which))) 22 | } 23 | 24 | companion object { 25 | private const val TAG = "NewTemplateDialog" 26 | 27 | fun show(manager: FragmentManager) = NewTemplateDialog().show(manager, TAG) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/TemplatePagerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.templates 2 | 3 | import androidx.fragment.app.Fragment 4 | import com.google.android.material.tabs.TabLayout 5 | import com.google.firebase.firestore.Query 6 | import com.supercilex.robotscouter.core.data.model.getTemplatesQuery 7 | import com.supercilex.robotscouter.core.data.templatesRef 8 | import com.supercilex.robotscouter.core.ui.LifecycleAwareLazy 9 | import com.supercilex.robotscouter.core.ui.onDestroy 10 | import com.supercilex.robotscouter.shared.scouting.TabPagerAdapterBase 11 | import com.supercilex.robotscouter.R as RC 12 | 13 | internal open class TemplatePagerAdapter(fragment: Fragment) : 14 | TabPagerAdapterBase(fragment, templatesRef) { 15 | override val editTabNameRes = R.string.template_edit_name_title 16 | override val tabs: TabLayout by fragment.LifecycleAwareLazy { 17 | fragment.requireActivity().findViewById(R.id.tabs) 18 | } onDestroy { 19 | it.setupWithViewPager(null) 20 | } 21 | 22 | init { 23 | holder.init { getTemplatesQuery(Query.Direction.DESCENDING) } 24 | init() 25 | } 26 | 27 | override fun getItem(position: Int) = TemplateFragment.newInstance(currentScouts[position].id) 28 | 29 | override fun getPageTitle(position: Int): String = 30 | fragment.getString(RC.string.template_tab_default_title, count - position) 31 | } 32 | -------------------------------------------------------------------------------- /feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/CheckboxTemplateViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.templates.viewholder 2 | 3 | import android.view.View 4 | import android.widget.CheckBox 5 | import android.widget.EditText 6 | import android.widget.ImageView 7 | import com.supercilex.robotscouter.core.data.model.update 8 | import com.supercilex.robotscouter.core.data.model.updateName 9 | import com.supercilex.robotscouter.core.model.Metric 10 | import com.supercilex.robotscouter.core.unsafeLazy 11 | import com.supercilex.robotscouter.shared.scouting.viewholder.CheckboxViewHolder 12 | import kotlinx.android.synthetic.main.scout_template_base_reorder.* 13 | import com.supercilex.robotscouter.R as RC 14 | 15 | internal class CheckboxTemplateViewHolder(itemView: View) : CheckboxViewHolder(itemView), 16 | MetricTemplateViewHolder { 17 | override val reorderView: ImageView by unsafeLazy { reorder } 18 | override val nameEditor = name as EditText 19 | 20 | private val checkBox: CheckBox = containerView.findViewById(RC.id.checkBox) 21 | 22 | init { 23 | init() 24 | name.setOnClickListener(null) 25 | } 26 | 27 | override fun onClick(v: View) { 28 | if (name.hasFocus()) metric.updateName(name.text.toString()) 29 | if (v.id == RC.id.checkBox) metric.update(checkBox.isChecked) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/EditTextTemplateViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.templates.viewholder 2 | 3 | import android.view.View 4 | import android.widget.EditText 5 | import android.widget.ImageView 6 | import com.supercilex.robotscouter.core.data.model.update 7 | import com.supercilex.robotscouter.core.model.Metric 8 | import com.supercilex.robotscouter.core.unsafeLazy 9 | import com.supercilex.robotscouter.shared.scouting.MetricViewHolderBase 10 | import kotlinx.android.synthetic.main.scout_template_base_reorder.* 11 | import kotlinx.android.synthetic.main.scout_template_notes.* 12 | 13 | internal class EditTextTemplateViewHolder( 14 | itemView: View 15 | ) : MetricViewHolderBase(itemView), 16 | MetricTemplateViewHolder { 17 | override val reorderView: ImageView by unsafeLazy { reorder } 18 | override val nameEditor = name as EditText 19 | 20 | init { 21 | init() 22 | text.onFocusChangeListener = this 23 | } 24 | 25 | override fun bind() { 26 | super.bind() 27 | text.setText(metric.value) 28 | } 29 | 30 | override fun onFocusChange(v: View, hasFocus: Boolean) { 31 | super.onFocusChange(v, hasFocus) 32 | if (!hasFocus && v === text) metric.update(text.text.toString()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/HeaderTemplateViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.templates.viewholder 2 | 3 | import android.view.View 4 | import android.widget.EditText 5 | import android.widget.ImageView 6 | import com.supercilex.robotscouter.core.model.Metric 7 | import com.supercilex.robotscouter.core.unsafeLazy 8 | import com.supercilex.robotscouter.shared.scouting.viewholder.HeaderViewHolder 9 | import kotlinx.android.synthetic.main.scout_template_base_reorder.* 10 | 11 | internal class HeaderTemplateViewHolder(itemView: View) : HeaderViewHolder(itemView), 12 | MetricTemplateViewHolder { 13 | override val reorderView: ImageView by unsafeLazy { reorder } 14 | override val nameEditor = name as EditText 15 | 16 | init { 17 | init() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/MetricTemplateViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.templates.viewholder 2 | 3 | import android.view.View 4 | import com.supercilex.robotscouter.core.data.model.updateName 5 | import com.supercilex.robotscouter.core.model.Metric 6 | 7 | internal interface MetricTemplateViewHolder, T> : TemplateViewHolder { 8 | var metric: M 9 | 10 | override fun onFocusChange(v: View, hasFocus: Boolean) { 11 | // Only save data when user is leaving metric 12 | if (!hasFocus && v === nameEditor) { 13 | metric.updateName(nameEditor.text.toString()) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /feature/templates/src/main/java/com/supercilex/robotscouter/feature/templates/viewholder/TemplateViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.templates.viewholder 2 | 3 | import android.view.MotionEvent 4 | import android.view.View 5 | import android.widget.EditText 6 | import androidx.recyclerview.widget.ItemTouchHelper 7 | import androidx.recyclerview.widget.RecyclerView 8 | 9 | internal interface TemplateViewHolder : View.OnFocusChangeListener { 10 | val reorderView: View 11 | val nameEditor: EditText 12 | 13 | fun init() { 14 | nameEditor.onFocusChangeListener = this 15 | } 16 | 17 | fun requestFocus() { 18 | nameEditor.requestFocus() 19 | } 20 | 21 | fun enableDragToReorder( 22 | viewHolder: RecyclerView.ViewHolder, 23 | helper: ItemTouchHelper 24 | ) = reorderView.setOnTouchListener(View.OnTouchListener { v, event -> 25 | if (event.action == MotionEvent.ACTION_DOWN) { 26 | viewHolder.itemView.clearFocus() // Saves data 27 | helper.startDrag(viewHolder) 28 | v.performClick() 29 | return@OnTouchListener true 30 | } 31 | false 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_default_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 12 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_delete_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_header_spinner.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_metric_checkbox.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_metric_counter.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_metric_header.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_metric_notes.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_metric_stopwatch.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_reorder_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/drawable/ic_ruler_grey_96dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/layout/fragment_template_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/layout/fragment_template_metric_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/layout/scout_template_base_reorder.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/layout/scout_template_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/layout/scout_template_counter.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/layout/scout_template_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/layout/scout_template_stopwatch.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/menu/template_list_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/menu/template_options.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 15 | 16 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /feature/templates/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /feature/trash/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation(project(":app:android-base")) 3 | implementation(project(":library:shared")) 4 | } 5 | -------------------------------------------------------------------------------- /feature/trash/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /feature/trash/src/main/java/com/supercilex/robotscouter/feature/trash/SelectableScrollView.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.trash 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.MotionEvent 6 | import androidx.core.widget.NestedScrollView 7 | 8 | internal class SelectableScrollView : NestedScrollView { 9 | constructor(context: Context) : super(context) 10 | 11 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) 12 | 13 | constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : 14 | super(context, attrs, defStyleAttr) 15 | 16 | override fun onInterceptTouchEvent(ev: MotionEvent) = false 17 | } 18 | -------------------------------------------------------------------------------- /feature/trash/src/main/java/com/supercilex/robotscouter/feature/trash/TrashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.trash 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.fragment.app.commit 6 | import com.supercilex.robotscouter.Bridge 7 | import com.supercilex.robotscouter.TrashActivityCompanion 8 | import com.supercilex.robotscouter.core.RobotScouter 9 | import com.supercilex.robotscouter.core.ui.ActivityBase 10 | import com.supercilex.robotscouter.R as RC 11 | 12 | @Bridge 13 | internal class TrashActivity : ActivityBase() { 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | setTheme(RC.style.RobotScouter_NoActionBar) 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_trash) 18 | if (savedInstanceState == null) { 19 | supportFragmentManager.commit { 20 | add(R.id.trash, TrashFragment.newInstance(), TrashFragment.TAG) 21 | } 22 | } 23 | } 24 | 25 | companion object : TrashActivityCompanion { 26 | override fun createIntent(): Intent = Intent(RobotScouter, TrashActivity::class.java) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /feature/trash/src/main/java/com/supercilex/robotscouter/feature/trash/TrashListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.feature.trash 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.selection.SelectionTracker 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.ListAdapter 8 | import com.supercilex.robotscouter.core.LateinitVal 9 | 10 | internal class TrashListAdapter : ListAdapter(differ) { 11 | var selectionTracker: SelectionTracker by LateinitVal() 12 | 13 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = TrashViewHolder( 14 | LayoutInflater.from(parent.context).inflate(R.layout.trash_item, parent, false) 15 | ) 16 | 17 | override fun onBindViewHolder(holder: TrashViewHolder, position: Int) { 18 | val trash = getItem(position) 19 | holder.bind(trash, selectionTracker.isSelected(trash.id)) 20 | } 21 | 22 | public override fun getItem(position: Int): Trash = super.getItem(position) 23 | 24 | private companion object { 25 | val differ = object : DiffUtil.ItemCallback() { 26 | override fun areItemsTheSame(oldItem: Trash, newItem: Trash) = oldItem.id == newItem.id 27 | 28 | override fun areContentsTheSame(oldItem: Trash, newItem: Trash) = oldItem == newItem 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /feature/trash/src/main/res/drawable/ic_close_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/trash/src/main/res/drawable/ic_delete_empty_96dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/trash/src/main/res/drawable/ic_restore_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature/trash/src/main/res/drawable/trash_item_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature/trash/src/main/res/drawable/trash_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /feature/trash/src/main/res/layout/activity_trash.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /feature/trash/src/main/res/layout/trash_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /feature/trash/src/main/res/menu/trash_options.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 18 | 19 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4g -XX:ReservedCodeCacheSize=500m -Dfile.encoding=UTF-8 2 | org.gradle.parallel=true 3 | org.gradle.caching=true 4 | android.useAndroidX=true 5 | android.enableJetifier=true 6 | android.enableR8=false 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /library/common/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.multiplatform") 3 | } 4 | 5 | kotlin { 6 | jvm("android") 7 | 8 | js("server") { 9 | useCommonJs() 10 | nodejs() 11 | } 12 | 13 | sourceSets { 14 | named("commonMain") { 15 | dependencies { 16 | implementation(Config.Libs.Kotlin.common) 17 | } 18 | } 19 | 20 | named("androidMain") { 21 | dependencies { 22 | implementation(Config.Libs.Kotlin.jvm) 23 | } 24 | } 25 | 26 | named("serverMain") { 27 | dependencies { 28 | implementation(Config.Libs.Kotlin.js) 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /library/common/src/commonMain/kotlin/com/supercilex/robotscouter/common/Collections.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.common 2 | 3 | val Iterable<*>.isSingleton: Boolean 4 | get() = iterator().let { 5 | if (!it.hasNext()) return false 6 | it.next() 7 | return !it.hasNext() 8 | } 9 | 10 | val Iterable<*>.isPolynomial: Boolean 11 | get() = iterator().let { 12 | return if (!it.hasNext()) false else !isSingleton 13 | } 14 | 15 | fun Array.second(): T { 16 | if (size < 2) throw NoSuchElementException("List has less than 2 elements.") 17 | return this[1] 18 | } 19 | 20 | fun List.second(): T { 21 | if (size < 2) throw NoSuchElementException("List has less than 2 elements.") 22 | return this[1] 23 | } 24 | -------------------------------------------------------------------------------- /library/core-data/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.internal.CacheImplementation 2 | 3 | androidExtensions { 4 | defaultCacheImplementation = CacheImplementation.NONE 5 | } 6 | 7 | dependencies { 8 | api(project(":library:core")) 9 | api(project(":library:core-model")) 10 | 11 | api(Config.Libs.Firebase.firestore) 12 | api(Config.Libs.Firebase.auth) 13 | api(Config.Libs.Firebase.indexing) 14 | 15 | Config.Libs.Jetpack.lifecycle.forEach { api(it) } 16 | 17 | implementation(Config.Libs.Jetpack.appCompat) { isTransitive = false } 18 | implementation(Config.Libs.Jetpack.pref) 19 | implementation(Config.Libs.Misc.glide) { isTransitive = false } 20 | 21 | implementation(Config.Libs.PlayServices.auth) { isTransitive = false } 22 | implementation(Config.Libs.FirebaseUi.firestore) { exclude(module = "recyclerview-v7") } 23 | implementation(Config.Libs.Firebase.functions) 24 | implementation(Config.Libs.Firebase.storage) 25 | implementation(Config.Libs.Firebase.messaging) 26 | implementation(Config.Libs.Firebase.config) 27 | Config.Libs.Jetpack.work.forEach { implementation(it) } 28 | 29 | implementation(Config.Libs.Misc.retrofit) 30 | implementation(Config.Libs.Misc.retrofitGson) 31 | } 32 | -------------------------------------------------------------------------------- /library/core-data/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Cleanup.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data 2 | 3 | import com.bumptech.glide.Glide 4 | import com.google.firebase.appindexing.FirebaseAppIndex 5 | import com.google.firebase.functions.ktx.functions 6 | import com.google.firebase.ktx.Firebase 7 | import com.supercilex.robotscouter.core.RobotScouter 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.GlobalScope 10 | import kotlinx.coroutines.invoke 11 | import kotlinx.coroutines.launch 12 | 13 | fun cleanup() { 14 | GlobalScope.launch { 15 | Dispatchers.IO { Glide.get(RobotScouter).clearDiskCache() } 16 | cleanupJobs() 17 | } 18 | FirebaseAppIndex.getInstance().removeAll().logFailures("cleanup") 19 | } 20 | 21 | fun emptyTrash(ids: List? = null) = Firebase.functions 22 | .getHttpsCallable("clientApi") 23 | .call(mapOf("operation" to "empty-trash", "ids" to ids)) 24 | .logFailures("emptyTrash", ids) 25 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Io.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data 2 | 3 | import android.webkit.MimeTypeMap 4 | import java.io.File 5 | 6 | /** @return this directory after ensuring that it either already exists or was created */ 7 | fun File.safeMkdirs(): File = apply { 8 | val create = { exists() || mkdirs() } 9 | check(create() || create()) { "Unable to create $this" } 10 | } 11 | 12 | /** @return this file after ensuring that it either already exists or was created */ 13 | fun File.safeCreateNewFile(): File = apply { 14 | parentFile?.safeMkdirs() 15 | 16 | val create = { exists() || createNewFile() } 17 | check(create() || create()) { "Unable to create $this" } 18 | } 19 | 20 | fun File.mimeType(): String { 21 | return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) ?: "*/*" 22 | } 23 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/Jobs.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data 2 | 3 | import androidx.work.WorkManager 4 | import androidx.work.await 5 | import com.supercilex.robotscouter.core.RobotScouter 6 | import com.supercilex.robotscouter.core.data.client.TEAM_MEDIA_UPLOAD 7 | 8 | internal suspend fun cleanupJobs() { 9 | WorkManager.getInstance(RobotScouter).apply { 10 | listOf(TEAM_MEDIA_UPLOAD) 11 | .map { cancelAllWorkByTag(it) } 12 | .forEach { it.result.await() } 13 | pruneWork() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/SimpleViewModelBase.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data 2 | 3 | import androidx.lifecycle.SavedStateHandle 4 | 5 | abstract class SimpleViewModelBase(state: SavedStateHandle) : ViewModelBase(state) { 6 | fun init() = init(null) 7 | 8 | final override fun onCreate(args: Unit?) = onCreate() 9 | 10 | open fun onCreate() = Unit 11 | } 12 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/TaskLogging.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data 2 | 3 | import com.google.android.gms.tasks.Task 4 | import com.google.firebase.firestore.DocumentReference 5 | import com.supercilex.robotscouter.core.CrashLogger 6 | import com.supercilex.robotscouter.core.logBreadcrumb 7 | 8 | fun Task.logFailures( 9 | tag: String, 10 | vararg hints: Any? 11 | ): Task = addOnFailureListener { 12 | for (hint in hints) logBreadcrumb(hint.toString()) 13 | logBreadcrumb("Source: $tag") 14 | CrashLogger.invoke(it) 15 | } 16 | 17 | fun Task.logFailures( 18 | tag: String, 19 | ref: DocumentReference, 20 | vararg hints: Any? 21 | ) = logFailures(tag, "Path: ${ref.path}", *hints) 22 | 23 | fun Task.logFailures( 24 | tag: String, 25 | refs: List, 26 | vararg hints: Any? 27 | ) = logFailures(tag, "Paths: ${refs.joinToString { it.path }}", *hints) 28 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/ViewModelBase.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data 2 | 3 | import androidx.annotation.CallSuper 4 | import androidx.lifecycle.SavedStateHandle 5 | import androidx.lifecycle.ViewModel 6 | import java.util.concurrent.atomic.AtomicBoolean 7 | 8 | abstract class ViewModelBase(protected val state: SavedStateHandle) : ViewModel() { 9 | private val isInitialized = AtomicBoolean() 10 | 11 | fun init(args: T) { 12 | if (isInitialized.compareAndSet(false, true)) { 13 | onCreate(args) 14 | } 15 | } 16 | 17 | protected abstract fun onCreate(args: T) 18 | 19 | @CallSuper 20 | override fun onCleared() { 21 | isInitialized.set(false) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/client/WorkerBase.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data.client 2 | 3 | import android.content.Context 4 | import androidx.work.CoroutineWorker 5 | import androidx.work.WorkerParameters 6 | import com.supercilex.robotscouter.core.logBreadcrumb 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.invoke 9 | 10 | internal abstract class WorkerBase( 11 | context: Context, 12 | workerParams: WorkerParameters 13 | ) : CoroutineWorker(context, workerParams) { 14 | override suspend fun doWork(): Result { 15 | if (runAttemptCount >= MAX_RUN_ATTEMPTS) return Result.failure() 16 | 17 | return try { 18 | Dispatchers.IO { doBlockingWork() } 19 | } catch (e: Exception) { 20 | logBreadcrumb("$javaClass errored: $e") 21 | Result.retry() 22 | } 23 | } 24 | 25 | protected abstract suspend fun doBlockingWork(): Result 26 | 27 | private companion object { 28 | const val MAX_RUN_ATTEMPTS = 7 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/model/ScoutsHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data.model 2 | 3 | import androidx.lifecycle.SavedStateHandle 4 | import com.supercilex.robotscouter.core.data.LifecycleAwareFirestoreArray 5 | import com.supercilex.robotscouter.core.data.QueryGenerator 6 | import com.supercilex.robotscouter.core.data.ViewModelBase 7 | import com.supercilex.robotscouter.core.model.Scout 8 | 9 | class ScoutsHolder(state: SavedStateHandle) : ViewModelBase(state) { 10 | lateinit var scouts: LifecycleAwareFirestoreArray 11 | private set 12 | 13 | override fun onCreate(args: QueryGenerator) { 14 | scouts = LifecycleAwareFirestoreArray(args, scoutParser) 15 | scouts.keepAlive = true 16 | } 17 | 18 | public override fun onCleared() { 19 | super.onCleared() 20 | scouts.keepAlive = false 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/model/TeamCache.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data.model 2 | 3 | import com.supercilex.robotscouter.core.model.Team 4 | import java.util.Collections 5 | 6 | open class TeamCache(teams: Collection) { 7 | val teams: List by lazy { 8 | Collections.unmodifiableList(teams.sorted()) 9 | } 10 | val teamNames: String by lazy { this.teams.getNames() } 11 | } 12 | -------------------------------------------------------------------------------- /library/core-data/src/main/java/com/supercilex/robotscouter/core/data/remote/TeamMediaApi.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.data.remote 2 | 3 | import com.google.gson.JsonObject 4 | import okhttp3.RequestBody 5 | import retrofit2.Retrofit 6 | import retrofit2.converter.gson.GsonConverterFactory 7 | import retrofit2.http.Body 8 | import retrofit2.http.Header 9 | import retrofit2.http.POST 10 | import retrofit2.http.Query 11 | 12 | internal interface TeamMediaApi { 13 | @POST("image") 14 | suspend fun postToImgurAsync( 15 | @Header("Authorization") auth: String, 16 | @Query("title") title: String, 17 | @Body file: RequestBody 18 | ): JsonObject 19 | 20 | companion object { 21 | val IMGUR_RETROFIT: Retrofit = Retrofit.Builder() 22 | .baseUrl("https://api.imgur.com/3/") 23 | .addConverterFactory(GsonConverterFactory.create()) 24 | .build() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/core-data/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Modèle %d 4 | Erreur dans la création du repérage:\nLe modèle que vous tentez d‘utiliser n’a pas encore été mis en cache. Veuillez ajouter au moins un repérage pendant que vous avez une connexion Internet pour que votre modèle puisse être mis en cache et utilisé hors ligne. 5 | 6 | 7 | Exportation en tableur 8 | Exportations complétées 9 | Affiche une notification actionnable pour gérer l\'exportation complétée. 10 | Exportations en cours 11 | Affiche le statut d\'une exportation en cours. 12 | 13 | -------------------------------------------------------------------------------- /library/core-data/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Template %d 4 | 5 | Error adding new scout:\nThe template you are attempting to use hasn’t been cached yet. 6 | Please add at least one scout while you have an internet connection so your template may be 7 | cached for offline use. 8 | 9 | 10 | 11 | Spreadsheet exporting 12 | Completed exports 13 | 14 | Displays an actionable notification to manage the completed export. 15 | 16 | In-progress exports 17 | Displays the status of an in-progress export. 18 | 19 | -------------------------------------------------------------------------------- /library/core-data/src/main/res/xml-v29/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /library/core-data/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /library/core-data/src/main/res/xml/remote_config_defaults.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | minimum_app_version 5 | -1 6 | 7 | 8 | 9 | show_rating_dialog 10 | true 11 | 12 | 13 | 14 | enable_auto_scout 15 | false 16 | 17 | 18 | -------------------------------------------------------------------------------- /library/core-model/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.internal.CacheImplementation 2 | 3 | androidExtensions { 4 | defaultCacheImplementation = CacheImplementation.NONE 5 | } 6 | 7 | dependencies { 8 | implementation(project(":library:common")) 9 | implementation(Config.Libs.Jetpack.core) 10 | 11 | compileOnly(Config.Libs.Firebase.firestore) { exclude(group = "com.google.guava") } 12 | compileOnly(Config.Libs.Misc.gson) 13 | } 14 | -------------------------------------------------------------------------------- /library/core-model/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /library/core-model/src/main/java/com/supercilex/robotscouter/core/model/MetricType.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.model 2 | 3 | import java.util.Collections 4 | 5 | enum class MetricType(val id: Int) { 6 | HEADER(5), 7 | BOOLEAN(0), 8 | NUMBER(1), 9 | STOPWATCH(4), 10 | TEXT(3), 11 | LIST(2); 12 | 13 | companion object { 14 | /** 15 | * Identical to the native values() method except that this one returns an immutable [List] 16 | * instead of an [Array] which must be copied defensively. 17 | * 18 | * @see enumValues 19 | */ 20 | val values: List = Collections.unmodifiableList(values().toList()) 21 | 22 | fun valueOf(id: Int): MetricType = requireNotNull(values.find { it.id == id }) { 23 | "Unknown metric type: $id" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/core-model/src/main/java/com/supercilex/robotscouter/core/model/OrderedModel.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.model 2 | 3 | interface OrderedModel { 4 | var position: Int 5 | } 6 | -------------------------------------------------------------------------------- /library/core-model/src/main/java/com/supercilex/robotscouter/core/model/OrderedRemoteModel.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.model 2 | 3 | import androidx.annotation.Keep 4 | import com.google.firebase.firestore.DocumentReference 5 | import com.google.firebase.firestore.Exclude 6 | 7 | interface OrderedRemoteModel : OrderedModel { 8 | @get:Keep 9 | override var position: Int 10 | 11 | @get:Exclude 12 | val ref: DocumentReference 13 | } 14 | -------------------------------------------------------------------------------- /library/core-model/src/main/java/com/supercilex/robotscouter/core/model/Scout.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.model 2 | 3 | import androidx.annotation.Keep 4 | import com.google.firebase.Timestamp 5 | import com.google.firebase.firestore.Exclude 6 | 7 | data class Scout( 8 | @Exclude 9 | @get:Exclude 10 | val id: String, 11 | 12 | @Exclude 13 | @get:Keep 14 | val templateId: String, 15 | 16 | @Exclude 17 | @get:Keep 18 | val name: String? = null, 19 | 20 | @Exclude 21 | @get:Keep 22 | val timestamp: Timestamp = Timestamp.now(), 23 | 24 | @Exclude 25 | @get:Exclude 26 | val metrics: List> = emptyList() 27 | ) 28 | -------------------------------------------------------------------------------- /library/core-model/src/main/java/com/supercilex/robotscouter/core/model/TemplateType.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.model 2 | 3 | import androidx.core.text.isDigitsOnly 4 | import java.util.Collections 5 | 6 | enum class TemplateType(val id: Int) { 7 | MATCH(0), 8 | PIT(1), 9 | EMPTY(2); 10 | 11 | companion object { 12 | val DEFAULT: TemplateType = MATCH 13 | /** 14 | * Identical to the native values() method except that this one returns an immutable [List] 15 | * instead of an [Array] which must be copied defensively. 16 | * 17 | * @see enumValues 18 | */ 19 | val values: List = Collections.unmodifiableList(values().toList()) 20 | 21 | fun valueOf(id: Int): TemplateType = requireNotNull(coerce(id.toString())) { 22 | "Unknown template type: $id" 23 | } 24 | 25 | fun coerce(id: String?): TemplateType? = if (id?.isDigitsOnly() == true) { 26 | values.find { it.id == id.toInt() } 27 | } else { 28 | null 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /library/core-ui/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.internal.CacheImplementation 2 | 3 | android { 4 | buildTypes { 5 | named("release") { 6 | postprocessing { 7 | consumerProguardFile("proguard-rules.pro") 8 | } 9 | } 10 | } 11 | } 12 | 13 | androidExtensions { 14 | defaultCacheImplementation = CacheImplementation.NONE 15 | } 16 | 17 | dependencies { 18 | api(project(":library:core")) 19 | 20 | api(Config.Libs.Jetpack.lifecycle.first()) 21 | api(Config.Libs.Jetpack.fragment) 22 | api(Config.Libs.Jetpack.material) 23 | api(Config.Libs.Jetpack.emoji) 24 | api(Config.Libs.Jetpack.constraint) 25 | api(Config.Libs.FirebaseUi.firestore) 26 | api(Config.Libs.Jetpack.rv) 27 | api(Config.Libs.Jetpack.rvSelection) 28 | 29 | implementation(Config.Libs.Jetpack.browser) 30 | implementation(Config.Libs.Jetpack.pref) 31 | implementation(Config.Libs.Misc.permissions) 32 | implementation(Config.Libs.Misc.flexbox) 33 | } 34 | -------------------------------------------------------------------------------- /library/core-ui/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keepnames class * extends com.supercilex.robotscouter.core.ui.FragmentBase 2 | -keepnames class * extends com.supercilex.robotscouter.core.ui.DialogFragmentBase 3 | -keepnames class * extends com.supercilex.robotscouter.core.ui.BottomSheetDialogFragmentBase 4 | -keepnames class * extends com.supercilex.robotscouter.core.ui.PreferenceFragmentBase 5 | -------------------------------------------------------------------------------- /library/core-ui/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/Flow.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.ui 2 | 3 | import android.content.Intent 4 | import android.os.Build 5 | import androidx.recyclerview.widget.RecyclerView 6 | 7 | fun Intent.addNewDocumentFlags(): Intent { 8 | if (Build.VERSION.SDK_INT >= 21) { 9 | addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) 10 | } else { 11 | @Suppress("DEPRECATION") 12 | addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) 13 | } 14 | addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT) 15 | return this 16 | } 17 | 18 | interface RecyclerPoolHolder { 19 | val recyclerPool: RecyclerView.RecycledViewPool 20 | } 21 | -------------------------------------------------------------------------------- /library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/StateHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.ui 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | 6 | class StateHolder(state: T) { 7 | private val _liveData = MutableLiveData(state) 8 | val liveData: LiveData get() = _liveData 9 | 10 | private var _value: T = state 11 | val value: T get() = _value 12 | 13 | fun update(notify: Boolean = true, block: T.() -> T) { 14 | synchronized(LOCK) { 15 | _value = block(value) 16 | if (notify) _liveData.postValue(value) 17 | } 18 | } 19 | 20 | private companion object { 21 | val LOCK = Object() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/views/CardMetricFlexboxLayout.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.ui.views 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import android.util.AttributeSet 6 | import com.google.android.flexbox.FlexboxLayout 7 | import com.supercilex.robotscouter.core.ui.CardMetric 8 | import com.supercilex.robotscouter.core.ui.CardMetricHelper 9 | 10 | class CardMetricFlexboxLayout : FlexboxLayout, CardMetric { 11 | override val helper = CardMetricHelper(this) 12 | 13 | constructor(context: Context) : super(context) 14 | 15 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) 16 | 17 | constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : 18 | super(context, attrs, defStyleAttr) 19 | 20 | init { 21 | helper.init() 22 | } 23 | 24 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 25 | super.onLayout(changed, left, top, right, bottom) 26 | super.onLayout(changed, left, top, right, bottom) 27 | } 28 | 29 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) = 30 | super.onSizeChanged(w, h, oldw, oldh) 31 | 32 | override fun onDraw(canvas: Canvas) { 33 | super.onDraw(canvas) 34 | super.onDraw(canvas) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/views/CardMetricLinearLayout.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.ui.views 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import android.util.AttributeSet 6 | import android.widget.LinearLayout 7 | import com.supercilex.robotscouter.core.ui.CardMetric 8 | import com.supercilex.robotscouter.core.ui.CardMetricHelper 9 | 10 | class CardMetricLinearLayout : LinearLayout, CardMetric { 11 | override val helper = CardMetricHelper(this) 12 | 13 | constructor(context: Context) : super(context) 14 | 15 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) 16 | 17 | constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : 18 | super(context, attrs, defStyleAttr) 19 | 20 | init { 21 | helper.init() 22 | } 23 | 24 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 25 | super.onLayout(changed, left, top, right, bottom) 26 | super.onLayout(changed, left, top, right, bottom) 27 | } 28 | 29 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) = 30 | super.onSizeChanged(w, h, oldw, oldh) 31 | 32 | override fun onDraw(canvas: Canvas) { 33 | super.onDraw(canvas) 34 | super.onDraw(canvas) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/views/SupportVectorDrawablesImageButton.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.ui.views 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.appcompat.widget.AppCompatImageButton 6 | import androidx.core.content.withStyledAttributes 7 | import com.supercilex.robotscouter.core.ui.R 8 | import com.supercilex.robotscouter.core.ui.getDrawableCompat 9 | import com.supercilex.robotscouter.core.ui.getIconThemedContext 10 | 11 | /** Supports custom icon styling. */ 12 | open class SupportVectorDrawablesImageButton : AppCompatImageButton { 13 | constructor(context: Context) : super(context) 14 | 15 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { 16 | applyDrawable(attrs) 17 | } 18 | 19 | constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : 20 | super(context, attrs, defStyleAttr) { 21 | applyDrawable(attrs) 22 | } 23 | 24 | private fun applyDrawable(set: AttributeSet) { 25 | context.withStyledAttributes(set, R.styleable.Icon) { 26 | setImageDrawable(getIconThemedContext(context).getDrawableCompat( 27 | getResourceId(R.styleable.Icon_iconDrawable, -1) 28 | )) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /library/core-ui/src/main/java/com/supercilex/robotscouter/core/ui/views/UnscrollableViewPager.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core.ui.views 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.util.AttributeSet 6 | import android.view.MotionEvent 7 | import androidx.viewpager.widget.ViewPager 8 | 9 | /** 10 | * [ViewPager] that prevents horizontal scrolling. 11 | */ 12 | class UnscrollableViewPager : ViewPager { 13 | constructor(context: Context) : super(context) 14 | 15 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) 16 | 17 | @SuppressLint("ClickableViewAccessibility") // We purposefully don't want to allow swiping 18 | override fun onTouchEvent(event: MotionEvent) = false 19 | 20 | override fun onInterceptTouchEvent(event: MotionEvent) = false 21 | } 22 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/color/enablable_button_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/color/list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/drawable-anydpi-v23/launch_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/drawable-hdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/library/core-ui/src/main/res/drawable-hdpi/ic_logo.png -------------------------------------------------------------------------------- /library/core-ui/src/main/res/drawable-xhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/library/core-ui/src/main/res/drawable-xhdpi/ic_logo.png -------------------------------------------------------------------------------- /library/core-ui/src/main/res/drawable-xxhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/library/core-ui/src/main/res/drawable-xxhdpi/ic_logo.png -------------------------------------------------------------------------------- /library/core-ui/src/main/res/drawable-xxxhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/library/core-ui/src/main/res/drawable-xxxhdpi/ic_logo.png -------------------------------------------------------------------------------- /library/core-ui/src/main/res/drawable/card_item_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/drawable/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/library/core-ui/src/main/res/drawable/ic_logo.png -------------------------------------------------------------------------------- /library/core-ui/src/main/res/drawable/launch_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 14 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Oui 4 | Non 5 | Sauvegarder 6 | Annuler 7 | 8 | Partager 9 | Ouvrir 10 | Supprimer 11 | Supprimé 12 | Annulé 13 | 14 | Aucune connexion Internet 15 | Connexion requise 16 | Connexion réussie ! 17 | Échec de la connexion 18 | Une erreur inconnue s\'est produite 19 | 20 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-large-port/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @dimen/spacing_large_medium 4 | @dimen/spacing_large 5 | 6 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-large/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @dimen/spacing_normal 4 | @dimen/spacing_normal 5 | @dimen/spacing_xlarge 6 | @dimen/spacing_large 7 | @dimen/spacing_xlarge 8 | @dimen/spacing_large 9 | 10 | 600dp 11 | 12 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-night-v27/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @android:color/white 4 | @color/material_grey_300 5 | 6 | #757575 7 | #BDBDBD 8 | @color/material_grey_700 9 | @color/material_grey_600 10 | #46264CAC 11 | #dc111111 12 | 13 | 14 | #3A3A3A 15 | 16 | @color/material_grey_800 17 | 18 | #EEEEEE 19 | 20 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-normal-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @dimen/spacing_xlarge 4 | @dimen/spacing_large 5 | 6 | 600dp 7 | 8 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 12 | 13 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-v27/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-xlarge-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @dimen/spacing_large_medium 4 | @dimen/spacing_large 5 | @dimen/spacing_xxlarge 6 | @dimen/spacing_large 7 | @dimen/spacing_xlarge 8 | @dimen/spacing_large 9 | 10 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values-xlarge-port/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @dimen/spacing_normal 4 | @dimen/spacing_normal 5 | 6 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /library/core-ui/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Yes 4 | No 5 | Save 6 | Undo 7 | 8 | Share 9 | Open 10 | Delete 11 | Deleted 12 | Cancelled 13 | 14 | No internet connection 15 | Sign in required 16 | Successfully signed in! 17 | Sign in failed 18 | An unknown error occurred 19 | 20 | -------------------------------------------------------------------------------- /library/core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.internal.CacheImplementation 2 | 3 | androidExtensions { 4 | defaultCacheImplementation = CacheImplementation.NONE 5 | } 6 | 7 | dependencies { 8 | api(project(":library:common")) 9 | 10 | api(Config.Libs.Kotlin.jvm) 11 | api(Config.Libs.Kotlin.coroutinesAndroid) 12 | api(Config.Libs.Kotlin.coroutinesTasks) 13 | api(Config.Libs.Firebase.analytics) 14 | api(Config.Libs.Firebase.crashlytics) 15 | api(Config.Libs.Jetpack.core) 16 | 17 | debugImplementation(Config.Libs.Misc.leakCanary) 18 | } 19 | -------------------------------------------------------------------------------- /library/core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /library/core/src/main/java/com/supercilex/robotscouter/core/Connectivity.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core 2 | 3 | import android.net.ConnectivityManager 4 | import androidx.core.content.getSystemService 5 | 6 | val isOnline get() = connectivityManager.activeNetworkInfo?.isConnected == true 7 | 8 | val isOffline get() = !isOnline 9 | 10 | private val connectivityManager by lazy { 11 | checkNotNull(RobotScouter.getSystemService()) 12 | } 13 | -------------------------------------------------------------------------------- /library/core/src/main/java/com/supercilex/robotscouter/core/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core 2 | 3 | import android.os.Build 4 | import android.os.Handler 5 | import android.os.Looper 6 | import android.provider.Settings 7 | import androidx.core.app.ActivityManagerCompat 8 | import androidx.core.content.getSystemService 9 | 10 | val mainHandler = Handler(Looper.getMainLooper()) 11 | val Thread.isMain get() = this === mainHandler.looper.thread 12 | 13 | val fullVersionName: String by lazy { 14 | // Get it from the package manager instead of the BuildConfig to support injected version names 15 | // from the automated build system. 16 | RobotScouter.packageManager.getPackageInfo(RobotScouter.packageName, 0).versionName 17 | } 18 | val fullVersionCode by lazy { 19 | // See fullVersionName 20 | RobotScouter.packageManager.getPackageInfo(RobotScouter.packageName, 0).run { 21 | if (Build.VERSION.SDK_INT >= 28) { 22 | longVersionCode 23 | } else { 24 | @Suppress("DEPRECATION") 25 | versionCode.toLong() 26 | } 27 | } 28 | } 29 | val providerAuthority: String by lazy { "${RobotScouter.packageName}.provider" } 30 | 31 | val isLowRamDevice: Boolean by lazy { 32 | ActivityManagerCompat.isLowRamDevice(checkNotNull(RobotScouter.getSystemService())) 33 | } 34 | val isInTestMode: Boolean by lazy { 35 | Settings.System.getString(RobotScouter.contentResolver, "firebase.test.lab") == "true" 36 | } 37 | -------------------------------------------------------------------------------- /library/core/src/main/java/com/supercilex/robotscouter/core/Properties.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core 2 | 3 | import kotlin.properties.ReadOnlyProperty 4 | import kotlin.properties.ReadWriteProperty 5 | import kotlin.reflect.KProperty 6 | 7 | fun unsafeLazy(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer) 8 | 9 | class LateinitVal : ReadWriteProperty { 10 | private var value: T? = null 11 | 12 | override fun getValue(thisRef: Any?, property: KProperty<*>): T = checkNotNull(value) { 13 | "Property ${property.name} should be initialized before get." 14 | } 15 | 16 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 17 | check(this.value == null) { 18 | "Property ${property.name} is a val and cannot change its value." 19 | } 20 | this.value = value 21 | } 22 | } 23 | 24 | class ValueSeeker(private val evaluator: () -> T) : ReadOnlyProperty { 25 | private var value: T? = null 26 | 27 | override fun getValue(thisRef: Any?, property: KProperty<*>) = 28 | value ?: synchronized(this) { value ?: evaluator().also { value = it } } 29 | } 30 | -------------------------------------------------------------------------------- /library/core/src/main/java/com/supercilex/robotscouter/core/RobotScouter.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.core 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | 6 | @Suppress("PropertyName") 7 | val RobotScouter 8 | get() = _globalContext 9 | @SuppressLint("StaticFieldLeak") 10 | @Suppress("ObjectPropertyName") 11 | lateinit var _globalContext: Context 12 | -------------------------------------------------------------------------------- /library/core/src/main/java/com/supercilex/robotscouter/core/Toasts.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("NOTHING_TO_INLINE", "unused") 2 | 3 | package com.supercilex.robotscouter.core 4 | 5 | import android.widget.Toast 6 | 7 | /** 8 | * Display the simple Toast message with the [Toast.LENGTH_SHORT] duration. 9 | * 10 | * @param message the message text resource. 11 | */ 12 | inline fun toast(message: Int): Toast = Toast 13 | .makeText(RobotScouter, message, Toast.LENGTH_SHORT) 14 | .apply { show() } 15 | 16 | /** 17 | * Display the simple Toast message with the [Toast.LENGTH_SHORT] duration. 18 | * 19 | * @param message the message text. 20 | */ 21 | inline fun toast(message: CharSequence): Toast = Toast 22 | .makeText(RobotScouter, message, Toast.LENGTH_SHORT) 23 | .apply { show() } 24 | 25 | /** 26 | * Display the simple Toast message with the [Toast.LENGTH_LONG] duration. 27 | * 28 | * @param message the message text resource. 29 | */ 30 | inline fun longToast(message: Int): Toast = Toast 31 | .makeText(RobotScouter, message, Toast.LENGTH_LONG) 32 | .apply { show() } 33 | 34 | /** 35 | * Display the simple Toast message with the [Toast.LENGTH_LONG] duration. 36 | * 37 | * @param message the message text. 38 | */ 39 | inline fun longToast(message: CharSequence): Toast = Toast 40 | .makeText(RobotScouter, message, Toast.LENGTH_LONG) 41 | .apply { show() } 42 | -------------------------------------------------------------------------------- /library/core/src/main/resources/META-INF/services/kotlinx.coroutines.CoroutineExceptionHandler: -------------------------------------------------------------------------------- 1 | com.supercilex.robotscouter.core.LoggingHandler 2 | -------------------------------------------------------------------------------- /library/shared-scouting/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api(project(":library:shared")) 3 | 4 | implementation(Config.Libs.Jetpack.cardView) 5 | implementation(Config.Libs.Misc.snap) 6 | } 7 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/java/com/supercilex/robotscouter/shared/scouting/MetricListHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.shared.scouting 2 | 3 | import androidx.lifecycle.SavedStateHandle 4 | import com.google.firebase.firestore.CollectionReference 5 | import com.supercilex.robotscouter.common.FIRESTORE_POSITION 6 | import com.supercilex.robotscouter.core.data.LifecycleAwareFirestoreArray 7 | import com.supercilex.robotscouter.core.data.ViewModelBase 8 | import com.supercilex.robotscouter.core.data.model.metricParser 9 | import com.supercilex.robotscouter.core.model.Metric 10 | 11 | class MetricListHolder(state: SavedStateHandle) : ViewModelBase(state) { 12 | lateinit var metrics: LifecycleAwareFirestoreArray> 13 | private set 14 | 15 | override fun onCreate(args: CollectionReference) { 16 | metrics = LifecycleAwareFirestoreArray({ args.orderBy(FIRESTORE_POSITION) }, metricParser) 17 | metrics.keepAlive = true 18 | } 19 | 20 | override fun onCleared() { 21 | super.onCleared() 22 | metrics.keepAlive = false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/java/com/supercilex/robotscouter/shared/scouting/MetricViewHolderBase.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.shared.scouting 2 | 3 | import android.view.View 4 | import android.widget.TextView 5 | import androidx.annotation.CallSuper 6 | import androidx.fragment.app.FragmentManager 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.supercilex.robotscouter.core.model.Metric 9 | import kotlinx.android.extensions.LayoutContainer 10 | import java.lang.ref.WeakReference 11 | 12 | abstract class MetricViewHolderBase, T>( 13 | override val containerView: View 14 | ) : RecyclerView.ViewHolder(containerView), LayoutContainer { 15 | lateinit var metric: M 16 | private set 17 | 18 | protected val name: TextView = itemView.findViewById(R.id.name) 19 | private lateinit var _fragmentManager: WeakReference 20 | // This is safe b/c we're only using it to show dialogs. Anyways, we'll be getting rid of those. 21 | protected val fragmentManager get() = checkNotNull(_fragmentManager.get()) 22 | 23 | fun bind(metric: M, manager: FragmentManager) { 24 | this.metric = metric 25 | this._fragmentManager = WeakReference(manager) 26 | 27 | bind() 28 | } 29 | 30 | @CallSuper 31 | protected open fun bind() { 32 | name.text = metric.name 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/java/com/supercilex/robotscouter/shared/scouting/viewholder/CheckboxViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.shared.scouting.viewholder 2 | 3 | import android.view.View 4 | import com.supercilex.robotscouter.core.data.model.update 5 | import com.supercilex.robotscouter.core.model.Metric 6 | import com.supercilex.robotscouter.shared.scouting.MetricViewHolderBase 7 | import com.supercilex.robotscouter.shared.scouting.R 8 | import kotlinx.android.synthetic.main.scout_base_checkbox.* 9 | 10 | open class CheckboxViewHolder( 11 | itemView: View 12 | ) : MetricViewHolderBase(itemView), View.OnClickListener { 13 | init { 14 | checkBox.setOnClickListener(this) 15 | name.setOnClickListener(this) 16 | } 17 | 18 | public override fun bind() { 19 | super.bind() 20 | checkBox.isChecked = metric.value 21 | checkBox.jumpDrawablesToCurrentState() // Skip animation on first load 22 | } 23 | 24 | override fun onClick(v: View) { 25 | if (v.id == R.id.checkBox) metric.update(checkBox.isChecked) 26 | if (v.id == R.id.name) checkBox.performClick() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/java/com/supercilex/robotscouter/shared/scouting/viewholder/HeaderViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.shared.scouting.viewholder 2 | 3 | import android.view.View 4 | import androidx.core.view.updateLayoutParams 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.supercilex.robotscouter.core.model.Metric 7 | import com.supercilex.robotscouter.shared.scouting.MetricViewHolderBase 8 | import com.supercilex.robotscouter.core.ui.R as RC 9 | 10 | open class HeaderViewHolder( 11 | itemView: View 12 | ) : MetricViewHolderBase(itemView) { 13 | private val topMargin = 14 | itemView.resources.getDimensionPixelSize(RC.dimen.list_item_padding_vertical_within) 15 | 16 | override fun bind() { 17 | super.bind() 18 | itemView.updateLayoutParams { 19 | topMargin = if (layoutPosition == 0) 0 else this@HeaderViewHolder.topMargin 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/color/activatable_button_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/color/button_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/drawable-anydpi-v21/button_outline_colored.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/drawable/button_outline_colored.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/drawable/ic_remove_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/drawable/ic_timer_off_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/drawable/ic_timer_on_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/layout/dialog_value.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/layout/scout_base_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Nom du repérage 4 | Chargement…\nVérifiez votre connexion Internet 5 | 6 | 7 | Modifier la valeur 8 | Valeur 9 | Incrémenter le compteur 10 | Décrémenter le compteur 11 | 12 | 13 | Démarrer 14 | Arrêter (%s) 15 | Cycle %d 16 | Moyenne 17 | Tour de chronomètre ajouté 18 | 19 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/values-v17/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/shared-scouting/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Scout name 4 | Loading…\nCheck your internet connection 5 | 6 | 7 | Edit value 8 | Value 9 | Increment counter 10 | Decrement counter 11 | 12 | 13 | Start 14 | Stop (%s) 15 | Cycle %d 16 | Average 17 | Added stopwatch lap 18 | 19 | -------------------------------------------------------------------------------- /library/shared/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api(project(":library:core-data")) 3 | api(project(":library:core-ui")) 4 | 5 | api(Config.Libs.FirebaseUi.auth) 6 | api(Config.Libs.PlayServices.auth) 7 | api(Config.Libs.Misc.glide) 8 | 9 | implementation(Config.Libs.FirebaseUi.facebook) 10 | implementation(Config.Libs.FirebaseUi.twitter) { isTransitive = true } 11 | implementation(Config.Libs.Firebase.links) 12 | } 13 | -------------------------------------------------------------------------------- /library/shared/src/main/java/com/supercilex/robotscouter/shared/SharedLifecycleResource.kt: -------------------------------------------------------------------------------- 1 | package com.supercilex.robotscouter.shared 2 | 3 | import androidx.lifecycle.ViewModel 4 | import java.util.concurrent.atomic.AtomicInteger 5 | 6 | /** 7 | * Manages lifecycle events for shared objects. Used to process fragment view lifecycle events for 8 | * activity scoped resources. 9 | */ 10 | class SharedLifecycleResource : ViewModel() { 11 | private val counts = mutableMapOf, AtomicInteger>() 12 | 13 | fun onCreate(resource: Any) { 14 | counts.getOrPut(resource.javaClass) { AtomicInteger() }.incrementAndGet() 15 | } 16 | 17 | fun onDestroy(resource: T, cleanup: T.() -> Unit) { 18 | val count = counts.getValue(resource.javaClass).decrementAndGet() 19 | if (count == 0) cleanup(resource) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /library/shared/src/main/res/anim/slide_in_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /library/shared/src/main/res/anim/slide_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /library/shared/src/main/res/color/empty_icon_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_add_a_photo_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_add_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_check_grey_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_content_paste_grey_96dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_delete_forever_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_info_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_launch_accent_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_mode_edit_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_person_grey_96dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_share_colorable_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/drawable/ic_star_accent_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /library/shared/src/main/res/layout/dialog_should_upload_media.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /secrets.tar.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SUPERCILEX/Robot-Scouter/8158790188cf0b9545f9b8ff80b0182f2eafd927/secrets.tar.enc -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `gradle-enterprise` 3 | } 4 | 5 | include( 6 | ":app:android-base", "app:server:functions", 7 | 8 | ":library:common", 9 | ":library:core", ":library:core-model", ":library:core-data", ":library:core-ui", 10 | ":library:shared", ":library:shared-scouting", 11 | 12 | ":feature:teams", ":feature:autoscout", 13 | ":feature:scouts", ":feature:templates", 14 | ":feature:exports", 15 | ":feature:trash", ":feature:settings" 16 | ) 17 | 18 | rootProject.name = "Robot-Scouter" 19 | --------------------------------------------------------------------------------