GithubHelp home page GithubHelp logo

robojackets / apiary-mobile Goto Github PK

View Code? Open in Web Editor NEW
0.0 7.0 0.0 1.85 MB

Mobile application for MyRoboJackets attendance and other features

License: Apache License 2.0

Kotlin 95.93% Ruby 4.07%
android kotlin hacktoberfest

apiary-mobile's Introduction

apiary-mobile

The companion Android app for MyRoboJackets

Local development

Dependencies

Most dependencies are provided via Gradle.

NFC functionality uses the NXP MIFARE TapLinx Android SDK. You must provide a license key and offline license key from the TapLinx Developer Center on https://www.mifare.net/en/products/tools/taplinx/.

Additionally, important licensing information about the TapLinx library is included in the libs directory, including the license and Software Content Register.

Note: For RoboJackets developers, reach out in #apiary-mobile in Slack to obtain our keys.

Once you have the two keys, append the following two lines to your Gradle local.properies file. Do not commit this file to version control.

taplinxKey=KEY_HERE
taplinxOfflineKey=OFFLINE_KEY_HERE
sentryDsn=SENTRY_DSN_HERE

Repository structure

There are 5 modules encompassing features and utilities.

  • app - The main application module

Note: To avoid circular dependencies, the below modules must not have the app module as a dependency. Place such code in the base (or a new) module.

  • base - Base files that are used in all other modules, settings screen UI
  • navigation - Utility code for navigating between screens
  • auth - Authentication logic and UI
  • attendance - Attendance logic and UI

Dependency management

Dependency versions are managed centrally in Dependencies.kt in the buildSrc module. If you change a version in Dependencies.kt, make sure to manually sync Gradle because Android Studio might not recognize that the change requires a Gradle sync.

After adding a dependency in Dependencies.kt, you must also add it to the appropriate Gradle Script (take a look at a build.gradle.kts file for one of the modules for examples).

Environment configuration

Environment definitions that allow changing what MyRoboJackets instance to use are specified in AppEnvironment.kt in the base module.

It's important to fetch environment values as close as possible to using them, since they could change after being retrieved.

Since public OAuth2 (with PKCE) is used for authentication, there is no OAuth2 client secret. If the included client ID for an environment does not work, you can create a new OAuth2 client in Nova in MyRoboJackets (requires the admin role).

Dependency injection

Hilt is used for dependency injection. The Hilt and Dagger annotations cheat sheet is a helpful resource.

Code linting

Detekt is used for linting Kotlin code. The recommended command to run it is

(Windows)

./gradlew detektAll -PdetektAutoFix=true

(*nix)

./gradlew detektAll -PdetektAutoFix=true

The detektAutoFix parameter will automatically fix simple issues.

Fastlane

We use Fastlane to automate steps of the Android release process, in combination with Concourse CI.

To install Fastlane, you'll need Ruby with the development kit installed. On Windows, install the latest 64-bit version of Ruby+Devkit from https://rubyinstaller.org/downloads/.

You can install the Fastlane dependencies by running bundle install from the root of this repository.

Additional Fastlane dependencies/notes

  • When running any Fastlane lane on Windows that requires sentry-cli, bear in mind that sometimes which sentry-cli is run. which does not exist on Windows, but the similarly named where command does, so you can get around any errors stemming from this by aliasing the command where to which.
  • You need to install GitVersion yourself.

Release management

Below are some instructions on the MyRoboJackets Android release process. Note that you will need additional permissions on this repo and the MyRoboJackets Android Google Play application to fully carry out this step:

  • Permission on this repo to create tags and releases (write access)
  • Permissions on the MyRoboJackets Android Google Play app. At a minimum:
    • Release apps to testing tracks
    • Release to production, exclude devices, and use Play App Signing

App releases don't have to perfectly coincide with PRs being merged, especially if two PRs are merged in close proximity. Our Concourse pipeline has jobs to automatically handle building, signing, and uploading production releases of the app.

  1. After you've merged all PRs to be included in the release, ensure the .update-priority file is set correctly according to the table below. Use priority 2 as the default. If you want to use priority 4 or 5, post in #apiary-mobile first.
    1. Update priority affects if and how often users receive in-app update prompts to update the app to the latest version.
