Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

Latest commit

 

History

History
175 lines (120 loc) · 13.3 KB

WizardAppReadme.md

File metadata and controls

175 lines (120 loc) · 13.3 KB

SDK Wizard Generated App Overview

This app was generated by the SDK Wizard. The following provides an overview of the generated code and a few suggestions on where to go to customize the app.

Table of Contents

SDK Version

The SDK version used by the application is set in the project.properties file.

sdkVersion=2.2.0

Adding Libraries

Additional SDK libraries such as the Offline OData library can be added by editing app's build.gradle (Module:app) file in the dependencies section as shown below.

// SAP Cloud Android SDK dependencies
implementation group: 'com.sap.cloud.android', name: 'fiori', version: sdkVersion
implementation group: 'com.sap.cloud.android', name: 'onboarding', version: sdkVersion
implementation group:'com.sap.cloud.android', name:'odata', version: sdkVersion
implementation group: 'com.sap.cloud.android', name: 'foundation', version: sdkVersion
implementation group: 'com.sap.cloud.android', name: 'offline-odata', version: sdkVersion

Network Requests

The app is designed to use a single OkHttpClient which is configured when the app is started. If you wish to make a network request (such as to upload usage data) to the SAP Cloud Platform Mobile Services, you can access an instance of the OkHttpClient using the below code.

AppUsageUploader.upload(ClientProvider.get());

Proxy Classes

The Wizard generated application leverages both service (EntityContainer) and proxy (Entity) classes generated by OData gradle plugin to interact with the specified mobile application hosted in Cloud Platform Mobile Services. Code generated by OData gradle plugin can be found under the app/build/generated/source/odata directory. The settings for the proxy generation are in the app's build.gradle file under an odata section.

Should the OData service change its metadata, corresponding classes will be regenerated on execution of relevant gradle tasks. It will impact the existing application if changes are compatible. See documentation for what constitutes compatible changes. SAPServiceManager is responsible for instantiating the generated service class.

Service Class

If you wish to make an OData query, you can access an instance of the service class using the below code. This example is from the service class generated against the sample back end.

//get the DataService class which we will use to query the Back-End OData service
SAPServiceManager sapServiceManager = ((SAPWizardApplication)getApplication()).getSAPServiceManager();
ESPMContainer espmContainer = sapServiceManager.getESPMContainer();

DataQuery query = new DataQuery()
        .filter(ProductCategory.numberOfProducts.greaterThan(0))
        .orderBy(ProductCategory.mainCategoryName)
        .orderBy(ProductCategory.categoryName);
espmContainer.executeQuery(query);

Onboarding Customizations

A few of the Welcome screen customizations include the title, app icon, description and privacy terms of service URL. For additional details consult Customizing the Welcome Screen.

Add a Custom Menu

The following steps will add a custom menu to the EntitySetListActivity.

In EntitySetListActivity.java, in the onCreateOptionsMenu, add the below line.

menu.add( Menu.NONE, 300, 1, "Custom Menu");

In the method, onOptionsItemSelected, add the following to handle the click of the menu.

case 300:
   LOGGER.debug("Custom menu clicked.");
   return true;

Architecture Components

This version utilize Android's new Architecture Components: ViewModel, Repository, DataBinding and LiveData. Each entity set will have its own set of activities, fragments, view model, XML layout and repository. When using the generated application as a starting point for development, unnecessary entity types can be purged by simply removing the corresponding files:

  • res/layout (files): <entity_set>_detail.xml, <entity_set>_create_update.xml
  • src/.../<package_name>/mdui (directory): <entity_set>
  • src/.../<package_name>/view_model (directory): <entity_set>

In addition, update the following files to remove reference to the unwanted entity set

  • src/.../<package_name>/viewmodel/EntityViewModelFactory.java
  • src/.../<package_name>/mdui/EntitySetListActivity.java
  • res/AndroidManifest.xml

Data Binding

XML layout files for each entity set uses one and two-way databinding to an instance of the proxy class for viewing and editing. For Android UI components like EditText, TextView, etc., the necessary adapters are available from the corresponding Android library for Java data types. However, the use of Fiori UI components and additional OData data types require specific binding adapters. Please refer to the databinding directory under src for examples of one-way and two-way databindings of a proxy class to KeyValueCell (one-way) and SimplePropertyFormCell (two-way) Fiori UI components.

Note that the list of InverseMethods used for two-way databinding can be trimmed based on the actual set of data types that are required for the project. For example, GeometryPoint and related types can be removed if metadata document does not contain these data types.

Offline Support

During offline application generation, a defining request, which queries the entire collection, will be generated for each entity set within the entity container. Thereafter, user can perform CRUD operations against local data offline. Local changes can be applied to the OData service when online through synchronization (upload followed by download). Local offline store is encrypted using a random string derived from the EncryptionUtil.getEncryptionKey method. While this is a security best practice, in the event the offline store needs to be submitted for support purposes, the encryption key must be extracted via a debug session or change the application to skip encryption.

