How to achieve interaction between Native and Hybrid Applications in Worklight? - android

I will start with explaining the use case I am trying to implement. I have two different applications:
Native android application, and
Worklight-based hybrid application
The use case starts with opening native android application. On a particular event I am opening the Hybrid application with some parameters.
In the Hybrid application, I am getting the passed parameters in the native side it, and now I want to use that data in the webview of the application (JavaScript, HTML). How can I achieve that?
For example:
I opened the first android application. Which have one text box and a button. I entered my mobile number in text box and hit the button. On the button click I have code which starts the other hybrid application and passes the mobile number with it.
I am able to extract that mobile number parameter on native side of code. How to pass that to the Web (JavaScript) part of it?
Any help will be appreciated.

If you are using Worklight 6.2, you can achieve this in 2 ways.
Use the Simple Data Sharing API
With this API I don't think you'll even need to try to get the data from the native view and move it back to the webview in your Hybrid app, it will just be available in the webview.
Explaining the concept and execution in this answer will make it too long; I suggest to first review the documentation and see whether it fits your needs.
But I suggest:
Use the Action Sender API
With this API you can easily move data from web to native or native to web.
In your case, you say you already have the data in the native code after opening the Hybrid application, and you only need to move it to the webview, so what that is required is to:
Add a sender in the native code
Add a receiver in the JavaScript code
Unfortunately at this time there is no training module available to demonstrate specifically this feature, but there will be.
This is the basic premise for what you'll need to do:
In the JavaScript you implement a receiver:
function wlCommonInit(){
WL.App.addActionReceiver ("doSomething", actionReceiver);
}
function actionReceiver(received){
// Do something with the received data.
alert (received.data.someProperty);
}
In the main Java class of the Hybrid application (or elsewhere, depending on your application) you implement the following in onInitWebFrameworkComplete after the else closing bracket:
public void onInitWebFrameworkComplete(WLInitWebFrameworkResult result){
...
...
else {
handleWebFrameworkInitFailure(result);
}
JSONObject data = new JSONObject();
try {
data.put("someProperty", 12345);
} catch (JSONException e) {
// handle it...
}
WL.getInstance().sendActionToJS("doSomething", data);
}
The end result would be that once you open the app, you'll be welcomed with an alert showing "12345".

I will describe the solution using code snippets.
First Open the hybrid application from a native application.
Intent intent = getPackageManager().getLaunchIntentForPackage(“URI Of Target Application”);
intent.putExtra("someData", someData);
startActivity(intent);
Now Worklight based hybrid application will start and from native part we will extract that passed data and store it in shared preferences:
Bundle dataBundle = getIntent().getExtras();
String someData = dataBundle.getString("someData");
sharedpreferences = getSharedPreferences(MyPREFERENCES, MODE_PRIVATE);
sharedpreferences.edit().putString("someData", someData);
sharedpreferences.commit();
Now make a plugin which you can call after the web part is ready for use.
SharedPreferences sharedpreferences = cordova.getActivity().getSharedPreferences(MyPREFERENCES,cordova.getActivity().MODE_PRIVATE);
if(sharedpreferences!=null) {
String param = sharedpreferences.getString("someData", "-1");
sharedpreferences.edit().remove("someData").commit();
callbackContext.success(param);
}
Call that plugin on web side of Worklight based hybrid application.
function onSuccessSharedData (param) {
Param is the passed parameter
}
Cordova.exec(onSuccessSharedData, onFailure, "pluginName", "action", []);

Related

How can I return an app to the foreground, considering the new security changes in API 29?