Update priority Description Examples Update timeline for users
0/1/2 Very low or low priority UI touchups that don't impact functionality, releases with options to opt-in to beta features No prompt initially. Optional prompt starting 14 days after release. Immediate update 21 days after release.
3 Medium priority Medium-priority bug fixes, performance improvements, non-time-sensitive feature launches No prompts for the first 3 days. Optional starting 4 days after release. Immediate update 21 days after release.
4 High priority or time-sensitive High priority bug fixes, time-sensitive feature launches Optional for the first 24 hours, then immediate.
5 Critical bug fixes Crashes/bugs impacting major features, urgent vulnerabilities Immediate update required.
  1. Create a new release on main using a tag with a name like v1.0.0. Use semantic versioning to determine how to increment the version number.
    1. Go to https://github.com/RoboJackets/apiary-mobile/releases
    2. Press the Draft a new release button.
    3. Decide on the new version tag; it must start with v. In general, you should increment the previous release's version using semantic versioning guidelines (most releases will be a 0.1.0 (minor) or 0.0.1 (patch) increment). Press Choose a tag, then enter the new tag name to create it on publish.
    4. Leave Release title blank. Instead, press Generate release notes. The release title and description should automatically fill in with the changes since the last release.
      1. In general, you shouldn't need to manually set the value of the Previous tag field, unless the release notes seem incorrect.
  2. When you publish the release (which creates a new tag), a Concourse job to create a draft Google Play internal test release will begin shortly.
    1. If it doesn't start, a common reason is that it wasn't alphabetically the latest tag, so the tagged-release resource didn't trigger a new build. We can disable old versions of the resource to trigger a new build.
  3. If the Concourse build-release job finishes successfully, you'll find a new draft release on the Internal Test track in Google Play. At this point, you should do some small QA efforts to verify the new build. Post in #apiary-mobile to have some people help you test. Note that access to the internal test track must be granted via the Google Play Console.
    1. Internal testers may need to uninstall the app to see the update if it was recently published.
  4. If no issues are found, it's time to release! Promote the build to the Production track in Google Play, add release notes, and save the release.
  5. Google Play typically spends a day or two reviewing the release, then makes it available. In general, expect it to take at least ~24 hours for a production release to be available to users.

apiary-mobile's People

Contributors

evan10s avatar kberzinch avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

apiary-mobile's Issues

Add in-app release notes

This is definitely not necessary by any means but it would pair nicely with our update prompts.

Merchandise distribution

Allow merchandise distribution via the app. See Figma for full spec.

The initial implementation should encompass:

  • Support for "rapid-fire" mode - pick one merch item, then tap BuzzCards to record distribution or error if already picked up
    • From prototyping on Figma, we decided on a two-stage mode for this. Tap, a dialog with more details (e.g., size) appears, then confirm

The API routes need to be added in Apiary; see RoboJackets/apiary#3072

Image
Image
Image
Image

Create attendance tracking screen UI

Figma mockups

Event/team selection

Before showing the BuzzCard scanning UI, fetch the team list and list of events happening today. Users can toggle between seeing events or visible and attendable (the same list the kiosk shows) teams, with teams being the default. After selecting a team or event, the UI transitions to the BuzzCard scanning mode.

BuzzCard scanning

