Capturing stacked Notifications using Notification Listener - android

This question has been asked before but I could not find a working solution to it.
I'm trying to read notifications using NotificationListener Service. Here is the code
import android.app.Notification;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.support.v4.content.LocalBroadcastManager;
import java.io.ByteArrayOutputStream;
public class NotificationService extends NotificationListenerService {
Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
String pack = sbn.getPackageName();
String ticker ="";
if(sbn.getNotification().tickerText !=null) {
ticker = sbn.getNotification().tickerText.toString();
}
Bundle extras = sbn.getNotification().extras;
String title = extras.getString("android.title");
String text = extras.getCharSequence("android.text").toString();
int id1 = extras.getInt(Notification.EXTRA_SMALL_ICON);
Bitmap id = sbn.getNotification().largeIcon;
Log.i("Package",pack);
Log.i("Ticker",ticker);
Log.i("Title",title);
Log.i("Text",text);
Intent msgrcv = new Intent("Msg");
msgrcv.putExtra("package", pack);
msgrcv.putExtra("ticker", ticker);
msgrcv.putExtra("title", title);
msgrcv.putExtra("text", text);
if(id != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
id.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
msgrcv.putExtra("icon",byteArray);
}
LocalBroadcastManager.getInstance(context).sendBroadcast(msgrcv);
}
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.i("Msg","Notification Removed");
}
}
This works well but, when notifications get stacked, it does not capture the full notifications.
John:Hi
John:2 new messages
I have tried using the following but that too does not work.
sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TEXT).toString();

The following method tries to get the extra text lines, if there are none it tries to fall back to the extra big text if it’s available.
CharSequence[] lines =
extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
if(lines != null && lines.length > 0) {
StringBuilder sb = new StringBuilder();
for (CharSequence msg : lines)
if (!TextUtils.isEmpty(msg)) {
sb.append(msg.toString());
sb.append('\n');
}
return sb.toString().trim();
}
CharSequence chars =
extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
if(!TextUtils.isEmpty(chars))
return chars.toString();

Related

Phone manager/Battery optimizer app kills foreground service