I have an app that makes an http request via the localhost to a separate, third-party app which I do not control, and waits for a response from that call before continuing. The workflow goes like this:
User is inside my app
User presses a button, which launches and calls out to the third-party application
User interacts with the third-party application
When the third-party application finishes its work, my app picks up the completed http response, and pulls itself back to the forefront via MoveTaskToFront for the user to continue working.
This functions properly in Android 9 and below, but the last step does not work in Android 10, I believe due to the new restrictions on launching activities from the background.
I have no control over the third-party app, so I cannot modify it to close itself when finished working, or request that the calling app be returned to the foreground when appropriate. Does anyone know of a workaround for this?
Edit: as requested, I've added the code snippet with the call out. This is a Xamarin project, so it's written in C#, but this particular code section is Android-platform-specific, so I am able to make Android system calls.
First I have to bring up the third-party app:
Intent intent = CrossCurrentActivity.Current.AppContext.PackageManager.GetLaunchIntentForPackage("com.bbpos.android.tsys");
if (intent != null)
{
// We found the activity now start the activity
intent.AddFlags(ActivityFlags.ClearTask);
CrossCurrentActivity.Current.AppContext.StartActivity(intent);
}
Then I call into it via the localhost, process the response, and want to switch back to my app.
using (var client = new HttpClient())
{
// by calling .Result we're forcing synchronicity
var response = client.GetAsync("http://127.0.0.1:8080/v2/pos?TransportKey=" + pTransportKey + "&Format=JSON").Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
// as above, forcing synchronicity
string responseString = responseContent.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<GeniusTransactionResponse>(responseString);
var manager = (ActivityManager)Application.Context.GetSystemService(Context.ActivityService);
var test = manager.AppTasks.First().TaskInfo.Id;
manager.AppTasks.First().MoveToFront();
//manager.MoveTaskToFront(CrossCurrentActivity.Current.Activity.TaskId, 0);
return result;
}
else
{
return null;
}
}
Quick update in case anyone else has this same issue: I was able to work around this by adding an Accessibility Service to the project. Simply having an Accessibility Service registered and enabled by the user allows MoveTaskToFront to function as it did in APIs <29; the actual service doesn't need to do anything.

Pepper Robot - How to launch tablet applications through DialogFlow?

I'm trying to incorporate Pepper's built in Android tablet more in DialogFlow interactions. Particularly, my goal is to open applications installed on the tablet itself for people to use while they're talking with Pepper. I'm aware there is a 'j-tablet-browser' app installed on Pepper's end that can let a person browse the tablet like an ordinary Android device, but I would like to take it one step further and directly launch an Android app, like Amazon's Alexa.
The best solution I can up with is:
User says specific utterance (e.g. "Pepper, open Alexa please")
DialogFlow launches the j-tablet-browser behavior
{
"speak": "Sure, just a second",
"action": "startApp",
"action_parameters": {
"appId": "j-tablet-browser/."
}
}
User navigates the Android menu manually to tap the Alexa icon
My ideal goal is to make the process seamless:
User says specific utterance (e.g. "Pepper, open Alexa please")
DialogFlow launches the Alexa app installed on the Android tablet
Does anyone have an idea how this could be done?
This is quite a broad question so I'll try and focus on the specifics for launching an app with a Dialogflow chatbot. If you don't already have a QiSDK Dialogflow chatbot running on Pepper, there is a good tutorial here which details the full process. If you already have a chatbot implemented I hope the below steps are general enough for you to apply to your project.
This chatbot only returns text results for Pepper to say, so you'll need to make some modifications to allow particular actions to be launched.
Modifying DialogflowDataSource
Step 2 on this page of the tutorial details how to send a text query to Dialogflow and get a text response. You'll want to modify it to return the full reponse object (including actions), not just the text. Define a new function called detectIntentFullResponse for example.
// Change this
return response.queryResult.fulfillmentText
// to this
return response.queryResult
Modifying DialogflowChatbot
Step 2 on this page shows how to implement a QiSDK Chatbot. Add some logic to check for actions in the replyTo function.
var response: DetectIntentResponse? = null
// ...
response = dataSource.detectIntentFullResponse(input, dialogflowSessionId, language)
// ...
return if (reponse.action != null) {
StandardReplyReaction(
ActionReaction(qiContext, response), ReplyPriority.NORMAL
)
} else if (reponse.answer != null) {
StandardReplyReaction(
SimpleSayReaction(qiContext, reponse.answer), ReplyPriority.NORMAL
)
} else {
StandardReplyReaction(
EmptyChatbotReaction(qiContext), ReplyPriority.FALLBACK
)
}
Now make a new Class, ActionReaction. Note that the below is incomplete, but should serve as an example of how you can determine which action to run (if you want others). Look at SimpleSayReaction for more implementation details.
class ActionReaction internal constructor(context: QiContext, private val response: DetectIntentResponse) :
BaseChatbotReaction(context) {
override fun runWith(speechEngine: SpeechEngine) {
if (response.action == "launch-app") {
var appID = response.parameters.app.toString()
// launch app at appID
}
}
}
As for launching the app, various approaches are detailed in other questions, such as here. It is possible to extend this approach to do other actions, such as running or retrieving online data.

