getNotification().extras Bundle object values are null for specific apps - android

I've an android app that parses notifications and displays all (most of) the contents in a custom interface.
I get the required data in all cases, but except for Google Pay (Formerly Tez in India) and Google Photos. In case of these apps, all the bundle values give me null, even checked while debugging. This is weird. Checked in the Notification Log hidden in Android Settings menu and it shows the values are present but at API level, I only get the bitmap and some "caption text". Rest all are NULL.
My code looks like below (null checks omitted)
Bundle extras = sbn.getNotification().extras;
content_title = extras.get(Notification.EXTRA_TITLE).toString();
a = extras.get(Notification.EXTRA_BIG_TEXT).toString();
b= extras.get(Notification.EXTRA_TEXT).toString();
c= extras.get(Notification.EXTRA_SUMMARY_TEXT).toString();
icon = (Bitmap) extras.get(Notification.EXTRA_LARGE_ICON);
lines = (CharSequence[]) extras.get(Notification.EXTRA_TEXT_LINES);
I suspect there is some kind of sensitive data protection stuff going on here. But why then only these two apps? Tez maybe a payments app, but why Google Photos? And what is the documentation and reference to such behavior?

Related

NFC-tag password protection with ST25 android SDK

I'm working with ST25 tags, more specifically type5 tags ST25DV64K. The ST25 SDK for android has some interesting examples and tutorials in it. I'm still struggling to use the code example provided at the end of the doc here concerning password-protected data, which consist in those lines:
byte[] password;
int passwordNumber = type5Tag.getPasswordNumber(area);
tag.presentPassword(passwordNumber, password);
NDEFMsg ndefMsg = myTag.readNdefMessage(area);
first problem, when I instanciate a type5 tag i don't see those methods for Type5Tag class:
import com.st.st25sdk.type5.*;
Type5Tag tag5;
tag5.??
Then, it is not clear how we are supposed to set up a password in the first place. I can't find any examples of setting up a password for a specific area, and removing it, and what is the format of the password that we can use? Is it possible to do this from android or do we have to use the ST25 app? Examples welcome! Thanks.
In the ST25 SDK Zip file, you will find an example of a basic Android App using the ST25 SDK Library (it is in \integration\android\examples\ST25AndroidDemoApp).
This example uses a class called “TagDiscovery” which is able to identify any ST25 Tag and to instantiate the right object. In your case, if you are only using ST25DV64K Tags, you will probably want to do something simple.
Here is what I suggest you:
In your android activity, I expect that you have subscribed to receive a notification every time an NFC tag is taped (in “ST25AndroidDemoApp” example, look at enableForegroundDispatch() in onResume() function).
To identify if the Intent corresponds to an “NFC Intent”, we check if the Intent’s Action is ACTION_NDEF_DISCOVERED, or ACTION_TECH_DISCOVERED or ACTION_TAG_DISCOVERED.
When this is the case, we know that it is an NFC Intent. We can then call this to get the instance of androidTag:
Tag androidTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
This object represents the current NFC tag in Android.
We’re now going to instantiate a ST25DVTag object.
import com.st.st25sdk.type5.st25dv.ST25DVTag;
…
AndroidReaderInterface readerInterface = AndroidReaderInterface.newInstance(androidTag);
byte[] uid = androidTag.getId();
uid = Helper.reverseByteArray(uid);
ST25DVTag myST25DVTag = new ST25DVTag(readerInterface, uid);
You now have an object called myST25DVTag that can be used to communicate with the tag!
For example, if you want to use the passwords:
byte[] password = new byte[]; // TODO: Fill the password
int passwordNumber = myST25DVTag.getPasswordNumber(area);
myST25DVTag.presentPassword(passwordNumber, password);
NDEFMsg ndefMsg = myST25DVTag.readNdefMessage(area);
Before doing that, you need to check which password is associated to this area. The tag has 3 passwords that can be freely assigned to any area. By default no password is set so you should set one. Here is an example where I use the password 2 for Area1:
int AREA1 = 1;
int passwordChosen = 2;
myST25DVTag.setPasswordNumber(AREA1, passwordChosen);
I suggest that you install the ”ST25 NFC Tap” Android App from Google Play: https://play.google.com/store/apps/details?id=com.st.st25nfc&hl=fr&gl=US
If you tap you ST25DV and go to the “Areas Security Status” menu, you will be able to see: the number of areas, which ones are protected by password for read and/or write, which password is used…etc
If you are interested, the source code of this application is available here: https://www.st.com/en/embedded-software/stsw-st25001.html
Tell me if something is unclear.
Disclaimer: I am on of the development team for the ST25 SDK.

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