-I am developing an application that records user location in a .csv file and uploads them to firebase storage and I've written a service to accomplish this. The service needs to run indefinitely until the user stops it (by unchecking a switch from the activity that launched the service). The service runs in foreground and after running for 15-20 minutes, the phone manager app gives "resource intensive app found" notification and kills the service. onDestroy() never gets called!. I return START_STICKY which is supposed to recreate the service and call onStartCommand() again but the service never gets created again!
I've seen some people write workarounds for this problem but they target specific android versions. My app targets android Pie and I haven't seen any "generic" solution to this problem yet. All solutions are either device/brand specific or API level specific. For now I'm testing the app on Huawei Y7 prime 2018 (Android O) and on my device, i got around this by manually disallowing the phone manager app to manage my application, this way the service does run indefinitely until the user kills it from the app. The nature of the application is such that i need minimum user engagement and i don't expect the users to go through the hassle of manually disabling my app from battery optimizer/phone manager app. My LocationService.java class is attached, if somebody could please provide either a better implementation of this service or some generic method to keep it alive, that'd be great. Even some hack to make sure that onDestroy() gets called before the system kills the application would work!
package com.example.bumps;
import android.Manifest;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class LocationService extends Service implements LocationListener {
private static final String CHANNEL_ID = "LocationServiceChannel";
private static final int ONGOING_NOTIFICATION_ID = 1;
public static final String PREFS_TAG = "PendingUploadPrefs";
private LocationManager locationManager;
DecimalFormat numberFormat = new DecimalFormat("#.000");
DecimalFormat coordinatesFormat = new DecimalFormat("#.00000");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
SimpleDateFormat sdfForDir = new SimpleDateFormat("dd-MM-yyyy_hh:mm:ss");
File locationDirectory;
String currentDataDirectory;
FirebaseStorage filesStorage;
String userUUID;
List<ResumeFileUpload> pendingFileUploads;
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor sharedPrefEditor;
private SOTWFormatter sotwFormatter;
#Override
public void onCreate() {
super.onCreate();
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
filesStorage = FirebaseStorage.getInstance();
pendingFileUploads = new ArrayList<>();
sharedPreferences = this.getSharedPreferences(PREFS_TAG, Context.MODE_PRIVATE);
sharedPrefEditor = sharedPreferences.edit();
sotwFormatter = new SOTWFormatter(this);
String timeStamp = sdfForDir.format(new Date());
currentDataDirectory = "RBD_" + timeStamp;
locationDirectory = new File(getExternalFilesDir(null), currentDataDirectory);
if (!locationDirectory.exists())
locationDirectory.mkdir();
}
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//userUUID = intent.getStringExtra("userUUID");
userUUID = sharedPreferences.getString("userUUID", "UNKNOWN");
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
0, 0, this);
}
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);
createNotificationChannel();
Notification notification =
new Notification.Builder(this, CHANNEL_ID)
.setContentTitle(getText(R.string.notification_title))
.setContentText(getText(R.string.notification_message))
.setSmallIcon(R.drawable.bump)
.setContentIntent(pendingIntent)
.setTicker(getText(R.string.ticker_text))
.build();
startForeground(ONGOING_NOTIFICATION_ID, notification);
return START_STICKY;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Location Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
try {
manager.createNotificationChannel(serviceChannel);
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationManager.removeUpdates(this);
}
File newFileLoc = writeLocationDataToFile(locationStringBuilder);
pendingFileUploads.add(new ResumeFileUpload(currentDataDirectory,
newFileLoc.getPath()));
uploadToStorage(newFileLoc);
/*if there are no pending files from some previous session, just save the ones
* that just got created*/
if (getPendingFileUploads() == null){
setList("PendingUploads", pendingFileUploads);
}else{
/*if there are pending uploads from some previous session, fetch them,
* update the list of pending uploads with and save for later upload*/
List<ResumeFileUpload> pUploads = getPendingFileUploads();
for (ResumeFileUpload resumeFileUpload: pendingFileUploads){
pUploads.add(resumeFileUpload);
}
setList("PendingUploads", pUploads);
}
}
private List<ResumeFileUpload> getPendingFileUploads(){
List<ResumeFileUpload> pendingUploads = null;
String serializedObject = sharedPreferences.getString("PendingUploads", null);
if (serializedObject != null){
Gson gson = new Gson();
Type type = new TypeToken<List<ResumeFileUpload>>(){}.getType();
pendingUploads = gson.fromJson(serializedObject, type);
}
return pendingUploads;
}
private boolean deleteFile(File file){
try {
Uri uri = Uri.fromFile(file);
File fdelete = new File(uri.getPath());
if (fdelete.exists()) {
if (fdelete.delete()) {
System.out.println("file Deleted :" + uri.getPath());
return true;
} else {
System.out.println("file not Deleted :" + uri.getPath());
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private File writeLocationDataToFile(StringBuilder data) {
String fileName = "Location.csv";
String colNamesLoc = "Timestamp,Latitude,Longitude,Accuracy,DirectionOfMotion,Speed\n";
File newFile = new File(locationDirectory, fileName);
try {
FileWriter fileWriter = new FileWriter(newFile);
fileWriter.append(colNamesLoc);
fileWriter.append(data.toString());
fileWriter.flush();
fileWriter.close();
Toast.makeText(this, "Saved To " + getExternalFilesDir(null)
+ "/" + fileName, Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
}
return newFile;
}
private void uploadToStorage(final File newFile){
Uri uri = Uri.fromFile(new File(newFile.getPath()));
StorageReference storageReference = filesStorage.getReference()
.child(userUUID + "/" + currentDataDirectory + "/" +
uri.getLastPathSegment());
UploadTask uploadTask = storageReference.putFile(uri);
uploadTask.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
Toast.makeText(LocationService.this,
newFile.getName() + " Upload Failed!", Toast.LENGTH_SHORT).show();
}
}).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
deleteFile(newFile);
Toast.makeText(LocationService.this,
newFile.getName() + " Upload Successful!", Toast.LENGTH_SHORT).show();
}
});
}
/*Location Listener*/
#Override
public void onLocationChanged(Location location) {
if (location != null) {
String lat = coordinatesFormat.format(location.getLatitude());
String lng = coordinatesFormat.format(location.getLongitude());
String bearing = "0.0";
if (location.getBearing() != 0.0)
bearing = String.valueOf(sotwFormatter.format(location.getBearing()));
int speed = (int) location.getSpeed();
String currentSpeed = String.valueOf(speed * 3.6f) + " KM/h";
String accuracy = "0.0";
if (location.getAccuracy() != 0.0)
accuracy = coordinatesFormat.format(location.getAccuracy());
String timeStamp = simpleDateFormat.format(new Date());
String locationData = timeStamp + ", " + lat +
", " + lng + ", " + accuracy + ", " + bearing + ", "
+ currentSpeed;
locationStringBuilder.append(locationData);
locationStringBuilder.append("\n");
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
/*SharedPreferences code*/
public <T> void setList(String key, List<T> list) {
Gson gson = new Gson();
String json = gson.toJson(list);
set(key, json);
}
public void set(String key, String value) {
sharedPrefEditor.putString(key, value);
sharedPrefEditor.commit();
}
}

Getting custom actions from notification

I use a service that automatically sends me push notifications and I want to automatically press the confirm option of these notifications. I'm able to capture the notification using the code below:
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.support.v4.content.LocalBroadcastManager;
import java.io.ByteArrayOutputStream;
public class NotificationHandler extends NotificationListenerService {
Context context;
#Override
public void onCreate() {
Log.e("STATE","HERE AGAIN");
super.onCreate();
context = getApplicationContext();
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
String pack = sbn.getPackageName();
String ticker = "";
if (sbn.getNotification().tickerText != null) {
ticker = sbn.getNotification().tickerText.toString();
}
Bundle extras = sbn.getNotification().extras;
// try {
// //sbn.getNotification().contentIntent.send();
// } catch (PendingIntent.CanceledException e) {
// e.printStackTrace();
// }
String title = extras.getString("android.title");
String text = extras.getCharSequence("android.text").toString();
int id1 = extras.getInt(Notification.EXTRA_SMALL_ICON);
Bitmap id = sbn.getNotification().largeIcon;
Log.i("NEW", "----------");
Log.i("Package", pack);
Log.i("Ticker", ticker);
Log.i("Title", title);
Log.i("Text", text);
sbn.getNotification().contentIntent.sen
for (String key : bundle.keySet()) {
Log.i("Intent", key);
}
Log.i("NEW", "----------");
Intent msgrcv = new Intent("Msg");
msgrcv.putExtra("package", pack);
msgrcv.putExtra("ticker", ticker);
msgrcv.putExtra("title", title);
msgrcv.putExtra("text", text);
if (id != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
id.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
msgrcv.putExtra("icon", byteArray);
}
LocalBroadcastManager.getInstance(context).sendBroadcast(msgrcv);
}
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.i("Msg", "Notification Removed");
}
}
I'm having trouble figuring out how to capture the custom option for confirming the notification and then clicking it. The confirm/deny buttons look similar to the buttons on this notification. Any ideas how I can capture this?
You can get the list of actions (if it exists) from the notification by using the following code
Notification n = sbn.getNotification();
for(Action action : n.actions){
String title = action.title.toString;
...
}
You can create a button with the action-title and then write the code onClick() to execute the action corresponding to the notifications.
To be more precise - you can call
action.actionIntent.send(); // or another variation of send()
from your onClick() on the button to execute the action corresponding to the pendingIntent supplied by the app that sent the notification.

