HTML Assignments
L01 - Assignment 1
Assignment 1
Deadline:
- Tuesday group: 21.10
- Thursday group: 23.10
Task 1 - 6 pts
Build the "ComposeShop" application with one main navigation structure handled by bottom navigation and a navigation drawer, and one nested navigation flow dedicated to the checkout process. An example run is shown in video L1.
The main application interface combines bottom navigation and a navigation drawer. All these screens should be part of one main navigation graph (NavHost).
Bottom Navigation Screens
Navigation happens by tapping the appropriate icon in the bottom bar, which calls navController.navigate("screen_name").
- Home screen: the first screen after application launch. It displays a list.
- Categories screen: a list of product categories, for example "Electronics", "Fashion", "Home".
- Cart screen: displays products added to the cart. Static data is acceptable. The "Go to checkout" button starts the nested navigation flow.
- Favorites screen: a list of products marked by the user as favorites.
Drawer Navigation Screens
- My profile screen: displays basic user information, for example name and email address.
- Order history screen: displays previous orders with date and status.
- Settings screen: contains configuration options, for example switches for dark mode or notifications. These can be simulated.
- "Log out" button: simulates the logout process and navigates to the start screen.
Nested Navigation Flow: Checkout Process
Navigation to this flow starts on the cart screen. After entering this mode, bottom navigation and the drawer should no longer be visible, so the user can focus fully on the purchase process. Navigation inside this graph is linear: the user goes from step 1 to step 2, and then to step 3.
- Screen 1: delivery address. Contains a simple form for entering address data. The "Next" button navigates to the next screen in the flow.
- Screen 2: payment method. Allows the user to choose one of several payment options, for example with
RadioButton. The "Summary" button navigates to the final step. - Screen 3: summary and confirmation. Displays all collected information: cart products, delivery address and selected payment method.
- The "Order and pay" button finishes the process. Its click performs the key navigation action: it removes the whole checkout graph (
checkout_graph) from the back stack, so the user cannot accidentally return to the payment screen with the back button. It then navigates the user back to the main application screen.
Oral answer - 4 pts
Grades
| grade | points |
|---|---|
| 3.0 | 6 pts |
| 3.5 | 7 pts |
| 4.0 | 8 pts |
| 4.5 | 9 pts |
| 5.0 | 10 pts |
Example run
Questions
Topics: Compose Navigation, NavHost, NavController, nested graphs, drawer, bottom navigation, back stack.
Additional questions: Activity, Intent, Context.
- What is an Activity and what is its role in an application built entirely with Jetpack Compose?
- What is Context and why is it needed in an Android application?
- What is the Intent mechanism used for in Android?
- What is the role of the NavHost component in Compose navigation?
- What is NavController used for?
- What is a route in the context of navigation?
- How do we define a screen, or navigation destination, inside NavHost?
- What does the startDestination parameter define in NavHost?
- What is nested navigation and why do we use it?
- What does navController.popBackStack() do?
- What is the back stack?
- How can you pass a simple argument, for example a product ID, to another screen in Compose Navigation?
- What is a deep link and how can an Intent Filter in the manifest be related to it?
- How does centralizing navigation routes, for example in an object, help maintain the code?
- Describe the Activity lifecycle and its most important states.
- What is an explicit Intent? Give an example.
- What is an implicit Intent? Give an example.
- How does the NavController state created with rememberNavController() survive recomposition?
- What is the setContent function used for in ComponentActivity?
- How can you open and close a navigation drawer programmatically?
- What are Application Context and Activity Context?
- Can one NavController manage multiple NavHost instances?
Assignment 2
Deadline:
- Tuesday group: 4.11
- Thursday group: 06.11
Task 1 - 6 pts
The "Kino GURU" application is used for browsing movie information and uses concurrency to fetch different independent data sets at the same time.
The application consists of two screens:
- a popular movies list
- a movie details screen
The key part is the details screen, which must fetch and display multiple pieces of information about the selected movie concurrently.
Movie list screen: after opening the screen, the application should show a loading state (CircularProgressIndicator). In the background, using a coroutine (launch), it should call the function suspend fun fetchPopularMovies(): List<Movie>, which returns a list of movies after 2 seconds, for example 10-15 items.
After the data is fetched, the movie list should be displayed on the screen, for example in LazyColumn. Each list item should be clickable.
Movie details screen: after tapping a movie on the movie list screen, the user is navigated to the details screen. Use Compose Navigation. On this screen, the application must fetch three independent data sets for the selected movie at the same time:
- basic details (
suspend fun fetchMovieDetails()): director, production year, genre; delay: 1.5 seconds - reviews (
suspend fun fetchMovieReviews()): list of user reviews; delay: 3 seconds - similar movies (
suspend fun fetchSimilarMovies()): list of several other titles; delay: 2 seconds
Use the async builder for each of the three requests to start them concurrently. The main coroutine (launch) must wait for all three operations to finish by using .await(). During the whole loading process, a progress indicator (CircularProgressIndicator) must be visible on the screen.
After all data is fetched, the screen should display it in separate sections, for example "Description", "Reviews" and "Recommended". Additionally, the screen should show text with the total data fetching time. This time should be about 3 seconds, which is the duration of the longest operation, not 6.5 seconds, which would be the sum of all durations.
Oral answer - 4 pts
Grades
| grade | points |
|---|---|
| 3.0 | 6 pts |
| 3.5 | 7 pts |
| 4.0 | 8 pts |
| 4.5 | 9 pts |
| 5.0 | 10 pts |
Example run
Questions
Topics: Blocking the UI thread, suspend, CoroutineScope, launch, async, await, Deferred, concurrency.
Additional questions: Activity lifecycle, Intent, Context.
- Why is running long operations on the main UI thread a problem?
- What is a coroutine and how does it differ from a traditional thread?
- What is a suspend function?
- What rule must we follow when calling suspend functions?
- What is the fundamental difference between Thread.sleep() and delay()?
- What is CoroutineScope and what is its role?
- What is structured concurrency?
- What happens to coroutines when their parent CoroutineScope is cancelled?
- What is the launch coroutine builder used for?
- Does launch return an operation result? If not, what does it return?
- What is the async coroutine builder used for?
- What is the main difference between launch and async?
- What is a Deferred object?
- What does the .await() function called on a Deferred object do?
- Explain why three requests that take 1.5s, 3s, and 2s concurrently take about 3s, not 6.5s.
- What does it mean that an operation is asynchronous?
- What does it mean that operations are concurrent?
- What is an Activity and what are its main lifecycle states?
- How is the Activity lifecycle related to lifecycleScope?
- Is Intent a synchronous or asynchronous operation?
- How can you check whether a coroutine is active?
- What does runBlocking do and why should we avoid using it in production Android code?
- Can you start a coroutine without a CoroutineScope?
- How can you handle an exception inside a coroutine started with launch?
- What is the difference between concurrency and parallelism?
- How is rememberCoroutineScope() related to the lifecycle of a Composable component?
Assignment 3
Deadline:
- Tuesday group: 02.12
- Thursday group: 04.12
Task 1 - 3 pts
Create an application that works as a simple dashboard for tracking fictional prices of two cryptocurrencies ("ComposeCoin" and "KotlinCoin") and the user's wallet balance.
Functional requirements:
- Architecture:
- The application must use the
MVVMarchitecture. - Create a
Repositoryclass that simulates data sources. - The
ViewModelis responsible for logic, combining streams and exposing state to the UI. - Data layer (
Repository): - The repository must expose two independent cold
Flowstreams: fun getComposeCoinPriceStream(): Flow<Double>: emits a new random price for "ComposeCoin" every 3 seconds.fun getKotlinCoinPriceStream(): Flow<Double>: emits a new random price for "KotlinCoin" every 5 seconds.- Logic layer (
ViewModel): - The
ViewModelmust create one mainUiStateobject containing: current prices of both currencies, the number of owned coins (you can assume fixed initial values, for example 10 ComposeCoins and 2 KotlinCoins), and the calculated total wallet value. - Use
flowOnso that simulated data fetching in the repository runs in the background,combineto combine streams, andstateInto convert a cold stream into a hot stream. - Presentation layer (UI -
Composable): - The screen should display data from
UiStateby observingStateFlowfrom theViewModel. - The interface should display a loading state (
CircularProgressIndicator) beforeStateFlowemits its first value.
Task 2 - 3 pts
Create an application that simulates product serial number verification. The validation result, success or error, should be displayed as a one-time notification.
Functional requirements:
- Architecture:
- The application must use the
MVVMarchitecture. - Data layer (
Repository): - Create a
SerialNumberRepositoryclass. - Define a regular function inside it:
fun verifySerialNumber(serial: String): Boolean {
// Verification simulation.
Thread.sleep(2500)
// Simple validation logic.
return serial.startsWith("SN-") && serial.length == 10
}
- Logic layer (
ViewModel): - The
ViewModelshould have aStateFlowstoring UI state (UiState). - Create the
onVerifyClicked()function. Inside this function, in aviewModelScope.launchcoroutine: - The
ViewModelmust expose validation events as a hotSharedFlow. This stream should be created from a coldFlow. - Use
withContextto move execution of the blockingrepository.verifySerialNumber()function to theDefaultdispatcher, andshareInto create a hot, shared stream that sends a one-time validation result (event) to all UI observers. In this task there is one observer: aSnackbar.
- Presentation layer (UI -
Composable): - The screen should contain a
TextFieldfor entering the serial number, a Verify button and aCircularProgressIndicatorvisible during verification. - The UI must subscribe to the
SharedFlowfrom theViewModel.
Oral answer - 4 pts
Grades
| grade | points |
|---|---|
| 3.0 | 6 pts |
| 3.5 | 7 pts |
| 4.0 | 8 pts |
| 4.5 | 9 pts |
| 5.0 | 10 pts |
Example runs
Questions
Topics: MVVM, StateFlow, SharedFlow, cold vs hot streams, stateIn, sharedIn, withContext, combine.
Additional questions: Android architecture, Activity, Intent, Context.
- What problem does application architecture, for example MVVM, solve?
- What is viewModelScope and how is it related to the ViewModel lifecycle?
- Describe the roles of the MVVM components: Model, View, and ViewModel.
- What is Context? Can it be safely stored in a ViewModel?
- What is the main responsibility of a ViewModel?
- Why does a ViewModel survive configuration changes, for example screen rotation?
- What is the difference between a cold and a hot data stream?
- Which stream type is Flow and which stream type is StateFlow?
- List the key features of StateFlow.
- What is StateFlow most commonly used for in MVVM architecture?
- What is .collectAsState() or collectAsStateWithLifecycle used for in Compose?
- What problem does SharedFlow solve?
- Why should we not use StateFlow to send one-time events, for example navigation events?
- Describe the _privateMutableStateFlow and publicStateFlow pattern. What is its purpose?
- What is .asStateFlow() used for?
- What is the combine operator used for on Flow streams?
- What is withContext used for? Give an example.
- How does Dispatchers.IO differ from Dispatchers.Default?
- What is the purpose of the stateIn operator?
- What does the parameter started = SharingStarted.WhileSubscribed(5000) mean?
- What is the purpose of the sharedIn operator?
- How does withContext differ from flowOn?
- What is an Activity and how does it communicate with a ViewModel?
- What is Context and why is it needed to access system resources?
- How can data be passed between two Activities using Intent?
- Explain what unidirectional data flow (UDF) is.
- How does collectAsState differ from collectAsStateWithLifecycle?
- What role does viewModelScope play in a ViewModel?
Assignment 4
Deadline:
- Tuesday group: 08.01
- Thursday group: 15.01
Task 1 - 3 pts
Create an application that works as a simple personal video game library. The user can add new games to a Room database, mark them as favorites, and filter the view to show either all games or only favorite games. The filter setting should be persisted with DataStore.
Functional requirements:
- Architecture:
- The application must use the
MVVMarchitecture. - Create two separate repositories:
GamesRepositoryfor operations on theRoomdatabase.SettingsRepositoryfor reading and writing settings withPreferences DataStore.- Data storage (
Room): - Create a
Gameentity (@Entity) with fields:id(primary key, auto-generated),title,genreandisFavorite(Boolean). - Create
GameDaowith the following methods: suspend fun insert(game: Game)suspend fun update(game: Game)suspend fun delete(game: Game)fun getAllGamesStream(): Flow<List<Game>>(returns a stream of all games)fun getFavoriteGamesStream(): Flow<List<Game>>(returns a stream of favorite games only,WHERE isFavorite = true)- Configure the
@Databaseclass. - Settings storage (
Preferences DataStore): - Use
Preferences DataStoreto save one setting:showOnlyFavorites(Boolean). - Create
SettingsRepository, which exposes aFlow<Boolean>with this setting and the functionsuspend fun setShowOnlyFavorites(showOnly: Boolean). - Logic layer (
ViewModel): - Create
GamesViewModel, which receives both repositories in the constructor (GamesRepositoryandSettingsRepository). - The
ViewModelmust create one mainUiStateobject containing: the list of games to display and the current value of theshowOnlyFavoritesfilter. - The
ViewModelmust listen for changes in the settings stream fromSettingsRepository. WhenshowOnlyFavoriteschanges, theViewModelmust switch to the appropriate stream fromGamesRepository(getAllGamesStreamorgetFavoriteGamesStream). You can useflatMapLatest. - The
ViewModelmust expose functions for: - adding a new game
- toggling the
isFavoritestatus for an existing game - changing the
showOnlyFavoritesfilter setting - Presentation layer (UI - Composable):
- The main screen should display the game list (
LazyColumn). Each list item should show the title, genre and a heart icon that allows togglingisFavorite. - At the top of the screen, for example in
TopAppBaror below it, place aSwitchconnected to theshowOnlyFavoritessetting. Changing it should persist the preference and automatically filter the game list. - Add a simple form, for example
TextFieldandButtonorFloatingActionButton, for adding new games to the database.
Task 2 - 3 pts
Create an application that fetches data from the public "Rick and Morty" API by using the Retrofit library. The application allows browsing a character list and dynamically filtering it by status: alive, dead or unknown.
API: The Rick and Morty API (https://rickandmortyapi.com/) does not require an API key.
Functional requirements:
- Architecture:
- The application must use the
MVVMarchitecture. - Create
RetrofitInstanceandApiServicefor API communication. - Logic layer (
ViewModel): - Create
CharacterViewModel. - The ViewModel should have a
StateFlowstoring the currentUiState.UiStateshould contain: the character list, loading state (isLoading), possible error and currently selected status filter. - The ViewModel must expose a function such as
fun onFilterChanged(newStatus: String), which: - updates
UiStateso theUIreflects the newly selected filter - starts a coroutine in
viewModelScopethat fetches a new filtered character list from theRepository, using the new status as a parameter - Presentation layer (UI - Composable):
- The screen should display a character list (
LazyColumn) with image, name and status. - At the top of the screen, place a group of buttons (
RowwithButtonorFilterChip) that allow the user to choose a filter: "All" (without the status parameter), "Alive", "Dead", "Unknown". - Tapping a filter button should call
onFilterChangedin theViewModel. - The interface must correctly handle the loading state (
CircularProgressIndicator) and display error messages. - Tip: to load images from a
URL, you can use the popularCoillibrary:implementation("io.coil-kt:coil-compose:version_number")
Oral answer - 4 pts
Grades
| grade | points |
|---|---|
| 3.0 | 6 pts |
| 3.5 | 7 pts |
| 4.0 | 8 pts |
| 4.5 | 9 pts |
| 5.0 | 10 pts |
Example runs
Questions
Topics: Room, DataStore, Retrofit, Entity, DAO, Database, @Query, @GET.
Additional questions: SQL basics, Activity, Intent, Context.
- How does DataStore differ from SharedPreferences?
- Why does reading data from DataStore return a Flow? What is the advantage?
- What is a relational database?
- How does SQL differ from SQLite?
- List and briefly describe the four basic CRUD operations in SQL.
- List the three main components or annotations of the Room library.
- What is the role of the @Entity annotation?
- What is @PrimaryKey used for?
- What is the role of an interface annotated with @Dao?
- What is the advantage of returning Flow from DAO functions?
- What is the Retrofit library used for?
- What is a REST API?
- What is JSON?
- What is the role of the ApiService interface in Retrofit?
- What is the @GET annotation used for?
- How can you pass a parameter to a request using the @Query annotation?
- What is a DTO (Data Transfer Object) and why do we use it?
- What is an Activity and what is its relevance to the database lifecycle?
- What is Context and why is it needed to create a Room database or DataStore instance?
- How can you open a website in a browser using Intent?
- What does OnConflictStrategy.REPLACE mean in the @Insert annotation?
- What does the @Upsert annotation do in Room?
- What is a converter, for example GsonConverterFactory, used for in Retrofit?
- What is baseUrl in Retrofit?
- What does a typical data flow look like in an architecture using Room and Retrofit?
Assignment 5
Deadline:
- Tuesday group: 03.02
- Thursday group: 29.01
Task 1 - 6 pts
Create an application that works as a simple currency converter. The user can choose the source and target currency from lists, enter an amount, and the application fetches current exchange rates from a public API and displays the converted result.
API: Use the free Frankfurter API (https://frankfurter.dev), which does not require an API key.
- Endpoint for fetching the list of available currencies:
https://api.frankfurter.dev/v1/currencies - Endpoint for fetching rates:
https://api.frankfurter.dev/v1/latest(you can use thebaseandsymbolsattributes)
- Architecture:
- The application must use MVVM architecture with a domain layer.
- Flow scheme:
View->ViewModel->Use Case->Repository->API(Retrofit). - Use
Hiltfor dependency injection across all layers. - Data layer:
Retrofit: createApiServicewith two suspend functions corresponding to the endpoints above.Repository: createCurrencyRepository, which acts as an intermediary in communication withApiService.- Domain layer (
Domain Layer): - Business logic must be encapsulated in
Use Caseclasses. Create at least threeUse Caseclasses: GetSupportedCurrenciesUseCase: fetch and optionally sort the list of available currency codes, for example "USD", "PLN", "EUR".ConvertCurrencyUseCase: perform the main business logic, converting an amount from one currency to another.FormatCurrencyResultUseCase: format the numeric result into a user-readableString, for example "100.00 USD = 405.50 PLN". ThisUse Caseis responsible for presentation logic.- Logic and presentation layer (ViewModel and UI):
ViewModel:- Must inject (
@Inject) all requiredUse Caseclasses. - Manages
UiState, which contains among other things: the list of available currencies, selected source and target currency, entered amount, loading state and final formatted result. - The
ViewModeldoes not perform any calculations or formatting directly. It only calls the appropriateUse Caseclasses and updates state based on their results. UI(Composable):- The screen should contain:
TextFieldfor entering the amount.- Two dropdown menus (
DropDownMenuorExposedDropdownMenuBox) for selecting source and target currency. The currency list should be fetched on startup withGetSupportedCurrenciesUseCase. - A Convert
Button, which starts the logic in the ViewModel. Textdisplaying the formatted result fromUiState.- The interface must handle the loading state, for example by disabling the button and showing
CircularProgressIndicator.
Oral answer - 4 pts
Grades
| grade | points |
|---|---|
| 3.0 | 6 pts |
| 3.5 | 7 pts |
| 4.0 | 8 pts |
| 4.5 | 9 pts |
| 5.0 | 10 pts |
Example run
Questions
Topics: Domain layer, Use Case, Hilt, @Inject, @Module, @Provides, @HiltViewModel.
Additional questions: Activity, Intent, Context in the context of DI.
- What problem does Dependency Injection (DI) solve?
- How does Hilt differ from Dagger?
- How does Hilt or another DI framework simplify working with MVVM?
- What is the role of the @HiltAndroidApp annotation and where do we place it?
- What is the role of the @AndroidEntryPoint annotation?
- How do we inject dependencies into a ViewModel using Hilt? Which two annotations do we use?
- How does @Inject constructor differ from a method annotated with @Provides? When do we use each one?
- What is a Hilt module (@Module)?
- What is the @InstallIn annotation used for?
- What does @InstallIn(SingletonComponent::class) mean?
- Why are Retrofit and Room Database usually provided as singletons?
- Which function do we use in a Composable to obtain a ViewModel managed by Hilt?
- Why do we introduce an additional domain layer in application architecture?
- What is a Use Case or Interactor?
- What is the main principle when designing Use Cases?
- How does a ViewModel become thinner after introducing Use Cases?
- What does operator fun invoke do in a Use Case class?
- Can a Use Case depend on a ViewModel? Justify your answer.
- Can a Use Case depend on the Android framework, for example Context? Why?
- Draw and describe the Clean Architecture flow: View -> ViewModel -> Use Case -> Repository.
- What are the benefits of having a domain layer?
- What is an Activity and how does Hilt manage dependencies during its lifecycle?
- What is Context and how does Hilt provide it? Which annotation do we use?
- What is Intent?
- How does Hilt provide SavedStateHandle to a ViewModel?
- Explain how Hilt builds a dependency graph using this example: a ViewModel needs a Repository, and the Repository needs an ApiService.
- What is the role of the @Singleton annotation?
- Does a Repository always have to be a singleton? Justify your answer.
- Describe how Hilt solves the problem of creating ViewModels with parameters.