Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
LudoNoens
Product and Topic Expert
Product and Topic Expert
3,944
This blog post contains some answers to questions that were previously covered in the post What's new with Hybrid Application Toolkit in 2019, combined with new information. I will also provide a link to a list of Frequently Asked Questions.

The goal is to guide you with your app development and for you to be able to resolve some of the common challenges by yourself. We don't need to reinvent the wheel or bump into the same issue someone else faced already. Let's get more productive.

Before we start, I have one recommendation, which is the same as on all my other blog posts related to Hybrid Application Toolkit:
When you start developing a new mobile app, we strongly recommend that you consider developing this with either MDK (Mobile Development Kit) for cross platform applications, or our native SDKs (SAP Cloud Platform SDK for iOS or SAP Cloud Platform SDK for Android).

 

Best Practices


Develop a web app


The most productive environment to develop your (mobile) app is in the cloud, as a web application. You should avoid developing code that is only able to run on a mobile device. It is much easier to test and debug your (UI5) application in your favourite browser. Mobile devices add more challenges due to limitations in memory, processing capacity, connectivity, building/packaging, downloading, screen size, etc. The development cycles/roundtrips are much shorter for web development. The debugging tools in 'desktop' Chrome/Safari/Firefox are pretty advanced; please make use of it!

Some recommendations:

  • Keep your code as much as possible common across your web app and mobile app. We have seen customer support requests where some parts of the code were unnecessary different. The customer mentioned the app was running fine as a web app, but failed as a mobile app. You won't be able to debug your app on the web, when your code only runs on mobile.

  • In case you are making use of APIs that are only available in the mobile environment, ensure those calls are 'guarded' with checks on whether the API is available before calling it. This ensures your web app doesn't break while running in a desktop browser.

  • Code that is handling data queries (OData) should be the same across web app and mobile app. There is no need to have this implemented in a mobile specific way. The only tricky part might be regarding the user, but this should not affect the other parts of the code.

  • Make use of events instead of polling or checking for status.

  • Keep it as simple as possible.


Migrating your app across multiple spaces (for Dev, QA, Prod)


You are developing an app in a Development space and now you need to move this into a QA space for testing, followed by a Prod space for production. How can you make use of the same project structure and App Id.

The challenge here is that when you use our Cloud Build Service (through SAP Mobile Services), it will automatically generate a new application, with new App Id, in SAP Mobile Services (but only if there is none already). This will require you to change various things manually and results in maintenance issues. To avoid this, please follow the following process:

  1. Create a project in SAP Web IDE Full-Stack and build the mobile app in this (Dev) environment.

  2. Push the project content, including the file .che/project.json into your Git repository. Note: the .che folder might be hidden. Use the option in SAP Web IDE Full-Stack to show hidden files.

  3. Go to the SAP Mobile Services Admin cockpit and export the app that was generated in step 1.

  4. Switch to your QA environment and open the SAP Mobile Services Admin cockpit there. Import the app you've exported in step 3.

  5. Open SAP Web IDE Full-Stack in this QA environment and clone the Git project into your workspace.

  6. Build the mobile app using our Cloud Build Service. Since you've already imported the app into SAP Mobile Services in step 4, there is no need to create a new one. Hence, the same App Id will be used.

  7. Your QA team might report issues found in the QA environment. As a developer, continue your development and bug fixing in the Dev environment. Push your code changes into your Git repository.

  8. To get the latest updates into the QA environment, pull the code changes from your Git repository and rebuild the app.

  9. Repeat steps 7 and 8 until you reach a point where things are ready for production.

  10. Switch to your Prod environment and open the SAP Mobile Services Admin cockpit there. Import the app you've exported in step 3.

  11. Open SAP Web IDE Full-Stack in this Prod environment and clone the Git project into your workspace.

  12. Build the mobile app using our Cloud Build Service. Since you've already imported the app into SAP Mobile Services in step 10, there is no need to create a new one. Hence, the same App Id will be used.

  13. Publish and distribute your app (after testing, of course).


As you can see, you should manage your code in a Git repository and use Git as a means to distribute the project across spaces/landscape/systems. We don't need to do anything special in Hybrid Application Toolkit to achieve this.