How to read whatsapp message from notification extras

Hi I'm working on an app in which I have to read images from whatsapp. I can read the first image as a bitmap with this line of code:
Bitmap btm = (Bitmap) sbn.getNotification().extras.get(Notification.EXTRA_PICTURE);
The problem is that if I receive more than one images I get null. How can I resolve this?
this is the NotificationListenerService:
package com.etaure.callany.testwhatsappimage;
import android.accounts.AccountManager;
import android.app.Notification;
import android.content.Intent;
import android.graphics.Bitmap;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
/**
* Created by administrator on 03/11/2015.
*/
public class MyNotificationListner extends NotificationListenerService {
static int i = 0;
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
if (sbn.getPackageName().equals("com.whatsapp")) {
Bitmap btm = (Bitmap) sbn.getNotification().extras.get(Notification.EXTRA_PICTURE);
}
}
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.w("Test", "NotificationRemoved");
}
}
this is a code that parses for you whatsapp images and shows ( if you want ) on your logcat all base64 string! Enjoy
`
if (extras.containsKey(Notification.EXTRA_PICTURE)) {
bmp = (Bitmap) extras.get(Notification.EXTRA_PICTURE);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
byteArrayS = byteArrayOutputStream .toByteArray();
encoded = Base64.encodeToString(byteArrayS, Base64.DEFAULT);
final int LOGCAT_MAX_LENGTH = 3950;
if (BuildConfig.DEBUG) {
while (encoded.length() > LOGCAT_MAX_LENGTH) {
int substringIndex = encoded.lastIndexOf(",", LOGCAT_MAX_LENGTH);
if (substringIndex == -1)
substringIndex = LOGCAT_MAX_LENGTH;
Log.d("encode", encoded.substring(0, substringIndex));
encoded = encoded.substring(substringIndex).trim();
}
Log.d("encode", encoded);
}
}
`

