Download How To... On-board users with SMP 3.0 OData API

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
SAP How-to Guide
Mobility
SAP Mobile Platform
How To... On-board users with SMP 3.0 OData API
(Android)
Applicable Releases:
SAP Mobile Platform 3.0 SP04
SAP Mobile SDK 3.0 SP05
Version 2.0
© Copyright 2015 SAP AG. All rights reserved.
All other product and service names mentioned are the trademarks of
No part of this publication may be reproduced or transmitted in any form
their respective companies. Data contained in this document serves
or for any purpose without the express permission of SAP AG. The
informational purposes only. National product specifications may vary.
information contained herein may be changed without prior notice.
The information in this document is proprietary to SAP. No part of this
Some software products marketed by SAP AG and its distributors
document may be reproduced, copied, or transmitted in any form or for
contain proprietary software components of other software vendors.
any purpose without the express prior written permission of SAP AG.
Microsoft, Windows, Excel, Outlook, and PowerPoint are registered
This document is a preliminary version and not subject to your license
trademarks of Microsoft Corporation.
agreement or any other agreement with SAP. This document contains
IBM, DB2, DB2 Universal Database, System i, System i5, System p,
System p5, System x, System z, System z10, System z9, z10, z9, iSeries,
pSeries, xSeries, zSeries, eServer, z/VM, z/OS, i5/OS, S/390, OS/390,
OS/400, AS/400, S/390 Parallel Enterprise Server, PowerVM, Power
Architecture, POWER6+, POWER6, POWER5+, POWER5, POWER,
only intended strategies, developments, and functionalities of the SAP®
product and is not intended to be binding upon SAP to any particular
course of business, product strategy, and/or development. Please note
that this document is subject to change and may be changed by SAP at
any time without notice.
OpenPower, PowerPC, BatchPipes, BladeCenter, System Storage, GPFS,
SAP assumes no responsibility for errors or omissions in this document.
HACMP, RETAIN, DB2 Connect, RACF, Redbooks, OS/2, Parallel Sysplex,
SAP does not warrant the accuracy or completeness of the information,
MVS/ESA, AIX, Intelligent Miner, WebSphere, Netfinity, Tivoli and
text, graphics, links, or other items contained within this material. This
Informix are trademarks or registered trademarks of IBM Corporation.
document is provided without a warranty of any kind, either express or
Linux is the registered trademark of Linus Torvalds in the U.S. and other
countries.
Adobe, the Adobe logo, Acrobat, PostScript, and Reader are either
trademarks or registered trademarks of Adobe Systems Incorporated in
the United States and/or other countries.
Oracle is a registered trademark of Oracle Corporation.
UNIX, X/Open, OSF/1, and Motif are registered trademarks of the Open
Group.
Citrix, ICA, Program Neighborhood, MetaFrame, WinFrame, VideoFrame,
and MultiWin are trademarks or registered trademarks of Citrix Systems,
Inc.
HTML, XML, XHTML and W3C are trademarks or registered trademarks
of W3C®, World Wide Web Consortium, Massachusetts Institute of
Technology.
Java is a registered trademark of Sun Microsystems, Inc.
implied, including but not limited to the implied warranties of
merchantability, fitness for a particular purpose, or non-infringement.
SAP shall have no liability for damages of any kind including without
limitation direct, special, indirect, or consequential damages that may
result from the use of these materials. This limitation shall not apply in
cases of intent or gross negligence.
The statutory liability for personal injury and defective products is not
affected. SAP has no control over the information that you may access
through the use of hot links contained in these materials and does not
endorse your use of third-party Web pages nor provide any warranty
whatsoever relating to third-party Web pages.
SAP “How-to” Guides are intended to simplify the product implementtation. While specific product features and procedures typically are
explained in a practical business context, it is not implied that those
features and procedures are the only approach in solving a specific
business problem using SAP NetWeaver. Should you wish to receive
JavaScript is a registered trademark of Sun Microsystems, Inc., used
additional information, clarification or support, please refer to SAP
under license for technology invented and implemented by Netscape.
Consulting.
SAP, R/3, SAP NetWeaver, Duet, PartnerEdge, ByDesign, SAP
Any software coding and/or code lines / strings (“Code”) included in this
BusinessObjects Explorer, StreamWork, and other SAP products and
documentation are only examples and are not intended to be used in a
services mentioned herein as well as their respective logos are
productive system environment. The Code is only intended better explain
trademarks or registered trademarks of SAP AG in Germany and other
and visualize the syntax and phrasing rules of certain coding. SAP does
countries.
not warrant the correctness and completeness of the Code given herein,
Business Objects and the Business Objects logo, BusinessObjects,
and SAP shall not be liable for errors or damages caused by the usage of
Crystal Reports, Crystal Decisions, Web Intelligence, Xcelsius, and other
the Code, except if such damages were caused by SAP intentionally or
Business Objects products and services mentioned herein as well as their
grossly negligent.
respective logos are trademarks or registered trademarks of Business
Disclaimer
Objects Software Ltd. Business Objects is an SAP company.
Some components of this product are based on Java™. Any code change
Sybase and Adaptive Server, iAnywhere, Sybase 365, SQL Anywhere,
in these components may cause unpredictable and severe malfunctions
and other Sybase products and services mentioned herein as well as their
and is therefore expressively prohibited, as is any decompilation of these
respective logos are trademarks or registered trademarks of Sybase, Inc.
components.
Sybase is an SAP company.
Any Java™ Source Code delivered with this product is only to be used by
SAP’s Support Services and may not be modified or altered in any way.
Document History
Document Version
Description
1.00
First official release of this guide
2.00
Based in AndroidStudio with improvements in the code
Typographic Conventions
Icons
Type Style
Icon
Example Text
Description
Words or characters quoted
from the screen. These
include field names, screen
titles, pushbuttons labels,
menu names, menu paths,
and menu options.
Cross-references to other
documentation
Example text
Example text
Example text
<Example
text>
EXAMPLE TEXT
Emphasized words or
phrases in body text, graphic
titles, and table titles
File and directory names and
their paths, messages,
names of variables and
parameters, source text, and
names of installation,
upgrade and database tools.
User entry texts. These are
words or characters that you
enter in the system exactly
as they appear in the
documentation.
Variable user entry. Angle
brackets indicate that you
replace these words and
characters with appropriate
entries to make entries in the
system.
Keys on the keyboard, for
example, F2 or ENTER.
Description
Caution
Note or Important
Example
Recommendation or Tip
Table of Contents
1.
Business Scenario ..................................................................................................................1
2.
Background Information ........................................................................................................1
3.
Prerequisites ..........................................................................................................................1
4.
Step-by-Step Procedure ....................................................................................................... 2
4.1
Create Android Project .................................................................................................... 2
4.2
Add required libraries ...................................................................................................... 5
4.3
Create Logon Screen ......................................................................................................12
4.4
Add Logon Screen Texts ...................................................................................12
4.3.2
Create LoginActivity ..........................................................................................13
Onboard users with LogonCore ..................................................................................... 17
4.4.1
Create UIListener ............................................................................................... 17
4.4.2
Create ODataBaseListener .............................................................................. 18
4.4.3
Create MyLogonCoreListener ......................................................................... 20
4.4.4
Implement LoginActivity .................................................................................. 23
4.5
Review AndroidManifest.xml ........................................................................................ 24
4.6
Open OfflineStore and Access Data Offline ................................................................. 24
4.7
5.
4.3.1
4.6.1
Create CredentialsProvider class .................................................................... 24
4.6.2
Create OfflineManager class............................................................................ 25
4.6.3
Complete MainActivity ..................................................................................... 25
Run the Application ........................................................................................................ 26
Appendix.............................................................................................................................. 29
How To... On-board users with SMP 3.0 OData API (Android)
1.
Business Scenario
Before any communication with the SAP backend systems can take place at all, the app needs to
on-board users onto the SAP Mobile Platform. The Mobile Application Framework (MAF) contains
the MAF logon component that provides easy integration for applications that use logon UI
behavior. However, certain use cases require more control over the look and feel of the logon
screen or don’t require the logon UI behavior. In these cases, developers can use the SMP 3.0
OData SDK to onboard users.
2.
Background Information
The goal of this exercise is to show the key pieces of code and information needed to onboard users
with the SMP 3.0 OData SDK.
Using the SMP OData 3.0 SDK to onboard users give developers more control about the look and
feel of the logon screen. However, if a more sophisticated mechanism of authentication is used (i.e
SAML, x.509 certificates) developers require to implement how the app will react to the
authentication challenges triggered by the authentication provider.
To understand how developers can leverage the MAF Logon component in more complex scenarios,
please visit help.sap.com
3.
Prerequisites
This exercise has the following prerequisites:
•
Java Standard Edition 7
•
Eclipse Kepler
•
Android SDK with API level 8 and 21 downloaded
•
Android ATD Eclipse plugin installed
•
To get the most out of this exercise, some experience with Java is recommended.
•
SAP Mobile Platform 3.0 SP04
This example assumes you have configured an application in SMP 3.0 called
com.sap.flight for more information on how to create an application configuration, please visit
Deploying Applications
•
SAP Mobile SDK 3.0 SP05
1
How To... On-board users with SMP 3.0 OData API (Android)
4.
Step-by-Step Procedure
The following sections provide a detailed step-by-step procedure on how to code an Android app to
on-board/register a user onto the SAP Mobile Platform using the SMP OData SDK.
The following section is optional, you can reuse an existing android project for the purpose of this
exercise
4.1
Create Android Project
...
1.
Open Android Studio and select Start a new Android Studio Project
2. Enter the following information to create the android project and click the Next button
2
How To... On-board users with SMP 3.0 OData API (Android)
3. Keep the default values as shown in the image and click Next.
4. You can choose any template to create an activity, for simplicity purposes we will chose a
Blank Activity
3
How To... On-board users with SMP 3.0 OData API (Android)
5. Enter the following information to create the activity and its corresponding layout
resources and click Finish
6. Your project should look like the image below.
4
How To... On-board users with SMP 3.0 OData API (Android)
4.2
Add required libraries
1. Locate your SMP 3.0 SDK installation folder. You will find the jar files in the two different
folders:
• <Client SDK dir>\NativeSDK\ODataFramework\Android\libraries
• <Client SDK dir>\NativeSDK\MAFReuse\Android\libraries
2. Using the Project view, copy the following jar libraries from the SDK installation folders and
paste it into the libs folder in Android Studio:
• AfariaSLL.jar
•
HttpConvAuthFlows.jar
•
ODataAPI.jar
• ClientHubSLL
•
HttpConversation.jar
•
odataoffline.jar (offline store)
• ClientLog.jar
•
maflogger.jar
•
ODataOnline.jar (online store)
• Common.jar
•
maflogoncore.jar
•
perflib.jar
• Connectivity.jar
•
maflogonui.jar
•
Request.jar
• CoreServices.jar
•
mafuicomponents.jar
•
sap-e2etrace.jar
• DataVaultLib.jar
•
mafsettingscreen.jar
•
SupportabilityFacade.jar
• E2ETrace.jar
•
MobilePlace.jar
•
XscriptParser.jar
5
How To... On-board users with SMP 3.0 OData API (Android)
6
How To... On-board users with SMP 3.0 OData API (Android)
3. Go to the Project view
4. Paste the libraries into the libs folder
5. A confirmation window will appear. Click OK
Remember all the required jar file are locates in two different folders, make sure you include all
the libraries indicated in step 2
7
How To... On-board users with SMP 3.0 OData API (Android)
6. Select the libraries, right click on the selected libraries and select Add as library from the
context menu
7. Add it to the app module and click OK
8
How To... On-board users with SMP 3.0 OData API (Android)
8. You can check the libraries were added in the build.gradle file located in the app module
9
How To... On-board users with SMP 3.0 OData API (Android)
Mandatory if using Offline Store
If you are going to use the Offline store, remember you need to integrate native libraries (.so
resources) in your app. The following steps explains how to do it in Android Studio
9. Right-click on main folder and select New -> Directory from the context menu
10. Enter jniLibs in the directory name field and click OK
11. Locate your SMP 3.0 SDK installation folder. You will find libmlcrsa16.so and the
libodataofflinejni.so files in the following folder: <Client SDK
dir>\NativeSDK\ODataFramework\Android\libraries\armeabi
10
How To... On-board users with SMP 3.0 OData API (Android)
12. Copy the armeabi folder and paste it into jniLibs in Android Studio. For the Offline Store you
will need libmlcrsa16.so and libodataofflinejni.so, you can delete the rest.
13. Save your changes
11
How To... On-board users with SMP 3.0 OData API (Android)
4.3
Create Logon Screen
We are assuming this app requires more control over the logon screen, which means that
developers have to design the logon screen and define the texts included in the screens.
4.3.1
Add Logon Screen Texts
Under res -> values folder you will find strings.xml file, which contains all the text that your
application uses
1.
Select the Android perspective
2. Go to res -> values and open strings.xml
3. strings.xml should look like the image below
4. Add the following labels and messages between the <resources> </resources> tag
<resources>
<string name="app_name">MyApp</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<!-- === Main texts ====================================== -->
<string name="title_welcome">Welcome %1$s</string>
<!-- === Logon texts ======================================== -->
<string name="lbl_host">Host</string>
<string name="lbl_port">Port</string>
<string name="lbl_username">Username</string>
<string name="lbl_password">Password</string>
<string name="lbl_https">HTTPS enabled</string>
<string name="btn_logon">Login</string>
<string name="msg_registration_progress">Registration is in Progress</string>
12
How To... On-board users with SMP 3.0 OData API (Android)
<string
<string
<string
<string
name="msg_registration_success">Registration was successful</string>
name="msg_registration_fail">Registration failed</string>
name="msg_already_registered">Device already registered</string>
name="title_activity_login">Login</string>
<!-- === default values ======================================== -->
<string name="value_host">SMP Host</string>
<string name="value_username">Username</string>
<string name="value_port">SMP Port</string>
<!-- === Main texts ======================================== -->
<string name="msg_offline_progress">Opening offline store</string>
<string name="msg_offline_success">Offline store is opened. It has %1$d
agencies</string>
<string name="msg_offline_fail">Offline store could not be opened. Error:
%1$s</string>
</resources>
4.3.2
1.
Create LoginActivity
Select File -> New -> Activity -> Blank Activity
2. Enter the following information to create the activity and its corresponding layout
resources and click Finish
13
How To... On-board users with SMP 3.0 OData API (Android)
3. You should see the activity_login.xml layout in the editor. At the bottom of the editor, click
on Text to add new UI elements to this screen.
4. Remove the hello_world text view
14
How To... On-board users with SMP 3.0 OData API (Android)
5. Copy the following xml code to define the logon screen.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.sap.sample.myapp.LoginActivity">
<EditText
android:id="@+id/txt_host"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/lbl_host">
<requestFocus />
</EditText>
<EditText
android:id="@+id/txt_port"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/lbl_port"
android:layout_below="@+id/txt_host">
</EditText>
<EditText
android:id="@+id/txt_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/lbl_username"
android:layout_below="@+id/txt_port"/>
<EditText
android:id="@+id/txt_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/lbl_password"
android:inputType="text|textPassword"
android:layout_below="@+id/txt_username"/>
<Button
android:id="@+id/logon_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:text="@string/btn_logon"
android:layout_below="@+id/txt_password"/>
</RelativeLayout>
15
How To... On-board users with SMP 3.0 OData API (Android)
5. The graphical layout should look like the image below
16
How To... On-board users with SMP 3.0 OData API (Android)
4.4
Onboard users with LogonCore
LogonCore is responsible for handling the registration/de-registration.
Android is Multi-Threaded, which means that applications responsibilities can be separated. We
need to create different classes to run the network operations in the background thread and the
changes in the screen in the main thread:
•
UIListener interface to notify the user and make changes in the UI
•
ODataBaseListener class to transition from the background thread to the main tread
through the UIListener
•
MyLogonCoreListener: Whether the onboarding process finishes successful or not, is this
listener who receives the response from the SMP server.
4.4.1
Create UIListener
UIListener is an interface that defines two methods. Activities can implement the UIListener
interface to receive responses from the server
1.
Go to Java -> com.sap.sample and select New -> Java Class from the context menu to
define an interface.
2. Enter UIListener in the name field and click OK
3. Define the UIListener interface with the following source code
package com.sap.sample.myapp;
/**
* Define two methods that activities can
* implement to receive responses from the server
*/
public interface UIListener {
void onODataRequestError(Exception e);
void onODataRequestSuccess(String info);
}
4. Save the changes
17
How To... On-board users with SMP 3.0 OData API (Android)
4.4.2
Create ODataBaseListener
The ODataBaseListener will define the methods to notify the user about successes or failures using
the main thread.
4. Go to Java -> com.sap.sample and select New -> Package from the context menu
5. Enter services and click OK
6. Select the newly created package and Go to New -> Java Class in the context menu
7. Enter ODataBaseListener and click OK
18
How To... On-board users with SMP 3.0 OData API (Android)
8. Implement the class with the following code
package com.sap.sample.myapp.services;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.sap.sample.myapp.UIListener;
import java.lang.ref.WeakReference;
/**
* Get the responses from the server in the background treads and
* send notifications to the activities through the main thread.
*/
public class ODataBaseListener {
private static final String TAG = ODataBaseListener.class.getSimpleName();
protected UIListener uiListener;
protected int operation;
private final int SUCCESS = 0;
private final int ERROR = -1;
private Handler uiHandler = new myHandler(this);
private static class myHandler extends Handler {
private WeakReference<ODataBaseListener> baseListenerWeakReference;
public myHandler(ODataBaseListener oDataBaseListener){
super(Looper.getMainLooper());
baseListenerWeakReference = new
WeakReference<ODataBaseListener>(oDataBaseListener);
}
@Override
public void handleMessage(Message msg) {
ODataBaseListener baseListener = baseListenerWeakReference.get();
if (msg.what == baseListener.SUCCESS) {
// Notify the Activity the is complete
String key = (String) msg.obj;
baseListener.uiListener.onODataRequestSuccess(key);
} else if (msg.what == baseListener.ERROR) {
Exception e = (Exception) msg.obj;
baseListener.uiListener.onODataRequestError( e);
}
}
}
public ODataBaseListener(int operation, UIListener uiListener) {
super();
this.operation = operation;
this.uiListener = uiListener;
}
/**
* Notify the UIListener that the request was successful.
* @param info an Exception that denotes the error that occurred.
*/
protected void notifySuccessToListener(String info) {
Message msg = uiHandler.obtainMessage();
msg.what = SUCCESS;
msg.obj = info;
uiHandler.sendMessage(msg);
Log.d(TAG, "notifySuccessToListener: " + info);
}
/**
* Notify the UIListener that the request has an error.
* @param exception an Exception that denotes the error that occurred.
*/
protected void notifyErrorToListener(Exception exception) {
Message msg = uiHandler.obtainMessage();
msg.what = ERROR;
msg.obj = exception;
uiHandler.sendMessage(msg);
Log.e(TAG, "notifyErrorToListener", exception);
}
}
19
How To... On-board users with SMP 3.0 OData API (Android)
4. Save the changes
4.4.3
Create MyLogonCoreListener
Define MyLogonCoreListener that implements the LogonCoreListener interface. This listener
implements the methods that are called when the registration finishes
1.
Right click on the services package and select New -> Java Class from the context menu
2. Enter MyLogonCoreListener and click OK
3. Make this class extend the ODataBaseListener and implements LogonCoreListener
/**
* This listener implements the methods that are called when the registration finishes
*/
public class MyLogonCoreListener extends ODataBaseListener implements
LogonCoreListener{
}
20
How To... On-board users with SMP 3.0 OData API (Android)
4. With your cursor in the definition of the class, click on the icon that appears on your left and
select Implement methods from the menu
5. Select all methods to implement and click OK
6. You also need to create a constructor matching the super class
21
How To... On-board users with SMP 3.0 OData API (Android)
7. The registrationFinished method has a Boolean b as parameter. It indicates the registration
was successful or not. Your class should look like the code below
package com.sap.sample.myapp.services;
import
import
import
import
com.sap.maf.tools.logon.core.LogonCoreListener;
com.sap.maf.tools.logon.logonui.api.LogonListener;
com.sap.sample.myapp.UIListener;
com.sybase.persistence.DataVault;
/**
* This listener implements the methods that are called when the registration finishes
*/
public class MyLogonCoreListener extends ODataBaseListener implements LogonCoreListener{
public static final String TAG = MyLogonCoreListener.class.getSimpleName();
public MyLogonCoreListener(int operation, UIListener uiListener) {
super(operation, uiListener);
}
@Override
public void registrationFinished(boolean b, String s, int i,
DataVault.DVPasswordPolicy dvPasswordPolicy) {
Log.d(TAG, "registrationFinished: "+b);
if (b){
try {
LogonCore lgCore = LogonCore.getInstance();
//For testing purposes we are not using password to create the secure store
//Consider enabling the password if the application handles sensitive
// information
lgCore.createStore(null, false);
//Persists the registration information into the secure store
// then clears the sensitive information (e.g. password arrays) from the
//memory
lgCore.persistRegistration();
notifySuccessToListener("successful: " + s);
} catch (LogonCoreException e) {
notifyErrorToListener(e);
}
} else {
notifyErrorToListener(new GenericException("registration failed: "+ s));
}
}
@Override
public void deregistrationFinished(boolean b) {
}
@Override
public void backendPasswordChanged(boolean b) {
}
@Override
public void applicationSettingsUpdated() {
}
@Override
public void traceUploaded() {
}
}
4. Save the changes
22
How To... On-board users with SMP 3.0 OData API (Android)
4.4.4
1.
Implement LoginActivity
Open LoginActivity
2. Copy the code from Appendix A – LoginActivity.java and paste it into your LoginActivity
class. The following steps explains in high level the important pieces of code form Login
Activity
3. Notice LoginActivity implements the interface UIListener that we defined in step Create
UIListener. In consequence it needs to implement the onODataRequestError method to
notify the user if an error occurred during the registration and the onODataRequestSuccess
method to redirect the user to the main screen.
public class LoginActivity extends Activity implements View.OnClickListener, UIListener {
…
@Override
public void onODataRequestError(Exception e) {
…
}
@Override
public void onODataRequestSuccess(String info) {
…
)
4. Notice LoginActivity uses the private methods initializeLogonCore and registerDevice
to initialize the Logon objects, get the SMP information from the screen and register the
user. Once the user is registered successful, the application will display the MainActivity.
5. More details can be found in Appendix A – LoginActivity.java
23
How To... On-board users with SMP 3.0 OData API (Android)
sa
4.5
Review AndroidManifest.xml
1.
Ensure that AndroidManifest.xml contains the INTERNET permission:
<!-- allow connections to Internet Services. -->
<uses-permission android:name="android.permission.INTERNET" />
2. Define as main activity the LogonActivity. This is a new activity that represents the logon
screen for this application. We are going to create it during this exercise.
<activity
android:name=".LogonActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
3. Define the MainActivity as a second screen that will be displayed after the registration
finishes.
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
android:screenOrientation="portrait" >
</activity>
4. Your manifest file should look like Appendix B – AndroidManifest.xml.
4.6
Open OfflineStore and Access Data Offline
When the onboarding process finishes, the application will display the main screen. Once the
MainActivity is displayed, we are going to open the online store and download the list of agencies
from the backend. In order to do it, we need to create two main classes:
•
•
4.6.1
CredentialsProvider: is the class that provides the username and password to the offline
store
OfflineManager is the class responsible to open the offline store and access the data
Create CredentialsProvider class
You can find the source code for the CredentialsProvider class in Appendix C –
CredentialsProvider. This class implements UsernamePasswordProvider interface to provide
username and password for basic authentication. Credentials can be provided either upfront or in
response to a challenge.
public class CredentialsProvider implements UsernamePasswordProvider{
@Override
public Object onCredentialsNeededUpfront(ISendEvent iSendEvent) {
…
}
@Override
public Object onCredentialsNeededForChallenge(IReceiveEvent iReceiveEvent) {
…
}
}
24
How To... On-board users with SMP 3.0 OData API (Android)
4.6.2
Create OfflineManager class
You can find the source code for the OfflineManager class in Appendix D – OfflineManager. This
class is responsible for opening the offline store and downloading the list of travel agencies (Agency
objects) for offline access.
Notice the CredentialsProvider class is used to create the HttpConversationManager when opening
the offline store
CredentialsProvider credProvider = CredentialsProvider
.getInstance(lgCtx);
HttpConversationManager manager = new CommonAuthFlowsConfigurator(
context).supportBasicAuthUsing(credProvider).configure(
new HttpConversationManager(context));
4.6.3
Complete MainActivity
You can find the source code for the MainActivity in Appendix G – MainActivity and Appendix F –
AsyncResult. This activity define an internal class that extends AsyncTask to execute network
operations like opening the offline store and downloading the list of agencies.
private class GetTask extends AsyncTask<Void, Void, AsyncResult<List<Agency>>>{
@Override
protected AsyncResult<List<Agency>> doInBackground(Void... voids) {
try {
OfflineManager.openOfflineStore(MainActivity.this);
return new AsyncResult<>(OfflineManager.getAgencies());
} catch (Exception e){
return new AsyncResult<>(e);
}
}
@Override
protected void onPostExecute(AsyncResult<List<Agency>> listAsyncResult) {
if (listAsyncResult.getException() != null || listAsyncResult.getData() == null) {
String message =
String.format(getString(R.string.msg_offline_fail),listAsyncResult.getException() );
Toast.makeText(MainActivity.this,message, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Error loading agencies", listAsyncResult.getException());
} else {
final List<Agency> agencies = listAsyncResult.getData();
String message = String.format(getString(R.string.msg_offline_success),
agencies.size());
Toast.makeText(MainActivity.this,message, Toast.LENGTH_SHORT).show();
Log.d(TAG, message);
}
}
}
Save your changes
25
How To... On-board users with SMP 3.0 OData API (Android)
4.7
1.
Run the Application
Check the registrations in the SAP Management Cockpit
2. In AndroidStudio, click on the debug icon
3. Select your testing device and click OK
26
How To... On-board users with SMP 3.0 OData API (Android)
4. Enter SMP host and port, username and
password and click Login
5. A progress message will be displayed
6. A successful message will be displayed and
the user will be redirected to the main screen
7. Another message indicating the offline store
was successfully opened with the number of
travel agencies should appear
27
How To... On-board users with SMP 3.0 OData API (Android)
8. To verify that the user was registered successful, you can open the SAP Management Cockpit
and check if the number of registrations increased when you register your device
Using the SMP OData SDK to onboard users give developers more control about the look and feel
of the logon screen. However, if a more sophisticated mechanism of authentication is used (i.e SAML,
x.509 certificates) developers require to implement how the app will react to the authentication
challenges triggered by the authentication provider.
To understand how developers can leverage the MAF Logon component in more complex scenarios,
please visit help.sap.com
Congratulations!, you completed this exercise.
28
How To... On-board users with SMP 3.0 OData API (Android)
5.
Appendix
Appendix A – LoginActivity.java
package com.sap.sample.myapp;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
android.app.Activity;
android.app.ProgressDialog;
android.content.Intent;
android.os.Bundle;
android.text.TextUtils;
android.util.Log;
android.view.Menu;
android.view.MenuItem;
android.view.View;
android.widget.Button;
android.widget.EditText;
android.widget.Toast;
com.sap.maf.tools.logon.core.LogonCore;
com.sap.maf.tools.logon.core.LogonCoreContext;
com.sap.maf.tools.logon.core.LogonCoreException;
com.sap.sample.myapp.services.MyLogonCoreListener;
public class LoginActivity extends Activity implements View.OnClickListener, UIListener {
private final String TAG = LoginActivity.class.getSimpleName();
private static final String VK_APPCID = "appcid";
private Button logonBtn;
private EditText hostEdit, portEdit, usernameEdit, passwordEdit;
private ProgressDialog progressDialog;
private String appConnId;
private LogonCore lgCore;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//Initialize UI elements in the screen
this.initializeViews();
//Get application connection id
this.initializeLogonCore();
//If application connection id exists, then display main screen
if (!TextUtils.isEmpty(appConnId)){
Intent goToNextActivity = new Intent(this, MainActivity.class);
startActivity(goToNextActivity);
}
}
@Override
public void onClick(View view) {
registerDevice();
}
@Override
public void onODataRequestError(Exception e) {
progressDialog.dismiss();
logonBtn.setEnabled(true);
//notify the user the registration fails
Toast.makeText(this, R.string.msg_registration_fail, Toast.LENGTH_LONG).show();
}
@Override
public void onODataRequestSuccess(String info) {
progressDialog.dismiss();
//Store application connection Id in the secure store
//This way next time the app runs, we know if the user has been
//registered before
try {
appConnId = lgCore.getLogonContext().getConnId();
if (appConnId != null) {
// store it
if (!lgCore.isStoreOpen()) lgCore.unlockStore(null);
lgCore.addObjectToStore(VK_APPCID, appConnId);
}
//notify the user the registration was complete successfully
Toast.makeText(this, R.string.msg_registration_success, Toast.LENGTH_LONG).show();
//Display the main screen
Intent goToNextActivity = new Intent(this,MainActivity.class);
startActivity(goToNextActivity);
29
How To... On-board users with SMP 3.0 OData API (Android)
} catch (LogonCoreException e) {
Log.e(TAG, "error getting application connection id", e);
//notify the user the registration fails
Toast.makeText(this, R.string.msg_registration_fail, Toast.LENGTH_LONG).show();
logonBtn.setEnabled(true);
}
}
/**
* Initialize UI elements
*/
private void initializeViews() {
logonBtn = (Button) findViewById(R.id.logon_button);
logonBtn.setOnClickListener(this);
hostEdit = (EditText) findViewById(R.id.txt_host);
portEdit = (EditText) findViewById(R.id.txt_port);
usernameEdit = (EditText) findViewById(R.id.txt_username);
passwordEdit = (EditText) findViewById(R.id.txt_password);
}
/**
* Initialize LogonCore component
*/
private void initializeLogonCore(){
//Get LogonCore instance
lgCore = LogonCore.getInstance();
//Create a LogonCoreListener for asynchronously registration
MyLogonCoreListener listener = new MyLogonCoreListener(Operation.Login.getValue(), this);
//Set the listener
lgCore.setLogonCoreListener(listener);
//Initialize LogonCore with application configuraton name
lgCore.init(this, <APPLICATION_NAME>);
//Check if application connection exists
try {
//check if secure store is available
if (lgCore.isStoreAvailable()) {
//Unlock the store
lgCore.unlockStore(null);
//Get application connection id
appConnId = lgCore.getObjectFromStore(VK_APPCID);
}
} catch (LogonCoreException e) {
Log.e(TAG, "error initializing logon core", e);
}
}
/**
* Onboard device with Mobile services
*/
private void registerDevice() {
logonBtn.setEnabled(false);
progressDialog =
ProgressDialog.show(this, "", getString(R.string.msg_registration_progress), true);
//Check if the Application Connection Id already exists
if (TextUtils.isEmpty(appConnId)){
//Get LogonCoreContext instance
LogonCoreContext lgCtx = lgCore.getLogonContext();
//Set host
lgCtx.setHost(hostEdit.getText().toString());
//Set port
int port = 8080;
try {
port = Integer.valueOf(portEdit.getText().toString());
} catch (NumberFormatException e) {
Log.e(TAG, "Invalid port value, default (8080) is taken!");
}
lgCtx.setPort(port);
//Set whether the registration uses secure connection or not
lgCtx.setHttps(false);
//set user creation policy
LogonCore.UserCreationPolicy ucPolicy = LogonCore.UserCreationPolicy.automatic;
lgCtx.setUserCreationPolicy(ucPolicy);
//Set username and password
try {
lgCtx.setBackendUser(usernameEdit.getText().toString());
lgCtx.setBackendPassword(passwordEdit.getText().toString());
} catch (LogonCoreException e) {
//Notifies the execution finished
onODataRequestError(e);
}
//Register user
30
How To... On-board users with SMP 3.0 OData API (Android)
lgCore.register(lgCtx);
} else {
//This means the user is already registered
Log.d(TAG, getString(R.string.msg_already_registered));
//notify the user the device is already regitered
Toast.makeText(this, R.string.msg_already_registered,
Toast.LENGTH_LONG).show();
}
}
}
Appendix B – AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sap.sample.myapp" >
<!-- allow connections to Internet Services. -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
</activity>
<activity
android:name=".LoginActivity"
android:label="@string/title_activity_login" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
31
How To... On-board users with SMP 3.0 OData API (Android)
Appendix C – CredentialsProvider
package com.sap.sample.myapp.services;
import
import
import
import
import
import
com.sap.maf.tools.logon.core.LogonCoreContext;
com.sap.maf.tools.logon.core.LogonCoreException;
com.sap.smp.client.httpc.authflows.UsernamePasswordProvider;
com.sap.smp.client.httpc.authflows.UsernamePasswordToken;
com.sap.smp.client.httpc.events.IReceiveEvent;
com.sap.smp.client.httpc.events.ISendEvent;
/**
* Created by I821448 on 12/22/2015.
*
*/
public class CredentialsProvider implements UsernamePasswordProvider{
private static CredentialsProvider instance;
private LogonCoreContext lgCtx;
private CredentialsProvider(LogonCoreContext logonContext) {
lgCtx = logonContext;
}
public static CredentialsProvider getInstance(LogonCoreContext logonContext) {
if (instance == null) {
instance = new CredentialsProvider(logonContext);
}
return instance;
}
@Override
public Object onCredentialsNeededUpfront(ISendEvent iSendEvent) {
try {
String username = lgCtx.getBackendUser();
String password = lgCtx.getBackendPassword();
return new UsernamePasswordToken(username, password);
} catch (LogonCoreException e) {
return null;
}
}
@Override
public Object onCredentialsNeededForChallenge(IReceiveEvent iReceiveEvent) {
try {
String username = lgCtx.getBackendUser();
String password = lgCtx.getBackendPassword();
return new UsernamePasswordToken(username, password);
} catch (LogonCoreException e) {
return null;
}
}
}
32
How To... On-board users with SMP 3.0 OData API (Android)
Appendix D – OfflineManager
package com.sap.sample.myapp.services;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
com.sap.maf.tools.logon.core.LogonCore;
com.sap.maf.tools.logon.core.LogonCoreContext;
com.sap.maf.tools.logon.logonui.api.LogonUIFacade;
com.sap.sample.myapp.GenericException;
com.sap.sample.myapp.types.Agency;
com.sap.smp.client.httpc.HttpConversationManager;
com.sap.smp.client.httpc.IManagerConfigurator;
com.sap.smp.client.httpc.authflows.CommonAuthFlowsConfigurator;
com.sap.smp.client.odata.ODataEntity;
com.sap.smp.client.odata.ODataEntitySet;
com.sap.smp.client.odata.ODataPayload;
com.sap.smp.client.odata.ODataPropMap;
com.sap.smp.client.odata.ODataProperty;
com.sap.smp.client.odata.impl.ODataErrorDefaultImpl;
com.sap.smp.client.odata.offline.ODataOfflineStore;
com.sap.smp.client.odata.offline.ODataOfflineStoreOptions;
com.sap.smp.client.odata.store.ODataRequestParamSingle;
com.sap.smp.client.odata.store.ODataRequestParamSingle.Mode;
com.sap.smp.client.odata.store.ODataResponseSingle;
com.sap.smp.client.odata.store.impl.ODataRequestParamSingleDefaultImpl;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class OfflineManager {
public static final String TAG = OfflineManager.class.getSimpleName();
private static ODataOfflineStore offlineStore;
private static final String TRAVEL_AGENCY_COLLECTION = "TravelAgencies_DQ";
public static final String TRAVEL_AGENCY_ENTRY_ID = "agencynum";
public static boolean openOfflineStore(Context context) throws GenericException {
if (offlineStore==null){
try {
//This instantiate the native UDB libraries which are located in the libodataofflinejni.so file
ODataOfflineStore.globalInit();
LogonCoreContext lgCtx = LogonCore.getInstance().getLogonContext();
String endPointURL = lgCtx.getAppEndPointUrl();
URL url = new URL(endPointURL);
Log.d(TAG, "openOfflineStore: appcid:" + lgCtx.getConnId());
Log.d(TAG, "openOfflineStore: endpointurl:"+ endPointURL);
// Define the offline store options.
// Connection parameter and credentials and the application connection id we got at the
registration
ODataOfflineStoreOptions options = new ODataOfflineStoreOptions();
options.host = url.getHost();
options.port = String.valueOf(url.getPort());
options.enableHTTPS = lgCtx.isHttps();
// the serviceRoot is the backend connector name, which is usually the same as the application
configuration name
// enter in the SMP management cockpit
options.serviceRoot= <APPLICATION_NAME>;
CredentialsProvider credProvider = CredentialsProvider
.getInstance(lgCtx);
//Without MAF Logon
HttpConversationManager manager = new CommonAuthFlowsConfigurator(
context).supportBasicAuthUsing(credProvider).configure(
new HttpConversationManager(context));
options.conversationManager = manager;
//put the APPCID in the HTTP Header
options.enableRepeatableRequests = false;
options.storeName="flight";
33
How To... On-board users with SMP 3.0 OData API (Android)
//This defines the oData collections which will be stored in the offline store
options.addDefiningRequest("reg1", TRAVEL_AGENCY_COLLECTION, false);
//Open offline store
offlineStore = new ODataOfflineStore(context);
offlineStore.openStoreSync(options);
Log.d(TAG, "openOfflineStore: library version" + ODataOfflineStore.libraryVersion());
return true;
} catch (Exception e) {
throw new GenericException(e);
}
} else {
return true;
}
}
public static List<Agency> getAgencies() throws GenericException{
ArrayList<Agency> agencyList = new ArrayList<Agency>();
if (offlineStore!=null){
Agency agency;
ODataProperty property;
ODataPropMap properties;
try {
String resourcePath = TRAVEL_AGENCY_COLLECTION + "?$orderby="+TRAVEL_AGENCY_ENTRY_ID+"%20desc";
ODataRequestParamSingle request = new ODataRequestParamSingleDefaultImpl();
request.setMode(Mode.Read);
request.setResourcePath(resourcePath);
ODataResponseSingle response = (ODataResponseSingle) offlineStore.executeRequest(request);
if (response.getPayloadType() == ODataPayload.Type.Error) {
ODataErrorDefaultImpl error = (ODataErrorDefaultImpl) response.getPayload();
throw new GenericException(error.getMessage());
} else if (response.getPayloadType() == ODataPayload.Type.EntitySet) {
ODataEntitySet feed = (ODataEntitySet) response.getPayload();
List<ODataEntity> entities = feed.getEntities();
for (ODataEntity entity: entities){
properties = entity.getProperties();
property = properties.get(TRAVEL_AGENCY_ENTRY_ID);
agency = new Agency((String)property.getValue());
…
agency.setEditResourceURL(entity.getEditResourcePath());
agencyList.add(agency);
}
} else {
throw new GenericException("Invalid payload: EntitySet expected but got " +
response.getPayloadType().name());
}
} catch (Exception e) {
throw new GenericException(e);
}
}
return agencyList;
}
}
34
How To... On-board users with SMP 3.0 OData API (Android)
Appendix E – Agency
package com.sap.sample.myapp.types;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
public class Agency implements Parcelable {
private String agencyId;
private String agencyName;
private String street;
private String city;
private String country;
private String website;
private String editResourceURL;
public Agency(String agencyId) {
super();
this.agencyId = agencyId;
}
public Agency(Parcel in){
readFromParcel(in);
}
public boolean isInitialized(){
return (!TextUtils.isEmpty(this.agencyId));
}
public String getAgencyId() {
return agencyId;
}
public void setAgencyId(String agencyId) {
this.agencyId = agencyId;
}
public String getAgencyName() {
return agencyName;
}
public void setAgencyName(String agencyName) {
this.agencyName = agencyName;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getWebsite() {
return website;
}
public void setWebsite(String website) {
this.website = website;
}
public String getEditResourceURL() {
return editResourceURL;
}
public void setEditResourceURL(String editResourceURL) {
this.editResourceURL = editResourceURL;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.agencyId);
dest.writeString(this.agencyName);
35
How To... On-board users with SMP 3.0 OData API (Android)
dest.writeString(this.street);
dest.writeString(this.city);
dest.writeString(this.country);
dest.writeString(this.website);
dest.writeString(this.editResourceURL);
}
public void readFromParcel(Parcel in)
{
this.agencyId = in.readString();
this.agencyName = in.readString();
this.street = in.readString();
this.city = in.readString();
this.country = in.readString();
this.website = in.readString();
this.editResourceURL = in.readString();
}
public static final Creator<Agency> CREATOR = new Creator<Agency>()
{
@Override
public Agency createFromParcel(Parcel in)
{
return new Agency(in);
}
@Override
public Agency[] newArray(int size)
{
return new Agency[size];
}
};
}
Appendix F – AsyncResult
package com.sap.sample.myapp.types;
public class AsyncResult<T> {
public static final Void VOID_RESULT = (Void) null;
private Exception exception;
private T data;
public AsyncResult(Exception exception) {
this.exception = exception;
}
public AsyncResult(T data) {
this.data = data;
}
public Exception getException() {
return exception;
}
public T getData() {
return data;
}
}
36
How To... On-board users with SMP 3.0 OData API (Android)
Appendix G – MainActivity
package com.sap.sample.myapp;
import
import
import
import
import
import
import
import
android.os.AsyncTask;
android.support.v7.app.ActionBarActivity;
android.os.Bundle;
android.support.v7.app.AppCompatActivity;
android.util.Log;
android.view.Menu;
android.view.MenuItem;
android.widget.Toast;
import com.sap.sample.myapp.services.OfflineManager;
import com.sap.sample.myapp.types.Agency;
import com.sap.sample.myapp.types.AsyncResult;
import java.util.List;
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new GetTask().execute();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private class GetTask extends AsyncTask<Void, Void, AsyncResult<List<Agency>>>{
@Override
protected AsyncResult<List<Agency>> doInBackground(Void... voids) {
try {
OfflineManager.openOfflineStore(MainActivity.this);
return new AsyncResult<>(OfflineManager.getAgencies());
} catch (Exception e){
return new AsyncResult<>(e);
}
}
@Override
protected void onPostExecute(AsyncResult<List<Agency>> listAsyncResult) {
if (listAsyncResult.getException() != null || listAsyncResult.getData() == null) {
String message =
String.format(getString(R.string.msg_offline_fail),listAsyncResult.getException() );
Toast.makeText(MainActivity.this,message, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Error loading agencies", listAsyncResult.getException());
} else {
final List<Agency> agencies = listAsyncResult.getData();
String message = String.format(getString(R.string.msg_offline_success), agencies.size());
Toast.makeText(MainActivity.this,message, Toast.LENGTH_SHORT).show();
Log.d(TAG, message);
}
}
}
}
37
www.sap.com/contactsap
www.sdn.sap.com/irj/sdn/howtoguides
© 2013 SAP AG or an SAP affiliate company. All rights reserved.
No part of this publication may be reproduced or transmitted in any
form or for any purpose without the express permission of SAP AG.
The information contained herein may be changed without prior notice.
Some software products marketed by SAP AG and its distributors contain
proprietary software components of other software vendors. National product
specifications may vary.
These materials are provided by SAP AG and its affiliated companies (“SAP
Group”) for informational purposes only, without representation or warranty of
any kind, and SAP Group shall not be liable for errors or omissions with
respect to the materials. The only warranties for SAP Group products and
services are those that are set forth in the express warranty statements
accompanying such products and services, if any. Nothing herein should be
construed as constituting an additional warranty.
SAP and other SAP products and services mentioned herein as well as their
respective logos are trademarks or registered trademarks of SAP AG in
Germany and other countries.
Please see
http://www.sap.com/corporate-en/legal/copyright/index.epx#trademark
for additional trademark information and notices.