on 2023 Mar 24 1:36 PM
Hi,
I want to make custom changes in SAP BTP Android onboarding screens. For ex. I need to make my own login screen(username and password enter screen).
I do not require the passcode screens.
Is there any possibility to do customization in onboarding screens?
Note: I'm using SAP BTP SDK Version - 5.1.2
I would like to thanks in advance for your help.
Hi Hima,
Including onboarding , what other feature do you like to use without flow? For example, usage , client policy , Log, Usage , Push etc. Would you please tell us the exact feature list ?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
I don't want to use SAP provided UI in my entire application.
For now, as per the requirement I have to onboard the user using my own login screen which has my own UI.
After successful onboarding, user should navigate to the dashboard screen.
I don't want to show any other screens(For example, usage , client policy , Log, Usage , Push etc) to user.
Can you please help me by sharing the code snippet or relevant document?
Here is a basic authentication code snippet for your reference.
1.Disable UI callback in onCreate method of application.kt
AuthenticationUiCallbackManager.setAuthenticationUiCallback { false }
2.Implement your own BasicAuthDialogAuthenticator.
class CustomBasicAuthAuthenticator(private val user: String, private val pwd: String) :
BasicAuthDialogAuthenticator() {
override fun authenticate(route: Route?, response: Response): Request? {
return response.request.newBuilder().header("Authorization", Credentials.basic(user, pwd)).build()
}
}
3.Establish connection with OKHttp client. Do following action when response is successful. Hope it's helpful.binding.login.setOnClickListener {
val user = binding.etUser.text.toString()
val pwd = binding.etPwd.text.toString()
val basicAuthOkHttpClient = OkHttpClient.Builder()
.addInterceptor(AppHeadersInterceptor(appid, deviceId, applicationVersion))
.authenticator(CustomBasicAuthAuthenticator(user, pwd))
.cookieJar(WebkitCookieJar())
.retryOnConnectionFailure(true)
.build()
val request = Request.Builder()
.get()
.url("$backendUrl/$appid")
.build()
CoroutineScope(Dispatchers.IO).launch {
basicAuthOkHttpClient.newCall(request = request).execute().use {
if(it.isSuccessful){
//To-do
requireActivity().runOnUiThread{
Toast.makeText(requireContext(),"Success with Code ${it.code}",Toast.LENGTH_LONG).show()
}
}
}
}
}
Hi lingnage12
Thank you for your support. The above given code snippet is working fine and user is onboarding now.
But if we use this code for onboarding then I am getting issue to open the stores.
I am getting error while executing the below statement
Problematic line of code:
UserSecureStoreDelegate.getInstance().getOfflineEncryptionKey()
(OR)
UserSecureStoreDelegate.getInstance().getData(OFFLINE_DATASTORE_ENCRYPTION_KEY)
(OR)<br>UserSecureStoreDelegate.getInstance().getRuntimeMultipleUserModeAsync()<br><br>Error I am getting: "lateinit property userStore has not been initialized"
This is working fine while using flows.
But while using foundation I am getting this error.
How to open the stores when using foundation for onboarding?
Can you please help me here.
Hi Hima,
The UserSecureStoreDelegate is part of Flow. It need flow start correctly and it will be initialzied correctly. Since you have not onboard with flow ,you will meet some problems with this class . Can you specify what you want to do next ? Like Initialize an offline data source? Or something else?
Hi lingnage12,
As you expected I am getting some issues with some classes(UserSecureStoreDelegate, FlowContextRegistry etc).
ClientProvider.get() also not working here even it is from foundation library.
My next step is I need to download the required entities to offline stores.
Initialize the offline store -> Open the stores ->Download the entities -> Navigate to Dashboard and show the data using offline stores data.
Later the data has to show in UI.
I have taken some code from wizard generated project.
As wizard generated project has used flows dependency internally to generate the project, I am getting issues.
Hi Hima,
Before you do onboarding with OkHttpClient, please invoke SettingsProvider.set(settingsParameters) and ClientProvider.set(okHttpClient).
And after successful onboarding, then you can initialize your own OfflineProvider with below code snippet:
val offlineODataParameters = OfflineODataParameters()
val offlineDelegate = OfflineODataDelegateImpl()
offlineODataProvider = OfflineODataProvider(
url,
offlineODataParameters,
ClientProvider.get(),
offlineDelegate
)
And then with proxy class and OfflineOdataProvider , you could initialize your own DataService class.
Hi lingnage12,
Still I'm getting the error, after using the above code.
2023-03-31 16:49:54.441 19927-19927/com.poc.btplib E/AndroidRuntime: FATAL EXCEPTION: mainSuppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@40880db, Dispatchers.Main]
Hi lingnage12,
1. If your app is killed , then user launches app again, do you want user to input basic credential again?
Yes. User must login every time after app relaunched.
2. If user switch happend , do you want offline store changed to new user also?
Yes, It has to support multi user.
As of now I'm developing the app from scratch using java.
While doing the initial sync I'm getting the below error in RealMe (Android 13) device.
2023-04-03 17:47:53.993 8748-20234/com.poc.btplib E/com.sap.cloud.mobile.odata.offline: [Thread-12] [-10346] The download failed to establish a socket connection to the server.
2023-04-03 17:47:53.996 8748-20234/com.poc.btplib E/class com.poc.btplib.service.OfflineOpenWorker: [Thread-12] [-10346] The download failed to establish a socket connection to the server.
Here to do the offline data downloading functionality I'm using AsyncTask.
Hi lingnage12,
Now the app is working fine after restarting the problematic device.
It seems the device problem and app is working fine in other devices.
Please update me regarding user login after relaunching the app (User must login every time after app relaunches).
Switch user offline data handling in multi user mode.
Hi Hima,
In your case ,you need to follow below steps:
Preparation Steps:
a.Enable Allow Upload of Pending Changes from Previous User (Enable Multiple User Mode) on Mobile Service Cockpit . Path :
Mobile Settings Exchange -> Shared Devices
b.Make sure Settings->Security->Trust configuration is configured. and Test configuration successfully.
If not ,you can download metadata here and upload to your subaccount (Go to your subaccount , left menu , security -> Trust configuration -> Click New Trust Configuration button and upload metadata)
c.Make sure SSO Mechanism/ Authentication of your connectivity is not basic.
Client:
1.After user onboards successfully , you can retrieve user:
user = SDKInitializer.getService(UserService::class)?.retrieveUser()
2.When initialize offline odata provider , please set current user with the user you got in step 1. and set isForceUploadOnUserSwitch to true.
val offlineODataParameters = OfflineODataParameters().apply {
storeName = OFFLINE_DATASTORE
isForceUploadOnUserSwitch = true
currentUser = user?.id
}
Then you can try offline operation with different user.
Hope it could solve your problem.
Hi Hima,
Does your onboarding code use flows module? Could you share your onboarding code?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
We are using below dependency. Please help here, we got stuck for few days on this issue.
implementation "com.sap.cloud.android:flowsv2:$versions.sapCloudAndroidSdk"
Main thing here is I need my own login screen (UI).
LaunchScreenSettings settings = new LaunchScreenSettings.Builder()
.setDemoButtonVisible(false)
.setHeaderLineLabel("Welcome")
.setPrimaryButtonText("Get Started")
.setFooterVisible(true)
.setUrlTermsOfService("http://www.sap.com")
.setUrlPrivacy("http://www.sap.com")
.addInfoViewSettings
(new LaunchScreenSettings.LaunchScreenInfoViewSettings(
R.drawable.ic_launcher_foreground,
getString(R.string.app_name),
"Explore the SAP BTP for Android wth the app")
).build();
binding.launchScreen.initialize(settings);
binding.launchScreen.setPrimaryButtonOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startConfigurationLoader();
}
});
private void startConfigurationLoader() {
ConfigurationLoaderCallback callback = new ConfigurationLoaderCallback() {
@Override
public void onCompletion(@Nullable ProviderIdentifier providerIdentifier, boolean isSuccess) {
if (isSuccess) {
startFlow();
} else {
new DialogHelper(getApplication(), R.style.OnboardingDefaultTheme_Dialog_Alert)
.showOKOnlyDialog(
fManager,
"Configuration loader failed to get configuration data.",
null, null, null
);
}
}
@Override
public void onError(@NonNull ConfigurationLoader configurationLoader, @NonNull ProviderIdentifier providerIdentifier,
@NonNull UserInputs userInputs, @NonNull ConfigurationProviderError configurationProviderError) {
new DialogHelper(getApplication(), R.style.OnboardingDefaultTheme_Dialog_Alert)
.showOKOnlyDialog(
fManager, String.format(
getResources().getString(
R.string.config_loader_on_error_description
),
providerIdentifier.toString(), configurationProviderError.getErrorMessage()
),
null, null, null
);
configurationLoader.processRequestedInputs(new UserInputs());
}
@Override
public void onInputRequired(@NonNull ConfigurationLoader configurationLoader, @NonNull UserInputs userInputs) {
configurationLoader.processRequestedInputs(new UserInputs());
}
};
ConfigurationProvider[] providers = new ConfigurationProvider[1];
providers[0] = new FileConfigurationProvider(MainActivity.this, "sap_mobile_services");
runOnUiThread(new Runnable() {
@Override
public void run() {
ConfigurationLoader loader = new ConfigurationLoader(MainActivity.this, callback, providers);
loader.loadConfiguration();
}
});
}
private void startFlow() {
AppConfig appConfig = prepareAppConfig();
if (appConfig == null) return;
FlowOptions temp = new FlowOptions();
FlowOptions options = new FlowOptions(InfoMessageOption.TOAST, ActivationOption.CHOOSE_BETWEEN_DS_QR, true,
true, true, true, temp.getApplicationTheme(),
true, true, true, OnboardingOrientation.UNSPECIFIED,
OAuth2WebOption.WEB_VIEW, SignedQRCodeOption.UNSPECIFIED, true, true, true);
FlowContext flowContext = new FlowContextBuilder()
.setApplication(appConfig)
.setMultipleUserMode(true)
.setFlowStateListener(new WizardFlowStateListener((SAPWizardApplication) getApplication()))
.setFlowOptions(options).build();
Flow.start(this, flowContext, new Function3<Short, Integer, Intent, Unit>() {
@Override
public Unit invoke(Short requestCode, Integer resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
((SAPWizardApplication) getApplication()).isApplicationUnlocked = true;
Intent intent = new Intent(getApplication(), OfflineDataAccessActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
return null;
}
});
}
private AppConfig prepareAppConfig() {
try {
JSONObject configData = DefaultPersistenceMethod.getPersistedConfiguration(this);
return AppConfig.createAppConfigFromJsonString(configData.toString());
} catch (ConfigurationPersistenceException e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
return null;
}
Hi,
I don't want to use SAP provided UI in my entire application.
For now, as per the requirement I have to onboard the user using my own login screen which has my own UI.
After successful onboarding, user should navigate to the dashboard screen.
I don't want to show any other screens(For example, usage , client policy , Log, Usage , Push etc) to user.
Can you please help me by sharing the code snippet or relevant document?
User | Count |
---|---|
71 | |
11 | |
10 | |
10 | |
10 | |
8 | |
7 | |
7 | |
5 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.