This screen consists of:

  • The result of the last tap (user's name, succcess/failure)
  • A status area to indicate when to scan the next BuzzCard. Possible states include:
    • Enable NFC to continue
    • Ready to scan
    • Sending data...
    • Error preventing scan (e.g., NFC error, no connection, etc.)
  • Count of total swipes, for fun
  • The currently selected team/event and a way to go back and change it

Nice to haves to round out the experience:

  • Flash a message if a BuzzCard is tapped while a swipe is already being processed
  • Play a sound on success or error

Add authentication screen

To include:

  • Login splash screen
  • Handling OAuth2 login process
  • Navigating to attendance screen on successful login

Add Google Play app update available notifications

Add notifications to nag and/or force users to update to the latest version of the app. The priority of each release can be set during our CI process.

My proposed update forcing strategy is below. The intent is to limit the number of older releases we have to maintain compatibility with, so this is intentionally aggressive.

  • If the app update priority is 5, it has to be installed immediately
  • If the app update priority is 4, and the update staleness (days since update was released) is <=1 day, show an optional update prompt. If the update staleness is at least 1 day, immediately install the update.
  • If the update priority is 3 and the update staleness is at least 4 days, show an optional update prompt.
  • If the update staleness is at least 14 days regardless of priority, show an optional prompt.
  • If the update staleness is at least 21 days regardless of priority, immediately install the update.

App update priorities, inspired by Google's suggestions:

  • Minor UI improvements: Low-priority update; request neither an optional update nor an immediate update. Update only when the user isn't interacting with your app.
  • Performance improvements: Medium-priority update; request an optional update.
  • Critical security update: High-priority update; request an immediate update.
Update priority Description Examples Update timeline for users
0/1/2 Very low or low priority UI touchups that don't impact functionality, releases with options to opt-in to beta features No prompt initially. Optional prompt starting 14 days after release. Immediate update 21 days after release.
3 Medium priority Medium-priority bug fixes, performance improvements, non-time-sensitive feature launches No prompts for the first 3 days. Optional starting 4 days after release. Immediate update 21 days after release.
4 High priority or time-sensitive High priority bug fixes, time-sensitive feature launches Optional for the first 24 hours, then immediate.
5 Critical bug fixes Crashes/bugs impacting major features, urgent vulnerabilities Immediate update required.

RuntimeException while refreshing user info if Apiary is down and maintenance page is returned

Sentry Issue: APIARY-ANDROID-1C

RuntimeException: <html>
<head>
  <title>Maintenance in progress</title>
</head>
<body style="font-family: sans-serif;margin:2em">
  <h1>Maintenance in progress</h1>
  <p>MyRoboJackets is currently unavailable due to scheduled maintenance. Please try again in a few hours.</p>
</body>
</html>

    at org.robojackets.apiary.ui.settings.SettingsViewModel$getUser$1.invokeSuspend(SettingsViewModel.kt:122)
...
(8 additional frame(s) were not displayed)

User info API error

Crash when logging out

Sentry isn't catching this, but:

  1. Login
  2. Record an attendance record by any method
  3. Logout

and the app crashes

Improve attendance events list ordering

Currently, the oldest events show first, which is counterintuitive. Also, maybe we should emphasize any events happening on the same day and perhaps even show a (dismissable) warning when taking attendance for an event that's not today

Add crash monitoring tool

Crashlytics and Sentry Android are both seemingly good options, worth doing more research to decide

Add NFC tap tips to app directly

I've found myself telling people this anyways so may as well put it as in-app education:

  • Align the center of the BuzzCard over the NFC on your phone. If you don't know where the NFC sensor is, search " nfc sensor location" using your preferred search enginer
  • You'll probably have to fiddle a little bit to find the best spot
  • If you have a particularly thick case or your phone isn't recognizing BuzzCards (usually it should vibrate) at all, try removing your case
  • Sometimes someone forgets their BuzzCard or one just won't scan. You can use the Enter GTID manually button and type in their GTID to take their attendance.

NoSuchElementException: List contains no element matching the predicate.

Sentry Issue: APIARY-ANDROID-T

NoSuchElementException: List contains no element matching the predicate.
    at androidx.navigation.compose.NavHostKt$NavHost$4.invoke(NavHost.kt:181)
    at androidx.navigation.compose.NavHostKt$NavHost$4.invoke(NavHost.kt:141)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
    at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
    at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
...
(38 additional frame(s) were not displayed)

Add settings screen

Needs to include:

  • App version info
  • Log out button
  • Basic info about logged in user
  • top secret fun easter egg

CI improvements

  • Make PR build notifications less spammy
  • Integrate with Sentry releases
  • Consider using deployments for PR build links
  • Add automated release notes and automatically release to internal test
  • Do not build tagged releases unless they are on main

IllegalStateException: Service has been disposed and rendered inoperable

Seems related to device rotation

Sentry Issue: APIARY-ANDROID-8

IllegalStateException: Service has been disposed and rendered inoperable
    at org.robojackets.apiary.auth.network.AuthHeaderInterceptor$intercept$1.invokeSuspend(AuthHeaderInterceptor.kt:20)
    at org.robojackets.apiary.auth.network.AuthHeaderInterceptor.intercept(AuthHeaderInterceptor.kt:18)
    at org.robojackets.apiary.network.UserAgentInterceptor.intercept(UserAgentInterceptor.kt:45)
...
(17 additional frame(s) were not displayed)

IllegalStateException: Restore State failed: destination 2081899864 cannot be found from the current destination Destina...

Sentry Issue: APIARY-ANDROID-13

IllegalStateException: Restore State failed: destination 2081899864 cannot be found from the current destination Destination(0x98d0beb0) route=settings
    at org.robojackets.apiary.MainActivity.AppNavigation(MainActivity.kt:246)
    at org.robojackets.apiary.MainActivity.access$AppNavigation(MainActivity.kt:81)
    at org.robojackets.apiary.MainActivity$onCreate$1$1$2$1$4$3$1$1.invoke(MainActivity.kt:214)
    at org.robojackets.apiary.MainActivity$onCreate$1$1$2$1$4$3$1$1.invoke(MainActivity.kt:213)
    at org.robojackets.apiary.base.ui.nfc.NfcRequiredKt.NfcRequired(NfcRequired.kt:58)
...
(48 additional frame(s) were not displayed)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.