A small utility written in Kotlin to convert Google Maps Activity Timeline to iCal (.ics) files for calendar import.
This is a Kotlin Multiplatform App but with only the Desktop implementation. It can be built and executed using IntelliJ IDEA on the desktop environment.
This is my first non-Android Kotlin project. It is within expectation that everything inside this project can be messy. I treat this as my playground that allows me to make all kinds of experiments and mistakes. While I thank you for your visit, I would appreciate if you could take things easy when you see me doing something crazy here. Thank you.
Important
I have in mind to rewrite the UI for this app. I want the new version to manage the API key and other settings on the UI, so we do not need a compile-time config file. Also, I want the app to allow us select/confirm the events being exported to the ICal file, so we do not have to delete unwanted items on the Calendar app afterwards. I have no idea how to design the new UI, that is why it is not moving forward right now. Will kickstart once the UI design is done.
This App is fully functional.
- 169 unit tests written as at 1 Jan 2023 to protect changes
- Dark theme auto detect and switching
- Reapplied JetBrain's official KMP template to make the project structure up-to-date
- Planned enhancements are now logged as issues.
I travelled quite a lot before the pandemic. Very often my daily itinerary changes at the last minute. This made me a headache recalling where I have been to previously.
I use Google Maps for navigation heavily, and I have it tracked my movement for quite many years.
I had an idea to extract my activity timeline from the Google Maps, but unfortunately there is no public API available. It might be possible to achieve that using some web scrapping techniques, but this approach is not likely to be an elegant one.
That is why I am trying another way round: By setting Google Maps to regularly export my activity timeline as JSON files, I can then process them using this Kotlin utility, and generate iCal (.ICS) files which I can then import to my calendars.
By doing so, I can keep a full record for the places I have actually been, and also the rough mileages I have spent on the road.
Trying to reuse all my Android development knowledge as possible, otherwise native replacements have been applied.
- Kotlin
- Kotlin Flow
- Kotlin Coroutines
- Compose Desktop UI
- MVVM architecture with use-cases
- Gradle Version Catalog
- Koin - Dependency injection
- Ktor - HTTP Client
- Kotlin Serialization - For JSON parsing. Replacing Moshi
- Time Zone Map - determine time zone
- jSystemThemeDetector - detect system dark theme setting
- Napier - Logging library for Kotlin Multiplatform
- MoKo MVVM - Mobile Kotlin Model-View-ViewModel architecture components
- JUnit 5 - Tests
- KOTest - Test runner and assertion library
- MockK - Mocking library
- Github Actions - CI
- Kover - code coverage
- codecov - code coverage
- Ktlint Gradle - ktlint plugin to check and apply code autoformat
- Mend Renovate - automatic dependency updates
- Go to Google Takeout
- Make sure you have included "Location History" in your export request.
- Create an export and wait for completion. This process can take a long time (possibly hours or days) to complete. You'll receive an email when your export is done.
- Unzip the data file.
- The JSON files under
/Location History/Semantic Location History
are the files we need. - Move the JSON files to a directory, for example
./composeApp/src/commonMain/resources
of this project. - Update, or create yor own configuration file
uk.ryanwong.gmap2ics.app.configs.DefaultConfig
to specify the input and output paths. - If you have created your own configuration file, update the file path in
Main.kt
- Run the project on IntelliJ IDEA
There is a plan to deprecate the configuration file automate the app build process. However, before that happened, you still have to properly maintain your own configuration file to build and run the app.
To start the app from command line: ./gradlew runReleaseDistributable