If you want to take this one step further and tie this into a Continuous Integration/Continuous Delivery (CI/CD) environment, then please take a look at this tutorial.

 

Frequently asked questions


In this section I will provide answers to three  of the most asked questions. A longer list of frequently asked questions is provided by following this link to the SAP document site. I plan to update this content once we receive more questions on a certain topic.

Is there a way to disable the certificate prompt on Android (X509)?


If you do not wish the user to see this screen, handling of client certificates can be disabled by simply adding the following line in config.xml:
<preference name="SAPKapselHandleX509Challenges" value="false" />

For my iOS app, I need to add settings in the Info.plist file. How can I do this for my project in SAP Web IDE?


Obviously, the Info.plist file is not available in the SAP Web IDE Full-Stack project. Using the config.xml file, you can pass settings to our Cloud Build Service, which will apply them in the Info.plist file that gets generated. You can add entries in the config.xml file by using the <config-file> element.

Example:
<platform name="ios">
<config-file target="*-Info.plist" parent="CFBundleURLTypes">
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>ABCDEF</string>
<string>APP-NAME</string>
</array>
</dict>
</array>
</config-file>
</platform>

Another example, for solving problems with accessing the camera on iOS:
<platform name="ios">
<config-file parent="NSCameraUsageDescription" target="*-Info.plist">
<string>We are using a camera to scan barcodes.</string>
</config-file>
</platform>

My app starts up with a blank screen and hangs. Now what ?


This is a very common issue and requires you to investigate further before calling support. There are a few possible causes for failures during startup / initialisation of the app. It might be issues loading dependent libraries; it could be that it tries to access a service URL that doesn't exist/respond; or various other causes.

Now what?: start debugging.

Connect to your app over USB (or wifi, for the pros) and check what errors the browser (Safari or Chrome) is reporting.

Check the logs on SAP Mobile Services Admin cockpit for connection issues.

Collect Network Traces on SAP Mobile Services Admin cockpit to identify what is going on.

Check the device logs.

 

Tips and tricks


In this section I will provide you with useful information that could help you in using SAP Web IDE Full-Stack and the HAT extension. I will update this section with new information, once it is available.

Avoiding download failures


If you are facing issues downloading your app or project archive while using the Chrome browser, please try using Firefox instead. Firefox is better than Chrome for resuming a broken downloading. Additional notes:

  • Before resuming the suspended download, please login to the SAP Mobile Service Admin cockpit to establish an authenticated session. The downloading link is protected by SAML2 authentication. If the logon session has timed out, resuming will not work.

  • Please do not close the browser during the suspend/resume. Resumable downloading will not work if you close the browser.

  • Before resuming the suspended download, please backup the partial files on disk. Sometimes the browser will automatically remove the partial file when a download is broken.


 

Reporting issues


I will not be monitoring this blog very frequently, so in case you run into issues with our SAP Web IDE Full-Stack extension or Cloud Build Service triggered via SAP Web IDE, please raise a support ticket for component CA-WDE-MOB.

If you have questions, feel free to comment down below. Again, please don't expect instant answers.

When the FAQ document will be updated, I will add a comment on the blog post so please follow the blog post to get notification of any updates.

Regards,

Ludo Noens - Product Owner, Hybrid Application Toolkit
40 Comments
dominik-st
Participant
Hi Ludo

The link to the CI tutorial is broken.

Best regards
Dominik
nurres
Explorer
0 Kudos

Hi Ludo,

I have an issue for understand the “Migrating your app across multiple spaces”. How configure dynamically the fioriURL in appConfig.js file unteher mobile folder?

nurres
Explorer
0 Kudos
Sorry, the fioriURL is updated automatically when rebuild app in each specific environment.

Thank!
LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
Sorry about that. The author removed the tutorial as it was a bit outdated. I hope an updated version will be published soon.
LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
Correct. We automatically apply your app's configuraton settings from Mobile Services with every build.
0 Kudos
Hi Ludo,

I am working on the migration process to move the Hybrid mobile apps built on SMP to SAP Mobile