Baidu maps on Android: access key does not work for location searching

I'm creating an Android app for a chinese client and they need map integration, so Google maps is not an option since all Google services are blocked in China. I'm trying to use Baidu maps, which is called Baidu LBS (location-based services) cloud.
Getting a basic map with no overlays to work was relatively easy. The process is described here (in Chinese, but the code speaks for itself if you don't understand the language). Downloading the latest Baidu Android SDK (v3.2.0 at time of writing) and integrating it into my Eclipse project as a library was no problem, but don't trust the documentation in that link too much even though it is the official one. Their examples often contain code that wouldn't even compile. The name of the .jar file for example was completely different from what you see in their screenshot.
Oh and also their .jar library is obfuscated which is super annoying to work with :-(
I needed to register a Baidu account and go to their control center to generate a key. To create an access key ("ak") for mobile you need to enter the SHA1 fingerprint of the keystore which signs your app, followed by the package name specified in your manifest.
Then I added the generated key to my manifest under the tag
<meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="xxx...xxx" />
I then copied code from their sample project's CloudSearchActivity because I have specific coordinates that I would like to display. I implemented the CloudListener interface as shown:
#Override
public void onGetSearchResult(final CloudSearchResult result, final int error)
{
Log.w("onGetSearchResult", "status=" + result.status + ". size=" + result.size + ". total=" + result.total + ". error=" + error);
if(null != result && null != result.poiList && 0 < result.poiList.size())
{
mBaiduMap.clear();
final BitmapDescriptor bitmapDescriptor=BitmapDescriptorFactory.fromResource(R.drawable.icon_address_grey);
LatLng latitudeLongitude;
LatLngBounds.Builder builder=new Builder();
for(final CloudPoiInfo info : result.poiList)
{
latitudeLongitude=new LatLng(info.latitude, info.longitude);
final OverlayOptions overlayOptions=new MarkerOptions().icon(bitmapDescriptor).position(latitudeLongitude);
mBaiduMap.addOverlay(overlayOptions);
builder.include(latitudeLongitude);
}
final LatLngBounds bounds=builder.build();
MapStatusUpdate mapStatusUpdate=MapStatusUpdateFactory.newLatLngBounds(bounds);
mBaiduMap.animateMapStatus(mapStatusUpdate);
}
}
And I added code to launch a query (also copied from their sample project):
#Override
public View onCreateView(final LayoutInflater layoutInflater, final ViewGroup viewGroup,
final Bundle savedInstanceState)
{
// initialize needs to be called
SDKInitializer.initialize(getApplication());
CloudManager.getInstance().init(MyFragment.this);
view=(ViewGroup)layoutInflater.inflate(R.layout.fragment_map, viewGroup, false);
mMapView=(MapView)view.findViewById(R.id.baiduMapView);
mBaiduMap=mMapView.getMap();
NearbySearchInfo info=new NearbySearchInfo();
info.ak="xxx...xxx";
info.geoTableId=12345;
info.tags="";
info.radius=30000;
info.location="116.403689,39.914957";
CloudManager.getInstance().nearbySearch(info);
return view;
}
Unfortunately I keep getting a status value of 102 from the server (according to this API page that means STATUS_CODE_SECURITY_CODE_ERROR. Now I don't know what to do.
Things that I don't understand:
Why do I need to repeat my access key ("ak") when building the query? Is it not enough to have it in the manifest once?
What is this "geoTableId" value in the query supposed to be?
Any ideas?
After many hours of research I have made some progress on the open questions.
The reason for the "ak" field in a cloud search query is not duplication, it is in fact a different access key. Somewhere in a hidden place Baidu says that access keys "for mobile" will not work for these cloud searches, you need an ak "for server". So the solution is to go back to the Baidu control center and create another key "for server". This key needs to be used in the query, while the "for mobile" key needs to remain in the manifest.
geoTableId is an identifier of your account, not unsimilar to the access keys. It is a (currently) 5 digit number that you need to obtain in the Baidu control center. The other keys were generated in the tab titled "API控制台" (API control desk), but for the geoTableId you need to switch to the tab called "数据管理" (data management). There I think I needed to press the "创建" (~create) button on top left, then enter a name, select "是" (yes) where they ask if this is for release (not sure about that translation) and then click "保存" (save). After this, your freshly generated number is displayed in the top field in parentheses behind the name you chose just now.
These steps have allowed me to send "successful" queries where the server answers with status 0 (STATUS_CODE_SUCCEED). However, so far all the answers I get are empty, I have yet to find a query which produces a non-empty answer. If anyone manages to do that, please let me know!

Tracking Wins and Loses in Google Play Game Services

I am implementing a Turn Base Multiplayer game using Google Play Services. There seems to be a major feature missing from it though: tracking wins and loses! Seems like a pretty important feature for any sort of competitive game.
Am I missing something? Is this already handled by Google in some way?
If not, I am wondering if anyone has implemented their own w/l tracking system, and how they approached it. I am worried about just saving it locally, as it might get out of sync with reality. I suppose I could use Google Cloud Storage, but I also worry that keeping track of which matches have been accounted for could be a little error prone (eg. counting wins/loses on the same match multiple times). Maybe using the Google Play Leaderboard system would be good, as you could compare with friends/etc.
You might want to try using the Events provided by Google Play Game Services. Here's the steps for using it:
Go to the Android Developer Console
On the sidebar click on Game Services, choose your App, then Events
Then create two new Events, one called 'Games Won' and the other 'Games Lost', then click to publish your changes
Click the Get Resources link to see the resource ids of your new events, and add them to your App in the res/values directory
When a game is won or lost, call the following code, where eventRef is your friendly resource name you added in the last step, either "events_games_won" or "events_games_lost":
int eventInt = activity.getApplicationContext().getResources().getIdentifier(eventRef, "string", "com.yourpackage.android");
String eventID = activity.getString(eventInt);
Games.Events.increment(apiClient, eventID, 1);
If you're using basegameutils, your apiClient instance can be obtained with aHelper.getApiClient(), otherwise follow these instructions to set it up.
To read the data you've saved, you can use a callback like this:
PendingResult<Events.LoadEventsResult> results = Games.Events.load(apiClient, true);
results.setResultCallback(new ResultCallback<Events.LoadEventsResult>() {
#Override
public void onResult(Events.LoadEventsResult result)
{
Events.LoadEventsResult r = (Events.LoadEventsResult)result;
EventBuffer eb = r.getEvents();
for (int i=0; i < eb.getCount(); i++) {
Event event = eb.get(i);
// do something, like cache the results for later
YourGameState.eventStats.put(event.getName(), (int)event.getValue());
}
eb.close();
listener.onResult();
}
});
The benefits of this approach over maintaining and managing counts locally are:
the wins and losses are stored in Google Play's cloud, and they'll be stored across all the user's devices
you can use the Events to create Quests (also described on Google Play), such as "Win 10 games this week", or "Go this week undefeated"
Google Play will give you stats on Events for everyone who plays your game, which can be very useful for analysing player activity and performance
And, of course, you can use the same approach to create Events for anything you want.
It depends on your implementation. I've created a chess application and I just use SharedPreferences to keep track of my wins and losses. If you use it properly it won't be error prone.
Eg.
SharedPreferences prefs = this.getSharedPreferences(
"com.example.app", Context.MODE_PRIVATE);
if (gameOver(Side.White)) {
prefs.edit().putLong(dateTimeKey, prefs.getLong(losses, 0) + 1).commit();
} else if (gameOver(Side.Black)){
prefs.edit().putLong(dateTimeKey, prefs.getLong(wins, 0) + 1).commit();
}

Categories

Resources