Show notification from background started service

So i'm using asmack library to listen for incoming xmpp packets. I've got the service implemented. It's started, bound, a method is run and then it's unbound. The service creates a PacketListener to get incoming messages, and displays a notification when a new message comes in. This all works while the app is the active activity, but doesn't work when it's fully closed (swiped away in the window list) and it's just the service running, the PacketListener doesn't fire. It fires as soon as the app is reopened though, firing off all notifications. I'm looking to get those notifications fired as they come in, instead of the service being suspended, if you will. The code for the service is here:
package com.jayseeofficial.xmpp.service;
import java.io.File;
import java.util.ArrayList;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Type;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import android.widget.Toast;
import com.jayseeofficial.xmpp.MainActivity;
import com.jayseeofficial.xmpp.Program;
import com.jayseeofficial.xmpp.R;
import com.jayseeofficial.xmpp.objects.Contact;
import com.jayseeofficial.xmpp.preferences.Preferences;
public class XMPPService extends Service {
private Connection conn = null;
private boolean loggedIn = false;
private final XMPPBinder binder = new XMPPBinder();
private Handler handler;
#Override
public IBinder onBind(Intent arg0) {
handler = new Handler(getMainLooper());
return binder;
}
public class XMPPBinder extends Binder {
public XMPPService getService() {
return XMPPService.this;
}
}
public void logIn() {
new LogInTask().execute();
}
public boolean isLoggedIn() {
return loggedIn;
}
private ConnectionConfiguration getConnectionConfiguration() {
ConnectionConfiguration config = new ConnectionConfiguration(
Preferences.Account.getServer(), Preferences.Account.getServerPort());
config.setSASLAuthenticationEnabled(true);
config.setCompressionEnabled(true);
config.setSecurityMode(SecurityMode.enabled);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
config.setTruststoreType("AndroidCAStore");
config.setKeystoreType("AndroidCAStore");
config.setTruststorePassword(null);
config.setTruststorePath(null);
} else {
config.setTruststoreType("BKS");
String path = System.getProperty("javax.net.ssl.trustStore");
if (path == null) {
path = System.getProperty("java.home") + File.separator + "etc" + File.separator
+ "security" + File.separator + "cacerts.bks";
}
config.setTruststorePath(path);
}
return config;
}
private void showNotification() {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setContentTitle("XMPP+").setContentText("running")
.setSmallIcon(R.drawable.speech_bubble);
Intent i = new Intent(this, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(i);
// Assign the intent
mBuilder.setContentIntent(stackBuilder.getPendingIntent(9127,
PendingIntent.FLAG_UPDATE_CURRENT));
NotificationManager mgr = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
Notification n = mBuilder.build();
n.flags |= Notification.FLAG_ONGOING_EVENT;
mgr.notify(1,n);
}
private void hideNotification() {
// TODO Implement hideNotification in XMPPService
}
private class LogInTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
showNotification();
// Set up and log in
conn = new XMPPConnection(getConnectionConfiguration());
conn.connect();
conn.login(Preferences.Account.getUsername(), Preferences.Account.getPassword());
// Set up to receive messages
PacketFilter filter = new PacketTypeFilter(Message.class);
conn.addPacketListener(new XMPPMessagePacketListener(), filter);
conn.sendPacket(new Presence(Type.available));
final ArrayList<Contact> allContacts = new ArrayList<Contact>();
// Populate contact list
for (RosterEntry re : conn.getRoster().getEntries()) {
Log.d("Roster entry: ", re.getUser() + ": " + re.getName());
Contact c = new Contact();
if (re.getName() == null) {
c.setName(re.getUser());
} else {
c.setName(re.getName());
}
c.setAddress(re.getUser());
allContacts.add(c);
}
handler.post(new Runnable() {
#Override
public void run() {
Program.contacts.addAll(allContacts);
}
});
loggedIn = true;
} catch (XMPPException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void ignored) {
if (loggedIn) {
Toast.makeText(XMPPService.this, "Logged in successfully", Toast.LENGTH_LONG)
.show();
} else {
Toast.makeText(XMPPService.this, "Failed to log in", Toast.LENGTH_LONG).show();
}
}
}
private class XMPPMessagePacketListener implements PacketListener {
#Override
public void processPacket(Packet packet) {
Message m = (Message) packet;
if (m.getBody() != null) {
com.jayseeofficial.xmpp.objects.Message message = new com.jayseeofficial.xmpp.objects.Message();
message.setRecipient(Preferences.Account.getUsername() + "#"
+ Preferences.Account.getServer());
String fullSender = m.getFrom();
String address = fullSender.substring(0, fullSender.indexOf('/'));
message.setSender(address);
message.setContents(m.getBody());
Program.showMessageNotification(message, 9127);
}
}
}
}
and the code for the starting/binding the service is here:
public static void init() {
if (!initialized) {
// Call other portions of init code first
SmackAndroid.init(context);
Preferences.init();
// Set up our variables
contacts = GlazedLists.threadSafeList(new BasicEventList<Contact>());
// Start up the xmpp connection
Intent ixmpp = new Intent(context, XMPPService.class);
context.startService(ixmpp);
context.bindService(ixmpp, xmppConnection, Context.BIND_AUTO_CREATE);
initialized = true;
}
}
So as I said, i want to receive notifications from the service running in the background when the foreground activity is closed. Is there any way of doing this? Maybe have it in it's own process? Something else?
EDIT: the ongoing notification sticks around and works when clicked, in case you're wondering
use startforeground method
http://developer.android.com/reference/android/app/Service.html#startForeground%28int,%20android.app.Notification%29
in onCreate of service
public void onCreate() {
startForeground (int id, Notification notification)
}

