diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/.gitignore b/petcare-sample/b2c/mobile-app/petcare-with-sdk/.gitignore new file mode 100644 index 000000000..d6d3870f2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/.gitignore @@ -0,0 +1,16 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties +*.aab diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/README.md b/petcare-sample/b2c/mobile-app/petcare-with-sdk/README.md new file mode 100644 index 000000000..cefe11a7d --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/README.md @@ -0,0 +1,264 @@ +# API Authentication Android Sample - WSO2 Identity Server + +## ⚠️ Read this first + +1. Required versions + ``` + Java version >= 17 + ``` + ``` + Android Studio version >= Giraffe + ``` + +#### 1. Setup steps Identity Server +1.1 Setup the latest version of WSO2 Identity Sever. + +1.2 [Create a mobile application](https://is.docs.wso2.com/en/next/guides/applications/register-mobile-app/) (Un tick the PKCE `Mandayory` checkbox) + +#### 2. Import the configurations +2.1 Create `config.xml` file in `res/values` + +``` + + + + https://example-app.com/redirect + openid internal_login + code + direct + + + + +``` + +>| Property | Value/s | +>|--------------------------|:------------------------------------------------------------------------------:| +>| oauth_client_base_url | Enter the base URL of the IS server here. If you are using an emulator to try with a locally hosted IS instance, make sure to replace `localhost` with `10.0.2.2` ! | +>| oauth_client_client_id | Client ID of the created mobile application in the identity server | +>| oauth_client_redirect_uri | This does not affect the API authentication flow, but is required since we are using `Authorization Code` grant flow. Hence use a random URL, better to keep the value as it is. Also make sure to use the same URL in the crated application as well. | +>| oauth_client_scope | `openid internal_login` | +>| oauth_client_response_mode | `direct` is the header value where we force the API authenitcation flow. | +>| oauth_client_google_web_client_id | Enter the client id of the Google credential that will be used to create the Google connection in the IS. Since we are using the IS to authenticate the user we need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, we need to send the user's ID token to the IS using HTTPS. Then, on the server, we are verifing the integrity of the ID token and use the user information contained in the token to establish the session. To generate the user's ID token for the IS, we will require the client id that is used to create the Google connection in the IS. For more details, https://developers.google.com/identity/sign-in/android/backend-auth | +>| oauth_client_google_web_client_secret | Enter the client secret of the Google credential that will be used to create the Google connection in the IS. | +>| data_source_resource_server_url | This is the url where we have hosted the pet care service. If you do not have that setup enter a random URL, the app is developed to show a dummy data if the resource server is not available. This should be a URL! | + +#### 3. Setup Google Login +3.1 Go to `https://console.cloud.google.com/` and create a new project. + +3.2 In the `credentials` section create two `Oauth Client IDs` one for `android app(Select Android)` and one for `WSO2 identity server(Select Web Application)`. +> Follow the steps to get the SHA-1 key to create Oauth Client ID for our Android app +> https://stackoverflow.com/a/67983215/10601286. +> +> After that make sure to sync the gradle of the project. This can be done using the `Sync project with Gradle files` icon in the top right hand corner of the IDE (or in Apple `Shift + Command + O`). +> ![image](https://github.com/wso2/samples-is/assets/46097917/dbd6738c-384d-495c-bc57-03bfd8dd7105) + +> `WSO2_CLIENT_ID_OF_GOOGLE` is the client ID of the client ID crated for the WSO2 identity server. + +3.3 Create a Google connection as a Trusted Token Issuer using the following curl command. +``` +curl --location 'https://localhost:9443/api/server/v1/identity-providers' \ +--header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/118.0' \ +--header 'Accept: application/json' \ +--header 'Accept-Language: en-US,en;q=0.5' \ +--header 'Accept-Encoding: gzip, deflate, br' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic YWRtaW46YWRtaW4=' \ +--data '{ + "image": "assets/images/logos/google.svg", + "isPrimary": false, + "roles": { + "mappings": [], + "outboundProvisioningRoles": [] + }, + "name": "Google-TTI-IDP", + "certificate": { + "certificates": [], + "jwksUri": "https://www.googleapis.com/oauth2/v3/certs" + }, + "claims": { + "userIdClaim": { + "uri": "http://wso2.org/claims/username" + }, + "roleClaim": { + "uri": "http://wso2.org/claims/role" + }, + "provisioningClaims": [] + }, + "description": "Login users with existing Google accounts.", + "alias": , + "homeRealmIdentifier": "", + "provisioning": { + "jit": { + "userstore": "PRIMARY", + "scheme": "PROVISION_SILENTLY", + "isEnabled": true + } + }, + "federatedAuthenticators": { + "defaultAuthenticatorId": "R29vZ2xlT0lEQ0F1dGhlbnRpY2F0b3I", + "authenticators": [ + { + "isEnabled": true, + "authenticatorId": "R29vZ2xlT0lEQ0F1dGhlbnRpY2F0b3I", + "properties": [ + { + "value": , + "key": "ClientId" + }, + { + "value": , + "key": "ClientSecret" + }, + { + "value": "https://localhost:9443/commonauth", + "key": "callbackUrl" + }, + { + "value": "scope=email openid profile", + "key": "AdditionalQueryParameters" + } + ] + } + ] + }, + "idpIssuerName": "https://accounts.google.com", + "isFederationHub": false, + "templateId": "google-idp" +}' +``` +3.4 Add the created Google connection to the created application (in step 2) as a level 1 sign-in option. + +#### 4. Supported authenticators +Currently this sample application supports the following authenticators. + +- Basic authenticatator +- Google authenticator +- TOTP +- Passkey authenticator + - NOTE + - This authenticator is only supported from `Android 13` and above. + - For this, we have to enrol a passkey in my account for the user's account. Also, you may the face a difficulty when accessing the registred passkey in your device, to overcome this you can register a passkey from another device. We are working on mitigating this issue. + +#### 5. Run the application + +5.1 To run the application you need to open the application from the Android Studio IDE, and select the project to open with an `Android` view from the project view selection. +![image](https://github.com/wso2/samples-is/assets/46097917/c5ebb28f-335e-4a0f-a560-5504f5402f55) + +5.2 You may require to sync the gradle files again. This can be done using the `Sync project with Gradle files` icon in the top right hand corner of the IDE (or in Apple `Shift + Command + O`). +![image](https://github.com/wso2/samples-is/assets/46097917/f4548481-9eda-425b-8535-eed610012723) + +###### Optional +> This project is recommended to run on an emulator version `Pixel 7` or above, becuase some authenticators like `Passkey` are not supported for older version of Andriod. To download the new emulator you can refer the following documentation. When selecting the device make sure to select `Pixel 7` or above version. +https://developer.android.com/studio/run/managing-avds + +5.3 After the gradle files are synced you can run the app from the `Run `app`` button on the top bar (or in Apple `Command + R`) +![image](https://github.com/wso2/samples-is/assets/46097917/2ec180e4-4aec-4c0c-83b7-258118bfd988) + +#### 6. Client attestation + +6.1 To test the client attestation you need to test the application from a real device, where the app downloaded from the playstore, for this you will require a Google play account. +This application is bound to one of my Google cloud projects, hence you need to change the package name of the application and associate the project with a new Google Project, for this you can use the Google project that you created for the step 3. +> Changing the package name of an Android application involves several steps. It's a common task, but it requires careful attention to detail to ensure that all components of the app work correctly after the change. Here are the general steps: +> +> #### Step 1: Backup Your Project +> +> Before making any changes, it's always a good idea to create a backup of your entire project to avoid potential data loss. +> +> #### Step 2: Update the Package Name in the Manifest +> +> 1. Open the `AndroidManifest.xml` file in your Android Studio. +> 2. Locate the `package` attribute in the `` element and change the package name to your desired name. + > + > ```xml +> package="com.your.old.packagename"> +> ``` + > + > Change it to: + > + > ```xml +> package="com.your.new.packagename"> +> ``` +> +> #### Step 3: Refactor the Package Name +> +> 1. Right-click on the old package name in the project explorer. +> 2. Select "Refactor" > "Rename" (or just press `Shift` + `F6`). +> 3. Enter the new package name and click "Refactor." +> +> #### Step 4: Update Gradle Files +> +> 1. Open the `build.gradle` file (Module: app). +> 2. Update the `applicationId` to the new package name. + > + > ```gradle +> android { +> ... +> defaultConfig { +> ... +> applicationId "com.your.new.packagename" +> } +> } +> ``` +> +> 3. Sync your project with Gradle by clicking on the "Sync Now" prompt that appears in the bar at the top. +> +> #### Step 5: Rename Source Code Directories +> +> 1. Right-click on the old package name directory and choose "Refactor" > "Rename." +> 2. Enter the new package name and click "Refactor." +> +> #### Step 6: Update References +> +> 1. Android Studio will update most references automatically, but double-check for any manual references in your code or resources. +> 2. Look for occurrences of the old package name and update them to the new one. +> +> #### Step 7: Clean and Rebuild +> +> 1. Clean your project by selecting "Build" > "Clean Project" from the menu. +> 2. Rebuild your project by selecting "Build" > "Rebuild Project." +> +> #### Step 8: Test Your App +> +> 1. Run your app on an emulator or a physical device to ensure that everything is working as expected. +> 2. Check for any errors or warnings in the logcat. + +Also make sure to subscribe to Google Play Integrity API from the project, you can do this from `Enabled API's & Services` in the Google cloud project. + +6.2 After that you need to create a new service account in the google project. +You can create a Service Account for yourself with the following scopes. +- Go to IAM& Admin -> Service Accounts +- Click Create Service Account +- Fill the name and click create and continue. +- You need to grant your service account the roles of Service Account User and Service Usage Consumer. +- Click continue and then Done +- You can see the service account added without keys, click : Actions -> Manage Keys for the service account. +- Click Add key and Select JSON. +- Save the JSON in secure place (We need this for Android Attestation Credentials for application metadata) + +6.3 After that, Update Application Advanced properties. +The application you created requires 2 properties to perform android attestation. +- Android package name +- androidAttestationServiceCredentials + - The JSON secret of Service Account downloaded. Note that this attribute is defined as a JSON object hence use the JSON key as it is. + +6.4 Go to the Google play console and create a new application for this application, after that you need to associate your cloud project for the app integrity. To do this, +- Navigate to Release > App integrity. Under Play Integrity API select **Link a Cloud project**. +- Choose the Cloud project you want to link to your app and this will enable Play Integrity API responses. +- You can now integrate the Play Integrity API into your app. +- Create a new release. + - For this we need a signed app bundle. To create a signed app bundle, + - In the Android Studio, go to **Build** > **Generate Signed App Bundle**, and select app bundle. + - Then create a new keystore (make sure to store the credentials of the keystore in a secure location), then upload the released bundle to the play console internal testing release section. +- In the testers section, add yourself as a tester in a new list of testers and select that list only for the testing. +- Then copy the web link and download the application from there. +- Download the application from that link, and you can test the client attestation from that application +> NOTE: +> When you have released multiple versions of the application, you may face a problem that the application you download from the link are still giving the old version, to mitigate this you can change the device and open the link or wait a few minutes. + +###### App Screens +![image](https://github.com/wso2/samples-is/assets/46097917/5a31af56-b3ae-4b37-992d-aae42046ab31) +![image](https://github.com/wso2/samples-is/assets/46097917/cc67d9da-2f47-408b-8b75-77499f03802f) +![image](https://github.com/wso2/samples-is/assets/46097917/65f04443-e43a-4c28-a68a-436f890deb7e) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/.gitignore b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/build.gradle.kts b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/build.gradle.kts new file mode 100644 index 000000000..ab216a568 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/build.gradle.kts @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +plugins { + alias(libs.plugins.androidApplication) + alias(libs.plugins.jetbrainsKotlinAndroid) + id("kotlin-android") + id("kotlin-kapt") + id("com.google.dagger.hilt.android") +} + +android { + namespace = "com.wso2_sample.api_auth_sample" + compileSdk = 34 + + defaultConfig { + applicationId = "com.wso2_sample.api_auth_sample" + minSdk = 26 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + manifestPlaceholders.putAll( + mapOf( + "appAuthRedirectScheme" to "wso2.apiauth.sample.android://login-callback", + "callbackUriHost" to "login-callback", + "callbackUriScheme" to "wso2.apiauth.sample.android" + ) + ) + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.10" + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(libs.androidx.appcompat) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.material3.android) + implementation(libs.androidx.ui.tooling.preview.android) + implementation(libs.integrity) + implementation(libs.play.services.tasks) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom.v20240400)) + + // Arrow + implementation(libs.arrow.core) + implementation(libs.arrow.fx.coroutines) + // Coil + implementation(libs.coil.compose) + // Dagger hilt + implementation(libs.hilt.android) + debugImplementation(libs.androidx.ui.tooling) + kapt(libs.hilt.android.compiler) + implementation(libs.androidx.hilt.navigation.compose) + + implementation(libs.androidx.lifecycle.runtime.compose) + + // Asgardeo android SDK + implementation(libs.asgardeo.android.ui) + + implementation(libs.okhttp) + implementation(libs.jackson.module.kotlin) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/proguard-rules.pro b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/androidTest/java/com/wso2_sample/api_auth_sample/ExampleInstrumentedTest.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/androidTest/java/com/wso2_sample/api_auth_sample/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..31859b59f --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/androidTest/java/com/wso2_sample/api_auth_sample/ExampleInstrumentedTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.wso2_sample.api_auth_sample", appContext.packageName) + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/AndroidManifest.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a275915b2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/MainActivity.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/MainActivity.kt new file mode 100644 index 000000000..2855d18b7 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/MainActivity.kt @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample + +import android.os.Bundle +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.compose.rememberNavController +import dagger.hilt.android.AndroidEntryPoint +import com.wso2_sample.api_auth_sample.ui.theme.Api_authenticator_sdkTheme +import com.wso2_sample.api_auth_sample.util.Event +import com.wso2_sample.api_auth_sample.util.EventBus +import com.wso2_sample.api_auth_sample.util.navigation.NavDestination +import com.wso2_sample.api_auth_sample.util.navigation.NavGraph +import com.wso2_sample.api_auth_sample.util.navigation.NavigationViewModel + +@AndroidEntryPoint +class MainActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + Api_authenticator_sdkTheme { + val lifecycle = LocalLifecycleOwner.current.lifecycle + val navigationController = rememberNavController() + + LaunchedEffect(key1 = lifecycle) { + repeatOnLifecycle(Lifecycle.State.STARTED) { + EventBus.events.collect { event -> + when (event) { + is Event.Toast -> { + // Show toast + Toast.makeText( + this@MainActivity, + event.message, + Toast.LENGTH_SHORT + ).show() + } + } + } + } + } + + LaunchedEffect(Unit) { + NavigationViewModel.navigationEvents.collect { + when (it) { + is NavigationViewModel.Companion.NavigationEvent.NavigateBack -> { + navigationController.popBackStack() + } + + is NavigationViewModel.Companion.NavigationEvent.NavigateToLanding -> { + navigationController.navigate(NavDestination.LANDING_SCREEN) + } + + is NavigationViewModel.Companion.NavigationEvent.NavigateToAuthWithData -> { + navigationController.navigate( + "${NavDestination.AUTH_SCREEN}?authenticationFlow={authenticationFlow}" + .replace( + "{authenticationFlow}", + newValue = it.data + ) + ) + } + + is NavigationViewModel.Companion.NavigationEvent.NavigateToHome -> { + navigationController.navigate(NavDestination.HOME_SCREEN) + } + + is NavigationViewModel.Companion.NavigationEvent.NavigateToProfile -> { + navigationController.navigate(NavDestination.PROFILE_SCREEN) + } + + is NavigationViewModel.Companion.NavigationEvent.NavigateToAddPet -> { + navigationController.navigate(NavDestination.ADD_PET_SCREEN) + } + } + } + } + + // A surface container using the 'background' color from the theme + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.surface + ) { + NavGraph(navController = navigationController) + } + } + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/PetCare.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/PetCare.kt new file mode 100644 index 000000000..f6425e5ae --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/PetCare.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class PetCare : Application() diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/di/RepositoryModule.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/di/RepositoryModule.kt new file mode 100644 index 000000000..36d846f87 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/di/RepositoryModule.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import com.wso2_sample.api_auth_sample.features.home.domain.repository.PetRepository +import com.wso2_sample.api_auth_sample.features.home.impl.repository.PetRepositoryImpl +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AsgardeoAuthRepository +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AttestationRepository +import com.wso2_sample.api_auth_sample.features.login.impl.repository.AsgardeoAuthRepositoryImpl +import com.wso2_sample.api_auth_sample.features.login.impl.repository.AttestationRepositoryImpl +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class RepositoryModule { + + @Binds + @Singleton + abstract fun bindAttestationRepository( + attestationRepositoryImpl: AttestationRepositoryImpl + ): AttestationRepository + + @Binds + @Singleton + abstract fun bindAsgardeoAuthRepository( + asgardeoAuthRepositoryImpl: AsgardeoAuthRepositoryImpl + ): AsgardeoAuthRepository + + @Binds + @Singleton + abstract fun petRepository( + petRepositoryImpl: PetRepositoryImpl + ): PetRepository +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/UserDetails.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/UserDetails.kt new file mode 100644 index 000000000..8196e165c --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/UserDetails.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.domain.models + +class UserDetails( + val imageUrl: String = "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?q=80&w=3570&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + val username: String? = null, + val email: String? = null, + val firstName: String? = null, + val lastName: String? = null, +) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/Pet.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/Pet.kt new file mode 100644 index 000000000..2c168a435 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/Pet.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.domain.models.pet + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.wso2_sample.api_auth_sample.util.Util + +@JsonDeserialize(using = PetDeserializer::class) +data class Pet( + val name: String? = null, + val breed: String? = null, + val nextAppointment: String? = null, + val imageUrl: String? = null +) { + companion object { + /** + * Create a pet with random data if the API response is empty or the application + * is used as a standalone application. + * + * @param name The name of the pet + * @param breed The breed of the pet + * + * @return The pet [Pet] + */ + fun createPetWithRandomData(name: String?, breed: String?): Pet { + val randomImageUrl = Util.getPetImageUrls().random() + val randomDate = Util.generateRandomDate() + return Pet( + name = name, + breed = breed, + nextAppointment = "Next appointment on $randomDate", + imageUrl = randomImageUrl + ) + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/PetDeserializer.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/PetDeserializer.kt new file mode 100644 index 000000000..b923400e6 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/PetDeserializer.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.domain.models.pet + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import com.wso2_sample.api_auth_sample.util.Util +import java.io.IOException + +class PetDeserializer : JsonDeserializer() { + @Throws(IOException::class) + override fun deserialize(jsonParser: JsonParser, ctxt: DeserializationContext): Pet { + val node = jsonParser.codec.readTree(jsonParser) + val name = node.get("name")?.asText() + val breed = node.get("breed")?.asText() + + val randomImageUrl = Util.getPetImageUrls().random() + val randomDate = Util.generateRandomDate() + + return Pet( + name = name, + breed = breed, + nextAppointment = "Next appointment on $randomDate", + imageUrl = randomImageUrl + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/PetModule.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/PetModule.kt new file mode 100644 index 000000000..31b9d0b31 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/models/pet/PetModule.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.domain.models.pet + +import com.fasterxml.jackson.databind.module.SimpleModule + +class PetModule : SimpleModule() { + init { + addDeserializer(Pet::class.java, PetDeserializer()) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/repository/PetRepository.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/repository/PetRepository.kt new file mode 100644 index 000000000..dbffc4ce3 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/domain/repository/PetRepository.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.domain.repository + +import com.wso2_sample.api_auth_sample.features.home.domain.models.pet.Pet + +interface PetRepository { + suspend fun getPets(accessToken: String): List? + + suspend fun addPet( + accessToken: String, + name: String, + breed: String, + dateOfBirth: String + ): Unit? +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/impl/less_secure_client/LessSecureClient.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/impl/less_secure_client/LessSecureClient.kt new file mode 100644 index 000000000..153f3743e --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/impl/less_secure_client/LessSecureClient.kt @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.impl.less_secure_client + +import okhttp3.OkHttpClient +import okhttp3.Protocol +import java.lang.ref.WeakReference +import java.security.GeneralSecurityException +import java.util.concurrent.TimeUnit +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager + +/** + * Use to create the [LessSecureHttpClient] for API calls in the SDK. + */ +internal class LessSecureHttpClient private constructor() { + private val client: OkHttpClient + + companion object { + private var lessSecureHttpClientInstance = WeakReference(null) + + /** + * Returns an instance of the [LessSecureHttpClient] class. + * + * @return [LessSecureHttpClient] instance. + */ + internal fun getInstance(): LessSecureHttpClient { + var lessSecureHttpClient = lessSecureHttpClientInstance.get() + if (lessSecureHttpClient == null) { + lessSecureHttpClient = LessSecureHttpClient() + lessSecureHttpClientInstance = WeakReference(lessSecureHttpClient) + } + return lessSecureHttpClient + } + } + + init { + val trustManager: X509TrustManager + val sslSocketFactory: SSLSocketFactory + try { + // Use a custom X509TrustManager that accepts all certificates + trustManager = trustAllTrustManager() + + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(null, arrayOf(trustManager), null) + sslSocketFactory = sslContext.socketFactory + } catch (e: GeneralSecurityException) { + throw RuntimeException(e) + } + client = OkHttpClient.Builder() + .sslSocketFactory(sslSocketFactory, trustManager) + .hostnameVerifier { _, _ -> true } // Bypass hostname verification + .connectTimeout(45, TimeUnit.SECONDS) + .readTimeout(45, TimeUnit.SECONDS) + .protocols(listOf(Protocol.HTTP_1_1)) + .build() + } + + private fun trustAllTrustManager(): X509TrustManager { + return object : X509TrustManager { + override fun checkClientTrusted( + chain: Array?, + authType: String? + ) { + // Do nothing, trust all clients + } + + override fun checkServerTrusted( + chain: Array?, + authType: String? + ) { + // Do nothing, trust all servers + } + + override fun getAcceptedIssuers(): Array { + return emptyArray() + } + } + } + + /** + * Returns the [OkHttpClient] instance. + * + * @return [OkHttpClient] instance. + */ + fun getClient(): OkHttpClient { + return client + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/impl/repository/PetRepositoryImpl.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/impl/repository/PetRepositoryImpl.kt new file mode 100644 index 000000000..55a2dd566 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/impl/repository/PetRepositoryImpl.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.impl.repository + +import android.util.Log +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.wso2_sample.api_auth_sample.features.home.domain.models.pet.Pet +import com.wso2_sample.api_auth_sample.features.home.domain.models.pet.PetModule +import com.wso2_sample.api_auth_sample.features.home.domain.repository.PetRepository +import com.wso2_sample.api_auth_sample.features.home.impl.less_secure_client.LessSecureHttpClient +import com.wso2_sample.api_auth_sample.util.Config +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okhttp3.Call +import okhttp3.Callback +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import org.json.JSONArray +import org.json.JSONObject +import java.io.IOException +import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +class PetRepositoryImpl @Inject constructor() : PetRepository { + private val client: OkHttpClient = LessSecureHttpClient.getInstance().getClient() + private val dataSourcesResourcesUrl: String? = Config.getDataSourceResourceServerUrl() + + override suspend fun getPets(accessToken: String): List = + if (dataSourcesResourcesUrl == null) { + getPetsFromLocalDataSource() + } else { + getPetsFromDataSource(accessToken) + } + + @Throws(IOException::class) + override suspend fun addPet( + accessToken: String, + name: String, + breed: String, + dateOfBirth: String + ): Unit? = withContext(Dispatchers.IO) { + suspendCoroutine { continuation -> + if (dataSourcesResourcesUrl == null) { + continuation.resumeWithException(Exception("Data source URL is not set")) + return@suspendCoroutine + } + + // authorize URL + val url = "$dataSourcesResourcesUrl/pets" + + // POST form parameters + val postData = JSONObject() + postData.put("name", name) + postData.put("breed", breed) + postData.put("dateOfBirth", dateOfBirth) + postData.put("vaccinations", JSONArray()) + + val requestBuilder: Request.Builder = Request.Builder().url(url) + requestBuilder.addHeader("Authorization", "Bearer $accessToken") + + val request: Request = requestBuilder.post( + postData.toString().toRequestBody("application/json".toMediaTypeOrNull()) + ).build() + + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + println(e) + continuation.resumeWithException(e) + } + + @Throws(IOException::class) + override fun onResponse(call: Call, response: Response) { + try { + if (response.code == 201) { + continuation.resume(Unit) + } else { + continuation.resumeWithException(Exception("Failed to add pet")) + } + } catch (e: Exception) { + Log.e("PetAPI Add Pet", e.toString()) + continuation.resumeWithException(e) + } + } + }) + } + } + + private fun getPetsFromLocalDataSource(): List = listOf( + Pet.createPetWithRandomData("Bella", "Cat - Persian"), + Pet.createPetWithRandomData("Charlie", "Rabbit - Holland Lop"), + Pet.createPetWithRandomData("Luna", "Dog - Golden Retriever"), + Pet.createPetWithRandomData("Max", "Hamster - Syrian"), + Pet.createPetWithRandomData("Oliver", "Dog - Poddle"), + Pet.createPetWithRandomData("Lucy", "Dog - Beagle") + ) + + private suspend fun getPetsFromDataSource(accessToken: String): List = + withContext(Dispatchers.IO) { + suspendCoroutine { continuation -> + // authorize URL + val url = "${dataSourcesResourcesUrl!!}/pets" + + val requestBuilder: Request.Builder = Request.Builder().url(url) + requestBuilder.addHeader("Authorization", "Bearer $accessToken") + + val request: Request = requestBuilder.get().build() + + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + println(e) + continuation.resumeWithException(e) + } + + @Throws(IOException::class) + override fun onResponse(call: Call, response: Response) { + try { + if (response.code == 200) { + val responseBody: String = response.body!!.string() + // reading the json + val pets: ArrayList = jacksonObjectMapper().registerModule( + PetModule() + ) + .readValue( + responseBody, + jacksonObjectMapper().typeFactory.constructCollectionType( + ArrayList::class.java, + Pet::class.java + ) + ) + continuation.resume(pets) + } else { + continuation.resumeWithException(Exception("Failed to get pets")) + } + } catch (e: Exception) { + Log.e("PetAPI Get Pets", e.toString()) + continuation.resumeWithException(e) + } + } + }) + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreen.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreen.kt new file mode 100644 index 000000000..b5352be59 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreen.kt @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.add_pet + +import android.annotation.SuppressLint +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.wso2_sample.api_auth_sample.features.home.presentation.util.add_pet.AddPetCard +import com.wso2_sample.api_auth_sample.features.home.presentation.util.add_pet.TopBar +import com.wso2_sample.api_auth_sample.ui.theme.Api_authenticator_sdkTheme +import com.wso2_sample.api_auth_sample.util.ui.LoadingDialog + +@Composable +internal fun AddPetScreen( + viewModel: AddPetScreenViewModel = hiltViewModel() +) { + val state = viewModel.state.collectAsStateWithLifecycle() + + val navigateToHome: () -> Unit = viewModel::navigateToHome + val addPets: (name: String, breed: String, dateOfBirth: String) -> Unit = viewModel::addPets + + LoadingDialog(isLoading = state.value.isLoading) + AddPetScreenContent(state.value, navigateToHome, addPets) +} + +@Composable +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") +fun AddPetScreenContent( + state: AddPetScreenState, + navigateToHome: () -> Unit = {}, + addPets: (name: String, breed: String, dateOfBirth: String) -> Unit = { _, _, _ -> } +) { + Scaffold( + modifier = Modifier + .fillMaxSize(), + containerColor = MaterialTheme.colorScheme.surface, + topBar = { + TopBar(navigateToHome = navigateToHome) + } + ) { innerPadding -> + Column( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + AddPetCard() + Spacer(modifier = Modifier.height(32.dp)) + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.padding(horizontal = 32.dp) + ) { + var name by remember { mutableStateOf("") } + var breed by remember { mutableStateOf("") } + var dateOfBirth by remember { mutableStateOf("") } + + OutlinedTextField( + value = name, + onValueChange = { name = it }, + label = { Text(text = "Pet Name") }, + modifier = Modifier.fillMaxWidth(), + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ) + ) + OutlinedTextField( + value = breed, + onValueChange = { breed = it }, + label = { Text(text = "Pet Breed") }, + modifier = Modifier.fillMaxWidth(), + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ) + ) + OutlinedTextField( + value = dateOfBirth, + onValueChange = { dateOfBirth = it }, + label = { Text(text = "Date of Birth") }, + modifier = Modifier.fillMaxWidth(), + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ) + ) + Button( + onClick = { addPets(name, breed, dateOfBirth) }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + ) { + Text( + text = "Add Pet", + color = MaterialTheme.colorScheme.surface + ) + } + } + + } + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun AddPetScreenPreview() { + Api_authenticator_sdkTheme { + AddPetScreenContent( + state = AddPetScreenState( + isLoading = false + ) + ) + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreenState.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreenState.kt new file mode 100644 index 000000000..eabea17a6 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreenState.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.add_pet + +data class AddPetScreenState( + val isLoading: Boolean = false, + val error: String = "", +) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreenViewModel.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreenViewModel.kt new file mode 100644 index 000000000..6f0ce0be1 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/add_pet/AddPetScreenViewModel.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.add_pet + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wso2_sample.api_auth_sample.features.home.domain.repository.PetRepository +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AsgardeoAuthRepository +import com.wso2_sample.api_auth_sample.util.navigation.NavigationViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import io.asgardeo.android.core.provider.providers.token.TokenProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class AddPetScreenViewModel @Inject constructor( + asgardeoAuthRepository: AsgardeoAuthRepository, + @ApplicationContext private val applicationContext: Context, + private val petRepository: PetRepository +) : ViewModel() { + + companion object { + const val TAG = "AddPetScreen" + } + + private val _state = MutableStateFlow(AddPetScreenState()) + val state = _state + + private lateinit var tokenProvider: TokenProvider + + init { + tokenProvider = asgardeoAuthRepository.getTokenProvider() + _state.update { + it.copy(isLoading = false) + } + } + + fun navigateToHome() { + viewModelScope.launch { + NavigationViewModel.navigationEvents.emit( + NavigationViewModel.Companion.NavigationEvent.NavigateToHome + ) + } + } + + fun addPets( + name: String, + breed: String, + dateOfBirth: String + ) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + tokenProvider.performAction(applicationContext) { accessToken, _ -> + viewModelScope.launch { + runCatching { + petRepository.addPet( + accessToken = accessToken!!, + name = name, + breed = breed, + dateOfBirth = dateOfBirth + ) + }.onSuccess { + _state.update { + it.copy(isLoading = false) + } + }.onFailure { e -> + _state.update { + it.copy(error = e.message!!, isLoading = false) + } + } + } + } + + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreen.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreen.kt new file mode 100644 index 000000000..d082a9e03 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreen.kt @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.home + +import android.annotation.SuppressLint +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.FabPosition +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.wso2_sample.api_auth_sample.features.home.domain.models.pet.Pet +import com.wso2_sample.api_auth_sample.features.home.presentation.util.AddPetFab +import com.wso2_sample.api_auth_sample.features.home.presentation.util.DoctorSearchField +import com.wso2_sample.api_auth_sample.features.home.presentation.util.EmergencyCard +import com.wso2_sample.api_auth_sample.features.home.presentation.util.TopBar +import com.wso2_sample.api_auth_sample.features.home.presentation.util.VetCard +import com.wso2_sample.api_auth_sample.features.home.presentation.util.pets_list.PetsList +import com.wso2_sample.api_auth_sample.ui.theme.Api_authenticator_sdkTheme + +@Composable +internal fun HomeScreen( + viewModel: HomeScreenViewModel = hiltViewModel() +) { + val state = viewModel.state.collectAsStateWithLifecycle() + + val navigateToProfile: () -> Unit = viewModel::navigateToProfile + val navigateToAddPet: () -> Unit = viewModel::navigateToAddPet + + HomeScreenContent(state.value, navigateToProfile, navigateToAddPet) +} + +@Composable +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") +fun HomeScreenContent( + state: HomeScreenState, + navigateToProfile: () -> Unit = {}, + navigateToAddPet: () -> Unit = {} +) { + Scaffold( + modifier = Modifier + .fillMaxSize(), + containerColor = MaterialTheme.colorScheme.surface, + topBar = { + TopBar(navigateToHome = {}, navigateToProfile = navigateToProfile) + }, + floatingActionButton = { AddPetFab(navigateToAddPet) }, + floatingActionButtonPosition = FabPosition.End, + ) { innerPadding -> + Column( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(32.dp) + ) { + Column( + modifier = Modifier + .padding(horizontal = 32.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + DoctorSearchField() + VetCard() + EmergencyCard() + } + HorizontalDivider( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Start), + thickness = 0.5.dp + ) + PetsList(state.pets) + Spacer(modifier = Modifier.height(16.dp)) + } + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun HomeScreenPreview() { + Api_authenticator_sdkTheme { + HomeScreenContent( + HomeScreenState( + isLoading = false, + pets = listOf( + Pet( + "Bella", + "https://cdn.pixabay.com/photo/2014/11/30/14/11/cat-551554_1280.jpg", + "Cat - Persian", + "Next appointment on 29/04/24" + ), + Pet( + "Charlie", + "https://cdn.pixabay.com/photo/2023/09/19/12/34/dog-8262506_1280.jpg", + "Rabbit - Holland Lop", + "Next appointment on 19/06/24" + ), + Pet( + "Luna", + "https://cdn.pixabay.com/photo/2023/08/18/15/02/dog-8198719_1280.jpg", + "Dog - Golden Retriever", + "Next appointment on 04/05/24" + ), + Pet( + "Max", + "https://cdn.pixabay.com/photo/2024/03/26/15/50/ai-generated-8657140_1280.jpg", + "Hamster - Syrian", + "Next appointment on 01/06/24" + ), + Pet( + "Oliver", + "https://cdn.pixabay.com/photo/2020/04/29/04/01/boy-5107099_1280.jpg", + "Dog - Poddle", + "Next appointment on 29/04/24" + ), + Pet( + "Lucy", + "https://cdn.pixabay.com/photo/2023/09/24/14/05/dog-8272860_1280.jpg", + "Dog - Beagle", + "Next appointment on 05/08/24" + ) + ) + ) + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreenState.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreenState.kt new file mode 100644 index 000000000..947d40eb9 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreenState.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.home + +import com.wso2_sample.api_auth_sample.features.home.domain.models.pet.Pet + +data class HomeScreenState( + val isLoading: Boolean = true, + val error: String = "", + val pets: List = emptyList() +) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreenViewModel.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreenViewModel.kt new file mode 100644 index 000000000..86e48301b --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/home/HomeScreenViewModel.kt @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.home + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wso2_sample.api_auth_sample.features.home.domain.repository.PetRepository +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AsgardeoAuthRepository +import com.wso2_sample.api_auth_sample.util.navigation.NavigationViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import io.asgardeo.android.core.provider.providers.token.TokenProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class HomeScreenViewModel @Inject constructor( + @ApplicationContext private val applicationContext: Context, + asgardeoAuthRepository: AsgardeoAuthRepository, + private val petRepository: PetRepository +) : ViewModel() { + + companion object { + const val TAG = "HomeScreen" + } + + private val _state = MutableStateFlow(HomeScreenState()) + val state = _state + + private lateinit var tokenProvider: TokenProvider + + init { + tokenProvider = asgardeoAuthRepository.getTokenProvider() + _state.update { + it.copy(isLoading = false) + } + } + + init { + getPets() + } + + private fun getPets() { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + tokenProvider.performAction(applicationContext) { accessToken, _ -> + viewModelScope.launch { + runCatching { + petRepository.getPets(accessToken!!) + }.onSuccess { pets -> + _state.update { + it.copy(pets = pets!!, isLoading = false) + } + }.onFailure { e -> + _state.update { + it.copy(error = e.message!!, isLoading = false) + } + } + } + } + } + } + + fun navigateToProfile() { + viewModelScope.launch { + NavigationViewModel.navigationEvents.emit( + NavigationViewModel.Companion.NavigationEvent.NavigateToProfile + ) + } + } + + fun navigateToAddPet() { + viewModelScope.launch { + NavigationViewModel.navigationEvents.emit( + NavigationViewModel.Companion.NavigationEvent.NavigateToAddPet + ) + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreen.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreen.kt new file mode 100644 index 000000000..fbcf009c9 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreen.kt @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.profile + +import android.annotation.SuppressLint +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.home.domain.models.UserDetails +import com.wso2_sample.api_auth_sample.features.home.presentation.util.TopBar +import com.wso2_sample.api_auth_sample.features.home.presentation.util.profile.NameSection +import com.wso2_sample.api_auth_sample.features.home.presentation.util.profile.ProfileAttribute +import com.wso2_sample.api_auth_sample.features.home.presentation.util.profile.ProfileImage +import com.wso2_sample.api_auth_sample.features.home.presentation.util.profile.SettingsAttribute +import com.wso2_sample.api_auth_sample.ui.theme.Api_authenticator_sdkTheme +import com.wso2_sample.api_auth_sample.util.ui.LoadingDialog + +@Composable +internal fun ProfileScreen( + viewModel: ProfileScreenViewModel = hiltViewModel() +) { + val state = viewModel.state.collectAsStateWithLifecycle() + + val navigateToHome: () -> Unit = viewModel::navigateToHome + + val logout: () -> Unit = viewModel::logout + + LoadingDialog(isLoading = state.value.isLoading) + ProfileScreenContent(state.value, navigateToHome, logout) +} + +@Composable +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") +fun ProfileScreenContent( + state: ProfileScreenState, + navigateToHome: () -> Unit = {}, + logout: () -> Unit +) { + Scaffold( + modifier = Modifier + .fillMaxSize(), + containerColor = MaterialTheme.colorScheme.surface, + topBar = { + TopBar(navigateToHome = navigateToHome, navigateToProfile = {}) + } + ) { innerPadding -> + Column( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + Column( + modifier = Modifier + .padding(start = 32.dp, end = 32.dp, top = 32.dp) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + ProfileImage(imageUrl = state.user?.imageUrl ?: "") + NameSection( + firstName = state.user?.firstName, + lastName = state.user?.lastName + ) + } + Spacer(modifier = Modifier.height(32.dp)) + Column( + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + ProfileAttribute(label = "Username", value = state.user?.username) + ProfileAttribute(label = "Email", value = state.user?.email) + ProfileAttribute(label = "First Name", value = state.user?.firstName) + ProfileAttribute(label = "Last Name", value = state.user?.lastName) + } + Spacer(modifier = Modifier.height(32.dp)) + Surface( + color = MaterialTheme.colorScheme.background, + modifier = Modifier.fillMaxHeight() + ) { + Column( + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier + .padding( + top = 32.dp, + start = 32.dp, + bottom = 32.dp + ) + .fillMaxSize() + + ) { + SettingsAttribute( + title = "Dark Mode", + iconId = R.drawable.dark_mode, + onClick = {} + ) + SettingsAttribute( + title = "Cards", + iconId = R.drawable.credit_card, + onClick = {} + ) + SettingsAttribute( + title = "Language", + iconId = R.drawable.language, + onClick = {} + ) + SettingsAttribute( + title = "Data Saver Mode", + iconId = R.drawable.data_saver_on, + onClick = {} + ) + SettingsAttribute( + title = "Help", + iconId = R.drawable.help_outline, + onClick = {} + ) + SettingsAttribute( + title = "Support Inbox", + iconId = R.drawable.support_inbox, + onClick = {} + ) + SettingsAttribute( + title = "Report a Problem", + iconId = R.drawable.report, + onClick = {} + ) + SettingsAttribute( + title = "Contact Us", + iconId = R.drawable.call, + onClick = {} + ) + SettingsAttribute( + title = "Rate Us", + iconId = R.drawable.star_rate, + onClick = {} + ) + SettingsAttribute( + title = "Log Out", + iconId = R.drawable.logout, + onClick = logout + ) + } + } + Box( + modifier = Modifier + .fillMaxSize() + .weight(1f) + .background(MaterialTheme.colorScheme.background) + ) + } + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun HomeScreenPreview() { + Api_authenticator_sdkTheme { + ProfileScreenContent( + ProfileScreenState( + isLoading = false, + user = UserDetails( + imageUrl = "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?q=80&w=3570&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + username = "johndoe", + email = "john@wso2.com", + firstName = "John", + lastName = "Doe" + ) + ), + {}, + {} + ) + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreenState.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreenState.kt new file mode 100644 index 000000000..f69918681 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreenState.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.profile + +import com.wso2_sample.api_auth_sample.features.home.domain.models.UserDetails + +data class ProfileScreenState( + val isLoading: Boolean = true, + val error: String = "", + val user: UserDetails? = null, +) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreenViewModel.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreenViewModel.kt new file mode 100644 index 000000000..a87ceea50 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/screens/profile/ProfileScreenViewModel.kt @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.screens.profile + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wso2_sample.api_auth_sample.features.home.domain.models.UserDetails +import com.wso2_sample.api_auth_sample.features.home.domain.repository.PetRepository +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AsgardeoAuthRepository +import com.wso2_sample.api_auth_sample.util.navigation.NavigationViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import io.asgardeo.android.core.provider.providers.authentication.AuthenticationProvider +import io.asgardeo.android.core.provider.providers.token.TokenProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class ProfileScreenViewModel @Inject constructor( + @ApplicationContext private val applicationContext: Context, + asgardeoAuthRepository: AsgardeoAuthRepository, + private val petRepository: PetRepository +) : ViewModel() { + + companion object { + const val TAG = "ProfileScreen" + } + + private val _state = MutableStateFlow(ProfileScreenState()) + val state = _state + + private var authenticationProvider: AuthenticationProvider + private var tokenProvider: TokenProvider + + init { + authenticationProvider = asgardeoAuthRepository.getAuthenticationProvider() + tokenProvider = asgardeoAuthRepository.getTokenProvider() + _state.update { + it.copy(isLoading = false) + } + } + + init { + getBasicUserInfo() + getDecodedIdToken() + } + + fun navigateToHome() { + viewModelScope.launch { + NavigationViewModel.navigationEvents.emit( + NavigationViewModel.Companion.NavigationEvent.NavigateToHome + ) + } + } + + private fun getBasicUserInfo() { + _state.update { + it.copy( + isLoading = true + ) + } + viewModelScope.launch { + runCatching { + authenticationProvider.getBasicUserInfo(applicationContext) + }.onSuccess { userDetails -> + _state.update { + it.copy( + user = UserDetails( + username = userDetails?.get("sub").toString(), + firstName = userDetails?.get("given_name").toString(), + lastName = userDetails?.get("family_name").toString(), + email = userDetails?.get("email").toString() + ), + isLoading = false + ) + } + _state.update { + it.copy( + isLoading = false + ) + } + }.onFailure { e -> + _state.update { + it.copy( + error = e.message!!, + isLoading = false + ) + } + } + } + } + + private fun getDecodedIdToken() { + viewModelScope.launch { + tokenProvider.getDecodedIDToken(applicationContext) + } + } + + fun logout() { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + try { + authenticationProvider.logout(applicationContext) + NavigationViewModel.navigationEvents.emit( + NavigationViewModel.Companion.NavigationEvent.NavigateToLanding + ) + } catch (e: Exception) { + _state.update { + it.copy( + error = e.message!!, + isLoading = false + ) + } + } + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/AddPetFab.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/AddPetFab.kt new file mode 100644 index 000000000..85d96baf4 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/AddPetFab.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util + +import androidx.compose.foundation.layout.offset +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Add +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.FloatingActionButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun AddPetFab( + navigateToAddPet: ()-> Unit +) { + FloatingActionButton( + onClick = navigateToAddPet, + elevation = FloatingActionButtonDefaults.elevation(0.dp), + shape = MaterialTheme.shapes.extraSmall, + modifier = Modifier.offset(x = (-16).dp) + ) { + Icon( + imageVector = Icons.Outlined.Add, + contentDescription = "Menu", + tint = MaterialTheme.colorScheme.surface + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/DoctorSearchField.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/DoctorSearchField.kt new file mode 100644 index 000000000..9eae67b34 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/DoctorSearchField.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun DoctorSearchField() { + OutlinedTextField( + value = "", + onValueChange = { null }, + label = { Text(text = "Search by doctor name") }, + modifier = Modifier.fillMaxWidth(), + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ), + trailingIcon = { + Icon( + imageVector = Icons.Outlined.Search, + contentDescription = "Menu", + modifier = Modifier + .size(24.dp), + tint = MaterialTheme.colorScheme.tertiaryContainer + ) + } + + ) +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/EmergencyCard.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/EmergencyCard.kt new file mode 100644 index 000000000..bbc54231b --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/EmergencyCard.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Call +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.wso2_sample.api_auth_sample.util.ui.UiUtil + +@Composable +fun EmergencyCard() { + Card( + modifier = Modifier + .fillMaxWidth() + .height(IntrinsicSize.Max), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surface, + contentColor = MaterialTheme.colorScheme.error + ), + border = BorderStroke(0.5.dp, MaterialTheme.colorScheme.error) + ) { + Row( + modifier = Modifier + .padding(16.dp, 24.dp) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(4.dp), + + ) { + Text( + text = "Need emergency medical help?", + style = MaterialTheme.typography.titleSmall + ) + Text( + text = "Our veterinarian’s are ready to help you", + style = MaterialTheme.typography.labelSmall + ) + } + Icon( + imageVector = Icons.Outlined.Call, + contentDescription = "Menu", + modifier = Modifier + .size(UiUtil.getScreenHeight().dp / 25) + ) + } + + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/TopBar.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/TopBar.kt new file mode 100644 index 000000000..f65c0f735 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/TopBar.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AccountCircle +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.util.ui.UiUtil + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TopBar( + navigateToHome: () -> Unit, + navigateToProfile: () -> Unit +) { + TopAppBar( + title = { + Image( + painter = painterResource(id = R.drawable.home_logo), + modifier = Modifier + .size(UiUtil.getScreenHeight().dp / 6) + .offset(x = (-16).dp) + .clickable { navigateToHome() }, + contentDescription = "Home Logo", + ) + }, + actions = { + Icon( + imageVector = Icons.Outlined.AccountCircle, + contentDescription = "Menu", + modifier = Modifier + .size(UiUtil.getScreenHeight().dp / 25) + .offset(x = 4.dp) + .clickable { navigateToProfile() }, + tint = MaterialTheme.colorScheme.tertiary + ) + }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp), + ) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/VetCard.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/VetCard.kt new file mode 100644 index 000000000..a91a3d919 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/VetCard.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.AssistChip +import androidx.compose.material3.AssistChipDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SuggestionChip +import androidx.compose.material3.SuggestionChipDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.util.ui.UiUtil + +@Composable +fun VetCard(){ + Card( + modifier = Modifier + .fillMaxWidth(), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.primary + ), + ) { + Box( + modifier = Modifier.fillMaxWidth() + ) { + Image( + painter = painterResource(id = R.drawable.person_dog_home), + contentDescription = "Person with dog", + modifier = Modifier + .height(UiUtil.getScreenHeight().dp / 5) + .offset(x = 8.dp, y = 4.dp) + .align(Alignment.CenterEnd) + ) + Column( + modifier = Modifier + .padding(start = 16.dp, top = 24.dp, bottom = 16.dp) + .fillMaxWidth() + .fillMaxHeight(), + verticalArrangement = Arrangement.SpaceBetween + ) { + Column { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + SuggestionChip( + colors = SuggestionChipDefaults.suggestionChipColors( + containerColor = MaterialTheme.colorScheme.surface + ), + border = null, + modifier = Modifier.height(16.dp), + onClick = {}, + label = { + Text( + text = "LIVE", + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.error, + ) + } + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = "Consult with", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.surface + ) + } + Column( + verticalArrangement = Arrangement.spacedBy(0.dp), + horizontalAlignment = Alignment.Start + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "an ", + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.surface + ) + Text( + text = "Experienced", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.surface, + modifier = Modifier.width(200.dp) + ) + } + Text( + text = "Veterinarian", + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.surface, + modifier = Modifier.offset(y = -6.dp) + ) + } + + } + Spacer(modifier = Modifier.height(8.dp)) + AssistChip( + onClick = {}, + label = { + Text( + text = "Consult Now", + color = MaterialTheme.colorScheme.primary + ) + }, + leadingIcon = { + Icon( + painter = painterResource(id = R.drawable.video_call), + contentDescription = "Menu", + modifier = Modifier + .size(24.dp) + .offset(x = 4.dp), + ) + }, + shape = MaterialTheme.shapes.extraSmall, + border = null, + colors = AssistChipDefaults.assistChipColors( + containerColor = MaterialTheme.colorScheme.surface + ), + modifier = Modifier.align(Alignment.Start) + ) + } + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/add_pet/AddPetCard.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/add_pet/AddPetCard.kt new file mode 100644 index 000000000..c13f2f703 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/add_pet/AddPetCard.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util.add_pet + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.util.ui.UiUtil + +@Composable +fun AddPetCard() { + val fullText = + "Add details of your cutie pet from here. So that we have the full details of " + + "your pet to help them get better quickly" + val partToBold = "Add details of your cutie pet from here." + + val annotatedString: AnnotatedString = AnnotatedString.Builder().apply { + fullText.split(partToBold).forEachIndexed { index, part -> + append(part) + if (index != fullText.split(partToBold).lastIndex) { + pushStyle( + SpanStyle(fontWeight = FontWeight.Bold) + ) + append(partToBold) + pop() + } + } + }.toAnnotatedString() + + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.CenterStart + ) { + Image( + painter = painterResource(id = R.drawable.add_pet_banner), + contentDescription = "Person with dog", + modifier = Modifier + .height(UiUtil.getScreenHeight().dp / 5) + .offset(x = 8.dp, y = 4.dp) + .align(Alignment.Center) + ) + Row { + Text( + text = annotatedString, + color = MaterialTheme.colorScheme.tertiaryContainer, + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.Start, + modifier = Modifier + .padding(start = 32.dp) + .width(200.dp) + ) + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/add_pet/TopBar.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/add_pet/TopBar.kt new file mode 100644 index 000000000..6a820baae --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/add_pet/TopBar.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util.add_pet + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TopBar( + navigateToHome: () -> Unit +) { + TopAppBar( + navigationIcon = { + IconButton(onClick = navigateToHome ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.KeyboardArrowLeft, + contentDescription = "Go back", + tint = MaterialTheme.colorScheme.primary + ) + } + }, + title = { + Text( + text = "Add Pet", + color = MaterialTheme.colorScheme.primary + ) + }, + modifier = Modifier + .fillMaxWidth() + .padding(end = 32.dp, start = 8.dp), + ) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/pets_list/PetCard.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/pets_list/PetCard.kt new file mode 100644 index 000000000..abf2a76c6 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/pets_list/PetCard.kt @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util.pets_list + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.KeyboardArrowRight +import androidx.compose.material.icons.outlined.DateRange +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import coil.compose.rememberAsyncImagePainter +import com.wso2_sample.api_auth_sample.features.home.domain.models.pet.Pet + +@Composable +fun PetCard(pet: Pet) { + Row( + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Image( + //painter = painterResource(id = pet.imageId), + painter = rememberAsyncImagePainter(pet.imageUrl), + contentDescription = pet.name, + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxHeight() + .weight(1f) + .height(56.dp) + .width(56.dp) + .clip(MaterialTheme.shapes.extraLarge) + ) + Column( + modifier = Modifier.weight(4f), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Column( + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + Text( + text = pet.name ?: "Dobby", + style = MaterialTheme.typography.bodyLarge + ) + Text( + text = pet.breed ?: "Golden Retriever", + style = MaterialTheme.typography.bodyMedium, + ) + } + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + imageVector = Icons.Outlined.DateRange, + contentDescription = "Menu", + tint = MaterialTheme.colorScheme.tertiaryContainer, + modifier = Modifier + .size(16.dp) + ) + + Text( + text = pet.nextAppointment ?: "Next appointment: 12th June 2025", + style = MaterialTheme.typography.labelSmall + ) + } + } + Icon( + imageVector = Icons.AutoMirrored.Outlined.KeyboardArrowRight, + contentDescription = "Menu", + tint = MaterialTheme.colorScheme.tertiaryContainer, + modifier = Modifier + .size(20.dp) + .padding(end = 8.dp) + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/pets_list/PetsList.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/pets_list/PetsList.kt new file mode 100644 index 000000000..59fae8ce2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/pets_list/PetsList.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util.pets_list + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.wso2_sample.api_auth_sample.features.home.domain.models.pet.Pet + +@Composable +fun PetsList(pets: List) { + Column( + modifier = Modifier.padding(horizontal = 32.dp), + ) { + Text( + text = "Your cutie pets", + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.tertiary + ) + Column( + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + pets.forEach { pet -> + PetCard(pet = pet) + } + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/NameSection.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/NameSection.kt new file mode 100644 index 000000000..8ef9c14be --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/NameSection.kt @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util.profile + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Star +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.unit.dp + +@Composable +fun NameSection(firstName: String?, lastName: String?) { + // If first name is null, set it to "Sarah" and if last name is null, set it to "Jones" + var first: String? = firstName + var last: String? = lastName + if (first == null) { + first = "Sarah" + } + if (last == null) { + last = "Jones" + } + + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "$firstName $lastName", + style = MaterialTheme.typography.titleLarge + ) + Row( + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + imageVector = Icons.Filled.Star, + contentDescription = "Email Icon", + modifier = Modifier + .size(16.dp) + .graphicsLayer(alpha = 0.99f) + .drawWithCache { + onDrawWithContent { + drawContent() + drawRect( + Brush.linearGradient( + colors = listOf( + Color(0xFFAE8625), + Color(0xFFF7EF8A), + ), + start = Offset.Zero, + end = Offset.Infinite + ), + blendMode = BlendMode.SrcAtop + ) + } + } + ) + Text( + text = "Gold member", + style = MaterialTheme.typography.bodyMedium.copy( + brush = Brush.linearGradient( + colors = listOf( + Color(0xFFAE8625), + Color(0xFFF7EF8A), + ), + start = Offset.Zero, + end = Offset.Infinite + ) + ) + ) + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/ProfileAttribute.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/ProfileAttribute.kt new file mode 100644 index 000000000..68ff9f09a --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/ProfileAttribute.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util.profile + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun ProfileAttribute( + label: String, + value: String? +) { + Row( + modifier = Modifier + .padding(start = 32.dp, end = 32.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = label, + style = MaterialTheme.typography.labelSmall + ) + Text( + text = value ?: "Value not available", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.tertiary + ) + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/ProfileImage.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/ProfileImage.kt new file mode 100644 index 000000000..71a2ba4b0 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/ProfileImage.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util.profile + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import coil.compose.rememberAsyncImagePainter + +@Composable +fun ProfileImage(imageUrl: String) { + Image( + modifier = Modifier + .size(104.dp) + .clip(CircleShape) + .border(1.dp, MaterialTheme.colorScheme.primary, CircleShape), + painter = rememberAsyncImagePainter(imageUrl), + contentDescription = "Profile Picture", + contentScale = ContentScale.FillBounds + ) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/SettingsAttribute.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/SettingsAttribute.kt new file mode 100644 index 000000000..21517aee2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/home/presentation/util/profile/SettingsAttribute.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.home.presentation.util.profile + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.KeyboardArrowRight +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp + +@Composable +fun SettingsAttribute( + title: String, + iconId: Int, + onClick: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { onClick() }, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + + ) { + Icon( + painter = painterResource(id = iconId), + contentDescription = title, + tint = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.size(16.dp) + ) + Text( + text = title, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.tertiary + ) + } + Box( + modifier = Modifier.padding(end = 32.dp) + ) { + Icon( + imageVector = Icons.AutoMirrored.Outlined.KeyboardArrowRight, + contentDescription = "Menu", + tint = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.size(16.dp) + ) + } + } + HorizontalDivider( + thickness = 0.5.dp + ) +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/model/error/AuthenticationError.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/model/error/AuthenticationError.kt new file mode 100644 index 000000000..d49a98a27 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/model/error/AuthenticationError.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.domain.model.error + +data class AuthenticationError( + val errorMessage: String, + val t: Throwable? = null +) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/repository/AsgardeoAuthRepository.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/repository/AsgardeoAuthRepository.kt new file mode 100644 index 000000000..fb066e5f8 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/repository/AsgardeoAuthRepository.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.domain.repository + +import io.asgardeo.android.core.provider.providers.authentication.AuthenticationProvider +import io.asgardeo.android.core.provider.providers.token.TokenProvider + +/** + * Use as a repository to handle the authentication related operations using Asgardeo authentication SDK. + */ +interface AsgardeoAuthRepository { + fun initializeAsgardeoAuth(integrityToken: String?) + fun getAuthenticationProvider(): AuthenticationProvider + fun getTokenProvider(): TokenProvider +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/repository/AttestationRepository.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/repository/AttestationRepository.kt new file mode 100644 index 000000000..339b89bd2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/domain/repository/AttestationRepository.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.domain.repository + +/** + * Repository interface for the Attestation feature. + */ +interface AttestationRepository { + suspend fun getPlayIntegrityTokenResponse(): String? +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/mapper/Mapper.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/mapper/Mapper.kt new file mode 100644 index 000000000..ef4fd311c --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/mapper/Mapper.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.impl.mapper + +import com.wso2_sample.api_auth_sample.features.login.domain.model.error.AuthenticationError + +fun Throwable.toAuthenticationError(): AuthenticationError { + val errorMessage: String = if (this.message != null) this.message!! else "Something went wrong" + return AuthenticationError(errorMessage, this) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/AsgardeoAuthRepositoryImpl.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/AsgardeoAuthRepositoryImpl.kt new file mode 100644 index 000000000..96aa3b8fc --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/AsgardeoAuthRepositoryImpl.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.impl.repository + +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AsgardeoAuthRepository +import com.wso2_sample.api_auth_sample.util.Config +import io.asgardeo.android.core.asgardeo_auth.AsgardeoAuth +import io.asgardeo.android.core.core_config.AuthenticationCoreConfig +import io.asgardeo.android.core.provider.providers.authentication.AuthenticationProvider +import io.asgardeo.android.core.provider.providers.token.TokenProvider +import javax.inject.Inject + +class AsgardeoAuthRepositoryImpl @Inject constructor() : AsgardeoAuthRepository { + private lateinit var asgardeoAuth: AsgardeoAuth + + override fun initializeAsgardeoAuth(integrityToken: String?) { + asgardeoAuth = AsgardeoAuth.getInstance( + AuthenticationCoreConfig( + // Change these values at your discretion + authorizeEndpoint = Config.getAuthorizeUrl(), + tokenEndpoint = Config.getTokenUrl(), + logoutEndpoint = Config.getLogoutUrl(), + userInfoEndpoint = Config.getUserInfoUrl(), + authnEndpoint = Config.getAuthnUrl(), + redirectUri = Config.getRedirectUri(), + clientId = Config.getClientId(), + scope = Config.getScope(), + googleWebClientId = Config.getGoogleWebClientId(), + isDevelopment = true, + integrityToken = integrityToken + ) + ) + } + + override fun getAuthenticationProvider(): AuthenticationProvider = + asgardeoAuth.getAuthenticationProvider() + + override fun getTokenProvider(): TokenProvider = + asgardeoAuth.getTokenProvider() +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/AttestationRepositoryImpl.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/AttestationRepositoryImpl.kt new file mode 100644 index 000000000..59462cfa2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/AttestationRepositoryImpl.kt @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.impl.repository + +import android.content.ContentValues +import android.content.Context +import android.util.Base64 +import android.util.Log +import com.google.android.gms.tasks.Task +import com.google.android.play.core.integrity.IntegrityManagerFactory +import com.google.android.play.core.integrity.IntegrityTokenRequest +import com.google.android.play.core.integrity.IntegrityTokenResponse +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AttestationRepository +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CompletableDeferred +import javax.inject.Inject +import kotlin.math.floor + +class AttestationRepositoryImpl @Inject constructor( + @ApplicationContext private val applicationContext: Context +): AttestationRepository { + private var playIntegrityTokenResponse: String? = null + private val initializationDeferred = CompletableDeferred() + + init { + setPlayIntegrityTokenResponse(applicationContext) + } + + private fun setPlayIntegrityTokenResponse(context: Context) { + getPlayIntegrityTokenResponse(context) { + playIntegrityTokenResponse = it + initializationDeferred.complete(Unit) + } + } + + private fun getPlayIntegrityTokenResponse( + context: Context, + callback: (String?) -> Unit + ) { + var playIntegrityTokenResponse: String? = null + + try { + val nonce: String = generateNonce() + + // Create an instance of a manager. + val integrityManager = IntegrityManagerFactory.create(context) + + // Request the integrity token by providing a nonce. + val integrityTokenResponse: Task = + integrityManager.requestIntegrityToken( + IntegrityTokenRequest.builder() + .setNonce(nonce) + .build() + ) + + integrityTokenResponse.addOnSuccessListener { + playIntegrityTokenResponse = it.token() + callback(playIntegrityTokenResponse) + }.addOnFailureListener { + Log.d(ContentValues.TAG, "API Error, see Android UI for error message") + callback(null) + } + } catch (e: Exception) { + Log.e( + "AttestationCallPlayIntegrity", + "Error in playIntegrityRequest: ${e.message}" + ) + + callback(null) + } + } + + /** + * Generates a nonce locally + */ + private fun generateNonce(): String = getNonceLocal() + + private fun String.encode(): String = + Base64.encodeToString( + this.toByteArray(charset("UTF-8")), + Base64.URL_SAFE + ) + + /** + * generates a nonce locally + */ + private fun getNonceLocal(): String { + var nonce = "" + val allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + for (i in 0 until 50) { + nonce += allowed[floor(Math.random() * allowed.length).toInt()].toString() + } + return nonce.encode() + } + + override suspend fun getPlayIntegrityTokenResponse(): String? { + initializationDeferred.await() + return playIntegrityTokenResponse + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/attestation/AttestationCallPlayIntegrity.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/attestation/AttestationCallPlayIntegrity.kt new file mode 100644 index 000000000..34ce7a0a2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/impl/repository/attestation/AttestationCallPlayIntegrity.kt @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.impl.repository.attestation + +import android.content.ContentValues +import android.content.Context +import android.util.Base64 +import android.util.Log +import com.google.android.gms.tasks.Task +import com.google.android.play.core.integrity.IntegrityManagerFactory +import com.google.android.play.core.integrity.IntegrityTokenRequest +import com.google.android.play.core.integrity.IntegrityTokenResponse +import kotlin.math.floor + +object AttestationCallPlayIntegrity { + fun getPlayIntegrityTokenResponse( + context: Context, + callback: (String?) -> Unit + ) { + var playIntegrityTokenResponse: String? = null + + try { + val nonce: String = generateNonce() + + // Create an instance of a manager. + val integrityManager = IntegrityManagerFactory.create(context) + + // Request the integrity token by providing a nonce. + val integrityTokenResponse: Task = + integrityManager.requestIntegrityToken( + IntegrityTokenRequest.builder() + .setNonce(nonce) + .build() + ) + + integrityTokenResponse.continueWith { + if (it.isSuccessful) { + playIntegrityTokenResponse = it.result?.token() + } else { + Log.d(ContentValues.TAG, "API Error, see Android UI for error message") + } + callback(playIntegrityTokenResponse) + } + +// // do play integrity api call +// integrityTokenResponse.addOnSuccessListener { response -> +// val integrityToken = response.token() +// continuation.resume(response.token()) +// }.addOnFailureListener { e -> +// Log.d(ContentValues.TAG, "API Error, see Android UI for error message") +// continuation.resume(null) +// } + } catch (e: Exception) { + Log.e( + "AttestationCallPlayIntegrity", + "Error in playIntegrityRequest: ${e.message}" + ) + + callback(null) + } + } + + /** + * Generates a nonce locally + */ + private fun generateNonce(): String = getNonceLocal() + + private fun String.encode(): String = + Base64.encodeToString( + this.toByteArray(charset("UTF-8")), + Base64.URL_SAFE + ) + + /** + * generates a nonce locally + */ + private fun getNonceLocal(): String { + var nonce = "" + val allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + for (i in 0 until 50) { + nonce += allowed[floor(Math.random() * allowed.length).toInt()].toString() + } + return nonce.encode() + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreen.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreen.kt new file mode 100644 index 000000000..c5f7953db --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreen.kt @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components.AuthUI +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components.BasicAuthComponent +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components.GetStarted +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components.GithubAuthComponent +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components.GoogleNativeAuthComponent +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.ContinueText +import com.wso2_sample.api_auth_sample.util.ui.LoadingDialog +import com.wso2_sample.api_auth_sample.ui.theme.Api_authenticator_sdkTheme +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components.PasskeyAuthComponent +import io.asgardeo.android.core.models.authentication_flow.AuthenticationFlow + +@Composable +internal fun AuthScreen( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticationFlow: AuthenticationFlow +) { + val state = viewModel.state.collectAsStateWithLifecycle() + LaunchedEffect(key1 = authenticationFlow) { + viewModel.setAuthenticationFlow(authenticationFlow) + } + LoadingDialog(isLoading = state.value.isLoading) + AuthScreenContent(state.value) +} + +@Composable +fun AuthScreenContent(state: AuthScreenState) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .padding(start = 32.dp, end = 32.dp, bottom = 32.dp) + .offset(y = 32.dp) + ) { + GetStarted() + } + Surface( + color = MaterialTheme.colorScheme.background, + modifier = Modifier.fillMaxHeight() + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .padding( + top = 16.dp, + start = 32.dp, + end = 32.dp, + bottom = 32.dp + ) + .fillMaxSize() + ) { + state.authenticationFlow?.let { authenticationFlow -> + AuthUI(authenticationFlow) + } + } + } + Box( + modifier = Modifier + .fillMaxSize() + .weight(1f) + .background(MaterialTheme.colorScheme.background) + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun AuthScreenPreview() { + Api_authenticator_sdkTheme { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Box( + modifier = Modifier + .padding(start = 32.dp, end = 32.dp, bottom = 32.dp) + .offset(y = 42.dp) + ) { + GetStarted() + } + Surface( + color = MaterialTheme.colorScheme.background, + modifier = Modifier.fillMaxHeight() + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .padding( + top = 16.dp, + start = 32.dp, + end = 32.dp, + bottom = 32.dp + ) + .fillMaxSize(1f) + ) { + BasicAuthComponent(onLoginClick = { _, _ -> }) + ContinueText() + GoogleNativeAuthComponent {} + PasskeyAuthComponent {} + GithubAuthComponent {} + } + } + Box( + modifier = Modifier + .fillMaxSize() + .weight(1f) + .background(MaterialTheme.colorScheme.background) + ) + } + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreenState.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreenState.kt new file mode 100644 index 000000000..be68436b7 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreenState.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen + +import io.asgardeo.android.core.models.autheniticator.Authenticator +import io.asgardeo.android.core.models.authentication_flow.AuthenticationFlowNotSuccess + +data class AuthScreenState( + val isLoading: Boolean = true, + val authenticationFlow: AuthenticationFlowNotSuccess? = null, + val detailedAuthenticator: Authenticator? = null, + val error: String = "" +) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreenViewModel.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreenViewModel.kt new file mode 100644 index 000000000..c72f744ed --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/AuthScreenViewModel.kt @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen + +import android.content.Context +import android.content.Intent +import android.os.Build +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.annotation.RequiresApi +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AsgardeoAuthRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import io.asgardeo.android.core.models.autheniticator.Authenticator +import io.asgardeo.android.core.models.authentication_flow.AuthenticationFlow +import io.asgardeo.android.core.models.authentication_flow.AuthenticationFlowNotSuccess +import io.asgardeo.android.core.provider.providers.authentication.AuthenticationProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class AuthScreenViewModel @Inject constructor( + @ApplicationContext private val applicationContext: Context, + asgardeoAuthRepository: AsgardeoAuthRepository +) : ViewModel() { + + companion object { + const val TAG = "AuthScreen" + } + + private val _state = MutableStateFlow(AuthScreenState()) + val state = _state + + private var authenticationProvider: AuthenticationProvider + + init { + authenticationProvider = asgardeoAuthRepository.getAuthenticationProvider() + _state.update { + it.copy(isLoading = false) + } + } + + fun setAuthenticationFlow(authenticationFlow: AuthenticationFlow) { + _state.update { + it.copy( + authenticationFlow = authenticationFlow as AuthenticationFlowNotSuccess, + isLoading = false + ) + } + } + + fun selectAuthenticator(authenticator: Authenticator) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + val detailedAuthenticator: Authenticator? = + authenticationProvider.selectAuthenticator(authenticator) + _state.update { + it.copy( + isLoading = false, + detailedAuthenticator = detailedAuthenticator + ) + } + } + } + + fun authenticate( + detailedAuthenticator: Authenticator?, + authParams: LinkedHashMap + ) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticate( + applicationContext, + detailedAuthenticator, + authParams + ) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + fun authenticateWithUsernamePassword( + authenticatorId: String, + username: String, + password: String + ) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithUsernameAndPassword( + applicationContext, + authenticatorId, + username, + password + ) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + fun authenticateWithTotp(authenticatorId: String, token: String) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithTotp( + applicationContext, + authenticatorId, + token + ) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + fun authenticateWithEmailOTP(authenticatorId: String, otpCode: String) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithEmailOtp( + applicationContext, + authenticatorId, + otpCode + ) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + fun authenticateWithSMSOTP(authenticatorId: String, otpCode: String) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithSMSOtp( + applicationContext, + authenticatorId, + otpCode + ) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + fun authenticateWithOpenIdConnect(authenticatorId: String) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithOpenIdConnect( + applicationContext, + authenticatorId + ) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + fun authenticateWithGoogleNative(authenticatorId: String) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithGoogleNative(applicationContext, authenticatorId) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + fun authenticateWithPasskey(authenticatorId: String) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithPasskey(applicationContext, authenticatorId) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + fun authenticateWithGoogleNativeLegacy( + authenticatorId: String, + googleAuthenticateResultLauncher: ActivityResultLauncher + ) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithGoogleNativeLegacy( + applicationContext, + authenticatorId, + googleAuthenticateResultLauncher + ) + _state.update { + it.copy( + isLoading = false + ) + } + } + } + + fun handleGoogleNativeLegacyAuthenticateResult(result: ActivityResult) { + viewModelScope.launch { + authenticationProvider.handleGoogleNativeLegacyAuthenticateResult( + applicationContext, + result.resultCode, + result.data!! + ) + } + } + + fun authenticateWithGithub(authenticatorId: String) { + viewModelScope.launch { + _state.update { + it.copy( + isLoading = true + ) + } + authenticationProvider.authenticateWithGithub( + applicationContext, + authenticatorId + ) + _state.update { + it.copy( + isLoading = false + ) + } + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/AuthUI.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/AuthUI.kt new file mode 100644 index 000000000..f17b66b4f --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/AuthUI.kt @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.runtime.Composable +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.ContinueText +import io.asgardeo.android.core.models.autheniticator.Authenticator +import io.asgardeo.android.core.models.autheniticator.AuthenticatorTypes +import io.asgardeo.android.core.models.authentication_flow.AuthenticationFlowNotSuccess + +@Composable +internal fun AuthUI(authenticationFlow: AuthenticationFlowNotSuccess) { + + val authenticators: ArrayList = authenticationFlow.nextStep.authenticators + + // show basic auth screen if basic auth is available + authenticators.forEach { + when (it.authenticator) { + AuthenticatorTypes.BASIC_AUTHENTICATOR.authenticatorType -> { + BasicAuth(authenticator = it) + if (authenticators.size > 1) { + ContinueText() + } + } + } + } + + authenticators.forEach { + when (it.authenticator) { + AuthenticatorTypes.GOOGLE_AUTHENTICATOR.authenticatorType -> { + GoogleNativeAuth(authenticator = it) + } + + AuthenticatorTypes.OPENID_CONNECT_AUTHENTICATOR.authenticatorType -> { + OpenIdRedirectAuth(authenticator = it) + } + + AuthenticatorTypes.GITHUB_REDIRECT_AUTHENTICATOR.authenticatorType -> { + GithubAuth(authenticator = it) + } + + AuthenticatorTypes.MICROSOFT_REDIRECT_AUTHENTICATOR.authenticatorType -> { + MicrosoftAuth(authenticator = it) + } + + AuthenticatorTypes.PASSKEY_AUTHENTICATOR.authenticatorType -> { + PasskeyAuth(authenticator = it) + } + + AuthenticatorTypes.TOTP_AUTHENTICATOR.authenticatorType -> { + TotpAuth(authenticator = it) + } + + AuthenticatorTypes.EMAIL_OTP_AUTHENTICATOR.authenticatorType -> { + EmailOTPAuth(authenticator = it) + } + + AuthenticatorTypes.SMS_OTP_AUTHENTICATOR.authenticatorType -> { + SMSOTPAuth(authenticator = it) + } + + else -> {} + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/BasicAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/BasicAuth.kt new file mode 100644 index 000000000..437f5bfcf --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/BasicAuth.kt @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.ui.theme.Api_authenticator_sdkTheme +import io.asgardeo.android.core.models.autheniticator.Authenticator + +@Composable +internal fun BasicAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + val state = viewModel.state.collectAsStateWithLifecycle() + + LaunchedEffect(key1 = authenticator.authenticatorId) { + viewModel.selectAuthenticator(authenticator) + } + BasicAuthComponent( + onLoginClick = { username, password -> + viewModel.authenticate( + state.value.detailedAuthenticator, + LinkedHashMap( + mapOf( + "username" to username, + "password" to password + ) + ) + ) + } + ) +} + +@Composable +fun BasicAuthComponent( + onLoginClick: (username: String, password: String) -> Unit +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + var username by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + + OutlinedTextField( + value = username, + onValueChange = { username = it }, + label = { Text(text = "Username") }, + modifier = Modifier.fillMaxWidth(), + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ) + + ) + Spacer( + modifier = Modifier.height(4.dp), + ) + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text(text = "Password") }, + modifier = Modifier.fillMaxWidth(), + visualTransformation = PasswordVisualTransformation(), + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ) + ) + + Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = { onLoginClick(username, password) }, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "Sign In", + color = MaterialTheme.colorScheme.surface + ) + } + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun BasicAuthPreview() { + Api_authenticator_sdkTheme { + BasicAuthComponent( + onLoginClick = { _, _ -> } + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/EmailOTPAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/EmailOTPAuth.kt new file mode 100644 index 000000000..567eb663c --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/EmailOTPAuth.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.AuthButton +import io.asgardeo.android.core.models.autheniticator.Authenticator + +@Composable +internal fun EmailOTPAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + EmailOTPAuthComponent( + onSubmit = { otpCode -> + viewModel.authenticateWithEmailOTP(authenticator.authenticatorId, otpCode) + } + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BottomSheetWithFormForEmailOTP( + isOpen: Boolean, + onDismiss: () -> Unit, + onSubmit: (otpCode: String) -> Unit +) { + var otpCode by remember { mutableStateOf("") } + + if (isOpen) { + ModalBottomSheet( + onDismissRequest = { + onDismiss() + } + ) { + Column( + modifier = Modifier + .padding(32.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.Center + ) { + Text( + text = "Enter Email OTP", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Medium + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "Enter the email OTP received in yor email", + style = MaterialTheme.typography.labelSmall, + ) + Spacer(modifier = Modifier.height(24.dp)) + OutlinedTextField( + value = otpCode, + onValueChange = { otpCode = it }, + label = { Text(text = "Email OTP") }, + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ), + modifier = Modifier.fillMaxWidth() + ) + Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = { + onSubmit(otpCode) + onDismiss() + }, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "Submit", + color = MaterialTheme.colorScheme.surface + ) + } + } + } + } +} + +@Composable +fun EmailOTPAuthComponent( + onSubmit: (otpCode: String) -> Unit +) { + var bottomSheetOpen by remember { mutableStateOf(false) } + + AuthButton( + onSubmit = { bottomSheetOpen = true }, + label = "Continue with Email OTP", + imageResource = R.drawable.email_otp, + imageContextDescription = "Email OTP" + ) + + BottomSheetWithFormForEmailOTP( + isOpen = bottomSheetOpen, + onDismiss = { bottomSheetOpen = false }, + onSubmit = onSubmit + ) +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun EmailOTPAuthPreview() { + EmailOTPAuthComponent(onSubmit = {}) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GetStarted.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GetStarted.kt new file mode 100644 index 000000000..1db85e321 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GetStarted.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.util.ui.UiUtil + +@Composable +fun GetStarted() { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Column( + modifier = Modifier.weight(1f), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + painter = painterResource(id = R.drawable.circle_logo), + contentDescription = "Logo", + modifier = Modifier.size(32.dp) + ) + Spacer(modifier = Modifier.size(8.dp)) + Text( + text = "Get Started", + style = MaterialTheme.typography.titleLarge + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Best pet care starts here.", + style = MaterialTheme.typography.labelLarge, + color = Color(0xFF939B9B) + ) + Spacer(modifier = Modifier.height(2.dp)) + Text( + text = "Schedule appointments & keep your furry friend healthy – all at your fingertips.", + style = MaterialTheme.typography.labelMedium + ) + } + Image( + painter = painterResource(id = R.drawable.person_dog_login), + contentDescription = "Artist image", + modifier = Modifier + .size(UiUtil.getScreenHeight().dp/4, UiUtil.getScreenHeight().dp/4) + .weight(0.64f) + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GithubAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GithubAuth.kt new file mode 100644 index 000000000..934e74fab --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GithubAuth.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import io.asgardeo.android.core.models.autheniticator.Authenticator +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.AuthButton + +@Composable +internal fun GithubAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + GithubAuthComponent( + onSubmit = { + viewModel.authenticateWithGithub(authenticator.authenticatorId) + } + ) +} + +@Composable +fun GithubAuthComponent( + onSubmit: () -> Unit +) { + AuthButton( + onSubmit = onSubmit, + label = "Continue with Github", + imageResource = R.drawable.github, + imageContextDescription = "Github" + ) +} + + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun GithubAuthPreview() { + GithubAuthComponent(onSubmit = {}) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GoogleNativeAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GoogleNativeAuth.kt new file mode 100644 index 000000000..57aef30f0 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/GoogleNativeAuth.kt @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import android.content.Intent +import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import io.asgardeo.android.core.models.autheniticator.Authenticator +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.AuthButton + +//@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) +@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) +@Composable +internal fun GoogleNativeAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + val launcher: ActivityResultLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + viewModel.handleGoogleNativeLegacyAuthenticateResult(result) + } + + GoogleNativeAuthComponent( + onSubmit = { + viewModel.authenticateWithGoogleNativeLegacy( + authenticator.authenticatorId, + launcher + ) + + // Using the Google Credential Manager API + //viewModel.authenticateWithGoogleNative(authenticator.authenticatorId) + } + ) +} + +@Composable +fun GoogleNativeAuthComponent( + onSubmit: () -> Unit +) { + AuthButton( + onSubmit = onSubmit, + label = "Continue with Google", + imageResource = R.drawable.google, + imageContextDescription = "Google" + ) +} + + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun GoogleNativeAuthPreview() { + GoogleNativeAuthComponent(onSubmit = {}) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/MicrosoftAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/MicrosoftAuth.kt new file mode 100644 index 000000000..d1ff242d3 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/MicrosoftAuth.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import io.asgardeo.android.core.models.autheniticator.Authenticator +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.AuthButton + +@Composable +internal fun MicrosoftAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + MicrosoftAuthComponent( + onSubmit = { + viewModel.authenticateWithGithub(authenticator.authenticatorId) + } + ) +} + +@Composable +fun MicrosoftAuthComponent( + onSubmit: () -> Unit +) { + AuthButton( + onSubmit = onSubmit, + label = "Continue with Microsoft", + imageResource = R.drawable.microsoft, + imageContextDescription = "Microsoft" + ) +} + + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun MicrosoftAuthPreview() { + MicrosoftAuthComponent(onSubmit = {}) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/OpenIdRedirectAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/OpenIdRedirectAuth.kt new file mode 100644 index 000000000..c6a8c9e07 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/OpenIdRedirectAuth.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import io.asgardeo.android.core.models.autheniticator.Authenticator +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.AuthButton + +@Composable +internal fun OpenIdRedirectAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + OpenIdRedirectAuthComponent( + onSubmit = { + viewModel.authenticateWithOpenIdConnect(authenticator.authenticatorId) + } + ) +} + +@Composable +fun OpenIdRedirectAuthComponent( + onSubmit: () -> Unit +) { + AuthButton( + onSubmit = onSubmit, + label = "Continue with OpenID Connect", + imageResource = R.drawable.enterprise_icon, + imageContextDescription = "OpenID Connect" + ) +} + + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun OpenIdRedirectAuthPreview() { + OpenIdRedirectAuthComponent(onSubmit = {}) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/PasskeyAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/PasskeyAuth.kt new file mode 100644 index 000000000..8edd2c8ef --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/PasskeyAuth.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import io.asgardeo.android.core.models.autheniticator.Authenticator +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.AuthButton + +@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) +@Composable +internal fun PasskeyAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + PasskeyAuthComponent( + onSubmit = { + viewModel.authenticateWithPasskey(authenticator.authenticatorId) + } + ) +} + +@Composable +fun PasskeyAuthComponent( + onSubmit: () -> Unit +) { + AuthButton( + onSubmit = onSubmit, + label = "Continue with Passkey", + imageResource = R.drawable.passkey, + imageContextDescription = "Passkey" + ) +} + + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun PasskeyAuthPreview() { + PasskeyAuthComponent(onSubmit = {}) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/SMSOTPAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/SMSOTPAuth.kt new file mode 100644 index 000000000..78a383e3f --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/SMSOTPAuth.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import io.asgardeo.android.core.models.autheniticator.Authenticator +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.AuthButton + +@Composable +internal fun SMSOTPAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + SMSOTPAuthComponent( + onSubmit = { otpCode -> + viewModel.authenticateWithSMSOTP(authenticator.authenticatorId, otpCode) + } + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BottomSheetWithFormForSMSOTP( + isOpen: Boolean, + onDismiss: () -> Unit, + onSubmit: (otpCode: String) -> Unit +) { + var otpCode by remember { mutableStateOf("") } + + if (isOpen) { + ModalBottomSheet( + onDismissRequest = { + onDismiss() + } + ) { + Column( + modifier = Modifier + .padding(32.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.Center + ) { + Text( + text = "Enter SMS OTP", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Medium + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "Enter the SMS OTP sent to your mobile number", + style = MaterialTheme.typography.labelSmall, + ) + Spacer(modifier = Modifier.height(24.dp)) + OutlinedTextField( + value = otpCode, + onValueChange = { otpCode = it }, + label = { Text(text = "SMS OTP") }, + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ), + modifier = Modifier.fillMaxWidth() + ) + Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = { + onSubmit(otpCode) + onDismiss() + }, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "Submit", + color = MaterialTheme.colorScheme.surface + ) + } + } + } + } +} + +@Composable +fun SMSOTPAuthComponent( + onSubmit: (SMSOTP: String) -> Unit +) { + var bottomSheetOpen by remember { mutableStateOf(false) } + + AuthButton( + onSubmit = { bottomSheetOpen = true }, + label = "Continue with SMS OTP", + imageResource = R.drawable.sms_otp, + imageContextDescription = "SMS OTP" + ) + + BottomSheetWithForm( + isOpen = bottomSheetOpen, + onDismiss = { bottomSheetOpen = false }, + onSubmit = onSubmit + ) +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun SMSOTPAuthPreview() { + SMSOTPAuthComponent(onSubmit = {}) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/TotpAuth.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/TotpAuth.kt new file mode 100644 index 000000000..a86236912 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/auth_screen/components/TotpAuth.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import io.asgardeo.android.core.models.autheniticator.Authenticator +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.AuthButton + +@Composable +internal fun TotpAuth( + viewModel: AuthScreenViewModel = hiltViewModel(), + authenticator: Authenticator +) { + TotpAuthComponent( + onSubmit = { token -> + viewModel.authenticateWithTotp(authenticator.authenticatorId, token) + } + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BottomSheetWithForm( + isOpen: Boolean, + onDismiss: () -> Unit, + onSubmit: (totp: String) -> Unit +) { + var totpCode by remember { mutableStateOf("") } + + if (isOpen) { + ModalBottomSheet( + onDismissRequest = { + onDismiss() + } + ) { + Column( + modifier = Modifier + .padding(32.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.Center + ) { + Text( + text = "Enter TOTP Code", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Medium + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "Enter the verification code from your authenticator application", + style = MaterialTheme.typography.labelSmall, + ) + Spacer(modifier = Modifier.height(24.dp)) + OutlinedTextField( + value = totpCode, + onValueChange = { totpCode = it }, + label = { Text(text = "TOTP Code") }, + shape = MaterialTheme.shapes.medium, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.background, + focusedContainerColor = MaterialTheme.colorScheme.background, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.tertiaryContainer, + unfocusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer, + focusedLabelColor = MaterialTheme.colorScheme.tertiaryContainer + ), + modifier = Modifier.fillMaxWidth() + ) + Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = { + onSubmit(totpCode) + onDismiss() + }, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "Submit", + color = MaterialTheme.colorScheme.surface + ) + } + } + } + } +} + +@Composable +fun TotpAuthComponent( + onSubmit: (totp: String) -> Unit +) { + var bottomSheetOpen by remember { mutableStateOf(false) } + + AuthButton( + onSubmit = { bottomSheetOpen = true }, + label = "Continue with TOTP", + imageResource = R.drawable.totp, + imageContextDescription = "TOTP" + ) + + BottomSheetWithForm( + isOpen = bottomSheetOpen, + onDismiss = { bottomSheetOpen = false }, + onSubmit = onSubmit + ) +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun TotpAuthPreview() { + TotpAuthComponent(onSubmit = {}) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreen.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreen.kt new file mode 100644 index 000000000..78fd07612 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreen.kt @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.landing_screen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.AssistChipDefaults +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.FooterImage +import com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component.LandingPageLogo +import com.wso2_sample.api_auth_sample.util.ui.LoadingDialog +import com.wso2_sample.api_auth_sample.ui.theme.Api_authenticator_sdkTheme + +@Composable +internal fun LandingScreen( + viewModel: LandingScreenViewModel = hiltViewModel() +) { + val state = viewModel.state.collectAsStateWithLifecycle() + LandingScreenContent(state.value, loginOnClick = viewModel::initializeAuthentication) +} + + +@Composable +fun LandingScreenContent( + state: LandingScreenState, + loginOnClick: () -> Unit +) { + Column(modifier = Modifier.fillMaxSize()) { + LoadingDialog(state.isLoading) + Column( + modifier = Modifier + .fillMaxSize() + .padding(top = 96.dp), + verticalArrangement = Arrangement.SpaceBetween, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(-8.dp) + ) { + LandingPageLogo() + Text( + text = "Helping you to take " + "\n good care of your pets", + textAlign = TextAlign.Center, + style = MaterialTheme.typography.labelMedium + ) + } + LoginButton(Modifier, loginOnClick) + FooterImage() + } + } +} + +@Composable +private fun LoginButton(modifier: Modifier = Modifier, onClcik: () -> Unit) { + Button( + modifier = modifier, + onClick = onClcik + ) { + Image( + painter = painterResource(id = R.drawable.small_white_logo), + contentDescription = "Logo", + modifier = Modifier + .size(AssistChipDefaults.IconSize) + ) + Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) + Text( + text = "Getting Started", + color = MaterialTheme.colorScheme.surface, + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun LandingScreenPreview() { + Api_authenticator_sdkTheme { + LandingScreenContent( + LandingScreenState( + isLoading = false + ), + loginOnClick = {} + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreenState.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreenState.kt new file mode 100644 index 000000000..451326364 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreenState.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.landing_screen + +data class LandingScreenState( + val isLoading: Boolean = true, + val error: String = "" +) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreenViewModel.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreenViewModel.kt new file mode 100644 index 000000000..30bc2c34b --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/screens/landing_screen/LandingScreenViewModel.kt @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.screens.landing_screen + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AsgardeoAuthRepository +import com.wso2_sample.api_auth_sample.features.login.domain.repository.AttestationRepository +import com.wso2_sample.api_auth_sample.util.Event +import com.wso2_sample.api_auth_sample.util.navigation.NavigationViewModel +import com.wso2_sample.api_auth_sample.util.ui.sendEvent +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import io.asgardeo.android.core.models.state.AuthenticationState +import io.asgardeo.android.core.provider.providers.authentication.AuthenticationProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import java.net.URLEncoder +import javax.inject.Inject + +@HiltViewModel +class LandingScreenViewModel @Inject constructor( + @ApplicationContext private val applicationContext: Context, + asgardeoAuthRepository: AsgardeoAuthRepository, + attestationRepository: AttestationRepository +) : ViewModel() { + companion object { + const val TAG = "LandingScreen" + } + + private val _state = MutableStateFlow(LandingScreenState()) + val state = _state + + private lateinit var authenticationProvider: AuthenticationProvider + private lateinit var authenticationStateFlow: SharedFlow + + init { + var integrityToken: String? = null + + viewModelScope.launch { + integrityToken = attestationRepository.getPlayIntegrityTokenResponse() + + asgardeoAuthRepository.initializeAsgardeoAuth(integrityToken) + authenticationProvider = asgardeoAuthRepository.getAuthenticationProvider() + authenticationStateFlow = authenticationProvider.getAuthenticationStateFlow() + + handleAuthenticationState() + isLoggedInStateFlow() + } + } + + fun initializeAuthentication() { + _state.update { landingScreenState -> + landingScreenState.copy(isLoading = true) + } + viewModelScope.launch { + authenticationProvider.initializeAuthentication(applicationContext) + _state.update { landingScreenState -> + landingScreenState.copy(isLoading = false) + } + } + } + + private fun isLoggedInStateFlow() { + viewModelScope.launch { + authenticationProvider.isLoggedInStateFlow(applicationContext) + _state.update { landingScreenState -> + landingScreenState.copy(isLoading = false) + } + } + } + + private fun handleAuthenticationState() { + viewModelScope.launch { + authenticationStateFlow.collect { + when (it) { + is AuthenticationState.Initial -> { + _state.update { landingScreenState -> + landingScreenState.copy(isLoading = false) + } + } + + is AuthenticationState.Unauthenticated -> { + _state.update { landingScreenState -> + landingScreenState.copy(isLoading = false) + } + NavigationViewModel.navigationEvents.emit( + NavigationViewModel.Companion.NavigationEvent.NavigateToAuthWithData( + URLEncoder.encode(it.authenticationFlow!!.toJsonString(), "utf-8") + ) + ) + } + + is AuthenticationState.Error -> { + _state.update { landingScreenState -> + landingScreenState.copy(error = it.toString(), isLoading = false) + } + sendEvent(Event.Toast(it.toString())) + } + + is AuthenticationState.Authenticated -> { + _state.update { landingScreenState -> + landingScreenState.copy(isLoading = false) + } + NavigationViewModel.navigationEvents.emit( + NavigationViewModel.Companion.NavigationEvent.NavigateToHome + ) + } + + else -> { + _state.update { landingScreenState -> + landingScreenState.copy(isLoading = true) + } + } + } + } + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/AuthButton.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/AuthButton.kt new file mode 100644 index 000000000..8f7e45240 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/AuthButton.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.AssistChipDefaults +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp + +@Composable +fun AuthButton( + onSubmit: () -> Unit, + label: String, + imageResource: Int, + imageContextDescription: String +) { + OutlinedButton( + onClick = onSubmit, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp, bottom = 8.dp), + border = BorderStroke(0.5.dp, MaterialTheme.colorScheme.primary), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.surface, + contentColor = MaterialTheme.colorScheme.primary, + ) + + ) { + Image( + painter = painterResource(id = imageResource), + contentDescription = imageContextDescription, + modifier = Modifier + .size(AssistChipDefaults.IconSize) + ) + Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) + Text(text = label) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/ContinueText.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/ContinueText.kt new file mode 100644 index 000000000..899da1345 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/ContinueText.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun ContinueText() { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 8.dp) + ) { + HorizontalDivider( + modifier = Modifier.weight(0.9f), + thickness = 0.5.dp + ) + Spacer(modifier = Modifier.padding(end = 8.dp)) + Text( + text = "or continue with", + style = MaterialTheme.typography.labelSmall + ) + Spacer(modifier = Modifier.padding(start = 8.dp)) + HorizontalDivider( + modifier = Modifier.weight(0.9f), + thickness = 0.5.dp + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/FooterImage.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/FooterImage.kt new file mode 100644 index 000000000..e91b30c06 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/FooterImage.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import com.wso2_sample.api_auth_sample.R + +@Composable +fun FooterImage() { + Box( + contentAlignment = Alignment.BottomCenter + ) { + Image( + painter = painterResource(R.drawable.animals_landing), + contentDescription = "Landing Page Logo", + modifier = Modifier.fillMaxWidth() + ) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/LandingPageLogo.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/LandingPageLogo.kt new file mode 100644 index 000000000..0dc1e1069 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/features/login/presentation/util/common_component/LandingPageLogo.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.features.login.presentation.util.common_component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.wso2_sample.api_auth_sample.R +import com.wso2_sample.api_auth_sample.util.ui.UiUtil + +@Composable +fun LandingPageLogo() { + Image( + painter = painterResource(R.drawable.landing_page_logo), + contentDescription = "Landing Page Logo", + modifier = Modifier.size(UiUtil.getScreenHeight().dp / 4, UiUtil.getScreenHeight().dp / 4) + ) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Shapes.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Shapes.kt new file mode 100644 index 000000000..33cc5ee9c --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Shapes.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.ui.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Shapes +import androidx.compose.ui.unit.dp + +val shapes = Shapes( + extraSmall = RoundedCornerShape(16.dp), + small = RoundedCornerShape(14.dp), + medium = RoundedCornerShape(12.dp), + large = RoundedCornerShape(8.dp), + extraLarge = RoundedCornerShape(4.dp) +) diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Theme.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Theme.kt new file mode 100644 index 000000000..883faf501 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Theme.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.ui.theme + +import android.app.Activity +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat +import com.wso2_sample.api_auth_sample.ui.theme.shapes + +private val LightColorScheme = lightColorScheme( + primary = Color(0xFF69A2F4), + onPrimary = Color(0xFF69A2F4), + primaryContainer = Color(0xFF69A2F4), + secondary = Color(0xFFFFEC88), + secondaryContainer = Color(0xFFFFEC88), + tertiary = Color(0xFF3E4747), + tertiaryContainer = Color(0xFF939B9B), + surface = Color(0xFFFEFEFE), + background = Color(0xFFF4F4F4), + error = Color(0xFFF4538A), + outlineVariant = Color(0xFF939B9B), +) + +@Composable +fun Api_authenticator_sdkTheme( + content: @Composable () -> Unit +) { + CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onSurface + ) + + val colorScheme = LightColorScheme + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = false + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + shapes = shapes, + content = { + CompositionLocalProvider( + content = content + ) + } + ) +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Type.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Type.kt new file mode 100644 index 000000000..76c0dbb16 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/ui/theme/Type.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import com.wso2_sample.api_auth_sample.R + +val montserratFontFamily = FontFamily( + Font(R.font.montserrat_light, FontWeight.Light), + Font(R.font.montserrat_regular, FontWeight.Normal), + Font(R.font.montserrat_regular, FontWeight.Normal, FontStyle.Italic), + Font(R.font.montserrat_medium, FontWeight.Medium), + Font(R.font.montserrat_semi_bold, FontWeight.SemiBold), + Font(R.font.montserrat_bold, FontWeight.Bold) +) + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = montserratFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 16.sp, + letterSpacing = 0.1.sp + ), + bodyMedium = TextStyle( + fontFamily = montserratFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.1.sp, + color = Color(0xFF939B9B) + ), + bodySmall = TextStyle( + fontFamily = montserratFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 10.sp, + lineHeight = 16.sp, + letterSpacing = 0.1.sp + ), + titleSmall = TextStyle( + fontFamily = montserratFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.1.sp + ), + labelLarge = TextStyle( + fontFamily = montserratFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 14.sp, + lineHeight = 24.sp, + letterSpacing = 0.1.sp + ), + labelMedium = TextStyle( + fontFamily = montserratFontFamily, + fontWeight = FontWeight.Light, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.1.sp, + color = Color(0xFF939B9B) + ), + labelSmall = TextStyle( + fontFamily = montserratFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 10.sp, + lineHeight = 16.sp, + letterSpacing = 0.1.sp, + color = Color(0xFF9E9E9E) + ), + titleLarge = TextStyle( + fontFamily = montserratFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 24.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp, + color = Color(0xFF3E4747) + ), + + + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/Config.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/Config.kt new file mode 100644 index 000000000..56ac95d32 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/Config.kt @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.util + +object Config { + private const val BASE_URL: String = + private const val DISCOVERY_URL: String = + private const val CLIENT_ID: String = + private const val REDIRECT_URI: String = "wso2.apiauth.sample.android://login-callback" + private const val SCOPE: String = "openid internal_login profile email" + private const val GOOGLE_WEB_CLIENT_ID = + /** + * This value is the URL of the pet care backend server. If you are planning to use this + * application as a standalone application without a backend server, you must set this value to + * **null**, else you can set the URL of the backend server here. + */ + private val DATA_SOURCE_RESOURCE_SERVER_URL: String? = null + + fun getAuthorizeUrl(): String { + return "$BASE_URL/oauth2/authorize" + } + + fun getTokenUrl(): String { + return "$BASE_URL/oauth2/token" + } + + fun getLogoutUrl(): String { + return "$BASE_URL/oidc/logout" + } + + fun getUserInfoUrl(): String { + return "$BASE_URL/oauth2/userinfo" + } + + fun getAuthnUrl(): String { + return "$BASE_URL/oauth2/authn" + } + + fun getDiscoveryUrl(): String { + return DISCOVERY_URL + } + + fun getClientId(): String { + return CLIENT_ID + } + + fun getRedirectUri(): String { + return REDIRECT_URI + } + + fun getScope(): String { + return SCOPE + } + + fun getGoogleWebClientId(): String { + return GOOGLE_WEB_CLIENT_ID + } + + fun getDataSourceResourceServerUrl(): String? { + return DATA_SOURCE_RESOURCE_SERVER_URL + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/EventBus.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/EventBus.kt new file mode 100644 index 000000000..be8fee852 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/EventBus.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.util + +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.receiveAsFlow + +/** + * A simple event bus to send events between different parts of the application + */ +object EventBus { + private val _events = Channel() + val events = _events.receiveAsFlow() + /** + * Send an event to the event bus + */ + suspend fun sendEvent(event: Event) { + _events.send(event) + } +} + +sealed interface Event { + /** + * Event to show a toast message + */ + data class Toast(val message: String): Event +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/Util.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/Util.kt new file mode 100644 index 000000000..d4dc67d52 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/Util.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.util + +object Util { + /** + * Generates a random date in the format "dd/MM/yyyy". + */ + fun generateRandomDate(): String { + val year = (2023..2025).random() + val month = (1..12).random() + val day = (1..28).random() + return "%02d/%02d/%04d".format(day, month, year) + } + + /** + * Returns an array of pet image URLs. + */ + fun getPetImageUrls(): Array = arrayOf( + "https://cdn.pixabay.com/photo/2014/11/30/14/11/cat-551554_1280.jpg", + "https://cdn.pixabay.com/photo/2023/09/19/12/34/dog-8262506_1280.jpg", + "https://cdn.pixabay.com/photo/2023/08/18/15/02/dog-8198719_1280.jpg", + "https://cdn.pixabay.com/photo/2024/03/26/15/50/ai-generated-8657140_1280.jpg", + "https://cdn.pixabay.com/photo/2020/04/29/04/01/boy-5107099_1280.jpg", + "https://cdn.pixabay.com/photo/2023/09/24/14/05/dog-8272860_1280.jpg" + ) +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/navigation/NavDestination.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/navigation/NavDestination.kt new file mode 100644 index 000000000..c4fe176aa --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/navigation/NavDestination.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.util.navigation + +import androidx.compose.runtime.Composable +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import com.wso2_sample.api_auth_sample.features.home.presentation.screens.add_pet.AddPetScreen +import com.wso2_sample.api_auth_sample.features.home.presentation.screens.add_pet.AddPetScreenViewModel +import com.wso2_sample.api_auth_sample.features.home.presentation.screens.home.HomeScreen +import com.wso2_sample.api_auth_sample.features.home.presentation.screens.home.HomeScreenViewModel +import com.wso2_sample.api_auth_sample.features.home.presentation.screens.profile.ProfileScreen +import com.wso2_sample.api_auth_sample.features.home.presentation.screens.profile.ProfileScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreen +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.auth_screen.AuthScreenViewModel +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.landing_screen.LandingScreen +import com.wso2_sample.api_auth_sample.features.login.presentation.screens.landing_screen.LandingScreenViewModel +import io.asgardeo.android.core.models.authentication_flow.AuthenticationFlow +import io.asgardeo.android.core.models.authentication_flow.AuthenticationFlowNotSuccess +import java.net.URLDecoder + +object NavDestination { + const val LANDING_SCREEN: String = LandingScreenViewModel.TAG + const val AUTH_SCREEN: String = AuthScreenViewModel.TAG + const val HOME_SCREEN: String = HomeScreenViewModel.TAG + const val PROFILE_SCREEN: String = ProfileScreenViewModel.TAG + const val ADD_PET_SCREEN: String = AddPetScreenViewModel.TAG +} + +@Composable +fun NavGraph(navController: NavHostController) { + NavHost( + navController = navController, + startDestination = NavDestination.LANDING_SCREEN + ) { + composable(NavDestination.LANDING_SCREEN) { + LandingScreen() + } + composable("${NavDestination.AUTH_SCREEN}?authenticationFlow={authenticationFlow}") { + val authenticationFlowString: String? = it.arguments?.getString("authenticationFlow") + val authenticationFlow: AuthenticationFlow = AuthenticationFlowNotSuccess.fromJson( + URLDecoder.decode(authenticationFlowString!!, "utf-8") + ) + AuthScreen(authenticationFlow = authenticationFlow) + } + composable(NavDestination.HOME_SCREEN) { + HomeScreen() + } + composable(NavDestination.PROFILE_SCREEN) { + ProfileScreen() + } + + composable(NavDestination.ADD_PET_SCREEN) { + AddPetScreen() + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/navigation/NavigationViewModel.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/navigation/NavigationViewModel.kt new file mode 100644 index 000000000..835a44266 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/navigation/NavigationViewModel.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.util.navigation + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import javax.inject.Inject + +@HiltViewModel +class NavigationViewModel @Inject constructor() : ViewModel() { + companion object { + val navigationEvents = MutableSharedFlow() + + sealed class NavigationEvent { + data object NavigateBack : NavigationEvent() + data object NavigateToLanding : NavigationEvent() + data object NavigateToHome : NavigationEvent() + data object NavigateToProfile : NavigationEvent() + data object NavigateToAddPet : NavigationEvent() + data class NavigateToAuthWithData(val data: String) : NavigationEvent() + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/LoadingDialog.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/LoadingDialog.kt new file mode 100644 index 000000000..53756b37e --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/LoadingDialog.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.util.ui + +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties + +@Composable +fun LoadingDialog(isLoading: Boolean) { + if (isLoading) { + Dialog( + onDismissRequest = {}, + properties = DialogProperties( + dismissOnClickOutside = false, + dismissOnBackPress = false + ) + ) { + Card( + modifier = Modifier + .width(100.dp) + .height(100.dp) + .padding(16.dp), + elevation = CardDefaults.cardElevation( + defaultElevation = 0.dp, + pressedElevation = 0.dp + ) + ) { + CircularProgressIndicator( + modifier = Modifier.padding(16.dp) + ) + } + } + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/UiUtil.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/UiUtil.kt new file mode 100644 index 000000000..88e9b9215 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/UiUtil.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.util.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalConfiguration + +object UiUtil { + @Composable + fun getScreenHeight(): Int { + val configuration = LocalConfiguration.current + return configuration.screenHeightDp + } + @Composable + fun getScreenWidth(): Int { + val configuration = LocalConfiguration.current + return configuration.screenWidthDp + } +} \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/ViewModelExt.kt b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/ViewModelExt.kt new file mode 100644 index 000000000..a756bd8c2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/java/com/wso2_sample/api_auth_sample/util/ui/ViewModelExt.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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. + */ + +package com.wso2_sample.api_auth_sample.util.ui + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wso2_sample.api_auth_sample.util.Event +import com.wso2_sample.api_auth_sample.util.EventBus +import kotlinx.coroutines.launch + +fun ViewModel.sendEvent(event: Event) { + viewModelScope.launch { + EventBus.sendEvent(event) + } +} diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/add_pet_banner.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/add_pet_banner.png new file mode 100644 index 000000000..9c90d2803 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/add_pet_banner.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/animals_landing.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/animals_landing.png new file mode 100644 index 000000000..83322c3bb Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/animals_landing.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/call.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/call.xml new file mode 100644 index 000000000..57648cda5 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/call.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/circle_logo.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/circle_logo.png new file mode 100644 index 000000000..59af020c6 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/circle_logo.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/credit_card.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/credit_card.xml new file mode 100644 index 000000000..668dd9102 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/credit_card.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/dark_mode.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/dark_mode.xml new file mode 100644 index 000000000..a9b7167b2 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/dark_mode.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/data_saver_on.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/data_saver_on.xml new file mode 100644 index 000000000..b08b877b6 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/data_saver_on.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/email_otp.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/email_otp.xml new file mode 100644 index 000000000..96246a465 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/email_otp.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/enterprise_icon.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/enterprise_icon.xml new file mode 100644 index 000000000..aea6362a5 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/enterprise_icon.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/github.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/github.xml new file mode 100644 index 000000000..81cd4668f --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/github.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/google.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/google.xml new file mode 100644 index 000000000..fb1fb6b64 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/google.xml @@ -0,0 +1,36 @@ + + + + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/help_outline.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/help_outline.xml new file mode 100644 index 000000000..59cbe25ba --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/help_outline.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/home_logo.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/home_logo.png new file mode 100644 index 000000000..e60960ce6 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/home_logo.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/ic_launcher_background.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..d517bf1e4 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/ic_launcher_foreground.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..7b0e016fa --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/landing_page_logo.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/landing_page_logo.png new file mode 100644 index 000000000..11d9a0b01 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/landing_page_logo.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/language.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/language.xml new file mode 100644 index 000000000..03db939b3 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/language.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/logout.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/logout.xml new file mode 100644 index 000000000..9ddfcdb06 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/logout.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/microsoft.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/microsoft.xml new file mode 100644 index 000000000..92a2c63c6 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/microsoft.xml @@ -0,0 +1,39 @@ + + + + + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/passkey.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/passkey.png new file mode 100644 index 000000000..9e1ac0993 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/passkey.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/person_dog_home.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/person_dog_home.png new file mode 100644 index 000000000..f96692fef Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/person_dog_home.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/person_dog_login.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/person_dog_login.png new file mode 100644 index 000000000..f33241219 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/person_dog_login.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/report.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/report.xml new file mode 100644 index 000000000..aa8e4bd47 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/report.xml @@ -0,0 +1,33 @@ + + + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/small_white_logo.png b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/small_white_logo.png new file mode 100644 index 000000000..2272ee417 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/small_white_logo.png differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/sms_otp.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/sms_otp.xml new file mode 100644 index 000000000..dee22c86b --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/sms_otp.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/star_rate.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/star_rate.xml new file mode 100644 index 000000000..ebb1b00bc --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/star_rate.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/support_inbox.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/support_inbox.xml new file mode 100644 index 000000000..1c1d9da8d --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/support_inbox.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/totp.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/totp.xml new file mode 100644 index 000000000..59e8023f7 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/totp.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/video_call.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/video_call.xml new file mode 100644 index 000000000..a3a6946f1 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/drawable/video_call.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_bold.ttf b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_bold.ttf new file mode 100644 index 000000000..ae33a4538 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_bold.ttf differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_light.otf b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_light.otf new file mode 100644 index 000000000..bca61ea67 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_light.otf differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_medium.otf b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_medium.otf new file mode 100644 index 000000000..cca3db8d9 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_medium.otf differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_regular.ttf b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_regular.ttf new file mode 100644 index 000000000..5b4b5afe6 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_regular.ttf differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_semi_bold.otf b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_semi_bold.otf new file mode 100644 index 000000000..c45067852 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/font/montserrat_semi_bold.otf differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 000000000..4671e1623 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 000000000..4671e1623 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 000000000..c209e78ec Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 000000000..b2dfe3d1b Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 000000000..4f0f1d64e Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 000000000..62b611da0 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 000000000..948a3070f Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..1b9a6956b Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 000000000..28d4b77f9 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..9287f5083 Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 000000000..aa7d6427e Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..9126ae37c Binary files /dev/null and b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/colors.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..bcf3ae89d --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/colors.xml @@ -0,0 +1,28 @@ + + + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/strings.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..b1a953811 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/strings.xml @@ -0,0 +1,21 @@ + + + + PetCare + \ No newline at end of file diff --git a/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/themes.xml b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/themes.xml new file mode 100644 index 000000000..753d96f02 --- /dev/null +++ b/petcare-sample/b2c/mobile-app/petcare-with-sdk/app/src/main/res/values/themes.xml @@ -0,0 +1,23 @@ + + + + + +