Services Cloud foundry.

Step1: Created a new application and opted for the services.

Step2: Uploaded my package app following a structure under App update service

Android

---www folder

---Config.xml

Step3:  Application got successfully uploaded and added my mobile connectivity connections

Step4: Built the uploaded app with Mobile Built service

Step5: After a successful build, Qr Code appears.

Step6: Scanned the QR Code and able to authentication the app with Cloud Platform subaccount credentials.

Step7: Clear Appears to be blank as a white page


 

Could you let me know what is the process we are missing? I have gone through standard SAP documentation but no clue.

 

Does Cloud Foundry Mobile services, support SMP built Hydrid Mobile apps?

 

Please help me with this.

 

Thank you,

Srikar Nagadevara.

 

 

 
LudoNoens
Product and Topic Expert
Product and Topic Expert
Hi Srikar,

SAP Cloud Platform mobile services available on Cloud Foundry does not support building hybrid apps. Only the product available on Neo supports this.

I can understand the confusion, as by default you will now get the Cloud Foundry option.

Also, what you have built is not a hybrid app based on Apache Cordova, but instead, you've created a native app based on Mobile Development Kit.

You have a few options:

  • redevelop the hybrid app as MDK app and continue on the path you have tried.

  • get access to SAP Cloud Platform mobile services on Neo. This is going to be a challenge and I cannot help you with this. You'll need to contact our sales.

  • develop the app using locally installed tooling (Apache Cordova) and the SMP 3 SDK. This will be a challenge, depending on how old the original app is, and whether you still have access to sources. Hint: you can extract an existing APK file and grab all the web sources from there. You will be on your own in setting up the development environment, since there is no cloud build available.


Hope this helps !

Regards,
Ludo
0 Kudos
Thanks, Ludo,

I am very much pleased with your detail explanation. I would do as per the suggestions.

Do we have this in a road map in the future in the cloud foundry or its totally no support situation?

 

Thank you,

Srikar Nagadevara
0 Kudos
Hi Ludo,

I have a project structure zip file with below and wanted to upload directly on NEO SAP Cloud Platform.

Below is the structure which is built in


Any way i can upload directly?

Also how would I test this locally with structure?

 

Please give me a hint

 

Thank You,

Srikar Nagadevara
LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
Here is the official statement: https://launchpad.support.sap.com/#/notes/2982808
LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos

Not sure whether I understand your question. You want to extract the web application from the hybrid app and deploy that to SAP Cloud Platform ?

When building hybrid apps in SAP Web IDE, you basically start with a web app. Using our cloud build service, we combine this with other code into a Cordova container. The end result is an APK.

web app + additional SAP code + Cordova = APK.

What I understand from your question is that you’ve extracted the APK and now want to know how to get back to the web app.

Hints:

  • The folders “smp”, “plugins”, “cordova-js-src” and probably “js”, “util” and “css”, and the files “index.html”, “cordova”, “cordova_plugins” were added by the cloud build. I’m sure this is not the complete list though.
  • The starting point for your Fiori app is the Component.js
  • I recommend starting from a very basic standalone Fiori UI5 web app and moving your app’s code into that, piece by piece:
    • Components.js
    • view folder
    • model folder
    • controller
  • For the structure of a basic Fiori app, see https://sapui5.hana.ondemand.com/1.36.6/docs/guide/003f755d46d34dd1bbce9ffe08c8d46a.html
  • Deploy this HTML5 app to SAP Cloud Platform, or run this locally using UI5 Tooling.

Hope this helps.

Regards,
Ludo

former_member682258
Participant
0 Kudos

Hi Ludo, Can I know how do we authenticate into deployed Hybrid Mobile apps on Mobile Services Neo from Active Directory?

If the user is in AD it should automatically authenticate in mobile app without asking user for Gateway service credentials.

I had a cloud connector set up established to the back-end service, Also I had AD configured for test users.

I am wondering how do we do this without maintaining User name and password in the destination on cloud destinations.

Please share your thoughts,

Thanks

Lawanya.

LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Lawanya,

I suggest to check this blog post and contact the author for further queries. You can also raise a support ticket to speed up the response.

