I am using MAFLogon for my login screen in an application build for SAP Mobile on HANA Cloud Platform, using SAP SDK.
LogonUIFacade mLogonUIFacade = LogonUIFacade.getInstance();
//Initialize the Logon UI Facade
mLogonUIFacade.init(this, mContext, getString(R.string.HCPMS_APP_ID));
// Present the logon screen to the user
setContentView(mLogonUIFacade.logon());
// Hide the splash screen (do this at the end, so defaults are not reset)
mLogonUIFacade.showSplashScreen(false);
The code above creates a built-in login Activity. How can i create a custom Activity or customize this one? (change logo, colors etc)
Alas, the MAF Logon component is not designed to be customizable to the extent you require. Claudia Pacheco's blog post "Customizing MAF Logon Component in Android" nicely outlines the options you have. The issue with customization in terms of branding is that there may be no end to the requirements: Some are happy with a custom logo, others would require a totally different layout. Therefore you should implement your own login page and implement the communication on top of the HttpConversation
flow, as described in the documentation. I put together a little example for you, to get you started:
res/layout/layout_main.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="logon.example.com.basicauthconvflow.MainActivity">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingEnd="8dp"
android:paddingStart="8dp"
tools:layout_editor_absoluteY="8dp"
tools:layout_editor_absoluteX="8dp">
<EditText
android:id="@+id/server_host"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Host (e.g. hcpms-p0123456trial.hanatrial.ondemand.com"
android:inputType="text"
android:maxLines="1" />
<EditText
android:id="@+id/application_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="com.logon.test"
android:ems="10"
android:hint="Application ID"
android:inputType="none"
android:maxLines="1" />
<EditText
android:id="@+id/user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="pXXXXXXX"
android:ems="10"
android:hint="User name"
android:inputType="none"
android:maxLines="1" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Password"
android:inputType="textPassword"
android:maxLines="1" />
<Button
android:id="@+id/login_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Log in"/>
</LinearLayout>
</ScrollView>
src/main/java/logon/example/com/basicauthconflow/MainActivity.java
package logon.example.com.basicauthconvflow;
import android.app.Activity;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.sap.smp.client.httpc.HttpConversationManager;
import com.sap.smp.client.httpc.HttpMethod;
import com.sap.smp.client.httpc.IHttpConversation;
import com.sap.smp.client.httpc.SAPCookieManager;
import com.sap.smp.client.httpc.authflows.CommonAuthFlowsConfigurator;
import com.sap.smp.client.httpc.authflows.UsernamePasswordProvider;
import com.sap.smp.client.httpc.authflows.UsernamePasswordToken;
import com.sap.smp.client.httpc.events.IReceiveEvent;
import com.sap.smp.client.httpc.events.ISendEvent;
import com.sap.smp.client.httpc.events.ITransmitEvent;
import com.sap.smp.client.httpc.listeners.IRequestListener;
import com.sap.smp.client.httpc.listeners.IResponseListener;
import com.sap.smp.client.httpc.utils.EmptyFlowListener;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpCookie;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
public class MainActivity extends AppCompatActivity {
/**
* Mobile Services registration API URI for this app
*/
URI registrationUri;
/**
* Mobile Services application ID
*/
String applicationId;
/**
* User name for login
*/
String user;
/**
* Password for login
*/
char[] password;
/**
* Mobile Services application connection ID ("registration ID") after login
*/
String appcid;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((Button)findViewById(R.id.login_button)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
applicationId = ((EditText)findViewById(R.id.application_id)).getText().toString();
user = ((EditText)findViewById(R.id.user_name)).getText().toString();
password = ((EditText)findViewById(R.id.password)).getText().toString().toCharArray();
String serverAuthority = ((EditText)findViewById(R.id.server_host)).getText().toString() + ":443";
Uri.Builder builder = new Uri.Builder();
// Assemble path to the Mobile Services registration API for this app
builder.scheme("https")
.encodedAuthority(serverAuthority)
.appendPath("odata")
.appendPath("applications")
.appendPath("latest")
.appendPath(applicationId)
.appendPath("Connections");
registrationUri = URI.create(builder.build().toString());
login();
}
});
}
/**
* Performs a login, registering with Mobile Services if required.
*/
private void login() {
if(isRegistered()) {
// There is a valid registration and the cookie manager has a session cookie
Toast.makeText(this, "Already registered with APPCID " + appcid, Toast.LENGTH_SHORT).show();
} else {
this.register();
}
}
/**
* Performs a registration request against Mobile Services and extracts the application connection
* ID from the response.
* The application connection ID is set as a side-effect of this method.
*/
private void register() {
final IHttpConversation conv = createConversation();
conv.setMethod(HttpMethod.POST);
conv.addHeader("Content-Type", "application/json; charset=utf-8");
conv.setRequestListener(new IRequestListener() {
@Override
public Object onRequestHeaderSending(ISendEvent event) {
return null;
}
@Override
public Object onRequestBodySending(ITransmitEvent event) throws IOException {
JSONObject json = new JSONObject();
try {
json.put("DeviceType", "Android");
event.getWriter().write(json.toString());
return null;
} catch (JSONException e) {
throw new IOException(e);
}
}
});
conv.setResponseListener(new IResponseListener() {
@Override
public void onResponseReceived(final IReceiveEvent event) throws IOException {
final int statusCode = event.getResponseStatusCode();
final Activity activity = MainActivity.this;
if (activity != null)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (statusCode == 201) {
// Extract the connection ID from the cookies.
if(extractAppcidFromResponse(event)) {
Toast.makeText(activity, "Registered with APPCID " + appcid, Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(activity, "Registration was not succesful (" + statusCode + ")", Toast.LENGTH_SHORT).show();
}
}
});
}
});
conv.start();
}
/**
* Create an HTTP conversation targeting the Mobile Services registration API and configured
* for Basic authentication based on the current activity instance state.
*
* @return The conversation
*/
private IHttpConversation createConversation() {
HttpConversationManager manager = new HttpConversationManager(this);
CommonAuthFlowsConfigurator configurator = new CommonAuthFlowsConfigurator(this);
configurator.supportBasicAuthUsing(new UsernamePasswordProvider() {
@Override
public Object onCredentialsNeededUpfront(ISendEvent event) {
return null;
}
@Override
public Object onCredentialsNeededForChallenge(IReceiveEvent event) {
return new UsernamePasswordToken(user, new String(password));
}
});
configurator.configure(manager);
URL serverUrl;
try {
serverUrl = registrationUri.toURL();
} catch (MalformedURLException e) {
Log.e(this.getClass().getSimpleName(), "Unexpected error constructing registration URL.", e);
return null;
}
// Create the conversation.
return manager.create(serverUrl);
}
/**
* Tells if there is an existing registration for this application.
* The application connection ID is set as a side-effect of this method.
*
* @return true if there is an existing registration; false otherwise
*/
private boolean isRegistered() {
return extractAppcidFromCookies(SAPCookieManager.getInstance().getCookieStore().get(registrationUri));
}
/**
* Extracts the Mobile Services application connection ID from the specified response event.
* The application connection ID is set as a side-effect of this method.
*
* @param event The event to search
* @return true if it could be found; false otherwise
*/
private boolean extractAppcidFromResponse(IReceiveEvent event) {
try {
return extractAppcidFromCookies(SAPCookieManager.getInstance().getCookieStore().get(
event.getResponseURL().toURI()));
} catch (URISyntaxException e) {
Log.e(this.getClass().getSimpleName(), "Unable to extract APPCID", e);
}
return false;
}
/**
* Extracts the Mobile Services application connection ID from the specified list of cookies.
* The application connection ID is set as a side-effect of this method.
*
* @param httpCookies The cookies to search
* @return true if it could be found; false otherwise
*/
private boolean extractAppcidFromCookies(List<HttpCookie> httpCookies) {
if (httpCookies != null)
for (HttpCookie httpCookie : httpCookies)
if ("X-SMP-APPCID".equals(httpCookie.getName())) {
appcid = httpCookie.getValue();
return true;
}
return false;
}
}