Customization of Offline Data Set

Defining requests can be changed in SAPServiceManager's initializeOffline method. By default, all data within each entity is downloaded. This is not normally done for a typical offline application as only a subset of the data is brought onto the device. For best practices of offline data definition, please refer to offline section of SDK documentation.

Server Assigned Key and Offline Usage

The generated application assumes that primary key(s) is assigned by the server. For online use case, the server key will be returned in the response of the operation. However, in offline use case, the key(s) is absent until an explicit synchronization is triggered through the main menu on the home screen. Since the key(s) is not even set, accessing them in two-way databinding during create/edit will cause an exception to be thrown. Hence, in create and update fragments, the key(s) is not visible. It is not advisable to initialize the key(s) due to the potential of duplicate key. While it is possible to generate random key values, it is not good for user experience as key value will change after synchronization. Hence, the compromise is to exclude them in create/edit screens.

Furthermore, one should always use read/editLink to access the new local instances.

Identification Property

For each entity set, a random non-key property is selected to identify an instance of corresponding entity type. This property is also used to sort the returned collection. For an app generated against the SAP Sample Service Back End OData service, the City property for the Customers entity set is selected. To change to a different property, the followng three files for the Customers entity set are to be updated: (search for Customer.city and replaced with another property in the Customer entity set)

  • CustomerViewModel.java
  • CustomersListActivity.java
  • CustomersDetailFragment.java

Note: it is best to select a property that normally does not have null values. In some cases, perhaps the key property is appropriate.

Detail Screens

The detail screen shows each property of the entity. The set of properties to be displayed can be changed by modifying the XML layout file: _detail.xml. For example, the Customers entity set will have the following layout files:

  • customers_detail.xml (detail)
  • customers_create_update.xml (create and update)

Show a Custom Screen

The first screen shown following the onboarding screens is the EntitySetListActivity screen. To instead show a different screen, modify LogonActivity.java and in the method startEntitySetListActivity, make the change below.

//Intent intent = new Intent(LogonActivity.this, EntitySetListActivity.class);
Intent intent = new Intent(LogonActivity.this, MyFirstActivity.class);

Push Notifications

If the option Enable Push was checked on the Project Configuration page of the wizard, classes to enable the app to receive push notifications appear in the package fcm. One customization might be to change the title of the notification message if it is not included in the received message. To do this, edit PushNotificationBroadcastReceiver.java and in the onReceive method modify the value of notificationTitle.

Configuration Loader

The Configuration Loader is used to acquire general, non-secure configuration information, such as the Service URL from one of several sources. These sources include the Android Managed Configuration (previously known as application restrictions), an embedded resource file, or the Discovery Service. The sources are tried in the above order until a valid configuration is returned or no input is supplied for a provider, such as the Discovery Service that requires input. By default, the configuration data is persisted to shared preferences. Subsequently, the data is retrieved from shared preferences and kept in the ConfigurationData object. The ConfigurationData object is contained in the SAPWizardApplication application object and accessed through the getConfigurationData method.

When a wizard generated application is initially started or reset, it needs to be onboarded. The onboarding process encompasses getting the configuration data and the initial authentication with the server. The onboarding process is handled by the launch screen activity, which invokes the startStandardOnboarding method of the LaunchScreenActionHandlerImpl class. The startStandardOnboarding method checks to see if there already is configuration data in shared preferences from a prior invocation and if so it moves on. Otherwise the ConfigurationLoader is run asynchronously through the startConfigurationLoader private method. The startStandardOnboarding method then loops until a configuration is successfully loaded or the loader fails. During this loop, the loader may request user input. If so, the private method promptForProviderInput is called to obtain it. Upon success, startStandardOnboarding performs the initial sever authentication, successfully exits the activity and onboarding is complete. Upon any failure, startStandardOnboarding returns and is re-invoked to try again.

The startConfigurationLoader private method simply instantiates a ConfigurationLoader object and calls loadConfiguration on it in a UI thread. The loadConfiguration method runs the loader in its own non-UI thread. The two threads communicate through messages passed through the ConfigurationLoaderCallback object methods and the ConfigurationLoader.processRequestedInputs method.

The ConfigurationLoader has several constructors. The one currently used employs the default queue and supplies the hard-coded Application ID to the discovery service provider. If desired, a developer can modify startConfigurationLoader and define their own configuration provider queue and pass it to the ConfigurationLoader constructor which takes a Context, the ConfigurationLoaderCallback, and a provider queue. The provider queue is simply an array of ConfigurationProvider objects in the order in which one wants them tried.

Troubleshooting

While you are syncing the project, a warning message can appear in Android Studio. "Could not find google-services.json while looking in [src/nullnull/debug, src/debug/nullnull, src/nullnull, src/debug, src/nullnullDebug] registerResGeneratingTask is depricated, use registerGeneratedResFolders(FileCollection)"

You can ignore this warning message, it won't cause any problem.