https://blogs.sap.com/2018/03/01/understanding-security-authentication-within-mobile-services-for-de...

Regards,
Ludo
0 Kudos
Hi Ludo,

We are working on Neo Platform Sub account and we have generated a test hybrid application.

Step1: Right Click and selected to build Packaged App.

Step2: Selected the Minimum android platform version 5.1

Step3: Waited until built has completed, downloaded the apk.

Step4: When I tested the APK with ANDROID version <=6 I get a white screen. But it works good with android versions >=7.

I have been testing the APK on android studio and I am very confused why it is not working with Android 6 (Marshmallow, API Level 23) but works on higher versions


 


Can you please share your thoughts on it please. I am using cordova plugins also inside the app.

 

Thank You,

Spandana Bollu.

 
LudoNoens
Product and Topic Expert
Product and Topic Expert
Hi Spandana,

We have investigated this issue and found incompatibilities in the JavaScript code used in UI5 and the Mobile SDK. For the time being, my advice is to use Android version 7 or above.

Take note that we are planning to drop support for Android versions 6 and below in an upcoming service pack release of the Mobile SDK.

If Android 6 or lower is a must have for you, then please raise a support ticket.

Thanks,
Ludo
poornamamatha
Participant
0 Kudos
Hi Ludo,

I have been working on creating mobile 3 hybrid apps recently and i have authorization question.

We are able to login into all the 3 apps with the sub-account user and password from the mobile. However if i want to restrict my user to only access only 2 out of 3 apps. How do we control it from the SCPm's ?

Do we have a concept of creating roles for each specific app and assigning them to the user?

if they are 100 users working and only 50 of them need to access the 3 apps how does the authorization takes place.

Any ideas?

Thanks a lot,

Mamatha Poorna
0 Kudos

Hi Ludo,

Thanks a Lot.

Unfortunately, we have an Android version 6 option only will raise a ticket for this.

Thank You,

SpandanaB.

LudoNoens
Product and Topic Expert
Product and Topic Expert
Hi Mamatha,

This is possible by making use of Role Collections configured in SAP Cloud Platform, mapping this to IDP groups, and then configuring Access Control in the SAP Mobile Services Cockpit.

More details can be found in the documentation:

https://help.sap.com/viewer/468990a67780424a9e66eb096d4345bb/Cloud/en-US/a1eca143b2eb4a598ca18d0b19a...

Regards,
Ludo
poornamamatha
Participant
0 Kudos
Hi Ludo,

I have gone through the document in SAP help portal as you suggested but still I am not clear what specific Roles has to Assign for End users to restrict for the Mobile Application for Authorization.

 

Regards,

Mamatha M
LudoNoens
Product and Topic Expert
Product and Topic Expert
Hi Mamatha,

For the Neo landscape, it is slightly different. I suggest to create a 'dummy' HTML5 application (e.g. MyApp1) in the SAP Cloud Platform cockpit for your app. Then define a new Role for this app (e.g. MyApp1Role).

Then, in the Security/Authorizations section of the SAP Cloud Platform cockpit, you can create a new Group (e.g. MyApp1Users). You can now Assign Roles (MyApp1Role) to this Group, and also create a mapping for trusted identify providers.

Hope this helps.

Regards,
Ludo

 
former_member682258
Participant
0 Kudos
Hi Ludo,

How can we de-register user credentials after logout from the mobile application?


Any suggestions?

 

Regards,

Lawanya

 
former_member682258
Participant
0 Kudos
Hi Ludo ,
I have implemented an Hybrid application. As soon as I am logging into the mobile device it starts with SAP cloud platform login page .Once login with the email it’s total installed registrations can be seen under app.

 

I am want to implement the logout functionality with the below code but it does not take to my login page or passcode screen

 

Code :

onLogout: function () {if (typeof sap.hybrid !== ‘undefined’) { sap.Logon.core.deleteRegistration(this.logonUnregisterSuccessCallback(), this.errorCallback()); } },logonUnregisterSuccessCallback: function (){ sap.Logon.core.loadStartPage(); }

 

Do we need to implement custom login when we need a logout ?

