diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index da2fa27..c945f47 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -60,7 +60,7 @@ jobs:
uses: gradle/gradle-build-action@v2
- name: Build Libraries
- run: ./gradlew build --no-daemon
+ run: ./gradlew :app:build --no-daemon
# - name: AVD cache
# uses: actions/cache@v3
# id: avd-cache
diff --git a/README.md b/README.md
index 6057007..cb0c26f 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,27 @@
-
![Frontegg_Android_SDK (Kotlin)](./logo.png)
Frontegg is a web platform where SaaS companies can set up their fully managed, scalable and brand aware - SaaS features
and integrate them into their SaaS portals in up to 5 lines of code.
-## Project Requirements
+## Table of Contents
+
+- [Get Started](#get-started)
+ - [Project Requirements](#project-requirements)
+ - [Prepare Frontegg workspace](#prepare-frontegg-workspace)
+ - [Setup Hosted Login](#setup-hosted-login)
+ - [Add Frontegg package to the project](#add-frontegg-package-to-the-project)
+ - [Set minimum sdk version](#set-minimum-sdk-version)
+ - [Configure build config fields](#configure-build-config-fields)
+ - [Config Android AssetLinks](#config-android-assetlinks)
+- [Usage](#usage)
+ - [Initialize FronteggApp](#initialize-fronteggapp)
+ - [Login with Frontegg](#login-with-frontegg)
+ - [Logout user](#logout)
+ - [Switch Tenant](#switch-tenant)
+
+## Get Started
+
+### Project Requirements
- Android SDK 26+
Set defaultConfig's minSDK to 26+ in build.gradle:
@@ -30,34 +47,27 @@ and integrate them into their SaaS portals in up to 5 lines of code.
}
```
-
-## Getting Started
-
- - [Prepare Frontegg workspace](#prepare-frontegg-workspace)
- - [Setup Hosted Login](#setup-hosted-login)
- - [Add frontegg package to the project](#add-frontegg-package-to-the-project)
- - [Setup build variables](#setup-build-variables)
- - [Initialize FronteggApp](#initialize-fronteggapp)
- - [Add custom loading screen](#add-custom-loading-screen)
- - [Config Android AssetLinks](#config-android-assetlinks)
-
### Prepare Frontegg workspace
Navigate to [Frontegg Portal Settings](https://portal.frontegg.com/development/settings), If you don't have application
follow integration steps after signing up.
-Copy FronteggDomain to future steps from [Frontegg Portal Domain](https://portal.frontegg.com/development/settings/domains)
+Copy FronteggDomain to future steps
+from [Frontegg Portal Domain](https://portal.frontegg.com/development/settings/domains)
### Setup Hosted Login
- Navigate to [Login Method Settings](https://portal.frontegg.com/development/authentication/hosted)
- Toggle Hosted login method
-- Add `{{LOGIN_URL}}/mobile/callback`
+- Add `{{ANDROID_PACKAGE_NAME}}://{{FRONTEGG_BASE_URL}}/ios/oauth/callback`
+- Replace `ANDROID_PACKAGE_NAME` with your application identifier
+- Replace `FRONTEGG_BASE_URL` with your Frontegg base url
-### Add frontegg package to the project
+### Add Frontegg package to the project
- Open you project
- Find your app's build.gradle file
-- Add the following to your dependencies section:
+- Add the following to your dependencies section:
+
```groovy
dependencies {
// Add the Frontegg Android Kotlin SDK
@@ -65,34 +75,123 @@ Copy FronteggDomain to future steps from [Frontegg Portal Domain](https://portal
}
```
-### Setup build variables
+### Set minimum sdk version
+
+To set up your Android minimum sdk version, open root gradle file at`android/build.gradle`,
+and add/edit the `minSdkVersion` under `buildscript.ext`:
+
+```groovy
+buildscript {
+ ext {
+ minSdkVersion = 26
+ // ...
+ }
+}
+```
+
+### Configure build config fields
-To setup your Android application to communicate with Frontegg, you have to use `manifestPlaceholders` property in your build.gradle
-file, this property will store frontegg hostname (without https) and client id from previous step:
+To set up your Android application on to communicate with Frontegg, you have to add `buildConfigField` property the
+gradle `android/app/build.gradle`.
+This property will store frontegg hostname (without https) and client id from previous step:
```groovy
-def fronteggDomain = "DOMAIN_HOST.com"
-def fronteggClientId = "CLIENT_ID"
+def fronteggDomain = "FRONTEGG_DOMAIN_HOST.com" // without protocol https://
+def fronteggClientId = "FRONTEGG_CLIENT_ID"
android {
defaultConfig {
+
manifestPlaceholders = [
+ "package_name" : applicationId,
"frontegg_domain" : fronteggDomain,
- frontegg_client_id: fronteggClientId
+ "frontegg_client_id": fronteggClientId
]
buildConfigField "String", 'FRONTEGG_DOMAIN', "\"$fronteggDomain\""
buildConfigField "String", 'FRONTEGG_CLIENT_ID', "\"$fronteggClientId\""
}
- /* Use buildTypes for release / debug configurations */
- // ...
+
+
+}
+```
+
+Add bundleConfig=true if not exists inside the android section inside the app gradle `android/app/build.gradle`
+
+```groovy
+android {
+ buildFeatures {
+ buildConfig = true
+ }
+}
+```
+
+### Permissions
+
+Add `INTERNET` permission to the app's manifest file.
+
+```xml
+
+
+
+```
+
+### Config Android AssetLinks
+
+Configuring your Android `AssetLinks` is required for Magic Link authentication / Reset Password / Activate Account /
+login with IdPs.
+
+To add your `AssetLinks` to your Frontegg application, you will need to update in each of your integrated Frontegg
+Environments the `AssetLinks` that you would like to use with that Environment. Send a POST request
+to `https://api.frontegg.com/vendors/resources/associated-domains/v1/android` with the following payload:
+
+```
+{
+ "packageName": "YOUR_APPLICATION_PACKAGE_NAME",
+ "sha256CertFingerprints": ["YOUR_KEYSTORE_CERT_FINGERPRINTS"]
}
```
+Each Android app has multiple certificate fingerprint, to get your `DEBUG` sha256CertFingerprint you have to run the
+following command:
+
+For Debug mode, run the following command and copy the `SHA-256` value
+
+NOTE: make sure to choose the Variant and Config equals to `debug`
+
+```bash
+./gradlew signingReport
+
+###################
+# Example Output:
+###################
+
+# Variant: debug
+# Config: debug
+# Store: /Users/davidfrontegg/.android/debug.keystore
+# Alias: AndroidDebugKey
+# MD5: 25:F5:99:23:FC:12:CA:10:8C:43:F4:02:7D:AD:DC:B6
+# SHA1: FC:3C:88:D6:BF:4E:62:2E:F0:24:1D:DB:D7:15:36:D6:3E:14:84:50
+# SHA-256: D9:6B:4A:FD:62:45:81:65:98:4D:5C:8C:A0:68:7B:7B:A5:31:BD:2B:9B:48:D9:CF:20:AE:56:FD:90:C1:C5:EE
+# Valid until: Tuesday, 18 June 2052
+
+```
+
+For Release mode, Extract the SHA256 using keytool from your `Release` keystore file:
+
+```bash
+keytool -list -v -keystore /PATH/file.jks -alias YourAlias -storepass *** -keypass ***
+```
+
+In order to use our API’s, follow [this guide](https://docs.frontegg.com/reference/getting-started-with-your-api) to
+generate a vendor token.
+
+## Usage
+
### Initialize FronteggApp
-Create a custom `App` class that extends `android.app.Application` to initialize `FronteggApp`
+Create a custom `App` class that extends `android.app.Application` to initialize `FronteggApp`:
```kotlin
package com.frontegg.demo
@@ -109,7 +208,6 @@ class App : Application() {
BuildConfig.FRONTEGG_DOMAIN,
BuildConfig.FRONTEGG_CLIENT_ID,
this, // Application Context
- null
)
}
}
@@ -118,208 +216,89 @@ class App : Application() {
Register the custom `App` in the app's manifest file
**AndroidManifest.xml:**
+
```xml
- >
+ android:name=".App">
```
-android:name=".App"
-### Add Frontegg Activity
+android:name=".App"
-Create `FronteggActivity` class that extends `com.frontegg.android.AbstractFronteggActivity` to handle authenticated redirects
+## Login with Frontegg
-**FronteggAcivity.kt:**
+In order to login with Frontegg, you have to call `FronteggAuth.instance.login` method with `activtity` context.
+Login method will open Frontegg hosted login page, and will return user data after successful login.
```kotlin
-package com.frontegg.demo
-
-import android.content.Intent
-import com.frontegg.android.AbstractFronteggActivity
-class FronteggActivity: AbstractFronteggActivity() {
+import com.frontegg.android.FronteggAuth
- /**
- * This function will be called everytime the FronteggActivity
- * successfully authenticated and should redirect the user to
- * authenticated screens
- *
- * NOTE: Replace the `MainActivity::class.java` with your Main Activity class
- */
- override fun navigateToAuthenticated() {
- val intent = Intent(this, MainActivity::class.java)
- startActivity(intent)
- finish()
+class FirstFragment : Fragment() {
+ // ...
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+
+ binding.loginButton.setOnClickListener {
+ FronteggAuth.instance.login(requireActivity())
}
+ }
+ // ...
}
-```
-
-Register the `FronteggActivity` in the app's manifest file
-**AndroidManifest.xml:**
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
```
-### Add Frontegg Logout Activity
-
+## Logout user
-Create `FronteggLogoutActivity` class that extends `com.frontegg.android.AbstractFronteggLogoutActivity` to handle logout redirects
-
-**FronteggLogoutActivity.kt:**
+In order to logout user, you have to call `FronteggAuth.instance.logout` method.
+Logout method will clear all user data from the device.
```kotlin
-package com.frontegg.demo
-import android.content.Intent
-import com.frontegg.android.AbstractFronteggLogoutActivity
+import com.frontegg.android.FronteggAuth
-class FronteggLogoutActivity: AbstractFronteggLogoutActivity() {
- /**
- * This function will be called everytime the FronteggLogoutActivity
- * successfully logged out and should redirect the user to
- * your the previous created `FronteggActivity`
- *
- * NOTE: Replace the `FronteggActivity::class.java` with your `FronteggActivity` class
- */
- override fun navigateToFronteggLogin() {
- val intent = Intent(this, FronteggActivity::class.java)
- startActivity(intent)
- finish()
+class FirstFragment : Fragment() {
+ // ...
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+
+ binding.logoutButton.setOnClickListener {
+ FronteggAuth.instance.logout()
}
+ }
+ // ...
}
-```
-Register the `FronteggLogoutActivity` in the app's manifest file
-
-**AndroidManifest.xml:**
-```xml
-
```
-### Add custom loading screen
-In order to customize Frontegg loading screen:
+## Switch Tenant
-- Create new layout file contains your loader screen design:
-```xml
-
-
-
-
-
-
-
-
-```
+In order to switch tenant, you have to call `FronteggAuth.instance.switchTenant` method with `activtity` context.
-- Add `R.layout.loader` FronteggApp.init as the 4th argument:
```kotlin
- package com.frontegg.demo
+
+import com.frontegg.android.FronteggAuth
+
+class FirstFragment : Fragment() {
+ // ...
- import android.app.Application
- import com.frontegg.android.FronteggApp
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- class App : Application() {
+ val tenantIds = FronteggAuth.instance.user.value?.tenantIds ?: listOf()
- override fun onCreate() {
- super.onCreate()
-
- FronteggApp.init(
- BuildConfig.FRONTEGG_DOMAIN,
- BuildConfig.FRONTEGG_CLIENT_ID,
- this,
- R.layout.loader // <<-- here
- )
- }
+ /**
+ * pick one from `tenantIds` list:
+ */
+ val tenantToSwitchTo = tenantIds[0]
+
+ binding.switchTenant.setOnClickListener {
+ FronteggAuth.instance.switchTenant(tenantToSwitchTo)
+ }
}
-```
-
-### Permissions
-
-Add `INTERNET` permission to the app's manifest file.
-
-```xml
-
-```
-
-
-### Config Android AssetLinks
-Configuring your Android `AssetLinks` is required for Magic Link authentication / Reset Password / Activate Account / login with IdPs.
-
-To add your `AssetLinks` to your Frontegg application, you will need to update in each of your integrated Frontegg Environments the `AssetLinks` that you would like to use with that Environment. Send a POST request to `https://api.frontegg.com/vendors/resources/associated-domains/v1/android` with the following payload:
-```
-{
- "packageName": "YOUR_APPLICATION_PACKAGE_NAME",
- "sha256CertFingerprints": ["YOUR_KEYSTORE_CERT_FINGERPRINTS"]
+ // ...
}
-```
-
-Each Android app has multiple certificate fingerprint, to get your `DEBUG` sha256CertFingerprint you have to run the following command:
-
-For Debug mode, run the following command and copy the `SHA-256` value
-```bash
-./gradlew signingReport
-
-###################
-# Example Output:
-###################
-
-# Variant: debugAndroidTest
-# Config: debug
-# Store: /Users/davidfrontegg/.android/debug.keystore
-# Alias: AndroidDebugKey
-# MD5: 25:F5:99:23:FC:12:CA:10:8C:43:F4:02:7D:AD:DC:B6
-# SHA1: FC:3C:88:D6:BF:4E:62:2E:F0:24:1D:DB:D7:15:36:D6:3E:14:84:50
-# SHA-256: D9:6B:4A:FD:62:45:81:65:98:4D:5C:8C:A0:68:7B:7B:A5:31:BD:2B:9B:48:D9:CF:20:AE:56:FD:90:C1:C5:EE
-# Valid until: Tuesday, 18 June 2052
```
-
-
-For Release mode, Extract the SHA256 using keytool from your `Release` keystore file:
-```bash
-keytool -list -v -keystore /PATH/file.jks -alias YourAlias -storepass *** -keypass ***
-```
-
-In order to use our API’s, follow [this guide](https://docs.frontegg.com/reference/getting-started-with-your-api) to generate a vendor token.
diff --git a/android/build.gradle b/android/build.gradle
index 2c7631c..7512564 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -6,15 +6,16 @@ plugins {
}
group 'com.frontegg.android'
-version '1.0.4'
+version '1.0.6'
+
android {
namespace 'com.frontegg.android'
- compileSdk 33
+ compileSdk 34
defaultConfig {
minSdk 26
- targetSdk 33
+ targetSdk 34
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
versionName "$version"
@@ -48,11 +49,12 @@ android {
dependencies {
implementation 'androidx.core:core-ktx:[1.7,)'
implementation 'io.reactivex.rxjava3:rxkotlin:3.0.1'
- implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.2'
implementation 'com.google.code.gson:gson:2.8.9'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
- implementation "androidx.browser:browser:1.5.0"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
+ implementation "androidx.browser:browser:1.6.0"
+ implementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.5.0'
}
afterEvaluate {
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index a827463..4a0f89a 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -2,9 +2,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/src/main/java/com/frontegg/android/FronteggAuth.kt b/android/src/main/java/com/frontegg/android/FronteggAuth.kt
index 31a2f4b..b104fcc 100644
--- a/android/src/main/java/com/frontegg/android/FronteggAuth.kt
+++ b/android/src/main/java/com/frontegg/android/FronteggAuth.kt
@@ -114,8 +114,6 @@ class FronteggAuth(
refreshTaskRunner?.cancel()
-
-
if (decoded.exp > 0) {
val now: Long = Instant.now().toEpochMilli()
val offset = (((decoded.exp * 1000) - now) * 0.80).toLong()
@@ -166,6 +164,7 @@ class FronteggAuth(
credentialManager.clear()
val handler = Handler(Looper.getMainLooper())
handler.post {
+ isLoading.value = false
callback()
}
}
@@ -176,4 +175,20 @@ class FronteggAuth(
fun login(activity: Activity) {
AuthenticationActivity.authenticateUsingBrowser(activity)
}
+
+
+ fun switchTenant(tenantId: String, callback: () -> Unit = {}) {
+ GlobalScope.launch(Dispatchers.IO) {
+
+ isLoading.value = true
+ api.switchTenant(tenantId)
+ refreshTokenIfNeeded()
+
+ val handler = Handler(Looper.getMainLooper())
+ handler.post {
+ isLoading.value = false
+ callback()
+ }
+ }
+ }
}
diff --git a/android/src/main/java/com/frontegg/android/models/Tenant.kt b/android/src/main/java/com/frontegg/android/models/Tenant.kt
new file mode 100644
index 0000000..9647a9d
--- /dev/null
+++ b/android/src/main/java/com/frontegg/android/models/Tenant.kt
@@ -0,0 +1,15 @@
+package com.frontegg.android.models
+
+class Tenant {
+ public lateinit var id: String
+ public lateinit var name: String
+ public var creatorEmail: String? = null
+ public var creatorName: String? = null
+ public lateinit var tenantId: String
+ public lateinit var createdAt: String
+ public lateinit var updatedAt: String
+ public var isReseller: Boolean = false
+ public lateinit var metadata: String
+ public lateinit var vendorId: String
+ public var website: String? = null
+}
diff --git a/android/src/main/java/com/frontegg/android/models/User.kt b/android/src/main/java/com/frontegg/android/models/User.kt
index 16b4da2..7e043fc 100644
--- a/android/src/main/java/com/frontegg/android/models/User.kt
+++ b/android/src/main/java/com/frontegg/android/models/User.kt
@@ -12,6 +12,8 @@ class User {
public lateinit var permissions: List
public lateinit var tenantId: String
public lateinit var tenantIds: List
+ public lateinit var tenants: List
+ public lateinit var activeTenant: Tenant
public var activatedForTenant: Boolean = false
public var metadata: String? = null
public var verified: Boolean = false
diff --git a/android/src/main/java/com/frontegg/android/services/Api.kt b/android/src/main/java/com/frontegg/android/services/Api.kt
index 21163d0..e0a0870 100644
--- a/android/src/main/java/com/frontegg/android/services/Api.kt
+++ b/android/src/main/java/com/frontegg/android/services/Api.kt
@@ -6,6 +6,7 @@ import com.frontegg.android.utils.ApiConstants
import com.frontegg.android.utils.CredentialKeys
import com.google.gson.Gson
import com.google.gson.JsonObject
+import com.google.gson.reflect.TypeToken
import okhttp3.Call
import okhttp3.Headers
import okhttp3.Headers.Companion.toHeaders
@@ -67,6 +68,26 @@ open class Api(
return this.httpClient.newCall(request)
}
+ private fun buildPutRequest(
+ path: String,
+ body: JsonObject,
+ additionalHeaders: Map = mapOf()
+ ): Call {
+ val url = "${this.baseUrl}/$path".toHttpUrl()
+ val requestBuilder = Request.Builder()
+ val bodyRequest =
+ body.toString()
+ .toRequestBody("application/json; charset=utf-8".toMediaType())
+ val headers = this.prepareHeaders(additionalHeaders);
+
+ requestBuilder.method("PUT", bodyRequest)
+ requestBuilder.headers(headers);
+ requestBuilder.url(url)
+
+ val request = requestBuilder.build()
+ return this.httpClient.newCall(request)
+ }
+
private fun buildGetRequest(path: String): Call {
val url = "$baseUrl/$path".toHttpUrl()
val requestBuilder = Request.Builder()
@@ -82,10 +103,27 @@ open class Api(
@Throws(IllegalArgumentException::class, IOException::class)
public fun me(): User? {
- val call = buildGetRequest(ApiConstants.me)
- val response = call.execute()
- if (response.isSuccessful) {
- return Gson().fromJson(response.body!!.string(), User::class.java)
+ val meCall = buildGetRequest(ApiConstants.me)
+ val meResponse = meCall.execute()
+ val tenantsCall = buildGetRequest(ApiConstants.tenants)
+ val tenantsResponse = tenantsCall.execute()
+
+ if (meResponse.isSuccessful && tenantsResponse.isSuccessful) {
+ // Parsing JSON strings into JsonObject
+ val gson = Gson()
+ val mapType = object : TypeToken>() {}.type
+
+ val meJsonStr = meResponse.body!!.string()
+ val tenantsJsonStr = tenantsResponse.body!!.string()
+
+ val meJson: MutableMap = gson.fromJson(meJsonStr, mapType)
+ val tenantsJson: MutableMap = gson.fromJson(tenantsJsonStr, mapType)
+
+ meJson["tenants"] = tenantsJson["tenants"] as Any
+ meJson["activeTenant"] = tenantsJson["activeTenant"] as Any
+
+ val merged = Gson().toJson(meJson)
+ return Gson().fromJson(merged, User::class.java)
}
return null
@@ -142,4 +180,11 @@ open class Api(
call.execute()
}
}
+
+ fun switchTenant(tenantId: String) {
+ val data = JsonObject()
+ data.addProperty("tenantId", tenantId)
+ val call = buildPutRequest(ApiConstants.switchTenant, data)
+ call.execute()
+ }
}
\ No newline at end of file
diff --git a/android/src/main/java/com/frontegg/android/utils/Constants.kt b/android/src/main/java/com/frontegg/android/utils/Constants.kt
index 7013f84..3c79a47 100644
--- a/android/src/main/java/com/frontegg/android/utils/Constants.kt
+++ b/android/src/main/java/com/frontegg/android/utils/Constants.kt
@@ -2,12 +2,15 @@ package com.frontegg.android.utils
import com.frontegg.android.FronteggApp
+
class ApiConstants {
companion object {
const val me: String = "identity/resources/users/v2/me"
+ const val tenants: String = "identity/resources/users/v3/me/tenants"
const val refreshToken: String = "oauth/token"
const val exchangeToken: String = "oauth/token"
const val logout: String = "identity/resources/auth/v1/logout"
+ const val switchTenant: String = "identity/resources/users/v1/tenant"
}
}
@@ -16,7 +19,11 @@ class Constants {
companion object {
fun oauthCallbackUrl(baseUrl: String): String {
- return "$baseUrl/android/${FronteggApp.getInstance().packageName}/callback"
+
+ val host = baseUrl.substring("https://".length)
+ val packageName = FronteggApp.getInstance().packageName
+
+ return "${packageName}://${host}/android/oauth/callback"
}
}
diff --git a/android/src/main/java/com/frontegg/android/utils/ObservableValue.kt b/android/src/main/java/com/frontegg/android/utils/ObservableValue.kt
index 2fa81c9..521edd6 100644
--- a/android/src/main/java/com/frontegg/android/utils/ObservableValue.kt
+++ b/android/src/main/java/com/frontegg/android/utils/ObservableValue.kt
@@ -1,5 +1,7 @@
package com.frontegg.android.utils
+import android.os.Handler
+import android.os.Looper
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.functions.Consumer
import io.reactivex.rxjava3.subjects.PublishSubject
@@ -25,6 +27,7 @@ class ObservableValue(value: T) {
}
fun subscribe(onNext: Consumer>): Disposable {
+ observable.subscribe()
val disposable = observable.subscribe(onNext)
onNext.accept(nullableObject)
return disposable
diff --git a/app/build.gradle b/app/build.gradle
index 538a4b1..9242747 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,7 +8,7 @@ def fronteggClientId = "b6adfe4c-d695-4c04-b95f-3ec9fd0c6cca"
android {
namespace 'com.frontegg.demo'
- compileSdk 33
+ compileSdk 34
defaultConfig {
applicationId "com.frontegg.demo"
@@ -61,9 +61,10 @@ dependencies {
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
implementation 'androidx.tracing:tracing:1.1.0'
implementation project(path: ':android')
-
-
-
+ implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
+ implementation 'de.hdodenhof:circleimageview:3.1.0'
+ implementation 'com.github.bumptech.glide:glide:4.12.0'
androidTestImplementation 'org.jetbrains.kotlin:kotlin-stdlib:' + rootProject.kotlinVersion
androidTestImplementation 'androidx.test:core:' + rootProject.coreVersion
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cc6e298..6f7c3fd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,36 +15,18 @@
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
-
+ android:label="@string/title_activity_navigation" >
+
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/App.kt b/app/src/main/java/com/frontegg/demo/App.kt
index 3dbdf9a..cfd1aa8 100644
--- a/app/src/main/java/com/frontegg/demo/App.kt
+++ b/app/src/main/java/com/frontegg/demo/App.kt
@@ -5,12 +5,17 @@ import com.frontegg.android.FronteggApp
class App : Application() {
+ companion object {
+ public lateinit var instance: App
+ }
override fun onCreate() {
super.onCreate()
+ instance = this
FronteggApp.init(
BuildConfig.FRONTEGG_DOMAIN,
BuildConfig.FRONTEGG_CLIENT_ID,
- this
+ this,
+
)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/FirstFragment.kt b/app/src/main/java/com/frontegg/demo/AuthFragment.kt
similarity index 66%
rename from app/src/main/java/com/frontegg/demo/FirstFragment.kt
rename to app/src/main/java/com/frontegg/demo/AuthFragment.kt
index 1918665..f5328c3 100644
--- a/app/src/main/java/com/frontegg/demo/FirstFragment.kt
+++ b/app/src/main/java/com/frontegg/demo/AuthFragment.kt
@@ -1,22 +1,22 @@
package com.frontegg.demo
-import android.content.Intent
+import android.app.Activity
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.navigation.fragment.findNavController
+import androidx.appcompat.app.AppCompatActivity
import com.frontegg.android.FronteggAuth
-import com.frontegg.demo.databinding.FragmentFirstBinding
+import com.frontegg.demo.databinding.FragmentAuthBinding
import io.reactivex.rxjava3.disposables.Disposable
/**
* A simple [Fragment] subclass as the default destination in the navigation.
*/
-class FirstFragment : Fragment() {
+class AuthFragment : Fragment() {
- private var _binding: FragmentFirstBinding? = null
+ private var _binding: FragmentAuthBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
@@ -28,7 +28,7 @@ class FirstFragment : Fragment() {
savedInstanceState: Bundle?
): View {
- _binding = FragmentFirstBinding.inflate(inflater, container, false)
+ _binding = FragmentAuthBinding.inflate(inflater, container, false)
return binding.root
}
@@ -36,22 +36,10 @@ class FirstFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
-
- binding.logoutButton.setOnClickListener {
-
- FronteggAuth.instance.logout()
- }
-
binding.loginButton.setOnClickListener {
FronteggAuth.instance.login(requireActivity())
}
-
- disposables.add(FronteggAuth.instance.user.subscribe {
- activity?.runOnUiThread {
- binding.textviewFirst.text = it.value?.email
- }
- })
}
override fun onDestroyView() {
diff --git a/app/src/main/java/com/frontegg/demo/FronteggActivity.kt b/app/src/main/java/com/frontegg/demo/FronteggActivity.kt
deleted file mode 100644
index 8dd6ed4..0000000
--- a/app/src/main/java/com/frontegg/demo/FronteggActivity.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.frontegg.demo
-
-
-class FronteggActivity() {
-// fun navigateToAuthenticated() {
-// val intent = Intent(this, MainActivity::class.java)
-// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
-//// startActivity(intent)
-//// finish()
-// }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/MainActivity.kt b/app/src/main/java/com/frontegg/demo/MainActivity.kt
deleted file mode 100644
index e79ba01..0000000
--- a/app/src/main/java/com/frontegg/demo/MainActivity.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.frontegg.demo
-
-import android.content.Intent
-import android.net.Uri
-import android.os.Bundle
-import android.util.Log
-import android.view.Menu
-import android.view.MenuItem
-import androidx.appcompat.app.AppCompatActivity
-import androidx.navigation.findNavController
-import androidx.navigation.ui.AppBarConfiguration
-import androidx.navigation.ui.navigateUp
-import androidx.navigation.ui.setupActionBarWithNavController
-import com.frontegg.android.utils.AuthorizeUrlGenerator
-import com.frontegg.demo.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-
-class MainActivity : AppCompatActivity() {
-
- private lateinit var appBarConfiguration: AppBarConfiguration
- private lateinit var binding: ActivityMainBinding
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- binding = ActivityMainBinding.inflate(layoutInflater)
- setContentView(binding.root)
-
-
- setSupportActionBar(binding.toolbar)
-
- val navController = findNavController(R.id.nav_host_fragment_content_main)
- appBarConfiguration = AppBarConfiguration(navController.graph)
- setupActionBarWithNavController(navController, appBarConfiguration)
-
- binding.fab.setOnClickListener { view ->
- Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).show()
-
- }
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- // Inflate the menu; this adds items to the action bar if it is present.
- menuInflater.inflate(R.menu.menu_main, menu)
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- return when (item.itemId) {
- R.id.action_settings -> true
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- override fun onSupportNavigateUp(): Boolean {
- val navController = findNavController(R.id.nav_host_fragment_content_main)
- return navController.navigateUp(appBarConfiguration)
- || super.onSupportNavigateUp()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/NavigationActivity.kt b/app/src/main/java/com/frontegg/demo/NavigationActivity.kt
new file mode 100644
index 0000000..dedcd82
--- /dev/null
+++ b/app/src/main/java/com/frontegg/demo/NavigationActivity.kt
@@ -0,0 +1,103 @@
+package com.frontegg.demo
+
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import com.google.android.material.bottomnavigation.BottomNavigationView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.navigation.NavController
+import androidx.navigation.findNavController
+import androidx.navigation.ui.AppBarConfiguration
+import androidx.navigation.ui.setupActionBarWithNavController
+import androidx.navigation.ui.setupWithNavController
+import com.frontegg.android.FronteggAuth
+import com.frontegg.android.utils.NullableObject
+import com.frontegg.demo.databinding.ActivityNavigationBinding
+import io.reactivex.rxjava3.functions.Consumer
+
+class NavigationActivity : AppCompatActivity() {
+
+ companion object {
+ private val TAG = NavigationActivity::class.java.canonicalName
+ }
+
+ private lateinit var binding: ActivityNavigationBinding
+ private var fronteggAuth = FronteggAuth.instance
+ private lateinit var navController: NavController
+
+
+ private val authenticatedTabs = AppBarConfiguration(
+ setOf(
+ R.id.navigation_home, R.id.navigation_tenants
+ )
+ )
+ private val nonAuthTabs = AppBarConfiguration(
+ setOf(
+ R.id.navigation_login
+ )
+ )
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ binding = ActivityNavigationBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ val navView: BottomNavigationView = binding.navView
+
+ navController = findNavController(R.id.nav_host_fragment_activity_navigation)
+
+ navView.setupWithNavController(navController)
+ setupActionBarWithNavController(navController, nonAuthTabs)
+
+ navController.setGraph(R.navigation.not_auth_navigation)
+ fronteggAuth.showLoader.subscribe(this.onShowLoaderChange)
+ fronteggAuth.isAuthenticated.subscribe(this.onIsAuthenticatedChange)
+ }
+
+ private var lastVisibilityState = View.VISIBLE
+ private val onShowLoaderChange: Consumer> = Consumer {
+
+ Log.d(TAG, "showLoader: ${it.value}")
+ runOnUiThread {
+ if (it.value) {
+ supportActionBar?.hide()
+ binding.container.visibility = View.GONE
+ } else {
+ binding.container.visibility = View.VISIBLE
+ if (lastVisibilityState == View.VISIBLE) {
+ supportActionBar?.show()
+ }
+ }
+ }
+ }
+
+ private val onIsAuthenticatedChange: Consumer> = Consumer {
+ Log.d(TAG, "isAuthenticated: ${it.value}")
+ runOnUiThread {
+ if (it.value) {
+ setupActionBarWithNavController(navController, authenticatedTabs)
+ navController.setGraph(R.navigation.navigation)
+ binding.navView.visibility = View.VISIBLE
+ setToolbarVisibility(true)
+ } else {
+ setupActionBarWithNavController(navController, nonAuthTabs)
+ navController.setGraph(R.navigation.not_auth_navigation)
+ binding.navView.visibility = View.GONE
+ setToolbarVisibility(false)
+ }
+ }
+ }
+
+
+ private fun setToolbarVisibility(visible: Boolean) {
+ lastVisibilityState = if (visible) View.VISIBLE else View.GONE
+ if (visible) {
+ supportActionBar?.show()
+ } else {
+ supportActionBar?.hide()
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/SecondFragment.kt b/app/src/main/java/com/frontegg/demo/SecondFragment.kt
deleted file mode 100644
index 67da33b..0000000
--- a/app/src/main/java/com/frontegg/demo/SecondFragment.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.frontegg.demo
-
-import android.os.Bundle
-import androidx.fragment.app.Fragment
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.navigation.fragment.findNavController
-import com.frontegg.demo.databinding.FragmentSecondBinding
-
-/**
- * A simple [Fragment] subclass as the second destination in the navigation.
- */
-class SecondFragment : Fragment() {
-
- private var _binding: FragmentSecondBinding? = null
-
- // This property is only valid between onCreateView and
- // onDestroyView.
- private val binding get() = _binding!!
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
-
- _binding = FragmentSecondBinding.inflate(inflater, container, false)
- return binding.root
-
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- binding.buttonSecond.setOnClickListener {
- findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
- }
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/ui/home/HomeFragment.kt b/app/src/main/java/com/frontegg/demo/ui/home/HomeFragment.kt
new file mode 100644
index 0000000..fb1ac51
--- /dev/null
+++ b/app/src/main/java/com/frontegg/demo/ui/home/HomeFragment.kt
@@ -0,0 +1,59 @@
+package com.frontegg.demo.ui.home
+
+import android.graphics.Color
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.bumptech.glide.Glide
+import com.frontegg.android.FronteggAuth
+import com.frontegg.demo.databinding.FragmentHomeBinding
+
+class HomeFragment : Fragment() {
+
+ private var _binding: FragmentHomeBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val homeViewModel = ViewModelProvider(this)[HomeViewModel::class.java]
+
+ _binding = FragmentHomeBinding.inflate(inflater, container, false)
+
+ val root: View = binding.root
+
+ binding.logoutButton.setBackgroundColor(Color.RED)
+ homeViewModel.user.observe(viewLifecycleOwner) {
+
+ if (it != null) {
+ Glide.with(requireContext()).load(it.profilePictureUrl)
+ .into(binding.image)
+ binding.name.text = it.name
+ binding.email.text = it.email
+ binding.tenant.text = it.activeTenant.name
+ }
+ }
+
+ binding.logoutButton.setOnClickListener {
+ FronteggAuth.instance.logout()
+ }
+
+ return root
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/ui/home/HomeViewModel.kt b/app/src/main/java/com/frontegg/demo/ui/home/HomeViewModel.kt
new file mode 100644
index 0000000..22fe263
--- /dev/null
+++ b/app/src/main/java/com/frontegg/demo/ui/home/HomeViewModel.kt
@@ -0,0 +1,25 @@
+package com.frontegg.demo.ui.home
+
+import android.os.Handler
+import android.os.Looper
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.frontegg.android.FronteggAuth
+import com.frontegg.android.models.User
+
+class HomeViewModel : ViewModel() {
+
+ private val _user = MutableLiveData().apply {
+ FronteggAuth.instance.user.subscribe {
+ Handler(Looper.getMainLooper()).post {
+ value = it.value
+ }
+ }
+ }
+
+
+ val user: LiveData = _user
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/ui/tenants/TenantsFragment.kt b/app/src/main/java/com/frontegg/demo/ui/tenants/TenantsFragment.kt
new file mode 100644
index 0000000..98a433f
--- /dev/null
+++ b/app/src/main/java/com/frontegg/demo/ui/tenants/TenantsFragment.kt
@@ -0,0 +1,112 @@
+package com.frontegg.demo.ui.tenants
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.ListView
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.frontegg.android.FronteggAuth
+import com.frontegg.android.models.Tenant
+import com.frontegg.demo.R
+import com.frontegg.demo.databinding.FragmentTenantsBinding
+
+class CustomAdapter(context: Context, private val dataSource: MutableList) : BaseAdapter() {
+ private val inflater: LayoutInflater =
+ context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+
+ private var activeTenant: Tenant? = null
+
+
+ override fun getCount(): Int = dataSource.size
+
+ override fun getItem(position: Int): Any = dataSource[position]
+
+ override fun getItemId(position: Int): Long = position.toLong()
+
+ fun setItems(items: List) {
+ this.dataSource.clear()
+ this.dataSource.addAll(items.sortedBy { it.name })
+ notifyDataSetChanged()
+ }
+
+ fun setActiveTenant(activeTenant: Tenant?) {
+ this.activeTenant = activeTenant
+ notifyDataSetChanged()
+ }
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
+ val view = convertView ?: inflater.inflate(R.layout.tenant_row_item, parent, false)
+ val name: TextView = view.findViewById(R.id.name)
+ val info: TextView = view.findViewById(R.id.info)
+
+ val item = getItem(position) as Tenant
+ name.text = item.name
+
+ view.setOnClickListener {
+ info.text = " (switching...)"
+ info.visibility = View.VISIBLE
+ FronteggAuth.instance.switchTenant(item.tenantId) {
+ info.visibility = View.GONE
+ }
+ }
+ if (activeTenant?.id == item.id) {
+ info.text = " (active)"
+ info.visibility = View.VISIBLE
+ } else {
+ info.text = ""
+ info.visibility = View.GONE
+ }
+
+ return view
+ }
+}
+
+class TenantsFragment : Fragment() {
+
+ private var _binding: FragmentTenantsBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+ private var tenants: MutableList = mutableListOf()
+
+ private lateinit var tenantsViewModel: TenantsViewModel
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ tenantsViewModel = ViewModelProvider(this)[TenantsViewModel::class.java]
+
+ _binding = FragmentTenantsBinding.inflate(inflater, container, false)
+
+ val listView: ListView = binding.tenantsList
+ val adapter = CustomAdapter(requireContext(), tenants)
+ listView.adapter = adapter
+
+ tenantsViewModel.tenants.observe(viewLifecycleOwner) {
+ adapter.setItems(it)
+ }
+
+ tenantsViewModel.activeTenant.observe(viewLifecycleOwner) {
+ adapter.setActiveTenant(it)
+ }
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/frontegg/demo/ui/tenants/TenantsViewModel.kt b/app/src/main/java/com/frontegg/demo/ui/tenants/TenantsViewModel.kt
new file mode 100644
index 0000000..684a117
--- /dev/null
+++ b/app/src/main/java/com/frontegg/demo/ui/tenants/TenantsViewModel.kt
@@ -0,0 +1,32 @@
+package com.frontegg.demo.ui.tenants
+
+import android.os.Handler
+import android.os.Looper
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.frontegg.android.FronteggAuth
+import com.frontegg.android.models.Tenant
+
+class TenantsViewModel : ViewModel() {
+
+
+ private val _tenants = MutableLiveData>().apply {
+ FronteggAuth.instance.user.subscribe {
+ Handler(Looper.getMainLooper()).post {
+ value = (it.value?.tenants ?: listOf())
+ }
+ }
+ }
+
+ private val _activeTenant = MutableLiveData().apply {
+ FronteggAuth.instance.user.subscribe {
+ Handler(Looper.getMainLooper()).post {
+ value = it.value?.activeTenant
+ }
+ }
+ }
+
+ val tenants: LiveData> = _tenants
+ val activeTenant: LiveData = _activeTenant
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/baseline_person_24.xml b/app/src/main/res/drawable/baseline_person_24.xml
new file mode 100644
index 0000000..8a1f52b
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_person_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
new file mode 100644
index 0000000..46fc8de
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_home_black_24dp.xml b/app/src/main/res/drawable/ic_home_black_24dp.xml
new file mode 100644
index 0000000..f8bb0b5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_home_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 0000000..78b75c3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 07f9675..0000000
--- a/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_navigation.xml b/app/src/main/res/layout/activity_navigation.xml
new file mode 100644
index 0000000..b23fdb0
--- /dev/null
+++ b/app/src/main/res/layout/activity_navigation.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
deleted file mode 100644
index e416e1c..0000000
--- a/app/src/main/res/layout/content_main.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_second.xml b/app/src/main/res/layout/fragment_auth.xml
similarity index 70%
rename from app/src/main/res/layout/fragment_second.xml
rename to app/src/main/res/layout/fragment_auth.xml
index bd90524..e0bd023 100644
--- a/app/src/main/res/layout/fragment_second.xml
+++ b/app/src/main/res/layout/fragment_auth.xml
@@ -4,24 +4,27 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".SecondFragment">
+ tools:context=".AuthFragment">
+ app:layout_constraintTop_toBottomOf="@id/textview_first" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_first.xml b/app/src/main/res/layout/fragment_first.xml
deleted file mode 100644
index 84c2921..0000000
--- a/app/src/main/res/layout/fragment_first.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
new file mode 100644
index 0000000..4718010
--- /dev/null
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_tenants.xml b/app/src/main/res/layout/fragment_tenants.xml
new file mode 100644
index 0000000..b0cc30b
--- /dev/null
+++ b/app/src/main/res/layout/fragment_tenants.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/tenant_row_item.xml b/app/src/main/res/layout/tenant_row_item.xml
new file mode 100644
index 0000000..a0260a5
--- /dev/null
+++ b/app/src/main/res/layout/tenant_row_item.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml
new file mode 100644
index 0000000..33df156
--- /dev/null
+++ b/app/src/main/res/menu/bottom_nav_menu.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
deleted file mode 100644
index a51e8f3..0000000
--- a/app/src/main/res/navigation/nav_graph.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml
new file mode 100644
index 0000000..0aa57e8
--- /dev/null
+++ b/app/src/main/res/navigation/navigation.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/not_auth_navigation.xml b/app/src/main/res/navigation/not_auth_navigation.xml
new file mode 100644
index 0000000..ea29cc6
--- /dev/null
+++ b/app/src/main/res/navigation/not_auth_navigation.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 125df87..b8c47cb 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,3 +1,6 @@
16dp
+
+ 16dp
+ 16dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4eee2b1..9a24af2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -10,4 +10,7 @@
Hello first fragment
Hello second fragment. Arg: %1$s
Logo
+ NavigationActivity
+ Profile
+ Tenants
\ No newline at end of file