connection to a NFC-tag in an android app

I'm trying to write an android app for may Nexus S that is able to write a simple Text Record on a NFC-Tag. My problem is the connection-establishment to the NFC-Tag.
I've implemented two activities called "TagWriter" & "TagWriterStartPage".
Here ist the "TagWriter"-activity:
package nfc.example.writer;
import java.util.Locale;
import com.google.common.base.Charsets;
import com.google.common.primitives.Bytes;
import android.app.Activity;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class TagWriter extends Activity
{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.tagwriter);
Intent intent = this.getIntent();
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
writeTag(tag);
}
private void writeTag(Tag t)
{
Ndef tag = Ndef.get(t);
Locale locale = Locale.US;
final byte[] langBytes = locale.getLanguage().getBytes(Charsets.US_ASCII);
String text = "Tag, you're it!";
final byte[] textBytes = text.getBytes(Charsets.UTF_8);
final int utfBit = 0;
final char status = (char) (utfBit + langBytes.length);
final byte[] data = Bytes.concat(new byte[] {(byte) status}, langBytes, textBytes);
NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
try
{
NdefRecord[] records = {record};
NdefMessage message = new NdefMessage(records);
tag.connect();
boolean connected = tag.isConnected();
boolean writeable = tag.isWritable();
if( connected && writeable)
{
tag.writeNdefMessage(message);
}
tag.close();
}
catch (Exception e)
{
//do error handling
}
}
}
The activity "TagWriter" is called when I put my device on the NFC-tag. Everytime the method "tag.connect()" is called, there occures an exception.
Can anyone help me to solve the problem???
Thanks,
Dennis
this might work better:
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Ndef ndef = Ndef.get(tag);
if (ndef != null) {
ndef.connect();
ndef.writeNdefMessage(message);
} else {
NdefFormatable format = NdefFormatable.get(tag);
if (format != null) {
format.connect();
format.format(message);
}
}
need other checks in there for real use (capacity, writable etc) - but that is bare bones..

Categories

Resources