Any thoughts? Please help me out

 

Regards,

Lawanya

LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
The loadStartPage opens the standard Cordova start page; which is probably not your login page or passcode screen.

I think you should be calling sap.Logon.init instead.

Please refer to https://help.sap.com/doc/3f9b228508c8481798df1ea12b8d99a6/3.0.15/en-US/api/sap.Logon.html

Regards,
Ludo
former_member682258
Participant
0 Kudos
Hi Ludo,

I have replaced sap.Logon.core.loadStartPage with sap.Logon.init but it is navigating to below page.


code:

unregister: function () {​​​​
var that = this;
if (typeof sap.hybrid !== 'undefined') {​​​​​​​​​​​
sap.Logon.core.deleteRegistration(this.logonUnregisterSuccessCallback(), this.errorCallback());
}​​​​​​​​​​​
}​​​​​​​​​​​,

logonUnregisterSuccessCallback: function (result) {​​​​​​​​​​​
var logonView = sap.logon.LogonJsView;
sap.Logon.init(successCallback, errorCallback, applicationId, defaultContext, logonView, "customCertProvider")
}​​​​​​​​​​​,
}​​​​​​​​​​​

Since, I am login into SAP cloud platform login page but unable to get back to it after logout.

Any ideas? please help me out

 

Regards,

Lawanya
LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Lawanya,

Our suggestions:

a. Try not providing the last two arguments in the call
sap.Logon.init(successCallback, errorCallback, applicationId, defaultContext)

b. Ensure that the app security is configured to use SAML in "defaultContext".

 

Hope this helps,

Ludo
0 Kudos
Hi Ludo,

I have completed working on an SAP Hybrid Mobile application in a developer based Neo SAP Cloud subaccount pointing to a development gateway destination.

How do I transport this Hybrid application to Quality and production?

Note:

I have used SAP Full stack WEB IDE to develop the Hybrid application. Do we need to export and import project zip files on Quality/PRD subaccounts (Like enabling WEB IDE again for Quality/PRD subaccounts)?

Could you please guide us or suggest.

Thank You,

Spandana B

 
LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Spandana,

Yes, you will indeed need to enable SAP Web IDE on each subaccount. In the steps I've described in this blog post, I am using a git repository as a means to transport the project. You could also export & import project zip files, but I think it is better to keep track of things in Git (making use of versioning, branching, etc.).

Regards,
Ludo

 
dominik-st
Participant
0 Kudos
Hi Ludo,