How to open Android Outlook application from an external one

I'm currently developing an Android application in order to display home screen widgets. Those ones are related to Microsoft Outlook (Events + Messages) in order to show incoming events and unread new messages in a kind of dynamic tiles.
The Msal graph library helps me a lot to authenticate and retrieve in formations which contains an identifier for each event / message results
But now I want to know if the outlook application is installed on the user device and if there is a way to open Outlook when the user click on the widget. Moreover if the user can open the corresponding clicked event or message with the identifier.
For example the Event widget currently displaying a birthday event. The user click on it. Then it opens Outlook and display directly that birthday event.
Regards
I don't think this is officially documented somewhere. But here's what you can do to find out about it.
You can list all Microsoft applications installed on your device...
val packages = context.packageManager
.getInstalledApplications(PackageManager.GET_META_DATA)
for (info in packages) {
if(info.packageName.startsWith("com.microsoft", true)){
Log.d("package name:" + info.packageName)
Log.d("Launch Activity: " + context.packageManager.getLaunchIntentForPackage(info.packageName))
}
}
Take a note of the "launch intent" displayed in the LogCat. You can use that to launch Outlook. Just make sure you don't hard-code those values because Microsoft can change those values at any point, for example the activity class can change. So, instead of doing this...
context.startActivity(
Intent().apply {
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
setPackage("com.microsoft.office.outlook")
component = ComponentName("com.microsoft.office.outlook", "com.microsoft.office.outlook.MainActivity")
}
)
Do this...
context.startActivity(
Intent().apply {
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
component = ComponentName(
outlookLaunchIntent?.component?.packageName,
outlookLaunchIntent?.component?.className
)
setPackage(outlookLaunchIntent.package)
}
)
Also, remember that getLaunchIntentForPackage and component can return null, so make sure you check for null values properly
I am relaying a suggestion from a couple of internal folks:
Please try to open the event using one of the following URLs:
ms-outlook://events/open?restid=%s&account=test#om.com (if you have a regular REST id)
ms-outlook://events/open?immutableid=%s&account=test#om.com (if you are using an immutable id)
Since immutable IDs are still in preview stage in Microsoft Graph, and customers should not use preview APIs in their production apps, I think option #1 applies to your case.
Please reply here if the URL works, or not, and if you have other related questions. I requested the couple of folks to keep an eye on this thread as well.
Well, i managed to open the outlook android application with the help of your code #Leo. As im not developping with Kotlin, ill post the JAVA code below :
Intent outlookLaunchIntent = context.getPackageManager().getLaunchIntentForPackage("com.microsoft.office.outlook");
if (outlookLaunchIntent != null) {
context.startActivity(outlookLaunchIntent );
}
Below code to open event/message in a web browser provided by webLink property of the graph API. (I only test for event and the url provided not working. Ill post a new issue on StackOverFlow for that but you already see the issue over there : https://github.com/microsoftgraph/microsoft-graph-docs/issues/4203
try {
Intent webIntent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse(calendarWebLink));
webIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(webIntent);
} catch (RuntimeException e) {
// The url is invalid, maybe missing http://
e.printStackTrace();
}
However im still stuck on the decicive goal of my widget item click which is to open the relative event/email in the Microsoft Outlook Android application.
Microsoft Outlook Android app contains widgets which can achieve what im looking for. So i wonder if it is possible to list its broadcast receivers.
The best thing i found is an old manifest for that app but it doesnt help me.
https://gist.github.com/RyPope/df0e61f477af4b73865cd72bdaa7d8c2
Hi may you try to open the event using one of the url:
ms-outlook://events/open?restid=%s&account=test#om.com (If the
user is having rest id)
ms-outlook://events/open?immutableid=%s&account=test#om.com (If
the user is having immutable id)

