WildFyre Android client base proposal
This is the current state of my attempt my making an Android client for WildFyre.
Since the original repository I started working on was a fresh new one, I put my code in this one using git replace --graft.
This means this diff would be a complete reboot of the codebase.
The 3 main parts are going to be the following:
- Current features
- Project dependencies
- Code organization
Logging in and logging out
Users can use their username and password to receive an authentication token. Only the token is stored, in the application preferences for simplicity. Logging out will remove the token from storage and bring back the user to the login screen.
The user interface can be divided in 3 parts:
- The toolbar at the top, containing (when necessary) a spinner to switch between areas, and displaying a badge with the user's notification count (if any)
- The navigation panel on the left, allowing the user to, from top to bottom:
- see and edit their profile bio and picture
- navigate through the app (obviously)
- changing settings
- The central zone, where content is displayed
The profile editor is a simple popup allowing the user to change their bio and choose a picture as their profile avatar.
The client checks the size of the picture to disallow images above 512k.
Only documents on the phone can be selected; no support for taking a picture with the camera has been implemented.
- Home, where new posts are to be shown
- Notifications, listing the user's notifications, and allowing them to clear all notifications
- Archive, listing the posts the user is subscribed to
- My posts, listing the user's own posts
- Theme, allowing the user to switch between a light and a dark theme (dark mode ftw)
- Notification badge, allowing the user to toggle the notification badge on the toolbar
Post lists: notifications, archive, own posts
3 out of the 4 destinations are roughly the same: Notifications, Archive and My posts.
The post are listed as previews containing part of their content, their author (unless the post is anonymous), as well as a subtitle.
In the case of notifications, the subtitle is the number of unread comments. In the case of archive posts or the user's own posts, the subtitle is the date at which the post was created.
Clicking on any post preview will display the post's full content as markdown.
Some design choices
Navigation menu replacing the bottom navigation
The bottom navigation is faster to use than having to open the left menu first, but has a major drawback: it permanently takes up vertical space.
Vertical space is also going to be used for extenguish/ignite buttons; and in general, a maximum amount of space should be reserved for the content rather than what's surrounding it.
Inverted icon colors
The icon of the current WildFyre application is the WildFyre logo in red/orange on a white background.
The icon used in this code is a white logo on an orange background instead. It looks at least 20% cooler this way.
The application is written entirely in Kotlin, so it naturally depends on the standard Kotlin library.
Androidx, Google material
Standard libraries providing widgets conforming to material design and backwards-compatibility APIs.
Used for building ViewModels from the MVVM architecture principles.
Used for creating the navigation graph and linking it to the navigation view.
Used to easily load and transform images from the web such as profile avatars.
Used for interfacing with the server API in place of the WildFyre cross-platform library for now.
Used for displaying posts content as markdown.
The application loosely follows the MVVM architecture, as per Google now recommends for Android apps.
The code hierarchy somewhat reflects it:
- net.wildfyre.client.data contains code related to data fetching and server API calls.
- net.wildfyre.client.viewmodels contains ViewModels keeping runtime data independenly from the UI.
- net.wildfyre.client.views contains UI classes: the main activity and its fragments.
This architecture separates layers so that each of them only accesses a single other layer: The UI elements use their viewmodels, the viewmodels use the repositories, and the repositories use the services.
This architecture disallows things like, for example, an activity or fragment directly making API calls using the web service.
Another design element following Google's recommendations is the usage of LiveData and a reactive pattern.
Instead of requesting data and then expecting the results, data is observed, and change requests are independent from observation.
Conventions for the following paragraphs:
- I: interface
- A: abstract class
- C: concrete class implementation
- O: object
- M: multiple classes/objects/functions
Global scope classes
|O||Constants.kt||Centralized place to store constant values used by other classes|
|C||Application.kt||Application subclass doing application scope initializations|
|I||AreaSelectingFragment.kt||Fragment for displaying an area spinner in the menu|
|A||FailureHandlingActivity.kt||Activity implementing FailureHandler (shows a toast with the failure's error message)|
|A||FailureHandlingFragment.kt||Fragment implementing FailureHandler (shows a toast with the failure's error message)|
|A||ItemsListFragment.kt||Fragment displaying a list of post previews that can be clicked to show the full post|
|A||PostsFragment.kt||Fragment extending ItemsListFragment and implementing AreaSelectingFragment to serve as base for ArchiveFragment and OwnPostsFragment|
|C||MainActivity.kt||The only activity instantiated the project, hosting the base UI (toolbar, navigation view) as well as fragments for displaying content|
|C||LoginFragment||Fragment requiring the user to authenticate themselves, and navigating to HomeFragment when the user has done so|
|C||HomeFragment.kt||Fragment for displaying new posts to the user|
|C||NotificationsFragment.kt||Fragment displaying previews of the user's notifications|
|C||ArchiveFragment.kt||Fragment displaying post previews from a user's archive|
|C||OwnPostsFragment.kt||Fragment displaying previews of the user's own posts|
|C||PostFragment.kt||Fragment displaying the full content of a single post to the user|
Quick visualization of fragment classes hierarchy:
├─FailureHandlingFragment ├─AreaSelectingFragment │ │ ├─────LoginFragment │ │ │ ├─────HomeFragment──────────────────┤ │ │ ├───┬─ItemsListFragment │ │ │ │ │ ├─────NotificationsFragment │ │ │ │ │ └───┬─PostsFragment─────────────┘ │ │ │ ├─────ArchiveFragment │ │ │ └─────OwnPostsFragments │ └─────PostFragment
|A||ItemsAdapter.kt||Base adapter for RecyclerViews used by ItemsListFragment implementations|
|C||NotificationsAdapter.kt||Adapter used by NotificationsFragment|
|C||PostsAdapter.kt||Adapter used by PostsFragment implementations|
|C||MarkdownRecyclerView.kt||RecyclerView ensuring that a post's images are always properly loaded|
|C||PostPlugin.kt||Markwon plugin that forces newlines to be honored and makes images grow as large as possible|
|M||Utils.kt||Contains a function replacing the [img: 42] image pattern with a markdown image pattern|
ViewModel classes mostly just hold and transform data for usage by activities and fragments.
They provide data fetched by repositories in a way that can be used in XML layouts with databinding.
|I||Failure.kt||Type used for representing errors occuring during operations such as API calls|
|C||FailureHandler.kt||Interface used to help failures bubble their way from the data layer up to the UI layer|
|M||Models.kt||Models used in the server API, as simple classes without any method|
|M||Repositories.kt||Singletons providing access to data independently from the service used. They abstract data aggregation from the rest of the app|
|M||Services.kt||Services (although there is only one for now) able to fetch the data needed by the app. The only service implemented is the web service connecting to api.wildfyre.net. It uses Retrofit in place of the WildFyre library until it is ready to be used|