is there a new Version of the CI/CD Tutorial ?
(the Link https://developers.sap.com/tutorials/ci-best-practices-mobile-cordova.html is still broken)

 

Best regards
Dominik
former_member682258
Participant
0 Kudos
Hi Ludo,

I followed your suggestions and sap.Logon.init(successCallback, errorCallback, applicationId, defaultContext) also triggered but what we should we do in successCallback() for logout?

Code snippet:

unregister: function () {
if (typeof sap.hybrid !== 'undefined') {
sap.Logon.core.deleteRegistration(this.logonUnregisterSuccessCallback(), this.errorCallback());
}
},

logonUnregisterSuccessCallback: function (result) {

var context = {
"serverHost": "mobile-xxxxxxxxxx.xxx.hana.ondemand.com",
"https": "true",
"serverPort": "xxx",
"communicatorId": "REST"
};

sap.Logon.init(this.successCallback(), this.errorCallback(), appID, context);
},

errorCallback: function (e) {
alert("An error occurred");
alert(JSON.stringify(e));
},

successCallback: function (context) {
alert("SuccessCall");
// applicationContext = context;
},

Please, Help me out.

Regards,
Lawanya
LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Dominik,

I believe these tutorials were replaced with SAP-samples on GitHub:

https://github.com/SAP-samples/cloud-mobile-services-continuous-integration

Regards,
Ludo
0 Kudos
Hi lnoens ,

 

I have followed exactly the way you described to deploy the Hybrid Mobile App from one system(Development) to Another(Quality). But every single time I try , it is creating a new version of APPID in Quality. Not really sure why .

 

Could this be the reason? When I imported the mobile App in to Quality Mobile Services ( I can see , it is still build required as shown below)

 


 

Can you please suggest me what to do in this regard. I do not want to have a different APP id
0 Kudos
Hi lnoens,

thank you for your blogs regarding the hybrid app toolkit.

I'm currently facing a problem with the automatically generated AppId during the Cloud-Build-Process for a hybrid Android app:


I did following steps:

  • Created an UI5 App (1.71) using the SAP WebIDE

  • Enabled it as a hybrid application

  • Added some cordova-plugins

  • Went to the mobile-Folder and changed the AppID-Parameter in the appConfig.js-File to xx.xxx.xxx (Sample)

  • Changed all the other necessary configs in the appConfig.js-File

  • Then I started the "Build Packaged App"-Process for the Cloud-Build.

  • Checked the box "Manually update configuration files"

  • Provided a signing profile for the Android Project

  • Started the build


The build was sucessful, but the Application ID in my Hybrid App was still generated by the Cloud-Build-Process, which i wanted to avoid, because I need a specific Application ID for some licensing purposes.

Can you please help me out there so the Build-Process wont generate an AppId but takes mine instead?

Kind regards,

Maximilian

 
0 Kudos
Hi Maximilian,

Before building the project, you need to manually create an application in Mobile Services cockpit and its configuration should match those you set in appConfig.js (what other configurations you modified?). Besides, the "id" attribute value in config.xml should be set to the one you specify. Then the project will be built against the app you created instead of auto generating the app id.

Regards,

Zhigao
0 Kudos
Hi Zhigao,

thanks for your fast reply, unfortunately this doesn't work either.

I've followed your steps:

  • Created an application in Mobile Services: (Config Templates: Hybrid)

  • I've also added the Cloud Build Feature


  • Then i created a SAPUI5 App via the WebIDE

  • Enabled it as an Hybrid Application

  • Changed the appConfig.js to the following:


  • Then id in the config.xml file:


  • And then i started the "Build Packaged App" Process (Android)

  • I set my signing profile and checked the box "Manually update configuration files"


But when i started the build, the AppID was generated and ist not based on my config-changes:


Did i miss something while creating the empty App in the mobile services?

Thanks for your help in advance.

Regards,

Maximilian
Hi Maximilian,

Although a new app is auto generated in the first build, the subsequent builds will be made against the manually created app if the "Manually update configuration files" checkbox is checked.

I specified the App Id in my appConfig.js and config.xml files after the first build, before I ran the 2nd build. I can see the two files are not modified during the build and these logs showed in my Web IDE console.

3:27:39 PM (hat) ===============================================================



3:27:41 PM (hat) Update the app description of the ManualAppId app



3:27:41 PM (hat) Parse appRoutes.js for backend declaration



3:27:41 PM (hat) Update the container of the ManualAppId app



3:27:42 PM (hat) Increase app version from 1.2 to 1.3



You can delete the auto-generated app in MS cockpit after verifying this method.


Regards,

Zhigao
0 Kudos
Hi Zhigao,

thx for the fast response 🙂

are you on the NEO-Stack or CF?

I'm currently on NEO.

Have you checked the "Manually update configuration files"-box already on the first build or only at the second build?

Regards,

Maximilian.
Web IDE and HAT is only available on Neo. I checked the checkbox only after the first build.

Actually the checkbox is intended for the build-for-CF scenario, but your scenario can take advantage of it.
0 Kudos
Hi Zhigao,

OMG thank you so much for helping me out, it seems to work this way and i can see that my user has registered itself on the app with the right appid.

You made my day 🙂

Kind regards,

Maximilian

 
bsgd
Explorer
0 Kudos
Hi Ludo,

first thanks for all your posts!

We have a problem on our env, our destination says 200, but if i open WebIDE i get a 401:


destination setup:


Do you have idea what could be wrong?

 

Kind regards,

Dominik
LudoNoens
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Dominik,

SAP Web IDE can only work with Mobile Services Neo through App2AppSSO. You are using Mobile Services on Cloud Foundry with Basic authentication.

Do you have Mobile Services on the Neo landscape ?

Thanks,
Ludo