This App has been developed as part of the MindOrks Android Online Course for Professionals. This is a mini demo version of the real Instagram app built with Android Jetpack and MVVM Architecture.
Note: Exercises that were also part of this course can be found here.
- User registration and management.
- Auto renewal of User access token.
- Tabs for viewing All User Posts, to create & publish a Post and to view "My Posts".
- Posts contain only a Photo which can be picked from System Gallery or captured through System Camera while creating a Post.
- Posts created by the logged-in User can be deleted by that logged-in User only.
- Allows to edit user profile information which includes-
- User profile picture
- Profile Name
- Profile Bio
- Post Likes screen to show who liked the Post, which gets launched on click of the likes count on a Post.
- Post Details screen to show the details of the Post, including the Post Photo and those who have liked the Post.
- Immersive Photo screen to view the Post Photo in detail, which gets launched on click of Post Photo in Post Details screen.
- User can like Posts by either-
- clicking on the Heart icon
- double tapping on Post Photo
- navigating to Post Likes or Post Details screen and then clicking on the Heart icon
Android device running with Android OS 5.0 (API Level 21) or above. Designed for Phones and NOT for Tablets.
- Register for the "MindOrks Instagram API" Key by enrolling to the MindOrks course. You will be given an API Key and a Base URL your application needs to point to.
- Create a Properties file named instagram_api.properties in the Project's root folder.
- Define a property named API_KEY_VAL and assign it the value of the API Key obtained from the registration process.
- Define another property named BASE_URL_VAL and assign it the value of the Base URL obtained from the registration process.
Note: This server is solely built and managed by MindOrks. So, please refrain from uploading any sensitive or copyrighted photo, while testing the application. Also, after signing up through this mobile application, do not forget your own login credentials since there is no way to recover if forgotten.
SignUp Screen | Login Screen | Home Screen / All Posts Tab |
---|---|---|
Upload Photo Post Tab | User Posts Tab | Edit Profile Screen |
---|---|---|
Likes Screen | Post Details Screen |
---|---|
Without Likes With Likes |
Without Likes With Likes |
Fullscreen Photo Immersive Screen (Portrait) | Fullscreen Photo Immersive Screen (Landscape) |
---|---|
- Kotlin
- Constraint Layout library to make layouts.
- Material Design library for Material UI.
- Android JetPack components-
- ViewBinding via Kotlin Property delegation, for binding UI elements with its objects.
- Room for caching data in local database and offline operation. Offline feature is not yet implemented in this application.
- ViewModel and LiveData to maintain the Activity's state, handle user actions and communicate updates to the Repositories.
- Android KTX for simplified and idiomatic Kotlin.
- Dagger2 for dependency injection of required services to the clients.
- Glide to load Images.
- Retrofit and OkHttp to communicate with the "MindOrks Instagram API" via REST calls.
- GSON to deserialize JSON to Java Objects.
- RxJava3 for Reactive programming.
- Timber for logging and debugging.
- Junit, Mockito, Hamcrest, Truth and Espresso for Testing.
App follows the recommended MVVM architecture with Repository pattern as advocated by Google, with certain tweaks as advocated by MindOrks in this course. Tweaks mainly consists of Base classes abstractions like-
- BaseActivity
- Facilitates the setup and abstraction to common tasks of an Activity like injecting dependencies, setting up the Views and
LiveData
observers, displaying Toasts, providing back key event for fragments and popping back stack accordingly, etc. - Each BaseActivity comes with a BaseViewModel which represents the Primary
ViewModel
of the Activity, providing abstraction for checking & establishing Network Connectivity, and to clear any Rx Disposables & other initializations when theViewModel
gets cleared. - BaseActivity also signals its
ViewModel
when it enters the CREATED state for the ViewModels to do required initializations.
- Facilitates the setup and abstraction to common tasks of an Activity like injecting dependencies, setting up the Views and
- BaseFragment
- Facilitates the setup and abstraction to common tasks of a Fragment like injecting dependencies, setting up the Views and
LiveData
observers, displaying Toasts, delegating back key event to its Activity, etc. - Each BaseFragment comes with a BaseViewModel which represents the Primary
ViewModel
of the Fragment, providing abstraction for checking & establishing Network Connectivity, and to clear any Rx Disposables & other initializations when theViewModel
gets cleared. - BaseFragment also signals its
ViewModel
when it enters the CREATED state for the ViewModels to do required initializations.
- Facilitates the setup and abstraction to common tasks of a Fragment like injecting dependencies, setting up the Views and
- Base Classes for
RecyclerView
's components-- BaseItemViewHolder -
- This ViewHolder is made Lifecycle aware in order to update its Item View in a lifecycle conscious way, i.e., it does NOT update the ItemView when it goes into background.
- Each Item's ViewHolder communicates with its own
ViewModel
, i.e., BaseItemViewModel for fetching information from any data source repository.
- BaseItemViewModel -
- This is the Base
ViewModel
for the Lifecycle aware BaseItemViewHolder. - Each Item's ViewModel updates its ItemView only when the BaseItemViewHolder, i.e., its ItemView is visible, by making use of
LiveData
.
- This is the Base
- BaseAdapter -
- This is the Adapter of
RecyclerView
hosted either by anActivity
or aFragment
. - ViewHolder's shown by this Adapter are of type BaseItemViewHolder.
- In order to make the BaseItemViewHolder Lifecycle aware, it observes the Lifecycle of the
Activity
/Fragment
hosting theRecyclerView
and triggers the Lifecycle state changes accordingly to each of the BaseItemViewHolders of RecyclerView Items.
- This is the Adapter of
- BaseItemViewHolder -
- When the hosting Activity/Fragment's
ViewModel
gets a new/updated data list to be shown, it is emitted to the RecyclerView's Adapter via aLiveData
. - RecyclerView's Adapter checks the new data with the current data, and notifies the data change accordingly using
AsyncListDiffer
withAsyncDifferConfig
. - This triggers calls to
onBindViewHolder()
of the Adapter for the Item ViewHolders at respective positions where the data has changed in order to be rebound. - The
onBindViewHolder()
of the Adapter delegates to the custombind()
method of BaseItemViewHolder, which in turn passes the item data provided by the Adapter to its BaseItemViewModel'sLiveData
. Transformations on thisLiveData
will get applied to fetch and keep whatever data needs to be shown on the ItemView when it becomes visible. So, if the ItemViews contain any images, they are downloaded and kept in the Item's BaseItemViewModel ready to be shown when the ItemView becomes visible. This maintains the state of the ItemView preventing unnecessary interactions with the data source repositories triggered by events such as configuration/orientation change. - When the ItemView becomes visible, all the transformed data events will be emitted to BaseItemViewHolder, which finally initializes the ItemView to show the data.
- In case of Item level interactions, events are only dispatched to the Item's BaseItemViewModel. If this requires a synchronization update in order to keep the corresponding item data in the main list in sync, then this event will be dispatched from the Item's BaseItemViewModel to the
ViewModel
of the hosting Activity/Fragment via a registered listener. - In case of Navigation based interactions, like navigating to some details screen on click of an Item, events are first dispatched to the Item's BaseItemViewModel and then from there to the
ViewModel
of the hosting Activity/Fragment via a registered listener.
- Listener
- BaseListenerObservable
- Meant for managing any kind of listeners used in the app, which registers listeners in a Thread-safe manner and dispatches callback events to registered listeners.
- Used for managing all listeners on BaseAdapter.
- BaseListenerObservable
- Dialogs
- BaseDialogFragment
- Facilitates the setup and abstraction to common tasks of a DialogFragment like injecting dependencies, setting up
LiveData
observers, constructing a templatedAlertDialog
using aMaterialAlertDialogBuilder
or a completeAlertDialog
with the provided custom View, displaying Toasts, etc. - Templated
AlertDialog
is constructed with UI fields that may be required for a particular category of Dialogs like say "Confirmation" or "Progress". Once templated, corresponding UI fields can be set with actual data at the time of displaying the Dialog and any unset fields in this case will be automatically hidden.
- Facilitates the setup and abstraction to common tasks of a DialogFragment like injecting dependencies, setting up
- BaseDialogViewModel
- Each BaseDialogFragment comes with a BaseDialogViewModel which represents the Primary
ViewModel
of the DialogFragment, providing abstraction to common tasks of DialogFragment like setting theLiveData
of UI fields and managing its state.
- Each BaseDialogFragment comes with a BaseDialogViewModel which represents the Primary
- BaseDialogFragment
- New Activity Result using
ActivityResultContract
- BaseActivityResultContracts
- Facilitates the setup of required Activity call contracts of the app.
- These contracts can also be used by the Fragments of the app.
- BaseActivityResultObserver
- Facilitates the setup of Lifecycle observers needed for receiving and handling the activity result in a separate class for the calls being registered via
ActivityResultRegistry
. - BaseActivity registers this Observer for Activity results.
- Facilitates the setup of Lifecycle observers needed for receiving and handling the activity result in a separate class for the calls being registered via
- BaseFragmentResultObserver
- Facilitates the setup of Lifecycle observers needed for receiving and handling the activity result in a separate class for the calls being registered via
ActivityResultRegistry
. - BaseFragment registers this Observer for Activity results.
- Facilitates the setup of Lifecycle observers needed for receiving and handling the activity result in a separate class for the calls being registered via
- BaseActivityResultContracts
- Fully Immersive Activity
- BaseImmersiveActivity
- An abstract BaseActivity to provide Fullscreen Immersion to activities that subclass this.
- Takes care of-
- publishing System UI visibility state change events to the Primary
ViewModel
of the activity. - observing Fullscreen Toggle request events on a registered UI element. This registration of UI element is required to be done by the subclasses.
- publishing System UI visibility state change events to the Primary
- BaseImmersiveViewModel
- This is the Primary
ViewModel
of BaseImmersiveActivity which is a subclass of BaseViewModel dedicated to managing common tasks of Fullscreen Immersion. - Manages the state of System UI visibility change and handles Fullscreen Toggle requests.
- This is the Primary
- BaseImmersiveActivity
- Event
- A wrapper to the content emitted as Single Live Events by a
LiveData
.
- A wrapper to the content emitted as Single Live Events by a
- Resource
- A Sealed class wrapper to Single Live Events associated with Status metadata information.
- Status metadata is an
Enum
of values - "SUCCESS", "ERROR", "LOADING" and "UNKNOWN".
- LiveDataExt
- Kotlin extension functions on
LiveData
to promote idiomatic code while observingLiveData
members. - This has extensions on
LiveData
of Event and also on Resource.
- Kotlin extension functions on
- ViewBindingUtils
- A set of Kotlin Lazy delegates which provides
ViewBinding
instance of anActivity
orFragment
or BaseItemViewHolder. - Use of Lazy delegates enables to get
ViewBinding
instance in an idiomatic way using a single statement in case ofActivity
andFragment
or using simple configuration in case of BaseItemViewHolder.
- A set of Kotlin Lazy delegates which provides
- BitStateTracker
- Tracks changes to a "State" using bit logic.
- Useful for merging state changes or updates to a Boolean value when working with
MediatorLiveData
.
- DialogManager
- Manages all dialogs shown by the subclasses of BaseActivity and BaseFragment of the app.
- Maintains a reference to the current active instance of
Dialog
shown. - Restores the active
Dialog
in case of configuration/orientation changes. This prevents a new instance of the sameDialog
from being shown in the event where an active instance of theDialog
was shown prior to such configuration/orientation change. - Provides methods for dismissing an active
Dialog
and also to show a newDialog
requested. - Takes care of dismissing any active Dialogs prior to displaying the requested one.
- AlertDialogExt
- Provides Kotlin extension functions on
AlertDialog
. - Mainly used for templating
AlertDialog
when built withMaterialAlertDialogBuilder
in BaseDialogFragment.
- Provides Kotlin extension functions on
- ImmersiveModeCompatExt
- Kotlin extension functions on
ComponentActivity
andWindow
to facilitate Fullscreen Immersive mode setup for anyComponentActivity
. - Provides extension method on-
Window
to register a listener on itsDecorView
to track and capture System UI visibility change events.ComponentActivity
to ensure app content is always shown below the device display cutouts if any in order to prevent content jump and overlap when System UI visibility changes.ComponentActivity
to toggle window immersion which internally alters the System UI visibility accordingly.
- Kotlin extension functions on
- RecyclerViewExt
- Kotlin extension functions on
RecyclerView
to promote idiomatic code while working with its properties.
- Kotlin extension functions on
- BottomNavigationViewActions
- Provides Espresso
ViewAction
s to perform Navigation action to Menu Items onBottomNavigationView
.
- Provides Espresso
- RecyclerViewItemActions
- Provides Espresso
ViewAction
s to perform interactions on anyView
inRecyclerView
Items.
- Provides Espresso
- TestActivityResultRegistry
ActivityResultRegistry
subclass to stub the Activity Result of the Activity launched for testing.- Additionally, records whether the requested Activity was successfully launched or not.
- ImageMatchers
- Provides Hamcrest
Matcher
s for matching the Drawables in Views. - Used for matching-
- A
Drawable
set as background image of anyView
. - Any of the
CompoundDrawable
set on aTextView
. - A
Drawable
set as the source image ofImageView
.
- A
- Provides Hamcrest
- RecyclerViewMatchers
- Provides Hamcrest
Matcher
s for matching anyView
inRecyclerView
Items.
- Provides Hamcrest
- TextInputLayoutMatchers
- Provides Hamcrest
Matcher
s for matching aTextInputLayout
. - Used for matching a
TextInputLayout
based on the Error set on it.
- Provides Hamcrest
- DataModelObjectProvider
- Provides easy to use dummy data model instances and functions to manipulate/transform data models.
- Used extensively for testing in both Instrumented and Local Unit tests.
main
└───java
└───com.mindorks.kaushiknsanji.instagram.demo
│ InstagramApplication.kt # Root Package contains the Application subclass
│
├───data # For the data sources and repositories of the app (Remote and Local)
│ ├───local # For Local database of the app and other local resources like Shared Preferences
│ │ ├───db # Local Room database
│ │ │ │ Converter.kt # This package contains the abstract Room database and any Converters it needs
│ │ │ │ DatabaseService.kt
│ │ │ │
│ │ │ ├───dao # Database access objects
│ │ │ │
│ │ │ └───entity # Database entities
│ │ │
│ │ └───prefs # Classes that communicate with Shared Preferences
│ │
│ ├───model # Application wide Data Models required by the App which may also be required by the Remote Request/Response Models
│ │
│ ├───remote # For Remote Request/Response Models and API related classes
│ │ │ Networking.kt # Utility class for configuring the Retrofit and providing the instance of required API Service
│ │ │
│ │ ├───api # API Endpoints and API Service interfaces
│ │ │
│ │ ├───auth # Authentication Token management and renewal
│ │ │
│ │ ├───model # Commonly embedded Remote Response Models
│ │ │
│ │ ├───request # Remote Request Models
│ │ │
│ │ └───response # Remote Response Models
│ │
│ └───repository # Classes that provide seamless access to data through the Repository pattern
│
├───di # For Dagger dependency injection
│ │ qualifiers.kt # Dagger Qualifiers
│ │ scopes.kt # Dagger Scopes
│ │
│ ├───component # Dagger Component Interfaces
│ │
│ └───module # Dagger Modules
│
├───ui # For everything related to UI Screens where each screen has Activity/Fragment and their ViewModels
│ ├───base # Base class abstractions of Activity, Fragment, DialogFragment, ViewModel, ActivityResult, etc.
│ │ │
│ │ └───listeners # Abstraction for the Listener entities of the app
│ │
│ ├───common # Any UI component common to or shared across the app
│ │ ├───dialogs # Common Dialogs of the app that has DialogFragments, ViewModels and Metadata classes if any pertaining to it
│ │ │ ├───option # Confirmation Alert Dialogs
│ │ │ │
│ │ │ ├───picture # Custom Dialog for Photo selection/capture
│ │ │ │
│ │ │ └───progress # Custom Dialog for Progress
│ │ │
│ │ └───likes # Post Likes RecyclerView item component which is common across multiple screens of the app
│ │
│ ├───detail # Post Details screen
│ │ │
│ │ └───photo # Post Photo screen for viewing Photo, launched from Post Details screen
│ │
│ ├───home # Home screen for all Posts
│ │ │
│ │ ├───posts # Post RecyclerView Item component displayed on Home screen
│ │ │
│ │ └───util # Utility classes if any for promoting Test Driven Development. Will be usually used by the ViewModels in MVVM.
│ │
│ ├───like # Post Likes screen
│ │
│ ├───login # Login screen
│ │
│ ├───main # Main screen
│ │
│ ├───photo # Post Photo upload screen
│ │
│ ├───profile # User Profile screen
│ │ │
│ │ ├───edit # Edit Profile screen, launched from User Profile screen
│ │ │
│ │ └───posts # Post RecyclerView Item component displayed on User Profile screen
│ │
│ ├───signup # SignUp screen
│ │
│ └───splash # Splash screen
│
└───utils # For all utilities required by the app
├───common # Utilities common to many functionalities within the app
│
├───display # Utilities pertaining to display like Dialogs, Menus, Themes, Toasts, Views, Text Appearance, etc.
│
├───factory # Factory utilities for supplying an instance of some entity like ViewModel
│
├───log # Utilities related to logs
│
├───network # Networking utilities
│
├───rx # Utilities for Reactive Rx Streams
│
└───widget # Utilities for View Widgets like RecyclerView, TextInputEditText, TextInputLayout, etc.
- The "test" source set will have the same structure as the "main" source set.
- If there are some utilities needed to facilitate testing, then it will be placed in
"<applicationId>/utils/test"
package.
test
└───java
└───com.mindorks.kaushiknsanji.instagram.demo
├───data # Local Unit Tests for data sources and repositories
│ └───repository
│
├───ui # Local Unit Tests for UI ViewModels and their utilities if any
│ ├───home
│ │ │
│ │ └───util
│ │
│ ├───login
│ │
│ ├───main
│ │
│ ├───signup
│ │
│ └───splash
│
└───utils # For testing "main" source set utilities and also for making utilities needed for facilitating tests
├───common # Local Unit Tests for common utilities
│
└───rx
TestSchedulerProvider.kt # SchedulerProvider implementation which provides the Reactive TestScheduler instance needed for Local Unit Tests
- The "androidTest" source set will have the same structure as the "main" source set.
- If there are some utilities needed to facilitate testing, then it will be placed in
"<applicationId>/utils/test"
package. - Fake implementations needed for Instrumented testing will be placed in the respective packages of original interfaces/implementations.
- TestRules needed for Instrumented Test setup will be placed in the
"rule"
package under the respective packages of the component being initialized for testing.
androidTest
└───java
└───com.mindorks.kaushiknsanji.instagram.demo
├───data # Fake implementations and TestRules for data sources
│ ├───local
│ │ └───rule # TestRules that need to communicate with the local database or resources for test setup
│ │ UserSessionRule.kt # TestRule for the setup of Signed-in User session
│ │
│ └───remote
│ └───api # Stubbed API Service interfaces with fake implementation for Instrumented Tests
│ FakeNetworkService.kt # Fake implementation of NetworkService
│
├───di # Dagger dependency injection needed for Instrumented Tests
│ ├───component # Dagger Test Component interfaces
│ │ TestComponent.kt # Test Component for exposing dependencies from the Test Module for Application
│ │
│ ├───module # Dagger Test Modules
│ │ ApplicationTestModule.kt # Test Module for Application
│ │
│ └───rule # Dagger TestRules for test setup
│ TestComponentRule.kt # TestRule for the setup of Dagger Test Component
│
├───ui # Instrumented Tests for UI screens (Activity or Fragment)
│ ├───home
│ │
│ ├───login
│ │
│ ├───main
│ │
│ ├───signup
│ │
│ └───splash
│
└───utils # For stubbing "main" source set utilities and also for making utilities needed for facilitating tests
├───network # Stubbed networking utilities with fake implementation for Instrumented Tests
│ FakeNetworkHelperImpl.kt # Fake implementation of NetworkHelper for testing Network related tasks
│
└───test # Utilities needed for facilitating Instrumented tests
│ InstrumentedTestUtils.kt # Utility functions commonly needed for many Instrumented tests and its utilities
│
├───action # Custom Espresso View Actions
│
├───activity # Utilities needed for working with or testing any Activity
│ TestActivityResultRegistry.kt # ActivityResultRegistry subclass to stub the result of Activity launched for tests
│
└───matcher # Custom Hamcrest Matchers
- The "sharedTest" source set will have the same structure as the "main" source set.
- Contains codes shared between "test" source set and "androidTest" source set.
- Mainly used for sharing Test utilities for Instrumented and/or Local Unit Tests which will be placed in
"<applicationId>/utils/test"
package.
sharedTest
└───java
└───com.mindorks.kaushiknsanji.instagram.demo
└───utils # For testing "main" source set utilities and also for making utilities needed for facilitating tests
└───test # Utilities needed for facilitating tests (Instrumented and/or Local Unit Tests)
DataModelObjectProvider.kt
TestConstants.kt
- Orientation based scaling of Images
- New Navigation component for App navigation
- Migration from Dagger to Hilt
- Offline functionality
- More Test cases
Copyright 2019 Kaushik N. Sanji
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.