OnProvideAssistDataListener example

Can someone please provide an example for a real case where I might need to use OnProvideAssistDataListener. I can't seem to wrap my head around it. I look at the source code, and then I look online. Someone online says
Application.OnProvideAssistDataListener allows to place into the
bundle anything you would like to appear in the
Intent.EXTRA_ASSIST_CONTEXT part of the assist Intent
I have also been reading through the Intent Docs.
There is an Now On Tap functionality implemented by Google. By long pressing the Home Button, you will get some  information displayed on the screen.  The information you get depends on what you're viewing on your screen at that time. (for eg: Music app displays information about music on the screen).
To provide additional information to the assistant, your app provides global application context by registering an app listener using registerOnProvideAssistDataListener() and supplies activity-specific information with activity callbacks by overriding onProvideAssistData() and onProvideAssistContent(). 
Now when the user activates the assistant, onProvideAssistData() is called to build a full ACTION_ASSIST Intent with all of the context of the current application represented as an instance of the AssistStructure. You can override this method to place anything you like into the bundle to appear in the EXTRA_ASSIST_CONTEXT part of the assist intent.
In the example below, a music app provides structured data to describe the music album that the user is currently viewing:
#Override
public void onProvideAssistContent(AssistContent assistContent) {
super.onProvideAssistContent(assistContent);
String structuredJson = new JSONObject()
.put("#type", "MusicRecording")
.put("#id", "https://example.com/music/recording")
.put("name", "Album Title")
.toString();
assistContent.setStructuredData(structuredJson);
}
For more info refer https://developer.android.com/training/articles/assistant.html

Embed zxing barcode scanner to the activity

I'm writing android application and my client requires a barcode scanner in it. They are really specific about it, so the layout they want is like this:
If a qr code found - it jumps to another window automatically. If manual pressed - you are asked to type manually and proceed with the rest of the app.
So basically I could embed zxing code to my app and add it to the activity but I don't want that and would like to have it as a separate app.
What I have at the moment is a separate activity called like this:
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.initiateScan();
I also tried this:
IntentIntegrator intentIntegrator = new IntentIntegrator(this);
Intent i = intentIntegrator.initiateCustomScan();
LocalActivityManager mgr = getLocalActivityManager();
Window w = mgr.startActivity("unique_per_activity_string", i);
View wd = w != null ? w.getDecorView() : null;
if(wd != null) {
scanButton.addView(wd);
}
But then I get java.lang.SecurityException:
03-19 12:22:55.890: E/AndroidRuntime(29394): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.menucard.barcode.scan/com.barcode.scan.ScanActivity}: java.lang.SecurityException: Requesting code from com.google.zxing.client.android (with uid 10139) to be run in process com.menucard.barcode.scan (with uid 10169)
Maybe someone has an idea how to add a separate app into my activity? Or other ways to accomplish this?
You can't embed an external app in another app via Intent unfortunately. The external app here needs to take over the whole screen, and is in landscape mode, for starters.
You should write your own app, but can reuse parts of Barcode Scanner in your app so that it's not entirely from scratch. Just please don't copy the AndroidManifest.xml file. I think it will also be clearly not confused with Barcode Scanner given the different UI. All that remains is to make sure you follow the terms of the Apache License (easy).
#MindaugasSvirskas, your last comment is exactly what I was about to post now:-) I have faced the same problem in the past, in several apps, and believe me, just make use of Intents, that's the way the whole Android system is designed, favouring intercommunication between apps. iOS programmers can easily integrate the scanning Zxing layout in their own layouts, but we are supposed to make use of intents, and I agree.

Categories

Resources