Compare commits
No commits in common. "main" and "gh-pages" have entirely different histories.
62
.github/workflows/android.yml
vendored
|
@ -1,62 +0,0 @@
|
|||
name: Android CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Build android app
|
||||
run: ./gradlew assembleDebug
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: ./gradlew test
|
||||
- name: Build web app
|
||||
run: ./gradlew :web:assemble
|
||||
- name: Build iOS shared code
|
||||
run: ./gradlew :common:compileKotlinIOS
|
||||
- name: Build macOS shared code
|
||||
run: ./gradlew :common:compileKotlinMacOS
|
||||
|
||||
# If main branch update, deploy to gh-pages
|
||||
- name: Deploy
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
|
||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BRANCH: gh-pages # The branch the action should deploy to.
|
||||
FOLDER: web/build/distributions # The folder the action should deploy.
|
||||
CLEAN: true # Automatically remove deleted files from the deploy branch
|
||||
|
||||
|
||||
androidTest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
|
||||
- name: Wear Instrumentation Tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 28
|
||||
target: android-wear
|
||||
script: ./gradlew wear:connectedAndroidTest
|
||||
|
||||
- name: Android Instrumentation Tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 29
|
||||
target: google_apis
|
||||
script: ./gradlew app:connectedAndroidTest
|
391
.gitignore
vendored
|
@ -1,391 +0,0 @@
|
|||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
.idea
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
/build
|
||||
/captures
|
||||
|
||||
*.xcworkspacedata
|
||||
*.xcuserstate
|
||||
*.xcscheme
|
||||
xcschememanagement.plist
|
||||
*.xcbkptlist
|
||||
### Kotlin template
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### Gradle template
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
### Windows template
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
### Android template
|
||||
# Built application files
|
||||
*.apk
|
||||
*.aar
|
||||
*.ap_
|
||||
*.aab
|
||||
|
||||
# Files for the ART/Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
gen/
|
||||
out/
|
||||
# Uncomment the following line in case you need and you don't have the release build type files in your app
|
||||
# release/
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Proguard folder generated by Eclipse
|
||||
proguard/
|
||||
|
||||
# Log Files
|
||||
|
||||
# Android Studio Navigation editor temp files
|
||||
.navigation/
|
||||
|
||||
# Android Studio captures folder
|
||||
captures/
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/gradle.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
.idea/dictionaries
|
||||
.idea/libraries
|
||||
# Android Studio 3 in .gitignore file.
|
||||
.idea/caches
|
||||
.idea/modules.xml
|
||||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
|
||||
.idea/navEditor.xml
|
||||
|
||||
# Keystore files
|
||||
# Uncomment the following lines if you do not want to check your keystore files in.
|
||||
#*.jks
|
||||
#*.keystore
|
||||
|
||||
# External native build folder generated in Android Studio 2.2 and later
|
||||
.externalNativeBuild
|
||||
.cxx/
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
# google-services.json
|
||||
|
||||
# Freeline
|
||||
freeline.py
|
||||
freeline/
|
||||
freeline_project_description.json
|
||||
|
||||
# fastlane
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# Version control
|
||||
vcs.xml
|
||||
|
||||
# lint
|
||||
lint/intermediates/
|
||||
lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
|
||||
### Xcode template
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||
*.xcscmblueprint
|
||||
*.xccheckout
|
||||
|
||||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||
DerivedData/
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
|
||||
## Gcc Patch
|
||||
/*.gcno
|
||||
|
||||
### macOS template
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Swift template
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## User settings
|
||||
|
||||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||
|
||||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
|
||||
## App packaging
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||
# Packages/
|
||||
# Package.pins
|
||||
# Package.resolved
|
||||
# *.xcodeproj
|
||||
#
|
||||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||
# hence it is not needed unless you have added a package configuration file to your project
|
||||
# .swiftpm
|
||||
|
||||
.build/
|
||||
|
||||
# CocoaPods
|
||||
#
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
#
|
||||
# Pods/
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||
# *.xcworkspace
|
||||
|
||||
# Carthage
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
Carthage/Build/
|
||||
|
||||
# Accio dependency management
|
||||
Dependencies/
|
||||
.accio/
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo.
|
||||
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||
|
||||
fastlane/screenshots/**/*.png
|
||||
|
||||
# Code Injection
|
||||
#
|
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||
# https://github.com/johnno1962/injectionforxcode
|
||||
|
||||
iOSInjectionProject/
|
||||
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Linux template
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
203
LICENSE
|
@ -1,203 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
144
README.md
|
@ -1,144 +0,0 @@
|
|||
# PeopleInSpace
|
||||
|
||||
Minimal **Kotlin Multiplatform** project with SwiftUI, Jetpack Compose, Compose for Wear OS, Compose for Desktop, Compose for Web, and Kotlin/JS + React clients along with Ktor backend. Currently running on
|
||||
* Android (Jetpack Compose)
|
||||
* Android App Widget (Compose based Glance API - contributed by https://github.com/yschimke)
|
||||
* Wear OS (Compose for Wear OS - primarily developed by https://github.com/yschimke)
|
||||
* iOS (SwiftUI)
|
||||
* iOS App Widget (SwiftUI)
|
||||
* watchOS (SwiftUI) (contributed by https://github.com/nealsanche)
|
||||
* macOS (SwiftUI)
|
||||
* Desktop (Compose for Desktop)
|
||||
* Web (Compose for Web)
|
||||
* Web (Kotlin/JS + React Wrapper) (contributed by https://github.com/PatilShreyas)
|
||||
* JVM (small Ktor back end service + `Main.kt` in `common` module)
|
||||
|
||||
It makes use of [Open Notify PeopleInSpace API](http://open-notify.org/Open-Notify-API/People-In-Space/) to show list of people currently in
|
||||
space and also the position of the International Space Station (inspired by https://kousenit.org/2019/12/19/a-few-astronomical-examples-in-kotlin/)!
|
||||
|
||||
The project is included as sample in the official [Kotlin Multiplatform Mobile docs](https://kotlinlang.org/docs/mobile/samples.html#peopleinspace) and also the [Google Dev Library](https://devlibrary.withgoogle.com/products/android)
|
||||
|
||||
Related posts:
|
||||
* [Minimal Kotlin Multiplatform project using Compose and SwiftUI](https://johnoreilly.dev/posts/minimal-kotlin-platform-compose-swiftui/)
|
||||
* [Adding some Storage (to) Space](https://johnoreilly.dev/posts/adding-sqldelight-to-peopleinspace/)
|
||||
* [Kotlin Multiplatform running on macOS](https://johnoreilly.dev/posts/kotlinmultiplatform-macos/)
|
||||
* [PeopleInSpace hits the web with Kotlin/JS and React](https://johnoreilly.dev/posts/peopleinspace-kotlinjs/)
|
||||
* [Using Koin in a Kotlin Multiplatform Project](https://johnoreilly.dev/posts/kotlinmultiplatform-koin/)
|
||||
* [Jetpack Compose for the Desktop!](https://johnoreilly.dev/posts/jetpack-compose-desktop-copy/)
|
||||
* [Comparing use of LiveData and StateFlow in a Jetpack Compose project](https://johnoreilly.dev/posts/jetpack-compose-stateflow-livedata/)
|
||||
* [Wrapping Kotlin Flow with Swift Combine Publisher in a Kotlin Multiplatform project](https://johnoreilly.dev/posts/kotlinmultiplatform-swift-combine_publisher-flow/)
|
||||
* [Using Swift Packages in a Kotlin Multiplatform project](https://johnoreilly.dev/posts/kotlinmultiplatform-swift-package/)
|
||||
* [Using Swift's new async/await when invoking Kotlin Multiplatform code](https://johnoreilly.dev/posts/swift_async_await_kotlin_coroutines/)
|
||||
* [Exploring new AWS SDK for Kotlin](https://johnoreilly.dev/posts/aws-sdk-kotlin/)
|
||||
|
||||
|
||||
Note that this repository very much errs on the side of minimalism to help more clearly illustrate key moving parts of a Kotlin
|
||||
Multiplatform project and also to hopefully help someone just starting to explore KMP to get up and running for first time (and is of course
|
||||
primarily focused on use of Jetpack Compose and SwiftUI). If you're at the stage of moving
|
||||
beyond this then I'd definitely recommend checking out [KaMPKit](https://github.com/touchlab/KaMPKit) from Touchlab.
|
||||
I also have the following samples that demonstrate the use of a variety of Kotlin Multiplatform libraries (and also use Jetpack Compose and SwiftUI).
|
||||
|
||||
* [BikeShare](https://github.com/joreilly/BikeShare)
|
||||
* [GalwayBus](https://github.com/joreilly/GalwayBus)
|
||||
* [MortyComposeKMM](https://github.com/joreilly/MortyComposeKMM)
|
||||
* [FantasyPremierLeague](https://github.com/joreilly/FantasyPremierLeague)
|
||||
* [StarWars](https://github.com/joreilly/StarWars)
|
||||
* [Chip-8](https://github.com/joreilly/chip-8)
|
||||
|
||||
|
||||
### Building
|
||||
You need to use Android Studio Arctic Fox (**note: Java 11 is now the minimum version required**). Requires XCode 13.2 or later (due to use of new Swift 5.5 concurrnecy APIs).
|
||||
|
||||
When opening iOS/watchOS/macOS projects remember to open `.xcworkspace` file (and not `.xcodeproj` one).
|
||||
|
||||
To exercise (React based) web client run `./gradlew :web:browserDevelopmentRun`.
|
||||
|
||||
To run backend you can either run `./gradlew :backend:run` or run `Server.kt` directly from Android Studio. After doing that you should then for example be able to open `http://localhost:9090/astros_local.json` in a browser.
|
||||
|
||||
|
||||
|
||||
### Compose for Web client
|
||||
|
||||
The Compose for Web client resides in the `compose-web` module and can be run by
|
||||
invoking `./gradlew :compose-web:jsBrowserDevelopmentRun`
|
||||
|
||||
### Compose for Desktop client
|
||||
|
||||
This client is available in `compose-desktop` module. Note that you need to use appropriate version of JVM when running (works for example with Java 11)
|
||||
|
||||
|
||||
### Backend code
|
||||
|
||||
Have tested this out in Google App Engine deployment. Using shadowJar plugin to create an "uber" jar and then deploying it as shown below. Should be possible to deploy this jar to other services as well.
|
||||
|
||||
```
|
||||
./gradlew :backend:shadowJar
|
||||
gcloud app deploy backend/build/libs/backend-all.jar
|
||||
```
|
||||
|
||||
### GraphQL backend
|
||||
|
||||
There's a GraphQL module (`graphql-server`) which can be run locally using `./gradlew :graphql-server:bootRun` with "playground" then available at http://localhost:8080/playground
|
||||
|
||||
|
||||
|
||||
### Screenshots
|
||||
|
||||
**iOS (SwiftUI)**
|
||||
<br/>
|
||||
<img width="546" alt="Screenshot 2021-02-27 at 12 09 02" src="https://user-images.githubusercontent.com/6302/109386736-ac1f0700-78f4-11eb-812e-4bf971a8c2a7.png">
|
||||
|
||||
**Android (Jetpack Compose)**
|
||||
<br/>
|
||||
<img width="555" alt="Screenshot 2021-03-07 at 17 03 46" src="https://user-images.githubusercontent.com/6302/110248059-2ab81c00-7f67-11eb-9b3a-2b04d1be43ef.png">
|
||||
|
||||
|
||||
**watchOS (SwiftUI)**
|
||||
<br/>
|
||||
<img width="250" alt="watchOS Screenshot 1" src="https://user-images.githubusercontent.com/6302/139499100-dc5112b0-04b9-4bdc-9c30-9975f3608eb3.png">
|
||||
<img width="250" alt="watch0S Screenshot 2" src="https://user-images.githubusercontent.com/6302/139499115-944b241d-8e92-428b-b86c-f599b456c4bf.png">
|
||||
|
||||
|
||||
|
||||
**Wear OS (Wear Compose)**
|
||||
<br/>
|
||||
<img width="250" alt="Wear Compose Screenshot 1" src="https://user-images.githubusercontent.com/6302/137623548-ac51ca72-572e-4009-8b34-315defdf93a5.png">
|
||||
<img width="250" alt="Wear Compose Screenshot 2" src="https://user-images.githubusercontent.com/6302/137640396-851489bb-e41d-47ef-badb-e2d22454eee4.png">
|
||||
<img width="250" alt="Wear Compose Screenshot 3" src="https://user-images.githubusercontent.com/6302/139468900-16ad4e95-41dc-427f-977c-b893b1751c78.png">
|
||||
|
||||
|
||||
**macOS (SwiftUI)**
|
||||
<br/>
|
||||
<img width="937" alt="Screenshot 2021-06-01 at 20 02 31" src="https://user-images.githubusercontent.com/6302/120376983-6ec37e80-c314-11eb-8279-7acc0c2d5206.png">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**Compose for Desktop**
|
||||
<br/>
|
||||
<img width="912" alt="Screenshot 2021-10-01 at 16 45 06" src="https://user-images.githubusercontent.com/6302/135652185-4ce9d8e3-f06e-4e9d-9930-3e900267f8bd.png">
|
||||
|
||||
|
||||
**Compose for Web**
|
||||
<br/>
|
||||
<img width="564" alt="Screenshot 2021-05-31 at 21 29 53" src="https://user-images.githubusercontent.com/6302/120240074-9dc7ea80-c257-11eb-9884-5870a3f4ef95.png">
|
||||
|
||||
**Web App (Kotlin/JS + React)**
|
||||
<br/>
|
||||
<img width="612" alt="Screenshot 2021-06-06 at 23 50 00" src="https://user-images.githubusercontent.com/19620536/120935764-eda82500-c721-11eb-9042-f15ade7473f7.png">
|
||||
|
||||
|
||||
|
||||
### Languages, libraries and tools used
|
||||
|
||||
* [Kotlin](https://kotlinlang.org/)
|
||||
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||
* [Kotlinx Serialization](https://github.com/Kotlin/kotlinx.serialization)
|
||||
* [Ktor client library](https://github.com/ktorio/ktor)
|
||||
* [Android Architecture Components](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
* [Koin](https://github.com/InsertKoinIO/koin)
|
||||
* [SQLDelight](https://github.com/cashapp/sqldelight)
|
||||
* [Jetpack Compose](https://developer.android.com/jetpack/compose)
|
||||
* [SwiftUI](https://developer.apple.com/documentation/swiftui)
|
||||
* [KMP-NativeCoroutines](https://github.com/rickclephas/KMP-NativeCoroutines)
|
2
app/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
/build
|
||||
*.iml
|
|
@ -1,105 +0,0 @@
|
|||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
id("com.github.ben-manes.versions")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = Versions.androidCompileSdk
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.surrus.peopleinspace"
|
||||
minSdk = Versions.androidMinSdk
|
||||
targetSdk = Versions.androidTargetSdk
|
||||
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = Versions.composeCompiler
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
resources.excludes.add("META-INF/licenses/**")
|
||||
resources.excludes.add("META-INF/AL2.0")
|
||||
resources.excludes.add("META-INF/LGPL2.1")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
with(Deps.Android) {
|
||||
implementation(material)
|
||||
implementation(osmdroidAndroid)
|
||||
}
|
||||
|
||||
with(Deps.AndroidX) {
|
||||
implementation(lifecycleRuntimeKtx)
|
||||
implementation(lifecycleViewmodelKtx)
|
||||
implementation(activityCompose)
|
||||
}
|
||||
|
||||
with(Deps.Glance) {
|
||||
implementation(appwidget)
|
||||
}
|
||||
|
||||
with(Deps.Compose) {
|
||||
implementation(compiler)
|
||||
implementation(ui)
|
||||
implementation(uiGraphics)
|
||||
implementation(foundationLayout)
|
||||
implementation(material)
|
||||
implementation(navigation)
|
||||
implementation(coilCompose)
|
||||
implementation(accompanistNavigationAnimation)
|
||||
implementation(uiTooling)
|
||||
}
|
||||
|
||||
with(Deps.Koin) {
|
||||
implementation(core)
|
||||
implementation(android)
|
||||
implementation(compose)
|
||||
testImplementation(test)
|
||||
testImplementation(testJUnit4)
|
||||
}
|
||||
|
||||
with(Deps.Test) {
|
||||
testImplementation(junit)
|
||||
androidTestImplementation(androidXTestJUnit)
|
||||
testImplementation(testCore)
|
||||
testImplementation(robolectric)
|
||||
testImplementation(mockito)
|
||||
|
||||
// Compose testing dependencies
|
||||
androidTestImplementation(composeUiTest)
|
||||
androidTestImplementation(composeUiTestJUnit)
|
||||
debugImplementation(composeUiTestManifest)
|
||||
}
|
||||
|
||||
implementation(project(":common"))
|
||||
}
|
21
app/proguard-rules.pro
vendored
|
@ -1,21 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -1,29 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class PeopleInSpaceRepositoryFake: PeopleInSpaceRepositoryInterface {
|
||||
val peopleList = listOf(Assignment("Apollo 11", "Neil Armstrong"),
|
||||
Assignment("Apollo 11", "Buzz Aldrin"))
|
||||
|
||||
val issPosition = IssPosition(53.2743394, -9.0514163)
|
||||
|
||||
override fun fetchPeopleAsFlow(): Flow<List<Assignment>> {
|
||||
return flowOf(peopleList)
|
||||
}
|
||||
|
||||
override fun pollISSPosition(): Flow<IssPosition> {
|
||||
return flowOf(issPosition)
|
||||
}
|
||||
|
||||
override suspend fun fetchPeople(): List<Assignment> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun fetchAndStorePeople() {
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import androidx.compose.ui.test.*
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import com.surrus.peopleinspace.ui.*
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class PeopleInSpaceTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private val peopleInSpaceRepository = PeopleInSpaceRepositoryFake()
|
||||
private val peopleInSpaceViewModel = PeopleInSpaceViewModel(peopleInSpaceRepository)
|
||||
|
||||
@Test
|
||||
fun testPeopleListScreen() {
|
||||
composeTestRule.setContent {
|
||||
PersonListScreen(personSelected = {},
|
||||
peopleInSpaceViewModel = peopleInSpaceViewModel
|
||||
)
|
||||
}
|
||||
|
||||
val peopleList = peopleInSpaceRepository.peopleList
|
||||
val personListNode = composeTestRule.onNodeWithTag(PersonListTag)
|
||||
personListNode.assertIsDisplayed()
|
||||
.onChildren().assertCountEquals(peopleList.size)
|
||||
|
||||
peopleList.forEachIndexed { index, person ->
|
||||
val rowNode = personListNode.onChildAt(index)
|
||||
rowNode.assertTextContains(person.name)
|
||||
rowNode.assertTextContains(person.craft)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testISSPositionScreen() {
|
||||
composeTestRule.setContent {
|
||||
ISSPositionScreen(peopleInSpaceViewModel = peopleInSpaceViewModel)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTag(ISSPositionMapTag).assertIsDisplayed()
|
||||
|
||||
val expectedIssPosition = peopleInSpaceRepository.issPosition
|
||||
composeTestRule
|
||||
.onNode(SemanticsMatcher.expectValue(IssPositionKey, expectedIssPosition))
|
||||
.assertExists()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.surrus.peopleinspace">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".PeopleInSpaceApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@style/Theme.PeopleInSpace">
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.PeopleInSpace.NoActionBar"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<receiver android:name="com.surrus.peopleinspace.glance.PeopleInSpaceWidgetReceiver"
|
||||
android:label="People in Space"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_info" />
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="com.surrus.peopleinspace.glance.ISSMapWidgetReceiver"
|
||||
android:label="ISS Map"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/iss_widget_info" />
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -1,33 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import android.app.Application
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.peopleinspace.di.appModule
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.android.ext.koin.androidLogger
|
||||
import org.koin.core.logger.Level
|
||||
import org.osmdroid.config.Configuration
|
||||
import java.io.File
|
||||
|
||||
class PeopleInSpaceApplication : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// needed for osmandroid
|
||||
Configuration.getInstance().userAgentValue = BuildConfig.APPLICATION_ID
|
||||
Configuration.getInstance().osmdroidTileCache = File(cacheDir, "osm").also {
|
||||
it.mkdir()
|
||||
}
|
||||
|
||||
initKoin {
|
||||
// https://github.com/InsertKoinIO/koin/issues/1188
|
||||
androidLogger(if (BuildConfig.DEBUG) Level.ERROR else Level.NONE)
|
||||
androidContext(this@PeopleInSpaceApplication)
|
||||
modules(appModule)
|
||||
}
|
||||
|
||||
Logger.d { "PeopleInSpaceApplication" }
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.surrus.peopleinspace.di
|
||||
|
||||
import com.surrus.peopleinspace.ui.PeopleInSpaceViewModel
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val appModule = module {
|
||||
viewModel { PeopleInSpaceViewModel(get()) }
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package com.surrus.peopleinspace.glance
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.ExperimentalUnitApi
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.Image
|
||||
import androidx.glance.ImageProvider
|
||||
import androidx.glance.action.actionStartActivity
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.background
|
||||
import androidx.glance.layout.Box
|
||||
import androidx.glance.layout.fillMaxSize
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.peopleinspace.R
|
||||
import com.surrus.peopleinspace.glance.util.BaseGlanceAppWidget
|
||||
import com.surrus.peopleinspace.ui.MainActivity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.component.inject
|
||||
import org.osmdroid.tileprovider.MapTileProviderBasic
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.Projection
|
||||
import org.osmdroid.views.drawing.MapSnapshot
|
||||
import org.osmdroid.views.overlay.IconOverlay
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class ISSMapWidget : BaseGlanceAppWidget<ISSMapWidget.Data>() {
|
||||
val repository: PeopleInSpaceRepositoryInterface by inject()
|
||||
|
||||
data class Data(val bitmap: Bitmap?)
|
||||
|
||||
override suspend fun loadData(): Data {
|
||||
val issPosition = repository.pollISSPosition().first()
|
||||
|
||||
val issPositionPoint = GeoPoint(issPosition.latitude, issPosition.longitude)
|
||||
|
||||
val stationMarker = IconOverlay(
|
||||
issPositionPoint,
|
||||
context.resources.getDrawable(R.drawable.ic_iss, context.theme)
|
||||
)
|
||||
|
||||
val source = TileSourceFactory.DEFAULT_TILE_SOURCE
|
||||
val projection = Projection(5.0, 480, 240, issPositionPoint, 0f, true, false, 0, 0)
|
||||
|
||||
val bitmap = withContext(Dispatchers.Main) {
|
||||
suspendCoroutine<Bitmap> { cont ->
|
||||
val mapSnapshot = MapSnapshot(
|
||||
{
|
||||
if (it.status == MapSnapshot.Status.CANVAS_OK) {
|
||||
val bitmap = Bitmap.createBitmap(it.bitmap)
|
||||
cont.resume(bitmap)
|
||||
}
|
||||
},
|
||||
MapSnapshot.INCLUDE_FLAG_UPTODATE or MapSnapshot.INCLUDE_FLAG_SCALED,
|
||||
MapTileProviderBasic(context, source, null),
|
||||
listOf(stationMarker),
|
||||
projection
|
||||
)
|
||||
|
||||
launch(Dispatchers.IO) {
|
||||
mapSnapshot.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Data(bitmap)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnitApi::class)
|
||||
@Composable
|
||||
override fun Content(data: Data?) {
|
||||
Box(
|
||||
modifier = GlanceModifier.background(Color.DarkGray).fillMaxSize().clickable(
|
||||
actionStartActivity<MainActivity>()
|
||||
)
|
||||
) {
|
||||
val bitmap = data?.bitmap
|
||||
if (bitmap != null) {
|
||||
Image(
|
||||
modifier = GlanceModifier.fillMaxSize(),
|
||||
provider = ImageProvider(bitmap),
|
||||
contentDescription = "ISS Location"
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
"Loading ISS Map...",
|
||||
style = TextStyle(
|
||||
color = ColorProvider(Color.White),
|
||||
fontSize = TextUnit(20f, TextUnitType.Sp)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package com.surrus.peopleinspace.glance
|
||||
|
||||
import com.surrus.peopleinspace.glance.util.BaseGlanceAppWidgetReceiver
|
||||
|
||||
class ISSMapWidgetReceiver : BaseGlanceAppWidgetReceiver<ISSMapWidget>() {
|
||||
override fun createWidget(): ISSMapWidget = ISSMapWidget()
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package com.surrus.peopleinspace.glance
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.ExperimentalUnitApi
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.appwidget.lazy.LazyColumn
|
||||
import androidx.glance.background
|
||||
import androidx.glance.layout.Row
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.text.FontWeight
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.peopleinspace.glance.util.BaseGlanceAppWidget
|
||||
import kotlinx.coroutines.flow.first
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class PeopleInSpaceWidget : BaseGlanceAppWidget<PeopleInSpaceWidget.Data>() {
|
||||
val repository: PeopleInSpaceRepositoryInterface by inject()
|
||||
|
||||
data class Data(val people: List<Assignment>)
|
||||
|
||||
override suspend fun loadData(): Data {
|
||||
return Data(repository.fetchPeopleAsFlow().first())
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnitApi::class)
|
||||
@Composable
|
||||
override fun Content(data: Data?) {
|
||||
LazyColumn(
|
||||
modifier = GlanceModifier.background(Color.DarkGray).padding(horizontal = 8.dp)
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
modifier = GlanceModifier.padding(bottom = 8.dp),
|
||||
text = "People in Space",
|
||||
style = TextStyle(
|
||||
color = ColorProvider(Color.White),
|
||||
fontSize = TextUnit(12f, TextUnitType.Sp),
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
)
|
||||
}
|
||||
if (data != null) {
|
||||
items(data.people.size) {
|
||||
Row {
|
||||
Text(
|
||||
text = data.people[it].name,
|
||||
style = TextStyle(
|
||||
color = ColorProvider(Color.White),
|
||||
fontSize = TextUnit(10f, TextUnitType.Sp)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package com.surrus.peopleinspace.glance
|
||||
|
||||
import com.surrus.peopleinspace.glance.util.BaseGlanceAppWidgetReceiver
|
||||
|
||||
class PeopleInSpaceWidgetReceiver : BaseGlanceAppWidgetReceiver<PeopleInSpaceWidget>() {
|
||||
override fun createWidget(): PeopleInSpaceWidget = PeopleInSpaceWidget()
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package com.surrus.peopleinspace.glance.util
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.glance.GlanceId
|
||||
import androidx.glance.LocalGlanceId
|
||||
import androidx.glance.LocalSize
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
abstract class BaseGlanceAppWidget<T>(initialData: T? = null) : GlanceAppWidget(), KoinComponent {
|
||||
val context: Context by inject()
|
||||
|
||||
var glanceId by mutableStateOf<GlanceId?>(null)
|
||||
var size by mutableStateOf<DpSize?>(null)
|
||||
var data by mutableStateOf<T?>(initialData)
|
||||
|
||||
private val coroutineScope = MainScope()
|
||||
|
||||
abstract suspend fun loadData(): T
|
||||
|
||||
fun initiateLoad() {
|
||||
coroutineScope.launch {
|
||||
data = loadData()
|
||||
|
||||
val currentGlanceId = snapshotFlow { glanceId }.filterNotNull().firstOrNull()
|
||||
|
||||
if (currentGlanceId != null) {
|
||||
update(context, currentGlanceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
glanceId = LocalGlanceId.current
|
||||
size = LocalSize.current
|
||||
|
||||
Content(data)
|
||||
}
|
||||
|
||||
@Composable
|
||||
abstract fun Content(data: T?)
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package com.surrus.peopleinspace.glance.util
|
||||
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
abstract class BaseGlanceAppWidgetReceiver<T : BaseGlanceAppWidget<*>> : GlanceAppWidgetReceiver(),
|
||||
KoinComponent {
|
||||
override val glanceAppWidget: GlanceAppWidget
|
||||
get() {
|
||||
return createWidget().apply {
|
||||
this.initiateLoad()
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun createWidget(): T
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val maroon200 = Color(0xFFb73d2a)
|
||||
val maroon500 = Color(0xFF800000)
|
||||
val maroon700 = Color(0xFF4f0000)
|
||||
val teal200 = Color(0xFF03DAC5)
|
||||
|
||||
val lowAvailabilityColor = Color(0xFFFF8C00)
|
||||
val highAvailabilityColor = Color(0xFF008000)
|
|
@ -1,74 +0,0 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.semantics.SemanticsPropertyKey
|
||||
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.peopleinspace.util.collectAsStateWithLifecycle
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.CustomZoomButtonsController
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.Marker
|
||||
|
||||
|
||||
const val ISSPositionMapTag = "ISSPositionMap"
|
||||
|
||||
val IssPositionKey = SemanticsPropertyKey<IssPosition>("IssPosition")
|
||||
var SemanticsPropertyReceiver.observedIssPosition by IssPositionKey
|
||||
|
||||
@Composable
|
||||
fun ISSPositionScreen(peopleInSpaceViewModel: PeopleInSpaceViewModel = getViewModel()) {
|
||||
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
|
||||
val issPosition by peopleInSpaceViewModel.issPosition
|
||||
.collectAsStateWithLifecycle(lifecycleOwner, IssPosition(0.0, 0.0))
|
||||
|
||||
val context = LocalContext.current
|
||||
val map = remember { MapView(context) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(title = { Text("ISS Position") })
|
||||
}
|
||||
) {
|
||||
Column {
|
||||
|
||||
Box {
|
||||
AndroidView({ map }, modifier = Modifier
|
||||
.fillMaxHeight().testTag(ISSPositionMapTag)
|
||||
.semantics { observedIssPosition = issPosition }
|
||||
){ map ->
|
||||
map.zoomController.setVisibility(CustomZoomButtonsController.Visibility.SHOW_AND_FADEOUT)
|
||||
map.setMultiTouchControls(true)
|
||||
|
||||
val mapController = map.controller
|
||||
mapController.setZoom(5.0)
|
||||
val issPositionPoint = GeoPoint(issPosition.latitude, issPosition.longitude)
|
||||
mapController.setCenter(issPositionPoint)
|
||||
|
||||
map.overlays.clear()
|
||||
val stationMarker = Marker(map)
|
||||
stationMarker.position = issPositionPoint
|
||||
stationMarker.title = "ISS"
|
||||
map.overlays.add(stationMarker)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.material.BottomNavigation
|
||||
import androidx.compose.material.BottomNavigationItem
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.LocationOn
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import com.google.accompanist.navigation.animation.AnimatedNavHost
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
||||
import com.surrus.common.remote.Assignment
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
MainLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Screen(val title: String) {
|
||||
object PersonList : Screen("PersonList")
|
||||
object PersonDetails : Screen("PersonDetails")
|
||||
object ISSPositionScreen : Screen("ISSPosition")
|
||||
}
|
||||
|
||||
data class BottomNavigationitem(
|
||||
val route: String,
|
||||
val icon: ImageVector,
|
||||
val iconContentDescription: String
|
||||
)
|
||||
|
||||
val bottomNavigationItems = listOf(
|
||||
BottomNavigationitem(
|
||||
Screen.PersonList.title,
|
||||
Icons.Default.Person,
|
||||
"People"
|
||||
),
|
||||
BottomNavigationitem(
|
||||
Screen.ISSPositionScreen.title,
|
||||
Icons.Filled.LocationOn,
|
||||
"ISS Position"
|
||||
)
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun MainLayout() {
|
||||
val navController = rememberAnimatedNavController()
|
||||
|
||||
PeopleInSpaceTheme {
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
BottomNavigation {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
|
||||
bottomNavigationItems.forEach { bottomNavigationitem ->
|
||||
BottomNavigationItem(
|
||||
icon = {
|
||||
Icon(
|
||||
bottomNavigationitem.icon,
|
||||
contentDescription = bottomNavigationitem.iconContentDescription
|
||||
)
|
||||
},
|
||||
selected = currentRoute == bottomNavigationitem.route,
|
||||
onClick = {
|
||||
navController.navigate(bottomNavigationitem.route) {
|
||||
popUpTo(navController.graph.id)
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) { paddingValues ->
|
||||
|
||||
AnimatedNavHost(navController, startDestination = Screen.PersonList.title) {
|
||||
composable(
|
||||
route = Screen.PersonList.title,
|
||||
exitTransition = {
|
||||
slideOutHorizontally() +
|
||||
fadeOut(animationSpec = tween(1000))
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideInHorizontally()
|
||||
}
|
||||
) {
|
||||
PersonListScreen(
|
||||
paddingValues = paddingValues,
|
||||
personSelected = {
|
||||
navController.navigate(Screen.PersonDetails.title + "/${it.name}")
|
||||
}
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Screen.PersonDetails.title + "/{person}",
|
||||
enterTransition = {
|
||||
slideInHorizontally() +
|
||||
fadeIn(animationSpec = tween(1000))
|
||||
},
|
||||
popExitTransition = {
|
||||
slideOutHorizontally()
|
||||
}
|
||||
) { backStackEntry ->
|
||||
PersonDetailsScreen(
|
||||
backStackEntry.arguments?.get("person") as String,
|
||||
popBack = { navController.popBackStack() }
|
||||
)
|
||||
}
|
||||
composable(Screen.ISSPositionScreen.title) {
|
||||
ISSPositionScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun DefaultPreview(@PreviewParameter(PersonProvider::class) person: Assignment) {
|
||||
MaterialTheme {
|
||||
PersonView(person, personSelected = {})
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class PeopleInSpaceViewModel(
|
||||
private val peopleInSpaceRepository: PeopleInSpaceRepositoryInterface
|
||||
) : ViewModel() {
|
||||
|
||||
val peopleInSpace = peopleInSpaceRepository.fetchPeopleAsFlow()
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
val issPosition = peopleInSpaceRepository.pollISSPosition()
|
||||
|
||||
fun getPerson(personName: String): Assignment? {
|
||||
return peopleInSpace.value.find { it.name == personName }
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.surrus.common.remote.Assignment
|
||||
|
||||
class PersonProvider : CollectionPreviewParameterProvider<Assignment>(
|
||||
listOf(
|
||||
Assignment("ISS", "Chris Cassidy"),
|
||||
Assignment("ISS", "Anatoli Ivanishin")
|
||||
)
|
||||
)
|
|
@ -1,56 +0,0 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.rememberImagePainter
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
@Composable
|
||||
fun PersonDetailsScreen(personName: String, popBack: () -> Unit) {
|
||||
val peopleInSpaceViewModel = getViewModel<PeopleInSpaceViewModel>()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(personName) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { popBack() }) {
|
||||
Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
val person = peopleInSpaceViewModel.getPerson(personName)
|
||||
person?.let {
|
||||
Text(person.name, style = MaterialTheme.typography.h4)
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
|
||||
val imageUrl = person.personImageUrl ?: ""
|
||||
if (imageUrl.isNotEmpty()) {
|
||||
Image(
|
||||
painter = rememberImagePainter(imageUrl),
|
||||
modifier = Modifier.size(240.dp), contentDescription = person.name
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.size(24.dp))
|
||||
|
||||
val bio = person.personBio ?: ""
|
||||
Text(bio, style = MaterialTheme.typography.body1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.rememberImagePainter
|
||||
import com.surrus.common.remote.Assignment
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
const val PersonListTag = "PersonList"
|
||||
|
||||
@Composable
|
||||
fun PersonListScreen(paddingValues: PaddingValues = PaddingValues(),
|
||||
personSelected: (person: Assignment) -> Unit,
|
||||
peopleInSpaceViewModel: PeopleInSpaceViewModel = getViewModel()
|
||||
) {
|
||||
val peopleState = peopleInSpaceViewModel.peopleInSpace.collectAsState()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(title = { Text("People In Space") })
|
||||
}
|
||||
) {
|
||||
LazyColumn(contentPadding = paddingValues, modifier = Modifier.testTag(PersonListTag)) {
|
||||
items(peopleState.value) { person ->
|
||||
PersonView(person, personSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PersonView(person: Assignment, personSelected: (person: Assignment) -> Unit) {
|
||||
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { personSelected(person) })
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
val personImageUrl = person.personImageUrl ?: ""
|
||||
if (personImageUrl.isNotEmpty()) {
|
||||
Image(
|
||||
painter = rememberImagePainter(personImageUrl),
|
||||
modifier = Modifier.size(60.dp), contentDescription = person.name
|
||||
)
|
||||
} else {
|
||||
Spacer(modifier = Modifier.size(60.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
|
||||
Column {
|
||||
Text(text = person.name, style = TextStyle(fontSize = 20.sp))
|
||||
Text(text = person.craft, style = TextStyle(color = Color.DarkGray, fontSize = 14.sp))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package com.surrus.peopleinspace.ui
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.darkColors
|
||||
import androidx.compose.material.lightColors
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
private val DarkColorPalette = darkColors(
|
||||
primary = maroon200,
|
||||
primaryVariant = maroon700,
|
||||
secondary = teal200
|
||||
)
|
||||
|
||||
private val LightColorPalette = lightColors(
|
||||
primary = maroon500,
|
||||
primaryVariant = maroon700,
|
||||
secondary = teal200
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun PeopleInSpaceTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable() () -> Unit
|
||||
) {
|
||||
val colors = if (darkTheme) {
|
||||
DarkColorPalette
|
||||
} else {
|
||||
LightColorPalette
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colors = colors,
|
||||
content = content
|
||||
)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package com.surrus.peopleinspace.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Composable
|
||||
fun <T> Flow<T>.collectAsStateWithLifecycle(
|
||||
owner: LifecycleOwner,
|
||||
initial: T,
|
||||
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
|
||||
): State<T> {
|
||||
return remember(this, owner) {
|
||||
flowWithLifecycle(owner.lifecycle, minActiveState)
|
||||
}.collectAsState(initial)
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
|
@ -1,263 +0,0 @@
|
|||
<vector android:height="50.6dp" android:viewportHeight="847"
|
||||
android:viewportWidth="385" android:width="23dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#BF9586" android:pathData="M88.188,10.5c0,9.583 0,19.167 0,28.75c29.532,-2.404 59.044,-4.063 88.626,-5.874c21.633,-1.324 67.814,-12.872 82.985,9.115c10.227,14.821 9.438,27.326 10.052,45.106c0.222,6.446 1.379,10.841 4.587,16.528c3.953,7.008 3.604,14.493 3.875,22.438c0.521,15.255 1.72,30.486 2.303,45.75c0.208,5.47 -1.598,16.568 0.822,21.563c2.621,5.41 12.739,8.2 17.65,11.33c6.127,3.905 9.348,10.715 13.413,16.671c3.825,5.605 9.62,10.142 13.437,15.999c7.843,12.035 11.888,26.538 18.242,39.403c13.548,27.425 22.34,52.58 16.52,83.551c-3.112,16.566 -10.924,25.477 -25.137,34.796c-5.239,3.435 -11.405,5.852 -17,8.707c-11.009,5.618 -10.466,6.807 -13.011,19.07c-3.189,15.369 -3.989,29.261 -3.989,44.959c0,12.234 8.642,27.777 12.46,39.571c9.394,29.011 16.033,59.139 22.958,88.842c3.271,14.033 3.362,29.792 4.534,44.159c1.112,13.652 7.868,26.241 9.832,39.771c1.001,6.899 3.091,14.62 3.091,21.608c0,8.577 -4.215,14.454 -3.375,22.436c1.776,16.891 9.226,27.403 19.287,40.269c5.157,6.595 11.396,30.684 7.088,38.608c-4.814,8.856 -30.952,14.176 -39.981,14.711c-8.112,0.48 -33.541,5.416 -33.901,-9.797c-0.282,-11.894 1.398,-25.152 -3.806,-36.204c-3.239,-6.878 -7.119,-13.728 -9.812,-20.837c-0.796,-2.102 0.088,-5.656 -1.984,-6.881c-3.258,-1.925 -5.235,-3.384 -7.789,-6.138c-9.567,-10.316 -14.569,-22.602 -19.337,-35.637c-4.508,-12.326 -13.262,-20.086 -13.885,-33.96c-0.559,-12.43 -10.433,-27.2 -15.835,-38.135c-5.704,-11.543 -9.188,-23.077 -11.713,-35.598c-1.412,-7.006 -3.737,-36.459 -11.332,-37.025c-1.829,15.894 -9.9,28.164 -12.611,43.704c-2.472,14.173 -3.737,27.892 -8.165,41.726c-2.421,7.563 0.887,11.288 0.273,18.595c-0.583,6.955 -0.649,14.153 -1.935,21.021c-2.729,14.577 -2.847,34.049 -9.001,47.491c-2.575,5.626 -4.624,14.581 -9.435,18.583c-9.454,7.864 -10.506,7.491 -9.125,19.567c0.862,7.539 1.563,15.033 2.141,22.601c0.697,9.13 -3.316,12.024 -9.445,18.65c-4.756,5.142 -7.461,12.823 -10.742,18.965c-3.082,5.769 -9.683,8.14 -15.578,10.221c-15.008,5.297 -28.782,5.051 -43.969,1.501c-4.272,-0.998 -5.2,-13.176 -5.532,-16.374c-0.886,-8.555 3.324,-13.692 7.392,-20.966c6.636,-11.864 10.248,-26.148 10.784,-39.561c0.146,-3.629 3.242,-15.388 1.947,-17.978c-3.64,-7.282 -5.106,-14.659 -5.747,-22.873c-1.13,-14.474 3,-28.828 3,-43.437c0,-14.442 2.25,-28.59 2.25,-42.97c0,-7.57 1.08,-15.837 -0.792,-23.217c-1.78,-7.019 -3.084,-14.493 -3.084,-21.755c0,-7.729 -0.71,-15.801 0,-23.495c0.62,-6.719 3.626,-13.327 3.626,-20.093c0,-15.694 0.143,-31.326 0.795,-47.008c0.614,-14.763 -6.546,-27.976 -5.171,-43.399c-3.574,4.958 -11.04,11.074 -8.795,17.115c2.02,5.438 -0.339,14.252 -1.64,19.95c-1.521,6.66 -4.761,14.174 -3.457,20.984c1.647,8.603 3.966,17.055 3.767,25.823c-0.166,7.296 -7.125,12.766 -7.125,19.241c0,8.876 -1.774,15.393 -4.259,23.982c-3.871,13.38 -19.129,21.46 -31.678,13.653c-13.762,-8.562 -14.932,-27.48 -18.42,-41.704c-1.693,-6.903 -2.893,-12.319 -2.893,-19.389c0,-6.342 5.089,-13.196 1.981,-19.604c-6.162,-12.703 -4.996,-31.832 -4.763,-45.896c0.1,-6.003 3.176,-11.979 3.97,-18.035c0.935,-7.132 0.442,-14.378 1.375,-21.479c1.939,-14.755 5.937,-29.42 6.063,-44.394c0.128,-15.263 -2.336,-28.45 1.25,-43.688c3.438,-14.61 4.556,-29.692 7.874,-44.312c1.698,-7.481 4.188,-14.859 6.269,-22.245c1.666,-5.911 7.521,-12.521 7.144,-18.688c-1.003,-16.431 -1.937,-32.863 -2.843,-49.299c-0.348,-6.318 2.657,-12.763 4.863,-18.512c2.016,-5.254 7.648,-10.292 7.314,-15.942c-0.982,-16.627 -1.959,-33.251 -3.083,-49.869c-0.932,-13.789 -5.834,-29.325 -3.97,-42.996c1.909,-13.999 10.471,-30.212 22.963,-37.397c3.423,-1.969 5.491,-2.645 9.403,-3.317c4.609,-0.792 2.816,-4.521 2.816,-8.901c0,-6.844 1.874,-16.853 -3.626,-22.209l1.126,-4.75h5L88.188,10.5z"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M177.313,498.377c3.891,-8.237 18.606,-4.169 26.527,-5.556c10.382,-1.819 19.325,-4.771 27.657,-11.667c7.175,-5.938 -16.429,-47.24 -19.811,-54.777c6.543,3.951 9.538,8.634 12.523,15.771c2.493,5.961 5.974,20.196 12.352,22.729c0,-10.295 -4.481,-17.948 -8.625,-27.377c3.917,-0.467 7.863,3.155 11.246,2.227c5.679,-1.559 7.76,-3.639 12.191,-7.663c7.106,-6.454 13.804,-13.243 20.563,-20.063c0.441,1.995 1.883,3.798 2.5,5.75c3.133,-1.061 6.125,-3.39 6.125,-6.873c0,4.334 -1.616,9.537 2.75,11.873c1.851,-3.701 3.917,-7.244 5,-11.25c1.517,5.222 -4.055,10.545 0,14.377c5.224,-4.229 5.066,-8.646 6.125,-15c-0.317,3.897 -3.38,15.785 0.125,17.687c5.361,2.907 0.382,15.139 -0.615,19.367c-1.517,6.43 -0.135,14.62 -0.135,21.219c0,6.054 -7.09,6.888 -12.125,8.478c1.385,-0.958 6.356,-6.029 2.5,-7.25c-1.548,-0.49 -7.915,5.095 -9.759,6.148c-3.383,1.933 -14.245,13.37 -4.429,11.163c7.908,-1.778 15.495,-5.342 23.075,-8.179c3.038,-1.137 7.907,13.503 9.362,16.617c-3.059,-1.284 -6.43,-1.805 -9.375,0c4.008,3.644 12.242,7.717 14,12.5c-5.283,-3.123 -14.578,-8.825 -20.75,-8.377c4.056,6.576 10.599,11.533 16.625,16.627c-8.388,-4.934 -23.594,-19.377 -33.188,-19.377c-3.955,0 -2.864,5.656 0.299,6.483c3.513,0.918 5.207,1.913 8.206,3.951c6.023,4.095 12.039,8.201 18.081,12.27c6.495,4.373 11.003,8.115 16.267,13.912c3.728,4.106 4.69,13.689 6.354,19.058c4.104,13.237 6.519,27.179 9.203,40.81c2.797,14.204 3.536,28.693 4.874,43.103c1.258,13.558 5.737,26.809 8.273,40.227c1.354,7.158 -2.989,6.807 -9.259,10.271c-7.018,3.879 -13.942,8.076 -21.111,11.667c-5.182,2.596 -11.945,6.9 -17.648,7.836c-4.267,0.699 -8.534,1.399 -12.802,2.099c-3.108,0.51 -2.841,-2.927 -6.05,-4.185c5.146,-7.459 1.767,-10.418 -6.125,-8c4.462,-1.58 13.732,-2.528 14.5,-7.5c-4.609,0 -9.266,-0.434 -13.765,0.616c-6.292,1.468 -6.918,-1.119 -10.505,-6.446c-6.276,-9.323 -2.648,-20.737 -7.933,-30.986c-9.508,-18.438 -19.429,-37.207 -24.644,-57.264c-2.403,-9.243 -3.474,-18.876 -5.092,-28.293c-0.976,-5.677 -0.207,-10.939 -5.304,-13.852c-2.14,-1.223 -11.521,-1.083 -11.875,-1.685c-5.846,-9.91 -11.865,-19.611 -18.103,-29.28C180.422,514.295 173.869,508.319 177.313,498.377z"
|
||||
android:strokeColor="#000000" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M107.813,452.377c0.662,7.818 7.573,-0.051 8.726,-3.574c0.799,-2.442 1.288,-4.989 2.836,-7.053c2.458,-3.276 4.482,3.172 7.312,2.627c7.851,-1.511 -3.45,-7.495 -3.354,-9.566c0.284,-6.145 0.569,-12.289 0.854,-18.434c2.78,6.672 2.48,12.478 10.5,8.623c-3.242,4.682 -1.119,10.25 0.87,14.983c3.115,7.413 2.651,11.308 11.006,11.644c1.936,0.078 5.023,0.665 6.589,-0.56c1.792,-1.4 4.864,-5.16 7.196,-4.544c4.365,1.153 11.735,7.631 15.839,3.354c2,-2.084 3.791,-14.872 4.785,-17.856c0.68,-2.038 -8.49,-9.621 -10.066,-12.06c-1.281,-1.982 -5.233,-6.089 -5.469,-8.461c-0.143,-1.433 2.902,-5.493 3.313,-7.5c0.985,-4.813 0.791,-11.56 -2.813,-15.25c5.115,-0.759 7.511,5.146 11.219,8.021c1.634,1.267 4.62,0.256 6.583,0.077c2.212,-0.202 4.081,3.232 5.452,4.75c6.738,7.461 10.967,14.387 15.487,23.228c4.245,8.302 29.737,51.044 17.677,55.779c-10.206,4.008 -20.369,6.146 -31.418,6.895c-4.441,0.301 -11.047,-1.76 -13.882,2.82c-2.647,4.276 -4.859,8.164 -5.242,13.243c-0.809,10.721 8.768,21.343 13.965,30.005c1.933,3.221 2.885,8.118 5.118,10.839c2.93,3.568 11.071,9.678 8.541,14.593c-4.434,-5.911 -8.677,-11.821 -12.783,-17.962c-2.179,-3.259 -14.341,-20.88 -10.114,-7.29c2.198,7.066 9.06,14.296 13.09,20.451c3.501,5.346 11.229,10.92 9.308,16.928c-6.502,-9.036 -11.682,-18.474 -21,-24.627c3.358,12.636 14.217,20.889 17.375,33.5c-3.501,-4.587 -6.176,-8.608 -10.499,-12.5c-1.248,6.69 3.76,14.123 7.499,19.377c-1.96,-3.109 -4.616,-5.467 -8.125,-3.25c5.959,13.073 4.768,27.96 0.872,42.041c-1.857,6.712 -5.293,13.104 -6.872,19.832c-1.673,7.131 1.418,13.344 1.155,20.528c-0.247,6.77 -0.717,12.667 -2.028,19.276c-1.235,6.218 -1.24,21.544 -5.251,26.445c-3.897,4.762 -14.587,3.338 -20.116,4.233c-7.723,1.251 -14.852,-0.356 -21.822,-0.356c-7.143,0 -14.781,-1.926 -21.355,-4.609c-8.343,-3.406 -10.384,-2.273 -8.957,-12.268c0.641,-4.491 1.877,-4.549 0.583,-10.379c-0.837,-3.766 -0.972,-5.803 -0.521,-9.621c0.898,-7.59 0.721,-15.146 1.673,-22.76c3.626,-28.988 -3.875,-56.686 -2.924,-85.688c0.253,-7.728 3.439,-15.249 3.439,-22.974c0,-12.982 0,-25.965 0,-38.947c0,-6.902 1.742,-16.386 -1.205,-22.505c-1.918,-3.984 -1.741,-9.742 -2.387,-14.091c-0.631,-4.241 3.594,-7.54 4.092,-11.908C93.697,461.048 103.875,453.231 107.813,452.377z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M132.813,333.126c-1.377,1.042 -3.415,1.537 -5,2.25c-0.477,-4.906 2.776,-7.961 3.874,-12.5c1.239,-5.12 0.422,-11.619 0.613,-16.866c0.174,-4.789 2.954,-26.351 -4.487,-26.26c0,6.742 -0.165,13.177 -0.876,19.876c-3.87,-14.32 -6.795,-35.584 -18,-46.25c-3.251,6.761 3.301,13.887 5.626,20c2.451,6.443 3.513,13.119 4.624,19.874c-1.381,-2.466 -3.023,-7.722 -5.25,-9.374c-3.273,-2.428 -5.79,-2.383 -3.766,2.44c3.456,8.235 7.302,16.302 9.642,24.934c-2.102,-2.208 -3.236,-1.818 -5.876,-2.25c-0.565,5.697 2.9,8.813 6.137,12.991c3.923,5.065 2.678,6.908 0.183,12.579c-4.49,10.2 -10.363,16.436 -12.793,27.579c-2.231,10.23 -7.96,19.427 -9.838,29.664c-2.069,11.28 -3.754,22.756 -6.446,33.963c-2.229,9.274 -6.165,18.1 -9.796,26.93c-2.084,5.068 -2.956,13.282 -8.696,13.544c-0.276,0.013 -12.788,-5.338 -13.25,-5.873c-5.348,-6.194 4.288,-25.591 -13.435,-18.857c-3.059,1.163 0.01,11.859 -2.505,12.31c-6.145,1.099 -12.29,2.198 -18.435,3.298c0,-5.784 0.638,-57.877 6.587,-57.877c9.175,0 18.76,0.362 27.495,3.414c4.817,1.684 9.912,4.216 14.918,5.213c3.249,0.647 8.238,-0.475 10.874,1.874c-3.423,-9.255 -21.051,-11.329 -29.124,-13.75c4.075,0 8.588,1.183 11.374,-2.5c-6.398,-3.075 -11.749,-2.5 -18.819,-2.5c-4.27,0 -14.445,2.682 -15.394,-0.554c-3.928,-13.395 -4.401,-27.219 -1.752,-40.956c5.139,-26.644 9.172,-52.179 17.139,-78.176c3.878,-12.655 14.55,-21.844 23.331,-31.039c4.184,-4.382 8.964,-7.804 13.773,-11.508c8.58,-6.609 9.221,-2.749 17.926,3.738c11.201,8.347 22.465,16.016 34.449,23.163c12.914,7.701 20.818,10.249 36.224,11.083c-12.943,2.275 -24.94,0.499 -33.912,11.121c-3.4,4.025 -1.588,11.855 -1.588,16.885c0,7.691 -0.261,15.433 0,23.12c0.208,6.112 0.973,12.208 1.254,18.319c0.435,9.464 3.937,12.673 9.786,20.248c1.064,1.378 3.858,0.235 4.433,1.983c1.005,3.058 1.729,6.416 3.207,9.282c3.239,6.282 4.753,12.585 7.571,19.041c-4.104,-2.364 -10.85,-11.873 -14.676,-11.873c-7.175,0 -12.04,4.398 -14.418,11.101c-1.937,5.458 -4.214,13.646 -2.613,19.308c1.266,4.478 10.759,14.278 5.457,16.092c-1.305,-4.268 -4.018,-7.016 -6.88,-10.279c-2.933,-3.344 0.063,-8.275 1.13,-12.348c-2.425,4.903 -4.489,10.206 -7.75,14.627c2.404,-14.487 4.463,-28.65 8.874,-42.627C135.533,365.693 148.464,330.283 132.813,333.126z"
|
||||
android:strokeColor="#000000" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M353.313,317.876c1.832,14.859 3.971,28.936 -0.25,43.5c-0.702,-2.944 -4.053,-7.742 -6.875,-4.5c-1.129,1.297 1.151,10.045 1.375,12c-2.174,-0.616 -4.152,0.03 -6.375,-1.126c-2.104,4.608 2.525,7.184 0,12.126c-0.68,-2.45 -2.335,-3.571 -3.625,-5.5c-4.204,5.386 -11.125,11.691 -17.5,14.373c-2.054,0.864 -4.669,-0.852 -6.398,1.311c-2.552,3.192 -1.175,6.221 -2.727,9.816c-6.025,-17.198 -6.989,4.204 -10.5,2.123c-3.62,-2.146 -4.851,-22.551 -5.579,-27.03c-1.564,-9.627 -7.46,-17.153 -12.957,-24.864c-2.509,-3.52 -5.083,-4.657 -2.065,-8.729c4.117,-5.557 -0.709,-8.31 -4.773,-12.375c11.021,-8.733 21.879,-16.624 36.75,-16.624C324.855,312.376 340.307,315.567 353.313,317.876z"
|
||||
android:strokeColor="#000000" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M191.688,268.626c0.824,-4.412 6.249,-3.299 9.875,-3.688c6.561,-0.703 8.555,-0.776 13.602,-4.946c6.745,-5.572 11.088,-13.126 15.472,-20.508c7.49,-12.61 23.45,-32.097 23.677,-46.984c11.12,4.083 21.685,7.713 32.022,13.537c9.611,5.414 13.216,11.935 20.7,19.775c7.4,7.753 13.906,16.94 18.408,26.654c2.926,6.315 -2.733,7.168 -8.166,10.033c-3.432,1.81 1.683,3.314 3.16,3.377c2.066,0.088 6.101,-3.146 8,-4.126c8.592,16.958 18.058,33.171 24.375,51.126c-9.041,-1.292 -18.043,-2.087 -27.125,-3c2.667,-0.542 5.333,-1.084 8,-1.626c-0.65,-5.397 -4.565,-3.559 -8.625,-3.124c3.603,-3.119 11.877,-5.827 8.125,-11.25c-11.156,5.758 -19.748,11.736 -32.002,13.98c-11.9,2.179 -20.854,9.082 -30.623,15.894c-1.335,-5.062 -4.372,-11.1 -1.899,-16.228c2.215,-4.592 9.656,-11.707 7.399,-16.146c-2.416,1.037 -4.528,2.822 -6,5c1.511,-6.764 11.443,-16.543 9.625,-22.5c-3.486,2.988 -6.497,5.697 -9.125,9.5c1.062,-6.274 5.02,-14.374 4.5,-20.5c-3.922,5.566 -10.846,13.542 -10.875,20.5c0,-5.667 0,-11.333 0,-17c-5.916,5.442 -4.375,10.362 -4.375,18.127c0,4.881 -5.006,9.649 -2.269,14.014c4.45,7.097 3.448,11.124 5.269,19.109c2.285,10.025 2.921,8.471 -4.625,16.624c0.096,-4.889 -0.421,-24.648 -3.931,-26.403c-6.937,-3.467 -6.933,-3.38 -7.93,-11.051c-0.732,-5.631 0.687,-9.145 -5.45,-9.651c-7.456,-0.615 -8.826,0.764 -11.097,-6.358c-1.392,-4.367 -2.84,-8.533 -6.843,-10.536C215.637,266.597 199.988,269.058 191.688,268.626z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M183.438,49.5c28.987,10.7 50.989,28.876 56.738,60.335c3.149,17.238 6.86,34.485 7.762,52.041c0.603,11.734 4.246,34.477 -4.75,44c-0.281,-1.839 -1.717,-2.813 -2.25,-4.25c-7.757,8.229 -40.307,33.065 -24.875,44.624c1.245,-10.528 2.029,-12.242 9.759,-19.536c5.454,-5.147 10.141,-11.35 15.116,-16.964c-6.548,11.136 -13.096,22.271 -19.645,33.407c-2.135,3.631 -4.359,6.318 -7.203,9.496c-4.473,4.999 -5.262,3.827 -11.902,3.847c19.361,-22.924 -16.904,-7.617 -23.75,-14.124c23.412,-7.863 39.81,-17.38 54.407,-37.95c1.781,-2.51 9.767,-9.809 10.231,-12.291c1.107,-5.904 0.747,-9.426 0.603,-15.089c-0.319,-12.538 0.029,-24.979 -2.308,-37.308c-2.008,-10.596 -3.277,-23.825 -11.621,-31.613c-7.361,-6.871 -19.631,-11.975 -29.118,-16.362c-2.632,-1.217 -9.318,-2.141 -12.405,-2.576c-7.768,-1.095 -15.536,-2.189 -23.303,-3.285c-12.811,-1.806 -20.455,0.426 -31.61,6.223C145.881,71.442 160.3,57.479 183.438,49.5z"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M263.688,100.626c10.691,9.33 8.421,31.461 9.375,44.374c1.189,16.093 2.32,32.37 1.375,48.626c-6.082,-2.493 -19.98,-4.598 -20.262,-11.707c-0.351,-8.869 -0.683,-17.675 -1.499,-26.509c-1.669,-18.071 -3.048,-38.176 -8.989,-55.284C247.552,99.189 262.908,95.396 263.688,100.626z"
|
||||
android:strokeColor="#000000" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="2"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M258.188,57.5c6.447,5.976 4.33,20.602 4.649,28.304c0.484,11.693 -12.542,2.919 -19.649,8.822c-6.668,-16.568 -17.521,-28.595 -31.5,-39.876C227.354,53.681 243.08,52.465 258.188,57.5z"
|
||||
android:strokeColor="#000000" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="2"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M257.563,52c-7.697,-4.972 -21.434,-5.167 -30.287,-4.662c-7.394,0.421 -14.788,0.843 -22.182,1.265c-1.857,0.105 -1.174,1.945 -3.306,1.204c-3.408,-1.186 -6.816,-2.371 -10.226,-3.557c-5.022,-1.747 -9.637,-4 -14.956,-4c-3.829,0 -16.521,-2.151 -19.231,0.563c-7.443,7.456 -24.992,2.195 -31.812,11.937c-3.743,-6.065 -34.692,1.03 -42.014,1.695c-5.863,0.533 -9.539,2.182 -14.353,5.444c-2.847,1.928 -8.524,8.728 -9.759,11.987c2.96,-2.054 4.959,-5.319 7.5,-2.5c13.106,-12.117 32.913,-9.998 50,-9.126C106.819,74.13 97.212,84.725 92.369,99.497c-4.368,13.321 -8.306,26.859 -8.306,40.865c0,9.299 0,18.597 0,27.896c0,2.938 -15.077,2.649 -18.346,2.501c-7.472,-0.339 -6.03,-33.322 -6.03,-40.07c0,-29.073 -13.36,-56.9 13.241,-78.785c7.922,-6.518 29.192,-7.702 39.31,-8C127.46,43.453 142.655,42 157.871,42c17.499,0 35.412,-1.459 52.82,-3.215c10.194,-1.028 19.386,-0.614 29.639,0.095C250.125,39.558 252.475,44.219 257.563,52z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#13132D" android:pathData="M181.469,199.969c-3,-14 -19,-21 -24,-33c-8,-20 8,-40 14,-59c1.493,-5.226 -0.358,-10.452 -2.641,-15.678c-27.291,-1.158 -54.426,11.526 -67.488,37.658c-5.551,11.104 -0.515,34.702 0.201,46.712c0.804,13.501 9.675,23.861 19.556,32.79c11.491,10.383 20.909,20.316 35.873,24.058c7.745,1.936 14.34,2.294 20.688,1.397C179.28,223.531 184.563,212.349 181.469,199.969z"/>
|
||||
<path android:fillColor="#363844" android:pathData="M236.417,154.423c-0.198,-12.696 0.148,-30.628 -10.512,-39.381c-12.791,-10.502 -24.972,-15.782 -40.842,-20.416c-5.345,-1.336 -10.793,-2.105 -16.236,-2.336c2.283,5.226 4.134,10.452 2.641,15.678c-6,19 -22,39 -14,59c5,12 21,19 24,33c3.095,12.38 -2.188,23.563 -3.813,34.937c7.68,-1.084 14.995,-4.011 23.531,-8.279c11.265,-5.632 23.966,-17.232 29.502,-28.776C237.611,183.419 236.664,170.254 236.417,154.423z"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M85.688,183.376c0.359,6.385 0.815,12.622 1.616,18.961c1.215,9.622 4.201,14.072 -3.203,20.291c-10.597,8.901 -19.846,19.709 -30.538,28.248c-1.719,-15.686 -5.664,-36.644 -0.699,-51.879c2.588,-7.943 5.519,-17.118 13.823,-20.871c2.048,-0.926 7.945,-1.376 10.375,-1.376C84.563,176.75 84.43,175.962 85.688,183.376z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M255.063,336.5c-12.07,7.293 -20.179,20.825 -33.509,26.438c-9.586,4.036 -17.755,24.561 -17.616,33.811c-7.695,-11.629 -3.978,-14.751 -0.375,-27.373c2.067,-7.245 0.225,-7.114 7.995,-8.032c7.362,-0.87 7.504,-1.348 12.692,-5.156c6.993,-5.132 7.232,-16.739 6.968,-24.278c-0.153,-4.39 -1.995,-12.023 -0.093,-16.097c1.298,-2.78 9.348,-4.055 12.063,-6.813C244.295,323.301 243.141,328.274 255.063,336.5z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M132.438,338.376c8.165,11.792 -3.332,31.704 -6.647,43.722c-4.535,16.438 -6.791,31.377 -8.853,48.401c0,-0.917 0,-1.833 0,-2.75c-5.878,2.924 -1.167,12.209 -5.435,15.963c-4.354,3.83 -10.554,3.804 -14.439,8.664c3.046,-3.877 12.355,-10.994 6.624,-15.5c-3.233,2.685 -6.736,5.103 -9.124,8.623c-0.542,-5.344 7.121,-12.93 10.048,-17.321c1.284,-1.926 4.568,-5.782 1.077,-7.052c-2.894,-1.052 -6.337,5.749 -8.125,8c-0.058,-4.182 12.552,-20.025 3.374,-17.127c2.589,-3.02 5.354,-5.36 8.063,-8.062c3.381,-3.373 -4.685,-3.078 -6.937,-0.562c1.932,-5.644 9.653,-8.011 11.624,-13.438c2.667,-7.343 -8.53,0.596 -10.5,2.438c2.005,-1.845 9.974,-6.61 10.75,-8.877c1.919,-5.603 -4.753,-4.012 -7.5,-1.123c-0.665,-6.499 8.108,-4.54 11.126,-9.127c5.165,-7.85 -3.121,-3.91 -6.876,-2.5c0.385,-3.74 19.824,-21.497 10.126,-20.499C123.098,347.333 130.067,336.511 132.438,338.376z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M101.813,196.75c10.273,12.271 19.525,22.736 31.922,32.786c9.674,7.842 30.274,15.593 42.702,14.214c-7.783,0.819 -15.567,1.639 -23.35,2.458c-1.069,0.112 -1.095,7.995 -1.209,9.301c-0.166,1.898 4.905,2.894 6.309,3.117c12.667,2.01 29.866,-1.376 42.875,-1.376c-11.194,1.023 -22.192,2.31 -33.427,2.034c-5.077,-0.125 -9.909,0.582 -14.424,-1.769c-2.445,-1.273 -15.292,-6.039 -15.433,-8.66c-0.318,-5.904 1.103,-9.957 -4.026,-13.054c-5.556,-3.354 -10.113,-6.211 -14.767,-10.669C109.949,216.475 101.813,209.615 101.813,196.75z"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="M97.563,190.876c0,6.226 0,12.453 0,18.679c0,3.119 9.819,11.409 11.679,13.47c3.544,3.928 8.143,7.421 12.445,10.476c7.465,5.299 10.569,4.526 11.376,13.876c-10.746,-6.686 -19.382,-12.845 -28.601,-21.472c-3.767,-3.524 -6.993,-6.896 -10.255,-10.891c-2.863,-3.506 -2.014,-13.691 -2.523,-18.145c-2.441,-21.336 -0.747,-42.723 -0.747,-63.674c0,-23.747 10.692,-45.102 25.614,-62.82c15.217,-18.069 38.202,-21.749 60.512,-21.749c-14.345,4.632 -25.385,13.759 -35.847,24.221c-5.155,5.155 -8.588,14.156 -11.946,20.523c-2.347,4.448 -12.167,9.425 -16.225,13.381c-6.28,6.124 -10.519,11.005 -15.108,18.439c-4.029,6.525 -3.124,11.899 -3.124,19.621C94.813,160.476 95.864,175.327 97.563,190.876z"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M85.688,247.376c-2.899,3.708 -5.82,-0.186 -9.124,-2.25C79.075,240.626 83.718,244.005 85.688,247.376z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M302.938,246.5c2.284,4.423 4.454,8.922 6.949,13.231c0.517,0.893 9.24,14.781 4.176,13.645c-2.995,-0.672 -15.817,-25.346 -15.25,-29.876C300.852,243.487 301.84,244.675 302.938,246.5z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M78.813,252.376c1.833,2.292 3.667,4.583 5.5,6.874c-0.708,-0.667 -1.417,-1.333 -2.126,-2c-4.151,3.751 -9.45,-4.85 -13.874,-6.874C70.348,245.576 76.249,249.985 78.813,252.376z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M201.938,249.5c-2.667,0.292 -5.333,0.584 -8,0.876C196.566,249.066 199.438,248.129 201.938,249.5z"
|
||||
android:strokeColor="#FF9100" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M185.313,251.25c-2.837,-0.36 -5.645,-0.811 -8.5,-0.874c4.716,-0.166 9.405,-0.25 14.124,-0.25C189.066,250.5 187.196,250.945 185.313,251.25z"
|
||||
android:strokeColor="#FF0000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M74.938,258.626c4.221,3.685 9.273,6.971 11.626,12.25c-9.548,-1.025 -15.959,-13.899 -26.626,-15C62.749,249.834 71.068,256.304 74.938,258.626z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M294.063,283.626c-1.86,4.494 -5.976,3.954 -3.433,-0.832c3.575,-6.73 5.245,-7.908 12.058,-11.418C301.957,276.394 297.258,279.967 294.063,283.626z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M206.438,277.25c-6.504,5.153 1.116,12.832 6.875,13c11.788,0.344 5.397,-5.984 7.125,-13.874c7.675,9.903 3.375,36.119 3.375,48.148c0,11.538 -0.197,16.738 -5.379,26.899c-1.858,3.645 -7.438,1.953 -11.219,1.953c-3.793,0 -4.625,-3.119 -6.152,-6.376c3.936,-0.354 7.872,-0.708 11.808,-1.062c2.611,-0.234 3.362,-6.438 0.237,-6.438c-8.761,0 -17.635,-0.435 -26.383,0.018c-8.762,0.453 -17.528,0.85 -26.287,1.358c-2.444,0.142 -8.262,-0.793 -9.374,2.25c-1.53,4.187 3.642,5.674 6.374,4.374c-0.941,7.436 -6.627,2.822 -9.124,-1.624c-3.179,-5.662 -1.998,-14.208 -2.348,-20.46c-0.718,-12.808 -1.881,-25.226 -1.044,-38.017c0.295,-4.513 -0.415,-10.647 6.014,-11.055c5.992,-0.38 12.11,-1.197 18.114,-1.077c14.296,0.287 28.592,0.573 42.889,0.859C210.111,276.5 208.28,276.973 206.438,277.25z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M330.063,281.626c-7.801,2.065 -14.775,6.5 -22.125,8.624C306.098,282.555 328.329,270.151 330.063,281.626z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M187.063,287.75c0.456,9.423 1.028,18.781 1.826,28.181c0.325,3.843 -18.59,5.104 -20.452,3.319c-3.411,-3.269 -1.124,-23.851 -1.124,-29.52C167.313,283.207 182.992,285.344 187.063,287.75z"
|
||||
android:strokeColor="#A89187" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#00FFFF" android:pathData="M183.938,313.25c-3.667,0.25 -7.333,0.5 -11,0.75c-0.324,-7.449 -0.861,-14.917 -0.874,-22.374c5.086,0.236 10.624,-1.876 10.926,3.801C183.306,301.368 183.621,307.31 183.938,313.25z"/>
|
||||
<path android:fillColor="#13007C"
|
||||
android:pathData="M240.438,295.75c-4.033,1.78 -9.745,4.139 -8.875,9.376c-0.438,-1.813 -3.396,-8.88 -0.977,-9.669C233.794,294.412 239.348,291.82 240.438,295.75z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M160.438,305.126c2.341,5.195 1.194,12.482 1.362,18.151c0.188,6.352 4.15,14.599 -5.195,14.599c-6.529,0 -6.112,-30.577 -1.167,-35.25C156.868,304.685 158.271,304.126 160.438,305.126z"
|
||||
android:strokeColor="#A89187" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M100.938,314.626c4.542,4.25 9.084,8.5 13.626,12.75c-2.396,-2.428 -4.744,-0.617 -7.5,-2c-2.879,-1.445 -5.72,-5.646 -8.016,-7.911c-3.885,-3.834 -16.117,-7.539 -15.234,-13.339C90.469,304.522 95.922,310.814 100.938,314.626z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M240.438,308.75c-0.458,0.375 -0.917,0.75 -1.375,1.126c-0.842,-1.17 -1.16,-1.971 -1.125,-3.376c1.125,0 2.25,0 3.375,0C241.021,307.25 240.729,308 240.438,308.75z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#6CFF00" android:pathData="M157.438,334.25c-5.526,-4.073 -2.617,-19.78 -2.75,-26.25C159.959,307.818 161.703,330.906 157.438,334.25z"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M250.063,322.376c-1.535,-2.114 -1.201,-4.872 -0.5,-7.5C249.899,317.364 250.067,319.87 250.063,322.376z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M313.188,333.126c0.198,1.239 0,2.615 0,3.874c-2.983,-2.688 -17.137,-11.68 -18,-15.25C293.536,314.919 312.031,331.788 313.188,333.126z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M64.438,330.126c-2.18,9.125 -2.329,27.813 -7.75,34.874c-3.957,-3.67 1.789,-21.867 2.607,-27.301c0.565,-3.756 0.688,-11.658 2.893,-14.573C67.064,316.678 64.911,327.942 64.438,330.126z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M189.813,323.75c0.578,5.492 2.899,8.989 -3.057,9.369c-1.688,0.108 -16.451,1.859 -16.735,0.706c-0.962,-3.899 -4.211,-12.347 2.96,-11.983C178.031,322.098 185.606,320.915 189.813,323.75z"
|
||||
android:strokeColor="#A89187" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M173.438,328.75c3.06,-2.572 8.379,-1.214 12.5,-1.124C181.771,328.001 177.604,328.376 173.438,328.75z"
|
||||
android:strokeColor="#FF0000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M103.688,337.25c3.458,5.849 -5.57,2.727 -5.25,-1.624C100.19,335.818 102.189,336.276 103.688,337.25z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFF00" android:pathData="M259.563,348.376c7.59,5.087 16.046,14.193 19.531,22.644c1.269,3.077 9.177,34.404 0.094,29.356c-5.037,-2.8 0.027,-21.725 -4.438,-27.534c-7.332,-9.54 -14.344,-15.845 -23.813,-23.342C253.541,344.552 255.862,344.098 259.563,348.376z"/>
|
||||
<path android:fillColor="#13007C"
|
||||
android:pathData="M200.563,358.876c-2.454,8.548 -5.058,15.983 -8.875,24c-2.937,-9.811 -17.504,-10.767 -19.624,0c-4.822,-5.033 -5.566,-10.553 -8.565,-16.639c-5.107,-10.366 -2.742,-7.805 1.627,-17.424c1.242,-2.734 16.699,-1.563 20.495,-1.563C196.289,347.25 195.563,349.386 200.563,358.876z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M277.563,336.5c-5.898,6.193 -4.518,6.5 0,13.75c3.227,5.179 7.723,10.085 9.848,15.829c4.428,11.968 8.423,23.756 6.652,36.547c-7.801,1.036 -5.098,-9.271 -5.234,-14.393c-0.167,-6.297 -4.068,-13.798 -5.918,-19.82c-3.323,-10.815 -13.645,-20.853 -22.348,-27.537c3.386,-2.913 6.771,-5.825 10.158,-8.738C273.422,329.814 275.447,334.287 277.563,336.5z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M260.313,367.75c4.569,7.092 8.239,12.441 10.227,20.592c0.796,3.262 3.246,13.1 0.854,15.621c-10.066,10.612 -20.093,26.737 -35.456,26.787c-9.021,0.029 -11.832,-7.586 -17.508,-12.816c-2.075,-1.912 -5.421,0.273 -8.18,-1.934c-3.102,-2.48 -4.067,-4.908 -1.604,-8.466c3.421,-4.941 -2.849,-12.047 2.167,-18.784c1.154,-1.551 16.55,-11.327 5.75,-10.75c2.772,-7.785 13.679,-12.316 20,-16.873C247.326,353.368 250.879,359.859 260.313,367.75z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M187.813,386c-1.686,2.064 -13.877,7.631 -11.274,0.979C178.616,381.671 185.527,377.577 187.813,386z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M335.563,384.376c-0.708,0.333 -1.417,0.667 -2.125,1c0.04,-2.644 -0.199,-2.056 1.625,-3.877C335.396,382.434 335.3,383.484 335.563,384.376z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M153.563,386.75c7.177,4.215 10.385,10.581 8.25,18.627c-0.931,3.508 -4.848,4.997 -2.021,8.81c2.966,4 5.933,8 8.898,12c3.103,4.185 8.78,15.929 -0.504,16.813c-3.04,0.29 -6.964,-3.873 -10.74,-3.873c-3.669,0 -6.358,5.126 -9.76,5.25c-4.806,0.175 -11.27,-9.867 -7.175,-14.499c3.208,-3.629 14.637,-12.92 9.113,-18.439c-3.267,-3.264 -7.083,1.137 -9.688,-4.189c-1.47,-3.005 0.455,-10.817 0.599,-14.234C140.797,386.836 150.813,376.118 153.563,386.75z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M327.063,389.876c-1.093,-0.67 -3.414,0.649 -0.625,-2.25C326.646,388.376 326.854,389.126 327.063,389.876z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M198.938,398.75c1.292,2.375 2.583,4.751 3.875,7.127c-4.046,-4.716 -6.986,-9.549 -10,-14.877C194.854,393.583 196.896,396.166 198.938,398.75z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M235.938,410.877c0.77,1.706 4.544,7.634 3.375,9.75c-2.836,5.135 -6.79,-7.477 -7.135,-8.308c-1.887,-4.545 -8.157,-17.23 -2.49,-19.943C232.261,398.487 234.114,404.493 235.938,410.877z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M208.063,444.877c6.279,10.938 2.696,27.903 -12.523,20.301C186.373,460.599 196.059,434.559 208.063,444.877z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M205.813,452.127c-0.415,4.371 -5.919,13.81 -9.125,5.5C193.979,450.608 201.321,448.549 205.813,452.127z"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M145.438,466.5c5.49,1.89 2.685,-2.333 5.626,-4.123c3.564,-2.169 5.859,-2.323 10.859,-2.684c8.859,-0.639 7.837,3.99 6.265,12.307c2.626,-4.195 12.711,-11 16.876,-7.75c4.48,3.495 -8.841,6.465 -10.476,10.39c-3.299,7.92 2.161,15.263 -10.365,14.085c-7.714,-0.726 -8.892,-9.377 -2.409,-12.848c-1.833,-1.626 -3.667,-3.251 -5.5,-4.877c-1.651,5.008 -2.901,15.814 -10.713,11.757c-7.489,-3.891 -15.028,-7.236 -22.794,-10.547c-4.938,-2.104 0.573,-11.096 3.194,-13.021C129.924,456.306 141.905,464.779 145.438,466.5z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M66.688,479.5c5.025,3.038 7.616,4.401 5.464,10.858c-1.75,5.251 -2.041,10.639 -3.838,15.769c-7.489,-3.137 -13.58,-6.435 -21.598,-2.684c-7.499,3.507 -15.022,6.804 -22.652,9.307c-0.806,-5.367 -7.504,-38.609 -2.341,-39.877C37.626,468.968 51.769,474.47 66.688,479.5z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M225.813,493.127c7.112,5.575 -17.545,7.534 -19.528,7.798c-5.713,0.76 -22.345,4.553 -26.471,-1.111c-3.587,-4.925 17.508,-2.536 19.956,-2.817C206.316,496.244 220.97,491.704 225.813,493.127z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M196.438,506.377c-3.442,4.375 -12.568,5.338 -16.124,0.873C183.83,502.563 192.15,503.856 196.438,506.377z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M65.188,512.5c5.698,5.89 10.519,29.75 3.876,35.627c-0.344,-3.045 0.308,-12.207 -1.626,-14.377c-2.802,-3.143 -3.66,0.448 -3.451,2.975c0.83,10.039 0.852,18.942 0.201,28.902c-0.611,9.343 -1.701,19.31 -8.895,26.025c-8.796,8.211 -16.757,-1.5 -21.457,-8.601c-10.636,-16.074 -11.455,-37.119 -8.901,-55.342C26.875,513.867 53.676,504.028 65.188,512.5z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M190.563,516.627c-1.925,0.501 -11.222,-1.199 -6.874,-3.612C187.177,511.078 192.1,511.25 190.563,516.627z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M347.563,706.127c2.827,10.743 -24.355,21.701 -31.46,25.866c-5.481,3.215 -15.369,2.255 -21.613,2.919c-8.831,0.939 -10.492,-2.626 -16.427,-9.162c10.097,-7.609 24.412,-6.044 35.649,-11.23c7.77,-3.586 15.539,-7.172 23.309,-10.758C344.429,700.343 345.8,695.744 347.563,706.127z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M107.063,728c2.812,0.779 5.708,1.041 8.5,0.25c0.254,3.753 23.388,8.174 25.75,5c4.42,5.243 8.703,0.566 13.624,0c1.673,-0.192 2.848,1.589 4.7,1.084c2.86,-0.781 5.351,-1.591 8.3,-1.584c-2.406,1.721 -2.468,2.997 -2.13,5.622c0.274,2.121 -3.735,3.727 -5.37,4.878c1.19,0.577 0.997,0.959 2.5,1.377c-5.047,5.384 -8.459,10.25 -15.844,10.25c-7.569,0 -15.8,-0.318 -23.156,-2.25c-6.461,-1.696 -14.617,-6.329 -19.652,-10.525c-3.844,-3.203 -10.049,-14.414 -7.722,-19.102C100.25,724.44 103.964,725.494 107.063,728z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M344.813,729.127c1.183,14.487 6.975,23.606 16.148,34.457c7.267,8.595 9.12,21.742 10.137,31.998c1.016,10.245 -27.745,13.067 -34.418,14.313c-12.13,2.265 -26.692,8.847 -25.367,-10.768c2.989,5.224 6.408,-1.427 9.41,-4.219c3.716,-3.457 12.509,-4.174 17.283,-5.562c4.608,-1.34 11.135,-4.383 15.932,-4.597c3.506,-0.156 9.207,4.035 11.25,1.127c7.558,-10.758 -27.36,-3.568 -30.514,-2.739c-6.93,1.821 -20.75,6.063 -22.861,14.112c-3.152,-4.81 -1.522,-12.932 -1.599,-18.576c-0.074,-5.439 -4.552,-8.488 -0.026,-13.297c-2.284,-0.462 -3.976,-1.593 -6.375,-1.377c2.567,-1.798 5.897,-1.922 5.5,-6.123c-3.174,-0.105 -6.572,0.672 -8.875,-1.627c2.47,-2.102 8,-0.46 8,-4.562c0,-1.918 -8.538,-1.552 -10.5,-3.438c2.265,-0.387 18.64,-0.854 16.125,-6.123c-3.014,-6.312 -15.192,4.396 -18.875,-1.877c7.186,-0.666 18.631,0.42 25.187,-3.3C325.761,733.895 341.331,720.169 344.813,729.127z"
|
||||
android:strokeColor="#000000" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M160.438,737.127c0.726,-0.258 1.461,-1.174 3,-0.75C162.438,736.627 161.438,736.877 160.438,737.127z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M123.313,761.25c7.675,0.866 15.349,1.731 23.024,2.598c3.334,0.376 4.321,14.507 5.1,18.402c3.142,15.709 -9.428,26.272 -16.621,39.235c-6.638,11.964 -21.567,15.892 -34.894,15.892c-7.624,0 -15.038,0.83 -18.359,-7.5c-2.55,-6.397 -0.152,-16.521 5,-21c-0.157,1.568 0.511,2.913 0.5,4.373c3.155,-5.297 7.952,-2.895 12.905,-3.705c7.833,-1.282 13.483,-3.006 20.438,1.344c2.335,1.46 2.588,-3.633 1.087,-4.952c-2.813,-2.473 -4.873,-3.85 -8.676,-3.751c-7.495,0.194 -14.904,0.518 -22.38,1.064c-0.392,-8.586 9.178,-18.335 9.626,-27.873c1.909,1.315 6.971,1.857 6.75,-2c-0.057,-0.988 -5.069,-4.182 -6.126,-5.25c0.747,-0.223 14.673,2.827 7.75,-3.25c4.56,1.721 10.91,4.372 14.126,-0.877c-5.984,-2.372 -21.022,-4.47 -19.626,-13.873C109.831,754.79 115.474,758.555 123.313,761.25z"
|
||||
android:strokeColor="#000000" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M207.813,145.5c13.659,-2.167 10.148,-31.902 -0.939,-28.458C195.919,120.445 195.34,143.716 207.813,145.5z"/>
|
||||
<path android:fillColor="#FA002A"
|
||||
android:pathData="M153.813,404c4.07,-9.431 -1.446,-6.081 -4.75,-14.123c-3.146,3.039 -3.12,6.077 -3.406,10.125C145.302,405.048 149.279,403.527 153.813,404z"
|
||||
android:strokeColor="#FA002A" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FA7800" android:pathData="M153.563,423.877c6.251,0 18.406,4.306 15.124,13c-10.103,-4.718 -12.256,-5.389 -21.5,0.873C142.788,433.468 150.714,427.534 153.563,423.877z"/>
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M63.04,523.027c0,0 -0.218,-0.135 -0.626,-0.388c-0.396,-0.275 -1.006,-0.624 -1.75,-1.019c-0.752,-0.344 -1.784,-0.754 -2.639,-0.543c-0.399,0.146 -0.739,0.391 -1.023,0.866c-0.273,0.477 -0.493,1.068 -0.642,1.752c-0.314,1.352 -0.434,2.944 -0.481,4.633c-0.041,1.693 -0.014,3.501 0.044,5.378c0.08,1.876 0.176,3.823 0.321,5.804c0.172,1.987 0.347,4.007 0.522,6.025c0.087,1.074 0.061,2.041 0.097,3.063l0.032,1.509c-0.014,0.506 -0.028,1.01 -0.042,1.513c-0.039,1.004 -0.042,2.003 -0.11,2.982c-0.074,0.978 -0.146,1.943 -0.218,2.893c-0.176,1.896 -0.382,3.725 -0.617,5.45c-0.247,1.726 -0.473,3.356 -0.738,4.854c-0.484,3 -0.973,5.482 -1.297,7.224c-0.333,1.74 -0.523,2.733 -0.523,2.733c-0.004,0.02 -0.022,0.032 -0.042,0.028c-0.019,-0.003 -0.031,-0.021 -0.028,-0.04c0,0 0.158,-1 0.434,-2.749c0.266,-1.75 0.673,-4.247 1.058,-7.256c0.215,-1.501 0.387,-3.135 0.576,-4.86c0.178,-1.726 0.322,-3.551 0.435,-5.438c0.041,-0.943 0.081,-1.903 0.122,-2.875c0.035,-0.971 0.006,-1.949 0.012,-2.937c-0.002,-0.493 -0.004,-0.988 -0.007,-1.485l-0.081,-1.506c-0.067,-0.991 -0.081,-2.053 -0.192,-2.993c-0.528,-3.981 -1.245,-8.004 -1.585,-11.82c-0.182,-1.909 -0.274,-3.771 -0.216,-5.555c0.063,-1.784 0.246,-3.49 0.727,-5.071c0.215,-0.79 0.596,-1.547 1.082,-2.221c0.484,-0.689 1.327,-1.19 2.125,-1.292c0.403,-0.029 0.785,-0.035 1.111,0.069c0.167,0.039 0.334,0.077 0.486,0.128c0.141,0.066 0.279,0.132 0.415,0.196c0.559,0.23 0.947,0.575 1.334,0.838c0.715,0.61 1.203,1.115 1.501,1.489c0.308,0.369 0.473,0.565 0.473,0.565c0.013,0.016 0.011,0.038 -0.004,0.051C63.07,523.034 63.053,523.035 63.04,523.027z"/>
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M248.966,363.022c0,0 -0.143,0.494 -0.503,1.273c-0.359,0.782 -0.916,1.855 -1.723,3.055c-0.405,0.601 -0.889,1.218 -1.399,1.872c-0.509,0.653 -1.099,1.304 -1.738,1.949c-0.647,0.64 -1.287,1.32 -2.011,1.949c-0.716,0.635 -1.425,1.292 -2.122,1.958c-0.734,0.628 -1.469,1.256 -2.191,1.874c-0.702,0.64 -1.497,1.145 -2.209,1.697c-0.724,0.541 -1.42,1.064 -2.129,1.491c-0.695,0.445 -1.352,0.866 -1.958,1.255c-0.596,0.403 -1.192,0.692 -1.693,0.991c-0.508,0.288 -0.95,0.539 -1.314,0.745c-0.729,0.413 -1.145,0.648 -1.145,0.648c-0.017,0.01 -0.039,0.004 -0.049,-0.013c-0.009,-0.017 -0.002,-0.039 0.014,-0.048c0,0 0.4,-0.261 1.102,-0.718c0.351,-0.228 0.775,-0.504 1.264,-0.822c0.48,-0.328 1.055,-0.649 1.616,-1.096c0.572,-0.428 1.192,-0.892 1.848,-1.383c0.669,-0.472 1.319,-1.039 1.993,-1.623c0.662,-0.596 1.402,-1.144 2.047,-1.823c0.663,-0.655 1.337,-1.321 2.01,-1.987c0.629,-0.711 1.245,-1.432 1.835,-2.153c0.6,-0.711 1.109,-1.488 1.658,-2.183c1.099,-1.39 2.111,-2.705 3.1,-3.735c0.503,-0.507 0.945,-1.003 1.384,-1.398c0.422,-0.41 0.821,-0.743 1.152,-1.015c0.664,-0.544 1.106,-0.806 1.106,-0.806c0.019,-0.011 0.042,-0.005 0.053,0.014C248.968,363.001 248.969,363.013 248.966,363.022z"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M337.563,739.377c3.495,12.533 8.209,19.954 16.625,29.873"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M360.188,792.75c-5.732,-1.196 -9.619,0.341 -15.125,2"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M137.438,771.5c-0.549,11.256 -4.222,15.88 -10.5,25.377"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M113.688,816.877c-6.847,2.125 -12.803,2.576 -20,3"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M250.813,403.876c0,0 -0.246,-0.283 -0.62,-0.8c-0.377,-0.515 -0.88,-1.265 -1.434,-2.183c-1.104,-1.838 -2.333,-4.381 -3.33,-7.011c-0.861,-2.711 -1.486,-5.505 -1.76,-7.644c-0.137,-1.07 -0.205,-1.97 -0.226,-2.606c-0.025,-0.635 -0.006,-1.006 -0.006,-1.006s0.232,0.3 0.578,0.838c0.35,0.537 0.813,1.313 1.313,2.254c0.999,1.884 2.071,4.453 2.907,7.105c0.993,2.631 1.755,5.351 2.143,7.459c0.193,1.054 0.313,1.949 0.37,2.584C250.81,403.502 250.813,403.876 250.813,403.876z"/>
|
||||
<path android:fillColor="#FA002A" android:pathData="M275.813,259.75c2.225,-4.67 23.463,-28.569 21.125,-31C291.535,223.132 265.829,261.555 275.813,259.75z"/>
|
||||
<path android:fillColor="#FA002A" android:pathData="M279.188,221.626c1.113,-2.504 7.331,-6.185 6.625,-1.376c-0.468,3.186 -6.914,7.801 -8.875,10.5c-3.701,5.094 -8.024,19.159 -14.125,20.5C260.807,241.297 272.867,228.612 279.188,221.626z"/>
|
||||
<path android:fillColor="#FA002A" android:pathData="M271.688,218c5.358,-5.668 10.277,-1.495 3.375,3.626C270.036,225.356 266.084,221.832 271.688,218z"/>
|
||||
<path android:fillColor="#FA002A" android:pathData="M262.563,221c0.733,-2.185 11.817,-15.949 13.875,-10.25C277.19,212.836 264.699,221.63 262.563,221z"/>
|
||||
<path android:fillColor="#FA002A" android:pathData="M257.063,218.5c1.981,-3.302 7.674,-15.482 12.375,-10.75C265.533,210.375 260.92,220.092 257.063,218.5z"/>
|
||||
<path android:fillColor="#1300C6" android:pathData="M255.938,220.5c5.788,4.081 13.771,2.381 13.25,9.626c-0.026,0.37 -8.12,15.954 -8.625,16.374c-3.659,3.046 -14.066,-2.89 -13.62,-7.8C247.461,232.996 250.542,223.237 255.938,220.5z"/>
|
||||
<path android:fillColor="#FA002A" android:pathData="M289.938,224.626c0.333,5.91 -7.832,12.249 -11.391,16.565c-3.909,4.741 -5.481,12.765 -11.234,15.309C267.316,249.658 281.768,220.788 289.938,224.626z"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M103.438,326.75c3.49,2.737 7.772,4.977 8.25,9.75c-6.138,-1.255 -25.76,-11.779 -24.25,-18.374C93.332,318.604 98.615,323.664 103.438,326.75z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M56.688,385.126c-2.159,1.787 -18.553,5.835 -19.124,2.25C36.463,380.464 55.263,383.531 56.688,385.126z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A60053"
|
||||
android:pathData="M78.813,394.25c-2.641,0.338 1.361,-0.095 2.25,0C80.313,394.25 79.563,394.25 78.813,394.25z"
|
||||
android:strokeColor="#A60053" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FFFF00" android:pathData="M53.563,463.25c5.965,4.16 13.117,6.081 18.874,10.5c-5.903,1.747 -17.396,-4.874 -23.671,-6.508c-9.498,-2.473 -17.147,-1.08 -26.453,-0.742c1.038,-5.998 14.309,-4.932 19.44,-4.529c12.097,0.948 3.525,-14.243 11.06,-13.971C53.063,453.083 53.313,458.167 53.563,463.25z"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M232.938,503.877c-1.235,9.531 -4.108,19.102 -5.75,28.623c-0.8,4.638 -0.996,10.029 -2.5,14.5c-0.903,2.686 -2.414,7.929 -5.25,1.127c-2.6,-6.236 4.209,-23.522 5.433,-29.439C225.71,514.622 229.626,495.624 232.938,503.877z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M240.188,505.5c0.49,18.999 -8.947,34.421 -10.5,52c0,-9.837 1.354,-19.467 1.851,-29.383C231.729,524.29 234.769,501.56 240.188,505.5z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M133.938,509.127c5.188,1.984 12.112,3.215 16,7.25c-4.437,3.969 -12.418,-0.811 -17.147,-2.577c-6.249,-2.335 -13.143,-3.05 -19.729,-4.385c-6.679,-1.354 -8.324,-4.569 -0.247,-4.165C120.03,505.61 126.891,507.542 133.938,509.127z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M247.313,506.377c0.537,4.927 -3.634,26.399 -7.125,18.5C238.668,521.438 242.461,503.3 247.313,506.377z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M127.188,517.127c0.583,0.541 1.167,1.082 1.75,1.623c-5.068,1.583 -18.704,0.165 -21.624,-4.373C113.616,511.233 121.182,514.888 127.188,517.127z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M119.813,524.877c0.844,6.79 -9.675,3.093 -10,-2.25C113.197,523.043 117.344,522.659 119.813,524.877z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M271.688,572c0.773,8.227 1.547,16.453 2.32,24.68c0.567,6.04 1.542,1.898 3.805,5.447c1.54,2.416 5.701,3.513 2.75,6.873c-0.929,-1.015 -2.388,-1.538 -3.375,-2.5c-0.857,3.351 0.093,5.071 3.375,4.25c-0.667,0.459 -1.333,0.918 -2,1.377c2.004,2.838 7.75,5.175 7.75,8.437c0,2.019 -8.258,4.788 -0.991,7.484c6.181,2.293 2.54,8.87 -2.073,5.052c-3.792,-3.139 -8.391,-7.059 -9.295,-11.989c-1.661,-9.063 -12.095,-62.388 -5.016,-65.983C269.854,560.751 270.771,566.376 271.688,572z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M143.813,612.377c-4.079,5.317 -11.072,6.564 -15.924,10.877c-4.354,3.87 -10.099,12.56 -15.576,8.996c2.428,-6.34 8.99,-11.623 14.68,-15.012c5.541,-3.3 4.569,-11.064 9.32,-12.861c0.277,2.033 1.548,3.96 1.75,6c2.81,-2.87 2.101,-13.25 4.375,-13.25C145.29,597.127 143.813,609.424 143.813,612.377z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M172.313,655c-0.79,4.546 -32.291,7.431 -35.626,4.627c5.006,-5.458 5.655,-5.415 15.571,-6.095C156.253,653.259 170.864,649.868 172.313,655z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M321.563,661c-2.592,3.908 -5.184,7.815 -7.775,11.724c-1.862,2.807 3.057,4.037 2.15,6.026c-2.131,4.678 -18.698,5.782 -23.62,7.209c-4.669,1.353 -2.869,-4.493 -1.093,-6.432c2.537,-2.768 8.138,-2.279 11.449,-4.583c7.477,-5.201 12.119,-11.943 18.014,-18.817C319.493,658.676 321.263,658.806 321.563,661z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M155.438,665.25c-0.743,5.374 -13.75,7.711 -13.75,1.375c0,-6.806 12.909,-2.51 16.25,-1.375C157.104,665.25 156.271,665.25 155.438,665.25z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#A89187"
|
||||
android:pathData="M309.313,690.127c-1.826,1.37 -12.029,4.19 -10.5,-0.25C299.974,686.507 308.207,687.872 309.313,690.127z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M344.813,692c-11.668,5.957 -22.197,12.215 -34.367,17.402c-4.347,1.853 -34.706,13.177 -37.07,7.036c-1.405,-3.65 14.092,-3.136 16.281,-3.277c5.316,-0.343 14.506,-2.691 19.035,-5.502c11.026,-6.844 23.721,-13.549 35.496,-19.159C344.766,689.629 344.917,690.744 344.813,692z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M102.563,714.5c17.159,-2.251 32.889,15.131 49.874,4.127c-0.667,0.75 -1.333,1.5 -2,2.25c5.008,0.152 14.498,-0.661 19.376,-3.127c-1.728,2.915 0.97,9.614 -1.861,10.357c-4.976,1.305 -9.879,2.143 -15.027,2.143c-4.826,0 -9.651,0 -14.477,0c-4.904,0 -8.795,-2 -13.506,-2c-6.384,0 -15.344,-4.258 -21.505,-7.373c-4.171,-2.109 -9.427,-1.75 -8.754,-7.307C95.183,709.453 100.844,712.602 102.563,714.5z"
|
||||
android:strokeColor="#000000" android:strokeLineJoin="round" android:strokeWidth="1.126"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="M88.849,22.891C88.601,17.992 89.917,6.615 83,5.75c-0.421,-0.053 -0.896,0.28 -1.087,0.633c-1.324,2.432 -0.769,4.769 -0.324,7.343c0.67,3.875 0.502,7.938 0.817,11.854c0.632,7.834 2.416,15.559 2.938,23.42c0.226,3.407 5.161,3.444 5.313,0C91.046,40.131 89.292,31.678 88.849,22.891z"/>
|
||||
<path android:fillColor="#CBFF00" android:pathData="M207.5,283a5.75,5.5 0,1 0,11.5 0a5.75,5.5 0,1 0,-11.5 0z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:height="64dp" android:viewportHeight="401.294"
|
||||
android:viewportWidth="401.294" android:width="64dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M115.342,188.015c-7.025,0 -12.741,5.716 -12.741,12.741s5.716,12.741 12.741,12.741c7.026,0 12.742,-5.716 12.742,-12.741S122.368,188.015 115.342,188.015zM115.342,205.497c-2.614,0 -4.741,-2.127 -4.741,-4.741s2.127,-4.741 4.741,-4.741c2.615,0 4.742,2.127 4.742,4.741S117.957,205.497 115.342,205.497z"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="M397.294,164.141c2.209,0 4,-1.791 4,-4V89.215c0,-2.209 -1.791,-4 -4,-4h-33.219c-2.209,0 -4,1.791 -4,4v70.926c0,2.209 1.791,4 4,4h12.61v8.999h-36.205c-6.008,-4.585 -13.401,-7.094 -20.986,-7.094c-17.762,0 -32.434,13.455 -34.376,30.706h-3.421v-9.79c0,-2.209 -1.791,-4 -4,-4h-28.993l-6.173,-11.125c-0.705,-1.271 -2.044,-2.059 -3.498,-2.059h-39.513v-6.524h16.196c2.209,0 4,-1.791 4,-4V73.281c0,-2.209 -1.791,-4 -4,-4h-40.266c-2.209,0 -4,1.791 -4,4v85.972c0,2.209 1.791,4 4,4h16.069v6.524h-38.468c-2.209,0 -4,1.791 -4,4v22.974h-4.52c-1.97,-15.047 -14.865,-26.705 -30.44,-26.705c-10.313,0 -19.703,5.125 -25.326,13.331c-5.623,-8.206 -15.012,-13.331 -25.326,-13.331H44.653v-5.905h6.324c2.209,0 4,-1.791 4,-4V89.215c0,-2.209 -1.791,-4 -4,-4H17.758c-2.209,0 -4,1.791 -4,4v70.926c0,2.209 1.791,4 4,4h6.325v5.905H4c-2.209,0 -4,1.791 -4,4v53.419c0,2.209 1.791,4 4,4h20.083v5.905h-6.325c-2.209,0 -4,1.791 -4,4v70.927c0,2.209 1.791,4 4,4h33.219c2.209,0 4,-1.791 4,-4v-70.927c0,-2.209 -1.791,-4 -4,-4h-6.325v-5.905h18.789c10.313,0 19.703,-5.125 25.326,-13.331c5.624,8.205 15.013,13.331 25.326,13.331c15.578,0 28.476,-11.663 30.441,-26.714h4.518v22.765c0,2.209 1.791,4 4,4h38.468v6.524h-16.069c-2.209,0 -4,1.791 -4,4v85.972c0,2.209 1.791,4 4,4h40.266c2.209,0 4,-1.791 4,-4V242.04c0,-2.209 -1.791,-4 -4,-4h-16.196v-6.524h39.513c1.454,0 2.792,-0.789 3.498,-2.059l6.173,-11.125h28.993c2.209,0 4,-1.791 4,-4v-9.581h3.447c2.037,17.151 16.66,30.497 34.35,30.497c7.581,0 14.972,-2.507 20.978,-7.087h36.214v9.209h-12.61c-2.209,0 -4,1.791 -4,4v70.927c0,2.209 1.791,4 4,4h33.219c2.209,0 4,-1.791 4,-4v-70.927c0,-2.209 -1.791,-4 -4,-4h-12.609v-9.209h12.435c2.209,0 4,-1.791 4,-4V177.14c0,-2.209 -1.791,-4 -4,-4h-12.435v-8.999H397.294zM199.521,155.253v-13.221h12.196v13.221H199.521zM191.521,112.267h-12.069V98.502h12.069V112.267zM199.521,98.502h12.196v13.765h-12.196V98.502zM191.521,120.267v13.765h-12.069v-13.765H191.521zM199.521,120.267h12.196v13.765h-12.196V120.267zM211.717,90.502h-12.196V77.281h12.196V90.502zM191.521,77.281v13.221h-12.069V77.281H191.521zM179.451,155.253v-13.221h12.069v13.221H179.451zM24.689,223.466v-45.419h22.038v45.419H24.689zM54.727,178.046h6.641v45.419h-6.641V178.046zM46.977,128.678v9.956H21.758v-9.956H46.977zM21.758,120.678v-9.957h25.219v9.957H21.758zM46.977,93.215v9.507H21.758v-9.507H46.977zM21.758,146.634h25.219v9.507H21.758V146.634zM32.083,164.141h4.569v5.905h-4.569V164.141zM8,178.046h8.689v45.419H8V178.046zM21.758,272.834v-9.956h25.219v9.956H21.758zM46.977,280.834v9.956H21.758v-9.956H46.977zM21.758,308.298v-9.507h25.219v9.507H21.758zM46.977,254.878H21.758v-9.507h25.219V254.878zM36.652,237.371h-4.569v-5.905h4.569V237.371zM69.368,222.667v-43.822c6.952,1.876 12.703,6.983 15.31,13.918v15.987C82.071,215.684 76.32,220.791 69.368,222.667zM114.093,223.466c-9.664,0 -18.222,-6.091 -21.415,-15.184v-15.05c3.193,-9.094 11.751,-15.185 21.415,-15.185c12.522,0 22.709,10.188 22.709,22.71C136.802,213.278 126.615,223.466 114.093,223.466zM191.521,246.04v13.221h-12.069V246.04H191.521zM199.521,289.027h12.196v13.765h-12.196V289.027zM191.521,302.792h-12.069v-13.765h12.069V302.792zM199.521,281.027v-13.765h12.196v13.765H199.521zM191.521,281.027h-12.069v-13.765h12.069V281.027zM179.451,310.792h12.069v13.221h-12.069V310.792zM199.521,324.012v-13.221h12.196v13.221H199.521zM211.717,246.04v13.221h-12.196V246.04H211.717zM157.052,177.778h11.046v45.739h-11.046V177.778zM236.678,223.516h-60.58v-45.739h60.58l5.311,9.571v26.596L236.678,223.516zM273.696,210.332h-23.707v-19.371h23.707V210.332zM368.075,272.834v-9.956h25.219v9.956H368.075zM393.294,280.834v9.956h-25.219v-9.956H393.294zM368.075,308.298v-9.507h25.219v9.507H368.075zM393.294,254.878h-25.219v-9.507h25.219V254.878zM292.892,200.647c0,-8.817 4.315,-16.641 10.94,-21.484v42.968C297.207,217.288 292.892,209.463 292.892,200.647zM319.493,227.248c-2.663,0 -5.234,-0.398 -7.661,-1.129v-50.944c2.428,-0.731 4.998,-1.129 7.661,-1.129c6.217,0 12.267,2.193 17.036,6.175c0.248,0.208 0.521,0.368 0.803,0.506v39.849c-0.29,0.141 -0.57,0.304 -0.821,0.513C331.747,225.061 325.703,227.248 319.493,227.248zM393.119,220.161h-47.787V181.14h47.787V220.161zM393.294,128.678v9.956h-25.219v-9.956H393.294zM368.075,120.678v-9.957h25.219v9.957H368.075zM393.294,93.215v9.507h-25.219v-9.507H393.294zM368.075,146.634h25.219v9.507h-25.219V146.634z"/>
|
||||
</vector>
|
|
@ -1,170 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="maroon_200">#FFb73d2a</color>
|
||||
<color name="maroon_500">#FF800000</color>
|
||||
<color name="maroon_700">#FF4f0000</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
|
@ -1,5 +0,0 @@
|
|||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
|
@ -1,3 +0,0 @@
|
|||
<resources>
|
||||
<string name="app_name">PeopleInSpace</string>
|
||||
</resources>
|
|
@ -1,3 +0,0 @@
|
|||
<resources>
|
||||
|
||||
</resources>
|
|
@ -1,23 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.PeopleInSpace" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<item name="colorPrimary">@color/maroon_500</item>
|
||||
<item name="colorPrimaryVariant">@color/maroon_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.PeopleInSpace.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.MyApplication.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="Theme.MyApplication.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
</resources>
|
|
@ -1,6 +0,0 @@
|
|||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="80dp"
|
||||
android:minHeight="80dp"
|
||||
android:targetCellWidth="4"
|
||||
android:targetCellHeight="2"
|
||||
android:resizeMode="none" />
|
|
@ -1,10 +0,0 @@
|
|||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="80dp"
|
||||
android:minHeight="80dp"
|
||||
android:targetCellWidth="2"
|
||||
android:targetCellHeight="2"
|
||||
android:minResizeWidth="40dp"
|
||||
android:minResizeHeight="40dp"
|
||||
android:maxResizeWidth="120dp"
|
||||
android:maxResizeHeight="120dp"
|
||||
android:resizeMode="horizontal|vertical" />
|
|
@ -1,32 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider.getApplicationContext
|
||||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.peopleinspace.di.appModule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.test.check.checkModules
|
||||
import org.koin.test.mock.MockProviderRule
|
||||
import org.mockito.Mockito
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class TestKoinGraph {
|
||||
private val context = getApplicationContext<Context>()
|
||||
|
||||
@get:Rule
|
||||
val mockProvider = MockProviderRule.create { clazz ->
|
||||
Mockito.mock(clazz.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `checking koin modules`() {
|
||||
initKoin {
|
||||
androidContext(context)
|
||||
modules(appModule)
|
||||
}.checkModules()
|
||||
}
|
||||
}
|
1
backend/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
/build
|
|
@ -1,37 +0,0 @@
|
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id("kotlin-platform-jvm")
|
||||
application
|
||||
kotlin("plugin.serialization")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
with(Deps.Kotlinx) {
|
||||
implementation(serializationCore) // JVM dependency
|
||||
implementation(coroutinesCore)
|
||||
}
|
||||
|
||||
with(Deps.Ktor) {
|
||||
implementation(serverCore)
|
||||
implementation(serverNetty)
|
||||
implementation(websockets)
|
||||
implementation(serverContentNegotiation)
|
||||
implementation(json)
|
||||
}
|
||||
|
||||
with(Deps.Log) {
|
||||
implementation(logback)
|
||||
}
|
||||
|
||||
implementation(project(":common"))
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("ServerKt")
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
runtime: java11
|
||||
entrypoint: java -Xmx64m -jar server.jar
|
|
@ -1,80 +0,0 @@
|
|||
val personImages = mapOf(
|
||||
"Chris Cassidy" to "https://www.nasa.gov/sites/default/files/styles/side_image/public/thumbnails/image/9368855148_f79942efb7_o.jpg?itok=-w5yoryN",
|
||||
"Anatoly Ivanishin" to "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Anatoli_Ivanishin_2011.jpg/440px-Anatoli_Ivanishin_2011.jpg",
|
||||
"Ivan Vagner" to "http://www.spacefacts.de/more/cosmonauts/photo/vagner_ivan_3.jpg",
|
||||
"Sergey Ryzhikov" to "https://spaceflight101.com/iss-expedition-50/wp-content/uploads/sites/118/2016/11/jsc2016e105228.jpg",
|
||||
"Kate Rubins" to "https://spaceflight101.com/iss-expedition-49/wp-content/uploads/sites/110/2016/09/26720141242_be992e9a20_o-768x1152.jpg",
|
||||
"Sergey Kud-Sverchkov" to "https://www.esa.int/var/esa/storage/images/esa_multimedia/images/2014/08/sergey_kud-sverchkov/14716838-1-eng-GB/Sergey_Kud-Sverchkov_pillars.jpg",
|
||||
"Mike Hopkins" to "https://pbs.twimg.com/media/Em5EbQOVEAAdZ0h?format=jpg&name=medium",
|
||||
"Victor Glover" to "https://pbs.twimg.com/media/Em5EbSnUYAEAgyl?format=jpg&name=medium",
|
||||
"Shannon Walker" to "https://pbs.twimg.com/media/Em5EbQPVoAATIx8?format=jpg&name=medium",
|
||||
"Soichi Noguchi" to "https://pbs.twimg.com/media/Em5EbSoVcAA3R2F?format=jpg&name=medium",
|
||||
"Mark Vande Hei" to "https://www.esa.int/var/esa/storage/images/esa_multimedia/images/2016/09/mark_vande_hei/16121862-1-eng-GB/Mark_Vande_Hei_pillars.jpg",
|
||||
"Oleg Novitskiy" to "https://spaceflight101.com/iss-expedition-50/wp-content/uploads/sites/118/2016/11/jsc2016e165868.jpg",
|
||||
"Pyotr Dubrov" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010288.jpg",
|
||||
"Shane Kimbrough" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010824.jpg",
|
||||
"Megan McArthur" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010823.jpg",
|
||||
"Akihiko Hoshide" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010825.jpg",
|
||||
"Thomas Pesquet" to "https://www.nasa.gov/sites/default/files/styles/full_width_feature/public/thumbnails/image/jsc2021e010826.jpg",
|
||||
"Nie Haisheng" to "http://www.spacefacts.de/more/taikonauts/photo/nie_haisheng_1.jpg",
|
||||
"Liu Boming" to "http://www.april12.eu/chinaastron/photo/liuboming.jpg",
|
||||
"Tang Hongbo" to "https://upload.wikimedia.org/wikipedia/commons/3/35/Tang_Hongbo.png",
|
||||
"Chris Sembroski" to "https://assets.newatlas.com/dims4/default/9fd3579/2147483647/strip/true/crop/828x1037+0+0/resize/767x960!/quality/90/?url=http%3A%2F%2Fnewatlas-brightspot.s3.amazonaws.com%2F20%2F75%2F205f2e594adf9492c0c0275e2ec6%2Fi4-chris-s-129a9147.jpg",
|
||||
"Hayley Arceneaux" to "https://pbs.twimg.com/media/E_RKiXIXEAECmiP.jpg",
|
||||
"Sian Procto" to "https://pbs.twimg.com/media/E_VcnA2XsAgp4r-.jpg",
|
||||
"Jared Isaacman" to "https://pbs.twimg.com/media/E_OTuR-XsAEhsln.jpg",
|
||||
"Anton Shkaplerov" to "https://alchetron.com/cdn/anton-shkaplerov-799e7545-54ae-4145-83d1-18f906d22b1-resize-750.jpeg",
|
||||
"Klim Shipenko" to "https://upload.wikimedia.org/wikipedia/pt/f/f9/Shipenko_klim.jpg",
|
||||
"Yulia Pereslid" to "https://metro.co.uk/wp-content/uploads/2021/10/PRI_203475964.jpg?quality=90&strip=all&zoom=1&resize=540%2C810",
|
||||
"Zhai Zhigang" to "https://dingyue.ws.126.net/2021/0701/aaa7e1e7j00qvkk13001hc000hs00lzg.jpg",
|
||||
"Wang Yaping" to "https://alchetron.com/cdn/wang-yaping-2f869150-f66f-4884-b638-62ff678084a-resize-750.jpeg",
|
||||
"Ye Guangfu" to "https://upload.wikimedia.org/wikipedia/commons/a/a2/Ye_Guangfu_in_2021.jpg",
|
||||
"Raja Chari" to "https://www.spacex.com/static/images/crew-3/portraits/SPACEX_Crew-3_RajaChari.jpg",
|
||||
"Tom Marshburn" to "https://www.spacex.com/static/images/crew-3/portraits/SPACEX_Crew-3_TomMarshburn.jpg",
|
||||
"Kayla Barron" to "https://www.spacex.com/static/images/crew-3/portraits/SPACEX_Crew-3_KaylaBarron.jpg",
|
||||
"Matthias Maurer" to "https://www.spacex.com/static/images/crew-3/portraits/SPACEX_Crew-3_MathiasMaurer.jpg",
|
||||
"Alexander Misurkin" to "https://spaceflight101.com/iss/wp-content/uploads/sites/37/2017/08/36468940815_70ef48a8b6_o-768x1152.jpg",
|
||||
"Yusaku Maezawa" to "https://pbs.twimg.com/media/FD599ihaUAAyjC7?format=jpg",
|
||||
"Yozo Hirano" to "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS1lYvHXPvl-eG3yX5MWAtvTaxkoiISJ5KXrg&usqp=CAU",
|
||||
)
|
||||
|
||||
val personBios = mapOf(
|
||||
"Chris Cassidy" to "Christopher John \"Chris\" Cassidy (born January 4, 1970, in Salem, Massachusetts) is a NASA astronaut and United States Navy SEAL. Chris Cassidy achieved the rank of captain in the U.S. Navy. He was the Chief of the Astronaut Office at NASA from July 2015 until June 2017.",
|
||||
"Anatoly Ivanishin" to "Anatoli Alekseyevich Ivanishin (Russian: Анатолий Алексеевич Иванишин; born 15 January 1969) is a Russian cosmonaut. His first visit to space was to the International Space Station on board the Soyuz TMA-22 spacecraft as an Expedition 29 / Expedition 30 crew member, launching in November 2011 and returning in April 2012. Ivanishin was the Commander of the International Space Station for Expedition 49.",
|
||||
"Ivan Vagner" to "Ivan Viktorovich Vagner (born 10 July 1985) is a Russian engineer and cosmonaut who was selected in October 2010. He graduated from the Baltic State Technical University in 2008, before working as an engineer for RKK Energia.\n\nHe began his first spaceflight in April 2020 as a Flight Engineer on Soyuz MS-16 and Expedition 62/63.",
|
||||
"Sergey Ryzhikov" to "Sergey Nikolayevich Ryzhikov (Russian: Сергей Николаевич Рыжиков; born on August 19, 1974), lieutenant colonel of Russian Air Force, is a Russian cosmonaut, selected in 2006. Ryzhikov launched on his first spaceflight on board the Soyuz MS-02 spacecraft. He spent approximately six months on board the International Space Station taking part in Expedition 49/50, returning to Earth on April 10, 2017",
|
||||
"Kate Rubins" to "Kathleen Hallisey \"Kate\" Rubins (born October 14, 1978) is a NASA astronaut. She became the 60th woman to fly in space when she launched on a Soyuz spacecraft to the International Space Station on July 7, 2016. She returned to Earth on October 30, 2016 aboard a Soyuz. She was a crew member of Expedition 48 and Expedition 49 of the International Space Station.",
|
||||
"Sergey Kud-Sverchkov" to "Sergey Vladimirovich Kud-Sverchkov was born on August 23, 1983 at the Baikonur Cosmodrome in the Kazakh Soviet Socialist Republic. Sergey Kud-Sverchkov is married and father of one daughter. Since April 2010, he is a Russian Cosmonaut of the Russian Space Agency Roscosmos. He is currently in space.",
|
||||
"Mike Hopkins" to "Michael Scott Hopkins was born on December 28, 1968 in Lebanon, Missouri but grew up on a farm in Richland, Missouri in a United Methodist family. After graduating from the School of the Osage High School in Lake of the Ozarks, Missouri, in 1987, he entered the University of Illinois at Urbana-Champaign. While there, he played defensive back for the Illinois Fighting Illini football team. He graduated in 1991 with a Bachelor of Science degree in aerospace engineering. He followed his undergraduate studies with a Master of Science degree in aerospace engineering from Stanford University, which he earned in 1992.",
|
||||
"Victor Glover" to "Victor Jerome Glover (born April 30, 1976) is a NASA astronaut of the class of 2013 and Pilot on the first operational flight of the SpaceX Crew Dragon to the International Space Station. Glover is a commander in the U.S. Navy where he pilots an F/A-18, and a graduate of the U.S. Air Force Test Pilot School.",
|
||||
"Shannon Walker" to "Shannon Walker (born 4 June 1965 in Houston, Texas) is an American physicist and a NASA astronaut selected in 2004. She launched on her first mission into space on 25 June 2010 onboard Soyuz TMA-19 and spent over 163 days in space.\n\nShe returned to space for her second long duration mission on 15 November 2020, onboard SpaceX Crew-1, the first operational flight of SpaceX's Crew Dragon spacecraft.",
|
||||
"Soichi Noguchi" to "Soichi Noguchi (野口 聡一, Noguchi Sōichi, born 15 April 1965 in Yokohama, Japan) is a Japanese aeronautical engineer and JAXA astronaut. His first spaceflight was as a Mission Specialist aboard STS-114 on 26 July 2005 for NASA's first \"return to flight\" Space Shuttle mission after the Columbia disaster. He was also in space as part of the Soyuz TMA-17 crew and Expedition 22 to the International Space Station (ISS), returning to Earth on 2 June 2010. He is the fifth Japanese astronaut to fly in space and the fourth to fly on the space shuttle. His third flight is onboard the Dragon 2 capsule for the SpaceX Crew-1 mission which launched successfully on November 15, 2020. This makes him one of only three astronauts to fly on three different launch systems.",
|
||||
"Mark Vande Hei" to "Mark Thomas Vande Hei (born November 10, 1966) is a retired United States Army officer and NASA astronaut who served as a flight Engineer for Expedition 53 and 54 on the International Space Station.",
|
||||
"Oleg Novitskiy" to "Oleg Viktorovich Novitskiy (Russian: Олег Викторович Новицкий; born October 12, 1971 in Červień, Belarus) is a former Lieutenant Colonel in the Russian Air Force who logged over 700 hours of flight time and was awarded for bravery. He is currently serving as a Russian cosmonaut with Roskosmos and has participated in multiple expeditions, during which he has spent over 340 days in space.",
|
||||
"Pyotr Dubrov" to "Pyotr Valerievich Dubrov (Russian: Пётр Валерьевич Дубров; born 30 January 1978) is a Russian engineer and cosmonaut selected by Roscosmos in 2012.",
|
||||
"Shane Kimbrough" to "Robert Shane Kimbrough (born June 4, 1967) is a retired United States Army officer, and a NASA astronaut. He was part of the first group of candidates selected for NASA astronaut training following the Space Shuttle Columbia disaster. Kimbrough is a veteran of two spaceflights, the first being a Space Shuttle flight, and the second being a six-month mission to the ISS on board a Russian Soyuz craft. He was the commander of the International Space Station for Expedition 50, and returned to Earth in April 2017.",
|
||||
"Megan McArthur" to "Katherine Megan McArthur (born August 30, 1971) is an American oceanographer, engineer, and a National Aeronautics and Space Administration (NASA) astronaut. She has served as a Capsule Communicator (CAPCOM) for both the space shuttle and space station. Megan McArthur has flown one space shuttle mission, STS-125. She is known as the last person to be hands on with the Hubble Space Telescope via the Canadarm. McArthur has served in a number of positions including working in the Shuttle Avionics Laboratory (SAIL).",
|
||||
"Akihiko Hoshide" to "Akihiko Hoshide (星出 彰彦, Hoshide Akihiko, born December 28, 1968) is a Japanese engineer and JAXA astronaut. On August 30, 2012, Hoshide became the third Japanese astronaut to walk in space.",
|
||||
"Thomas Pesquet" to "Thomas Gautier Pesquet (French pronunciation: \u200B[tɔma gotje pɛskɛ]; born 27 February 1978 in Rouen) is a French aerospace engineer, pilot, and European Space Agency astronaut. Pesquet was selected by ESA as a candidate in May 2009, and he successfully completed his basic training in November 2010. From November 2016 to June 2017, Pesquet was part of Expedition 50 and Expedition 51 as a flight engineer.Pesquet returned to space in April 2021 on board the SpaceX Crew Dragon for a second six-month stay on the ISS.",
|
||||
"Nie Haisheng" to "Nie Haisheng (simplified Chinese: 聂海胜; traditional Chinese: 聶海勝; pinyin: Niè Hǎishèng; born 13 October 1964) is a Chinese military pilot and CNSA astronaut.",
|
||||
"Liu Boming" to "Liu Boming (simplified Chinese: 刘伯明; traditional Chinese: 劉伯明; pinyin: Liú Bómíng; born September 1966) is a Chinese pilot selected as part of the Shenzhou program. A fighter pilot in the People's Liberation Army Air Force, he was selected to be an CNSA member in 1998.",
|
||||
"Tang Hongbo" to "Tang Hongbo (Chinese: 汤洪波; born October 1975) is a Chinese pilot selected as part of the Shenzhou program.",
|
||||
"Chris Sembroski" to "Christopher Sembroski (born August 28, 1979) is an American data engineer, Air Force veteran, and commercial astronaut, currently living in Everett, Washington, United States. He is a Lockheed Martin employee and private astronaut for the Inspiration4 mission.The position was given to Sembroski after a friend had declined the prize, transferring it to Sembroski.Sembroski has long had an interest in space, being an amateur astronomer and rocketeer.",
|
||||
"Hayley Arceneaux" to "Hayley Arceneaux is a St. Jude Children's Research Hospital employee, bone cancer survivor and private astronaut who is now a physician assistant; she joined billionaire Jared Isaacman on SpaceX's first private spaceflight Inspiration4 launched on September 15, 2021. At age 29, Arceneaux became the youngest American in space.",
|
||||
"Sian Procto" to "Sian Hayley Proctor is an American geology professor, science communicator, and commercial astronaut. She was selected as the pilot for the Inspiration4 private orbital spaceflight conducted on 15th September 2021, aboard a SpaceX-operated Crew Dragon space capsule.She is a geology professor at South Mountain Community College in Arizona.She is also a major in the Civil Air Patrol where she serves as the aerospace education officer for its Arizona Wing.",
|
||||
"Jared Isaacman" to "Jared Isaacman (born February 11, 1983) is an American billionaire businessman, pilot and amateur astronaut. He is the founder and CEO of Shift4 Payments, a payment processor. Isaacman served as commander of the SpaceX flight Inspiration4, launched September 15, 2021",
|
||||
"Anton Shkaplerov" to "Anton Nikolaevich Shkaplerov (Russian: Антон Николаевич Шкаплеров; born 20 February 1972) is a Russian cosmonaut. He is a veteran of four spaceflights and is a former Commander of the International Space Station.",
|
||||
"Klim Shipenko" to "Klim Alekseevich Shipenko (Russian: Клим Алексеевич Шипенко; born 16 June 1983) is a Russian film director, screenwriter, actor and producer. In 2021, Shipenko is planning to shoot portions of a science fiction film aboard the International Space Station. It is to be the second narrative film shot in space, and first feature film shot in space.",
|
||||
"Yulia Pereslid" to "Yulia Sergeevna Peresild (Russian: Ю́лия Серге́евна Переси́льд; born 5 September 1984) is a Russian stage and film actress. ",
|
||||
"Zhai Zhigang" to "Zhai Zhigang (born October 10, 1966) is a major general of the People's Liberation Army Strategic Support Force (PLASSF) in active service as a People's Liberation Army Astronaut Corps (PLAAC) taikonaut. During the Shenzhou 7 mission in 2008, he became the first Chinese citizen to carry out a spacewalk. He was a People's Liberation Army Air Force (PLAAF) fighter pilot.",
|
||||
"Wang Yaping" to "Colonel Wang Yaping (born 27 January 1980) is a Chinese military pilot and astronaut. Wang was the second female astronaut selected to the People's Liberation Army Astronaut Corps, and the second Chinese woman in space.",
|
||||
"Ye Guangfu" to "Colonel Ye Guangfu (Chinese: 叶光富; born 1 September 1980[1]) is a Chinese People's Liberation Army Astronaut Corps (PLAAC) astronaut selected as part of the Shenzhou program.",
|
||||
"Raja Chari" to "Raja Jon Vurputoor \"Grinder\" Chari (born June 24, 1977; Colonel, United States Air Force) is an American test pilot and NASA astronaut. He is a graduate of the U.S. Air Force Academy, Massachusetts Institute of Technology, and U.S. Naval Test Pilot School, and has over 2,000 flying hours.",
|
||||
"Tom Marshburn" to "Thomas Henry \"Tom\" Marshburn (born August 29, 1960) is an American physician and a NASA astronaut. He is a veteran of two spaceflights to the International Space Station.",
|
||||
"Kayla Barron" to "Kayla Jane Barron (born September 19, 1987; LCDR, USN) is an American submarine warfare officer, engineer and NASA astronaut.",
|
||||
"Matthias Maurer" to "Matthias Josef Maurer (born 18 March 1970 in St. Wendel, Saarland) is a German European Space Agency astronaut and materials scientist, who was selected in 2015 to take part in space training.",
|
||||
"Alexander Misurkin" to "Alexander Alexanderovich Misurkin (Russian: Aлександр Aлександрович Мисуркин) (born September 23, 1977), a major in the Russian Air Force, is a Russian cosmonaut, selected in 2006. He flew aboard Soyuz TMA-08M on 28 March 2013 as his first space mission, and launched on Soyuz MS-06 as his second flight, in 2017. He was Commander of the International Space Station for Expedition 54.",
|
||||
"Yusaku Maezawa" to "Yusaku Maezawa (前澤 友作, Maezawa Yūsaku, born 22 November 1975) is a Japanese billionaire entrepreneur and art collector. He founded Start Today in 1998 and launched the online fashion retail website Zozotown in 2004, now Japan's largest. Most recently, Maezawa introduced a custom-fit apparel brand ZOZO and at-home measurement system, the ZOZOSUIT, in 2018. As of July 2021, he is estimated by Forbes to have a net worth of $1.9 billion.",
|
||||
"Yozo Hirano" to "Yozo Hirano (Japanese: 平野 陽三, 1985- ) born in Imabari, Ehime Prefecture, Japan, is a Japanese spaceflight participant. He is scheduled to fly on Soyuz MS-20.\n\n"
|
||||
+ "He is foreseen to fly with Yusaku Maezawa, who will pay for both seats; his function will be as production assistant of Maezawa and to document the flight."
|
||||
)
|
|
@ -1,54 +0,0 @@
|
|||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.AstroResult
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
import io.ktor.server.plugins.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun main() {
|
||||
val koin = initKoin(enableNetworkLogs = true).koin
|
||||
val peopleInSpaceApi = koin.get<PeopleInSpaceApi>()
|
||||
peopleInSpaceApi.baseUrl = "http://api.open-notify.org"
|
||||
|
||||
val port = System.getenv().getOrDefault("PORT", "8080").toInt()
|
||||
embeddedServer(Netty, port) {
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
|
||||
routing {
|
||||
|
||||
get("/astros.json") {
|
||||
val ar = peopleInSpaceApi.fetchPeople()
|
||||
val result = AstroResult(ar.message, ar.number, ar.people.map {
|
||||
val personImageUrl = personImages[it.name]
|
||||
val personBio = personBios[it.name]
|
||||
Assignment(it.craft, it.name, personImageUrl, personBio)
|
||||
})
|
||||
call.respond(result)
|
||||
}
|
||||
|
||||
get("/iss-now.json") {
|
||||
val result = peopleInSpaceApi.fetchISSPosition()
|
||||
call.respond(result)
|
||||
}
|
||||
|
||||
get("/astros_local.json") {
|
||||
val result = AstroResult(
|
||||
"success", 3,
|
||||
listOf(
|
||||
Assignment("ISS", "Chris Cassidy"),
|
||||
Assignment("ISS", "Anatoly Ivanishin"),
|
||||
Assignment("ISS", "Ivan Vagner")
|
||||
)
|
||||
)
|
||||
call.respond(result)
|
||||
}
|
||||
}
|
||||
}.start(wait = true)
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
buildscript {
|
||||
val kotlinVersion: String by project
|
||||
println(kotlinVersion)
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven(uri("https://plugins.gradle.org/m2/")) // For kotlinter-gradle
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// keeping this here to allow AS to automatically update
|
||||
classpath("com.android.tools.build:gradle:7.1.0")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
|
||||
classpath("org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}")
|
||||
|
||||
with(Deps.Gradle) {
|
||||
classpath(sqlDelight)
|
||||
classpath(shadow)
|
||||
classpath(kotlinter)
|
||||
classpath(gradleVersionsPlugin)
|
||||
classpath("com.rickclephas.kmp:kmp-nativecoroutines-gradle-plugin:${Versions.kmpNativeCoroutinesVersion}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply(plugin = "org.jmailen.kotlinter")
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers")
|
||||
maven(url = "https://jitpack.io")
|
||||
maven(url = "https://maven.pkg.jetbrains.space/public/p/kotlinx-coroutines/maven")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// On Apple Silicon we need Node.js 16.0.0
|
||||
// https://youtrack.jetbrains.com/issue/KT-49109
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class) {
|
||||
rootProject.the(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension::class).nodeVersion = "16.0.0"
|
||||
}
|
1
buildSrc/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
build
|
|
@ -1,7 +0,0 @@
|
|||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
object Versions {
|
||||
const val androidMinSdk = 21
|
||||
const val androidCompileSdk = 31
|
||||
const val androidTargetSdk = androidCompileSdk
|
||||
|
||||
const val kotlinCoroutines = "1.6.0"
|
||||
const val koin = "3.1.4"
|
||||
const val ktor = "2.0.0-beta-1"
|
||||
const val kotlinxSerialization = "1.3.2"
|
||||
const val kotlinxHtmlJs = "0.7.3"
|
||||
|
||||
const val kmpNativeCoroutinesVersion = "0.11.1-new-mm"
|
||||
|
||||
const val compose = "1.1.0-rc01"
|
||||
const val composeCompiler = "1.1.0-rc02"
|
||||
const val wearCompose = "1.0.0-alpha13"
|
||||
const val navCompose = "2.4.0-rc01"
|
||||
const val accompanist = "0.22.0-rc"
|
||||
|
||||
const val composeDesktopWeb = "1.0.1"
|
||||
|
||||
const val junit = "4.12"
|
||||
const val androidXTestJUnit = "1.1.3"
|
||||
const val testCore = "1.3.0"
|
||||
const val mockito = "3.11.2"
|
||||
const val robolectric = "4.6.1"
|
||||
|
||||
const val sqlDelight = "1.5.3"
|
||||
const val shadow = "7.0.0"
|
||||
const val kotlinterGradle = "3.4.5"
|
||||
|
||||
const val material = "1.4.0"
|
||||
const val activityCompose = "1.4.0-beta01"
|
||||
const val lifecycleKtx = "2.4.0-rc01"
|
||||
const val lifecycleRuntimeKtx = lifecycleKtx
|
||||
const val lifecycleViewmodelKtx = lifecycleKtx
|
||||
const val osmdroidAndroid = "6.1.10"
|
||||
|
||||
const val kotlinReact = "17.0.1-pre.146-kotlin-1.4.30"
|
||||
const val kotlinReactDom = "17.0.1-pre.146-kotlin-1.4.30"
|
||||
const val kotlinReactRouterDom = "5.1.2-pre.110-kotlin-1.4.0"
|
||||
const val kotlinStyled = "5.2.1-pre.146-kotlin-1.4.30"
|
||||
|
||||
const val slf4j = "1.7.30"
|
||||
const val logback = "1.2.3"
|
||||
const val kermit = "1.0.0"
|
||||
|
||||
const val gradleVersionsPlugin = "0.39.0"
|
||||
}
|
||||
|
||||
object Deps {
|
||||
object Gradle {
|
||||
const val kotlinter = "org.jmailen.gradle:kotlinter-gradle:${Versions.kotlinterGradle}"
|
||||
const val shadow = "gradle.plugin.com.github.jengelman.gradle.plugins:shadow:${Versions.shadow}"
|
||||
const val sqlDelight = "com.squareup.sqldelight:gradle-plugin:${Versions.sqlDelight}"
|
||||
const val gradleVersionsPlugin = "com.github.ben-manes:gradle-versions-plugin:${Versions.gradleVersionsPlugin}"
|
||||
}
|
||||
|
||||
object Kotlinx {
|
||||
const val serializationCore = "org.jetbrains.kotlinx:kotlinx-serialization-core:${Versions.kotlinxSerialization}"
|
||||
const val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
|
||||
const val coroutinesTest = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.kotlinCoroutines}"
|
||||
const val htmlJs = "org.jetbrains.kotlinx:kotlinx-html-js:${Versions.kotlinxHtmlJs}"
|
||||
}
|
||||
|
||||
object Android {
|
||||
const val material = "com.google.android.material:material:${Versions.material}"
|
||||
const val osmdroidAndroid = "org.osmdroid:osmdroid-android:${Versions.osmdroidAndroid}"
|
||||
}
|
||||
|
||||
object AndroidX {
|
||||
const val lifecycleRuntimeKtx = "androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycleRuntimeKtx}"
|
||||
const val lifecycleViewmodelKtx = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.lifecycleViewmodelKtx}"
|
||||
const val activityCompose = "androidx.activity:activity-compose:${Versions.activityCompose}"
|
||||
}
|
||||
|
||||
object Test {
|
||||
const val junit = "junit:junit:${Versions.junit}"
|
||||
const val androidXTestJUnit = "androidx.test.ext:junit:${Versions.androidXTestJUnit}"
|
||||
const val mockito = "org.mockito:mockito-inline:${Versions.mockito}"
|
||||
const val robolectric = "org.robolectric:robolectric:${Versions.robolectric}"
|
||||
const val testCore = "androidx.test:core:${Versions.testCore}"
|
||||
|
||||
const val composeUiTest = "androidx.compose.ui:ui-test:${Versions.compose}"
|
||||
const val composeUiTestJUnit = "androidx.compose.ui:ui-test-junit4:${Versions.compose}"
|
||||
const val composeUiTestManifest = "androidx.compose.ui:ui-test-manifest:${Versions.compose}"
|
||||
}
|
||||
|
||||
object Compose {
|
||||
const val compiler = "androidx.compose.compiler:compiler:${Versions.composeCompiler}"
|
||||
const val ui = "androidx.compose.ui:ui:${Versions.compose}"
|
||||
const val uiGraphics = "androidx.compose.ui:ui-graphics:${Versions.compose}"
|
||||
const val uiTooling = "androidx.compose.ui:ui-tooling:${Versions.compose}"
|
||||
const val foundationLayout = "androidx.compose.foundation:foundation-layout:${Versions.compose}"
|
||||
const val material = "androidx.compose.material:material:${Versions.compose}"
|
||||
const val navigation = "androidx.navigation:navigation-compose:${Versions.navCompose}"
|
||||
|
||||
const val wearFoundation = "androidx.wear.compose:compose-foundation:${Versions.wearCompose}"
|
||||
const val wearMaterial = "androidx.wear.compose:compose-material:${Versions.wearCompose}"
|
||||
const val wearNavigation = "androidx.wear.compose:compose-navigation:${Versions.wearCompose}"
|
||||
|
||||
const val coilCompose = "io.coil-kt:coil-compose:1.3.1"
|
||||
const val accompanistNavigationAnimation = "com.google.accompanist:accompanist-navigation-animation:${Versions.accompanist}"
|
||||
}
|
||||
|
||||
object Koin {
|
||||
const val core = "io.insert-koin:koin-core:${Versions.koin}"
|
||||
const val test = "io.insert-koin:koin-test:${Versions.koin}"
|
||||
const val testJUnit4 = "io.insert-koin:koin-test-junit4:${Versions.koin}"
|
||||
const val android = "io.insert-koin:koin-android:${Versions.koin}"
|
||||
const val compose = "io.insert-koin:koin-androidx-compose:${Versions.koin}"
|
||||
}
|
||||
|
||||
object Ktor {
|
||||
const val serverCore = "io.ktor:ktor-server-core:${Versions.ktor}"
|
||||
const val serverNetty = "io.ktor:ktor-server-netty:${Versions.ktor}"
|
||||
const val contentNegotiation = "io.ktor:ktor-client-content-negotiation:${Versions.ktor}"
|
||||
const val json = "io.ktor:ktor-serialization-kotlinx-json:${Versions.ktor}"
|
||||
|
||||
const val serverContentNegotiation = "io.ktor:ktor-server-content-negotiation:${Versions.ktor}"
|
||||
|
||||
const val websockets = "io.ktor:ktor-websockets:${Versions.ktor}"
|
||||
const val clientCore = "io.ktor:ktor-client-core:${Versions.ktor}"
|
||||
const val clientJson = "io.ktor:ktor-client-json:${Versions.ktor}"
|
||||
const val clientLogging = "io.ktor:ktor-client-logging:${Versions.ktor}"
|
||||
const val clientSerialization = "io.ktor:ktor-client-serialization:${Versions.ktor}"
|
||||
const val clientAndroid = "io.ktor:ktor-client-android:${Versions.ktor}"
|
||||
const val clientJava = "io.ktor:ktor-client-java:${Versions.ktor}"
|
||||
const val clientIos = "io.ktor:ktor-client-ios:${Versions.ktor}"
|
||||
const val clientJs = "io.ktor:ktor-client-js:${Versions.ktor}"
|
||||
}
|
||||
|
||||
object SqlDelight {
|
||||
const val runtime = "com.squareup.sqldelight:runtime:${Versions.sqlDelight}"
|
||||
const val coroutineExtensions = "com.squareup.sqldelight:coroutines-extensions:${Versions.sqlDelight}"
|
||||
const val androidDriver = "com.squareup.sqldelight:android-driver:${Versions.sqlDelight}"
|
||||
const val nativeDriver = "com.squareup.sqldelight:native-driver:${Versions.sqlDelight}"
|
||||
const val nativeDriverMacos = "com.squareup.sqldelight:native-driver-macosx64:${Versions.sqlDelight}"
|
||||
const val sqliteDriver = "com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}"
|
||||
}
|
||||
|
||||
object React {
|
||||
const val react = "org.jetbrains:kotlin-react:${Versions.kotlinReact}"
|
||||
const val dom = "org.jetbrains:kotlin-react-dom:${Versions.kotlinReactDom}"
|
||||
const val routerDom = "org.jetbrains:kotlin-react-router-dom:${Versions.kotlinReactRouterDom}"
|
||||
const val styled = "org.jetbrains:kotlin-styled:${Versions.kotlinStyled}"
|
||||
}
|
||||
|
||||
object Ok {
|
||||
const val okhttp = "com.squareup.okhttp3:okhttp:4.9.2"
|
||||
const val loggingInterceptor = "com.squareup.okhttp3:logging-interceptor:4.9.2"
|
||||
}
|
||||
|
||||
object Log {
|
||||
const val slf4j = "org.slf4j:slf4j-simple:${Versions.slf4j}"
|
||||
const val logback = "ch.qos.logback:logback-classic:${Versions.logback}"
|
||||
const val kermit = "co.touchlab:kermit:${Versions.kermit}"
|
||||
}
|
||||
|
||||
object Glance {
|
||||
const val tiles = "androidx.glance:glance-wear-tiles:1.0.0-alpha02"
|
||||
const val appwidget = "androidx.glance:glance-appwidget:1.0.0-alpha02"
|
||||
}
|
||||
}
|
3
common/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
|||
/build
|
||||
*.iml
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
id("kotlinx-serialization")
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.native.cocoapods")
|
||||
id("com.squareup.sqldelight")
|
||||
id("com.rickclephas.kmp.nativecoroutines")
|
||||
id("com.chromaticnoise.multiplatform-swiftpackage") version "2.0.3"
|
||||
}
|
||||
|
||||
// CocoaPods requires the podspec to have a version.
|
||||
version = "1.0"
|
||||
|
||||
android {
|
||||
compileSdk = Versions.androidCompileSdk
|
||||
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
||||
defaultConfig {
|
||||
minSdk = Versions.androidMinSdk
|
||||
targetSdk = Versions.androidTargetSdk
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget = when {
|
||||
System.getenv("SDK_NAME")?.startsWith("iphoneos") == true -> ::iosArm64
|
||||
System.getenv("NATIVE_ARCH")?.startsWith("arm") == true -> ::iosSimulatorArm64 // available to KT 1.5.30
|
||||
else -> ::iosX64
|
||||
}
|
||||
iosTarget("iOS") {}
|
||||
|
||||
val sdkName: String? = System.getenv("SDK_NAME")
|
||||
val isWatchOSDevice = sdkName.orEmpty().startsWith("watchos")
|
||||
if (isWatchOSDevice) {
|
||||
watchosArm64("watch")
|
||||
} else {
|
||||
watchosX64("watch")
|
||||
}
|
||||
|
||||
macosX64("macOS")
|
||||
android()
|
||||
jvm()
|
||||
|
||||
cocoapods {
|
||||
// Configure fields required by CocoaPods.
|
||||
summary = "PeopleInSpace"
|
||||
homepage = "https://github.com/joreilly/PeopleInSpace"
|
||||
}
|
||||
|
||||
js(IR) {
|
||||
useCommonJs()
|
||||
browser()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
sourceSets["commonMain"].dependencies {
|
||||
|
||||
with(Deps.Ktor) {
|
||||
implementation(clientCore)
|
||||
implementation(clientJson)
|
||||
implementation(clientLogging)
|
||||
implementation(contentNegotiation)
|
||||
implementation(json)
|
||||
}
|
||||
|
||||
with(Deps.Kotlinx) {
|
||||
implementation(coroutinesCore)
|
||||
implementation(serializationCore)
|
||||
}
|
||||
|
||||
with(Deps.SqlDelight) {
|
||||
implementation(runtime)
|
||||
implementation(coroutineExtensions)
|
||||
}
|
||||
|
||||
with(Deps.Koin) {
|
||||
api(core)
|
||||
api(test)
|
||||
}
|
||||
|
||||
with(Deps.Log) {
|
||||
api(kermit)
|
||||
}
|
||||
}
|
||||
sourceSets["commonTest"].dependencies {
|
||||
implementation(Deps.Koin.test)
|
||||
implementation(Deps.Kotlinx.coroutinesTest)
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
}
|
||||
|
||||
sourceSets["androidMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientAndroid)
|
||||
implementation(Deps.SqlDelight.androidDriver)
|
||||
}
|
||||
sourceSets["androidTest"].dependencies {
|
||||
implementation(Deps.Test.junit)
|
||||
}
|
||||
|
||||
sourceSets["jvmMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientJava)
|
||||
implementation(Deps.SqlDelight.sqliteDriver)
|
||||
implementation(Deps.Log.slf4j)
|
||||
}
|
||||
|
||||
sourceSets["iOSMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientIos)
|
||||
implementation(Deps.SqlDelight.nativeDriver)
|
||||
}
|
||||
sourceSets["iOSTest"].dependencies {
|
||||
}
|
||||
|
||||
sourceSets["watchMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientIos)
|
||||
implementation(Deps.SqlDelight.nativeDriver)
|
||||
}
|
||||
|
||||
sourceSets["macOSMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientIos)
|
||||
implementation(Deps.SqlDelight.nativeDriverMacos)
|
||||
}
|
||||
|
||||
sourceSets["jsMain"].dependencies {
|
||||
implementation(Deps.Ktor.clientJs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
sqldelight {
|
||||
database("PeopleInSpaceDatabase") {
|
||||
packageName = "com.surrus.peopleinspace.db"
|
||||
sourceFolders = listOf("sqldelight")
|
||||
}
|
||||
}
|
||||
|
||||
multiplatformSwiftPackage {
|
||||
packageName("PeopleInSpace")
|
||||
swiftToolsVersion("5.3")
|
||||
targetPlatforms {
|
||||
iOS { v("13") }
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
Pod::Spec.new do |spec|
|
||||
spec.name = 'common'
|
||||
spec.version = '1.0'
|
||||
spec.homepage = 'https://github.com/joreilly/PeopleInSpace'
|
||||
spec.source = { :git => "Not Published", :tag => "Cocoapods/#{spec.name}/#{spec.version}" }
|
||||
spec.authors = ''
|
||||
spec.license = ''
|
||||
spec.summary = 'PeopleInSpace'
|
||||
|
||||
spec.vendored_frameworks = "build/cocoapods/framework/common.framework"
|
||||
spec.libraries = "c++"
|
||||
spec.module_name = "#{spec.name}_umbrella"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
spec.pod_target_xcconfig = {
|
||||
'KOTLIN_PROJECT_PATH' => ':common',
|
||||
'PRODUCT_MODULE_NAME' => 'common',
|
||||
}
|
||||
|
||||
spec.script_phases = [
|
||||
{
|
||||
:name => 'Build common',
|
||||
:execution_position => :before_compile,
|
||||
:shell_path => '/bin/sh',
|
||||
:script => <<-SCRIPT
|
||||
if [ "YES" = "$COCOAPODS_SKIP_KOTLIN_BUILD" ]; then
|
||||
echo "Skipping Gradle build task invocation due to COCOAPODS_SKIP_KOTLIN_BUILD environment variable set to \"YES\""
|
||||
exit 0
|
||||
fi
|
||||
set -ev
|
||||
REPO_ROOT="$PODS_TARGET_SRCROOT"
|
||||
"$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \
|
||||
-Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \
|
||||
-Pkotlin.native.cocoapods.archs="$ARCHS" \
|
||||
-Pkotlin.native.cocoapods.configuration=$CONFIGURATION
|
||||
SCRIPT
|
||||
}
|
||||
]
|
||||
end
|
21
common/proguard-rules.pro
vendored
|
@ -1,21 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -1,2 +0,0 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.surrus.common" />
|
|
@ -1,19 +0,0 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.squareup.sqldelight.android.AndroidSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.android.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver =
|
||||
AndroidSqliteDriver(PeopleInSpaceDatabase.Schema, get(), "peopleinspace.db")
|
||||
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Android.create() }
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package com.surrus.common.di
|
||||
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import com.surrus.common.repository.PeopleInSpaceRepository
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.common.repository.platformModule
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.KoinAppDeclaration
|
||||
import org.koin.dsl.module
|
||||
|
||||
fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclaration = {}) =
|
||||
startKoin {
|
||||
appDeclaration()
|
||||
modules(commonModule(enableNetworkLogs = enableNetworkLogs), platformModule())
|
||||
}
|
||||
|
||||
// called by iOS etc
|
||||
fun initKoin() = initKoin(enableNetworkLogs = false) {}
|
||||
|
||||
fun commonModule(enableNetworkLogs: Boolean) = module {
|
||||
single { createJson() }
|
||||
single { createHttpClient(get(), get(), enableNetworkLogs = enableNetworkLogs) }
|
||||
|
||||
single { CoroutineScope(Dispatchers.Default + SupervisorJob() ) }
|
||||
|
||||
single<PeopleInSpaceRepositoryInterface> { PeopleInSpaceRepository() }
|
||||
|
||||
single { PeopleInSpaceApi(get()) }
|
||||
}
|
||||
|
||||
fun createJson() = Json { isLenient = true; ignoreUnknownKeys = true }
|
||||
|
||||
|
||||
fun createHttpClient(httpClientEngine: HttpClientEngine, json: Json, enableNetworkLogs: Boolean) = HttpClient(httpClientEngine) {
|
||||
install(ContentNegotiation) {
|
||||
json(json)
|
||||
}
|
||||
if (enableNetworkLogs) {
|
||||
install(Logging) {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.INFO
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package com.surrus.common.di
|
||||
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
|
||||
class PeopleInSpaceDatabaseWrapper(val instance: PeopleInSpaceDatabase?)
|
|
@ -1,27 +0,0 @@
|
|||
package com.surrus.common.remote
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.request.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
@Serializable
|
||||
data class AstroResult(val message: String, val number: Int, val people: List<Assignment>)
|
||||
|
||||
@Serializable
|
||||
data class Assignment(val craft: String, val name: String, var personImageUrl: String? = "", var personBio: String? = "")
|
||||
|
||||
@Serializable
|
||||
data class IssPosition(val latitude: Double, val longitude: Double)
|
||||
|
||||
@Serializable
|
||||
data class IssResponse(val message: String, val iss_position: IssPosition, val timestamp: Long)
|
||||
|
||||
class PeopleInSpaceApi(
|
||||
private val client: HttpClient,
|
||||
var baseUrl: String = "https://people-in-space-proxy.ew.r.appspot.com",
|
||||
) : KoinComponent {
|
||||
suspend fun fetchPeople() = client.get("$baseUrl/astros.json").body<AstroResult>()
|
||||
suspend fun fetchISSPosition() = client.get("$baseUrl/iss-now.json").body<IssResponse>()
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import org.koin.core.module.Module
|
||||
|
||||
expect fun platformModule(): Module
|
|
@ -1,91 +0,0 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope
|
||||
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
||||
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
interface PeopleInSpaceRepositoryInterface {
|
||||
fun fetchPeopleAsFlow(): Flow<List<Assignment>>
|
||||
fun pollISSPosition(): Flow<IssPosition>
|
||||
suspend fun fetchPeople(): List<Assignment>
|
||||
suspend fun fetchAndStorePeople()
|
||||
}
|
||||
|
||||
class PeopleInSpaceRepository : KoinComponent, PeopleInSpaceRepositoryInterface {
|
||||
private val peopleInSpaceApi: PeopleInSpaceApi by inject()
|
||||
|
||||
@NativeCoroutineScope
|
||||
private val coroutineScope: CoroutineScope = MainScope()
|
||||
private val peopleInSpaceDatabase: PeopleInSpaceDatabaseWrapper by inject()
|
||||
private val peopleInSpaceQueries = peopleInSpaceDatabase.instance?.peopleInSpaceQueries
|
||||
|
||||
val logger = Logger.withTag("PeopleInSpaceRepository")
|
||||
|
||||
init {
|
||||
coroutineScope.launch {
|
||||
fetchAndStorePeople()
|
||||
}
|
||||
}
|
||||
|
||||
override fun fetchPeopleAsFlow(): Flow<List<Assignment>> {
|
||||
// the main reason we need to do this check is that sqldelight isn't currently
|
||||
// setup for javascript client
|
||||
return peopleInSpaceQueries?.selectAll(
|
||||
mapper = { name, craft, personImageUrl, personBio ->
|
||||
Assignment(name = name, craft = craft, personImageUrl = personImageUrl, personBio = personBio)
|
||||
}
|
||||
)?.asFlow()?.mapToList() ?: flowOf(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun fetchAndStorePeople() {
|
||||
logger.d { "fetchAndStorePeople" }
|
||||
try {
|
||||
val result = peopleInSpaceApi.fetchPeople()
|
||||
|
||||
// this is very basic implementation for now that removes all existing rows
|
||||
// in db and then inserts results from api request
|
||||
// using "transaction" accelerate the batch of queries, especially inserting
|
||||
peopleInSpaceQueries?.transaction {
|
||||
peopleInSpaceQueries.deleteAll()
|
||||
result.people.forEach {
|
||||
peopleInSpaceQueries.insertItem(
|
||||
it.name,
|
||||
it.craft,
|
||||
it.personImageUrl,
|
||||
it.personBio
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// TODO report error up to UI
|
||||
logger.w(e) { "Exception during fetchAndStorePeople: $e" }
|
||||
}
|
||||
}
|
||||
|
||||
// Used by web and apple clients atm
|
||||
override suspend fun fetchPeople(): List<Assignment> = peopleInSpaceApi.fetchPeople().people
|
||||
|
||||
override fun pollISSPosition(): Flow<IssPosition> {
|
||||
return flow {
|
||||
while (true) {
|
||||
val position = peopleInSpaceApi.fetchISSPosition().iss_position
|
||||
emit(position)
|
||||
logger.d { position.toString() }
|
||||
delay(POLL_INTERVAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val POLL_INTERVAL = 10000L
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
ALTER TABLE People ADD COLUMN personImageUrl TEXT;
|
||||
ALTER TABLE People ADD COLUMN personBio TEXT;
|
|
@ -1,16 +0,0 @@
|
|||
|
||||
CREATE TABLE People(
|
||||
name TEXT NOT NULL PRIMARY KEY,
|
||||
craft TEXT NOT NULL,
|
||||
personImageUrl TEXT,
|
||||
personBio TEXT
|
||||
);
|
||||
|
||||
insertItem:
|
||||
INSERT OR REPLACE INTO People(name, craft, personImageUrl, personBio)VALUES(?,?,?,?);
|
||||
|
||||
selectAll:
|
||||
SELECT * FROM People;
|
||||
|
||||
deleteAll:
|
||||
DELETE FROM People;
|
|
@ -1,43 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.common.di.commonModule
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import com.surrus.common.repository.platformModule
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.module
|
||||
import org.koin.test.KoinTest
|
||||
import org.koin.test.inject
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class PeopleInSpaceTest: KoinTest {
|
||||
private val repo : PeopleInSpaceRepositoryInterface by inject()
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
Dispatchers.setMain(StandardTestDispatcher())
|
||||
|
||||
startKoin{
|
||||
modules(
|
||||
commonModule(true),
|
||||
platformModule(),
|
||||
module {
|
||||
single { PeopleInSpaceDatabaseWrapper(null) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetPeople() = runTest {
|
||||
val result = repo.fetchPeople()
|
||||
println(result)
|
||||
assertTrue(result.isNotEmpty())
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.ios.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Ios.create() }
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import io.ktor.client.engine.js.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
PeopleInSpaceDatabaseWrapper(null)
|
||||
}
|
||||
single { Js.create() }
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.surrus
|
||||
|
||||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
fun main() {
|
||||
runBlocking {
|
||||
val koin = initKoin(enableNetworkLogs = true).koin
|
||||
val api = koin.get<PeopleInSpaceApi>()
|
||||
println(api.fetchPeople())
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.java.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
|
||||
.also { PeopleInSpaceDatabase.Schema.create(it) }
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Java.create() }
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.ios.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Ios.create() }
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.surrus.common.repository
|
||||
|
||||
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
||||
import com.surrus.common.di.PeopleInSpaceDatabaseWrapper
|
||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||
import io.ktor.client.engine.ios.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
actual fun platformModule() = module {
|
||||
single {
|
||||
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
||||
PeopleInSpaceDatabaseWrapper(PeopleInSpaceDatabase(driver))
|
||||
}
|
||||
single { Ios.create() }
|
||||
}
|
2
compose-desktop/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
/build
|
||||
*.iml
|
|
@ -1,29 +0,0 @@
|
|||
import org.jetbrains.compose.compose
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("org.jetbrains.compose") version Versions.composeDesktopWeb
|
||||
application
|
||||
}
|
||||
|
||||
group = "me.joreilly"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven(url = "https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(compose.desktop.currentOs)
|
||||
implementation(project(":common"))
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("MainKt")
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Window
|
||||
import androidx.compose.ui.window.application
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.skia.Image.Companion.makeFromEncoded
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
private val koin = initKoin(enableNetworkLogs = true).koin
|
||||
|
||||
fun main() = application {
|
||||
val windowState = rememberWindowState()
|
||||
|
||||
var peopleState by remember { mutableStateOf(emptyList<Assignment>()) }
|
||||
var selectedPerson by remember { mutableStateOf<Assignment?>(null) }
|
||||
|
||||
val peopleInSpaceApi = koin.get<PeopleInSpaceApi>()
|
||||
|
||||
LaunchedEffect(true) {
|
||||
peopleState = peopleInSpaceApi.fetchPeople().people
|
||||
selectedPerson = peopleState.first()
|
||||
}
|
||||
|
||||
Window(
|
||||
onCloseRequest = ::exitApplication,
|
||||
state = windowState,
|
||||
title = "People In Space"
|
||||
) {
|
||||
|
||||
Row(Modifier.fillMaxSize()) {
|
||||
|
||||
Box(Modifier.width(250.dp).fillMaxHeight().background(color = Color.LightGray)) {
|
||||
PersonList(peopleState, selectedPerson) {
|
||||
selectedPerson = it
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(1.dp).fillMaxHeight())
|
||||
|
||||
Box(Modifier.fillMaxHeight()) {
|
||||
selectedPerson?.let {
|
||||
PersonDetailsView(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PersonList(
|
||||
people: List<Assignment>,
|
||||
selectedPerson: Assignment?,
|
||||
personSelected: (person: Assignment) -> Unit
|
||||
) {
|
||||
|
||||
// workaround for compose desktop but if LazyColumn is empty
|
||||
if (people.isNotEmpty()) {
|
||||
LazyColumn {
|
||||
items(people) { person ->
|
||||
PersonView(person, selectedPerson, personSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PersonView(
|
||||
person: Assignment,
|
||||
selectedPerson: Assignment?,
|
||||
personSelected: (person: Assignment) -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().clickable(onClick = { personSelected(person) })
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
Column {
|
||||
Text(
|
||||
person.name,
|
||||
style = if (person.name == selectedPerson?.name) MaterialTheme.typography.h6 else MaterialTheme.typography.body1
|
||||
)
|
||||
|
||||
Text(text = person.craft, style = TextStyle(color = Color.DarkGray, fontSize = 14.sp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PersonDetailsView(person: Assignment) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(16.dp).fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
|
||||
item(person) {
|
||||
|
||||
Text(person.name, style = MaterialTheme.typography.h4)
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
|
||||
val imageUrl = person.personImageUrl
|
||||
imageUrl?.let {
|
||||
val imageAsset = fetchImage(it)
|
||||
imageAsset?.let {
|
||||
Image(
|
||||
it,
|
||||
contentDescription = "personName",
|
||||
modifier = Modifier.size(240.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.size(24.dp))
|
||||
|
||||
val bio = person.personBio ?: ""
|
||||
Text(bio, style = MaterialTheme.typography.body1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun fetchImage(url: String): ImageBitmap? {
|
||||
var image by remember(url) { mutableStateOf<ImageBitmap?>(null) }
|
||||
|
||||
LaunchedEffect(url) {
|
||||
loadFullImage(url)?.let {
|
||||
image = makeFromEncoded(toByteArray(it)).asImageBitmap()
|
||||
}
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
fun toByteArray(bitmap: BufferedImage): ByteArray {
|
||||
val baos = ByteArrayOutputStream()
|
||||
ImageIO.write(bitmap, "png", baos)
|
||||
return baos.toByteArray()
|
||||
}
|
||||
|
||||
suspend fun loadFullImage(source: String): BufferedImage? = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
val url = URL(source)
|
||||
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
|
||||
connection.connectTimeout = 5000
|
||||
connection.connect()
|
||||
|
||||
val input: InputStream = connection.inputStream
|
||||
val bitmap: BufferedImage? = ImageIO.read(input)
|
||||
bitmap
|
||||
}.getOrNull()
|
||||
}
|
3
compose-web/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
|||
/build
|
||||
*.iml
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
plugins {
|
||||
kotlin("multiplatform")
|
||||
id("org.jetbrains.compose") version Versions.composeDesktopWeb
|
||||
}
|
||||
|
||||
version = "1.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js(IR) {
|
||||
browser()
|
||||
binaries.executable()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation(compose.web.widgets)
|
||||
implementation(compose.web.core)
|
||||
implementation(compose.runtime)
|
||||
|
||||
implementation(project(":common"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// workaround for https://youtrack.jetbrains.com/issue/KT-48273
|
||||
afterEvaluate {
|
||||
rootProject.extensions.configure<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension> {
|
||||
versions.webpackDevServer.version = "4.0.0"
|
||||
versions.webpackCli.version = "4.9.0"
|
||||
}
|
||||
}
|
||||
|
||||
compose.desktop {
|
||||
application {
|
||||
mainClass = ""
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
import androidx.compose.runtime.*
|
||||
import com.surrus.common.di.initKoin
|
||||
import com.surrus.common.remote.Assignment
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.repository.PeopleInSpaceRepository
|
||||
import com.surrus.common.repository.PeopleInSpaceRepositoryInterface
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import org.jetbrains.compose.common.foundation.layout.Column
|
||||
import org.jetbrains.compose.web.css.*
|
||||
import org.jetbrains.compose.web.dom.*
|
||||
import org.jetbrains.compose.web.renderComposable
|
||||
|
||||
private val koin = initKoin(enableNetworkLogs = true).koin
|
||||
|
||||
@InternalCoroutinesApi
|
||||
fun main() {
|
||||
val repo = koin.get<PeopleInSpaceRepositoryInterface>()
|
||||
|
||||
renderComposable(rootElementId = "root") {
|
||||
Style(TextStyles)
|
||||
|
||||
var people by remember { mutableStateOf(emptyList<Assignment>()) }
|
||||
|
||||
LaunchedEffect(true) {
|
||||
people = repo.fetchPeople()
|
||||
}
|
||||
|
||||
val issPosition by produceState(initialValue = IssPosition(0.0, 0.0), repo) {
|
||||
repo.pollISSPosition().collect { value = it }
|
||||
}
|
||||
|
||||
Div(attrs = { style { padding(16.px) } }) {
|
||||
H1(attrs = { classes(TextStyles.titleText) }) {
|
||||
Text("People In Space")
|
||||
}
|
||||
H2 {
|
||||
Text("ISS Position: latitude = ${issPosition.latitude}, longitude = ${issPosition.longitude}")
|
||||
}
|
||||
|
||||
people.forEach { person ->
|
||||
Div(
|
||||
attrs = {
|
||||
style {
|
||||
display(DisplayStyle.Flex)
|
||||
alignItems(AlignItems.Center)
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
||||
val imageUrl = person.personImageUrl ?: ""
|
||||
Img(
|
||||
src = imageUrl,
|
||||
attrs = {
|
||||
style {
|
||||
width(48.px)
|
||||
property("padding-right", 16.px)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Span(attrs = { classes(TextStyles.personText) }) {
|
||||
Text("${person.name} (${person.craft})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object TextStyles : StyleSheet() {
|
||||
|
||||
val titleText by style {
|
||||
color(rgb(23,24, 28))
|
||||
fontSize(50.px)
|
||||
property("font-size", 50.px)
|
||||
property("letter-spacing", (-1.5).px)
|
||||
property("font-weight", 900)
|
||||
property("line-height", 58.px)
|
||||
|
||||
property(
|
||||
"font-family",
|
||||
"Gotham SSm A,Gotham SSm B,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Droid Sans,Helvetica Neue,Arial,sans-serif"
|
||||
)
|
||||
}
|
||||
|
||||
val personText by style {
|
||||
color(rgb(23,24, 28))
|
||||
fontSize(24.px)
|
||||
property("font-size", 28.px)
|
||||
property("letter-spacing", "normal")
|
||||
property("font-weight", 300)
|
||||
property("line-height", 40.px)
|
||||
|
||||
property(
|
||||
"font-family",
|
||||
"Gotham SSm A,Gotham SSm B,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Droid Sans,Helvetica Neue,Arial,sans-serif"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>People In Space</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="compose-web.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,30 +0,0 @@
|
|||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# -XX:+UseParallelGC: https://developer.android.com/studio/releases/gradle-plugin#optimize-gc-jdk-11
|
||||
org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=false
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# XCode
|
||||
xcodeproj=./ios/PeopleInSpaceSwiftUI
|
||||
|
||||
# Kotlin/Native clients can override this through updating common.podspec
|
||||
kotlinVersion=1.6.10
|
||||
|
||||
kotlin.native.binary.memoryModel=experimental
|
||||
kotlin.native.binary.freezing=disabled
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +0,0 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
185
gradlew
vendored
|
@ -1,185 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
104
gradlew.bat
vendored
|
@ -1,104 +0,0 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
graphql-server/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
build
|
|
@ -1,41 +0,0 @@
|
|||
plugins {
|
||||
id("kotlin-platform-jvm")
|
||||
id("org.jetbrains.kotlin.plugin.spring") version("1.6.10")
|
||||
id("org.jetbrains.kotlin.plugin.serialization")
|
||||
id("org.springframework.boot") version("2.5.6")
|
||||
id("com.google.cloud.tools.appengine") version("2.4.2")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation("com.expediagroup:graphql-kotlin-spring-server:5.3.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
|
||||
|
||||
testImplementation("com.squareup.okhttp3:okhttp:4.9.3")
|
||||
|
||||
with(Deps.Log) {
|
||||
implementation(logback)
|
||||
}
|
||||
|
||||
implementation(project(":common"))
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
optIn("kotlin.RequiresOptIn")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appengine {
|
||||
stage {
|
||||
setArtifact(tasks.named("bootJar").flatMap { (it as Jar).archiveFile })
|
||||
}
|
||||
deploy {
|
||||
projectId = "peopleinspace-graphql"
|
||||
version = "GCLOUD_CONFIG"
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
runtime: java11
|
||||
|
||||
entrypoint: java -Xmx64m -jar graphql-server.jar
|
|
@ -1,20 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.surrus.common.di.initKoin
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
|
||||
|
||||
val koin = initKoin(enableNetworkLogs = true).koin
|
||||
|
||||
@SpringBootApplication
|
||||
class DefaultApplication {
|
||||
}
|
||||
|
||||
fun runServer(): ConfigurableApplicationContext {
|
||||
return runApplication<DefaultApplication>()
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.expediagroup.graphql.server.operations.Subscription
|
||||
import com.surrus.common.remote.IssPosition
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.reactive.asPublisher
|
||||
import org.reactivestreams.Publisher
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
|
||||
|
||||
@Component
|
||||
class IssPositionSubscription : Subscription {
|
||||
private val logger: Logger = LoggerFactory.getLogger(IssPositionSubscription::class.java)
|
||||
private var peopleInSpaceApi: PeopleInSpaceApi = koin.get()
|
||||
|
||||
|
||||
fun issPosition(): Publisher<IssPosition> {
|
||||
return flow {
|
||||
while (true) {
|
||||
val position = peopleInSpaceApi.fetchISSPosition().iss_position
|
||||
logger.info("ISS position = $position")
|
||||
emit(position)
|
||||
delay(POLL_INTERVAL)
|
||||
}
|
||||
}.asPublisher()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val POLL_INTERVAL = 10000L
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
import com.expediagroup.graphql.server.operations.Query
|
||||
import com.surrus.common.remote.PeopleInSpaceApi
|
||||
import com.surrus.common.remote.Assignment
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
data class People(val people: List<Assignment>)
|
||||
|
||||
@Component
|
||||
class RootQuery : Query {
|
||||
private var peopleInSpaceApi: PeopleInSpaceApi = koin.get()
|
||||
|
||||
suspend fun allPeople(): People = People(peopleInSpaceApi.fetchPeople().people)
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package com.surrus.peopleinspace
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runServer()
|
||||
}
|
||||
|