I have one project as a library of another one because I need whitelabeling it (and more projects for other partners). In the library project, I have implemented push notifications system. I've executed the library project as a normal project and the pushs work like a charm. My problem happens when i am importing this project as a library in another project. The receiver is never called.
There is one post with the same issue but the solution is not working for me. I am getting crazy!!!
Android C2DM and lib project
The main problem is that I am not receiving the message from C2DM. My code is the next:
Manifests of the both project (I have the same piece of code in both projects) (I am using the tags lib_project and app_project to be clear):
<receiver
android:name="com.lib_project.C2DMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.app_project.android" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.app_project.android" />
</intent-filter>
</receiver>
And the permissions:
<uses-permission android:name="com.app_project.android.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
android:name="com.app_project.android.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
And also I have declared in the manifest the service that manage the push notifications but it is called properly.
<service android:name="com.lib_project.android.C2DMReceiver" />
Really, I don't know what is wrong. I think is ok but is not working. Thanks in advance.
It is my class C2DMBaseReceiver :
public abstract class C2DMBaseReceiver extends IntentService {
private static final String C2DM_RETRY = "com.google.android.c2dm.intent.RETRY";
public static final String REGISTRATION_CALLBACK_INTENT = "com.google.android.c2dm.intent.REGISTRATION";
private static final String C2DM_INTENT = "com.google.android.c2dm.intent.RECEIVE";
// Logging tag
private static final String TAG = "C2DM";
// Extras in the registration callback intents.
public static final String EXTRA_UNREGISTERED = "unregistered";
public static final String EXTRA_ERROR = "error";
public static final String EXTRA_REGISTRATION_ID = "registration_id";
public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";
// wakelock
private static final String WAKELOCK_KEY = "C2DM_LIB";
private static PowerManager.WakeLock mWakeLock;
private final String senderId;
/**
* The C2DMReceiver class must create a no-arg constructor and pass the
* sender id to be used for registration.
*/
public C2DMBaseReceiver(String senderId) {
// senderId is used as base name for threads, etc.
super(senderId);
this.senderId = senderId;
}
/**
* Called when a cloud message has been received.
*/
protected abstract void onMessage(Context context, Intent intent);
/**
* Called on registration error. Override to provide better
* error messages.
*
* This is called in the context of a Service - no dialog or UI.
*/
public abstract void onError(Context context, String errorId);
/**
* Called when a registration token has been received.
*/
public void onRegistered(Context context, String registrationId) throws IOException {
// registrationId will also be saved
}
/**
* Called when the device has been unregistered.
*/
public void onUnregistered(Context context) {
}
#Override
public final void onHandleIntent(Intent intent) {
try {
Context context = getApplicationContext();
if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
handleRegistration(context, intent);
} else if (intent.getAction().equals(C2DM_INTENT)) {
onMessage(context, intent);
} else if (intent.getAction().equals(C2DM_RETRY)) {
C2DMessaging.register(context, senderId);
}
} finally {
// Release the power lock, so phone can get back to sleep.
// The lock is reference counted by default, so multiple
// messages are ok.
// If the onMessage() needs to spawn a thread or do something else,
// it should use it's own lock.
mWakeLock.release();
}
}
/**
* Called from the broadcast receiver.
* Will process the received intent, call handleMessage(), registered(), etc.
* in background threads, with a wake lock, while keeping the service
* alive.
*/
static void runIntentInService(Context context, Intent intent) {
if (mWakeLock == null) {
// This is called from BroadcastReceiver, there is no init.
PowerManager pm =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
WAKELOCK_KEY);
}
mWakeLock.acquire();
// Use a naming convention, similar with how permissions and intents are
// used. Alternatives are introspection or an ugly use of statics.
String receiver = context.getPackageName() + ".C2DMReceiver";
intent.setClassName(context, receiver);
context.startService(intent);
}
private void handleRegistration(final Context context, Intent intent) {
final String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID);
String error = intent.getStringExtra(EXTRA_ERROR);
String removed = intent.getStringExtra(EXTRA_UNREGISTERED);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "dmControl: registrationId = " + registrationId +
", error = " + error + ", removed = " + removed);
}
if (removed != null) {
// Remember we are unregistered
C2DMessaging.clearRegistrationId(context);
onUnregistered(context);
return;
} else if (error != null) {
// we are not registered, can try again
C2DMessaging.clearRegistrationId(context);
// Registration failed
Log.e(TAG, "Registration error " + error);
onError(context, error);
if ("SERVICE_NOT_AVAILABLE".equals(error)) {
long backoffTimeMs = C2DMessaging.getBackoff(context);
Log.d(TAG, "Scheduling registration retry, backoff = " + backoffTimeMs);
Intent retryIntent = new Intent(C2DM_RETRY);
PendingIntent retryPIntent = PendingIntent.getBroadcast(context,
0 /*requestCode*/, retryIntent, 0 /*flags*/);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.ELAPSED_REALTIME,
backoffTimeMs, retryPIntent);
// Next retry should wait longer.
backoffTimeMs *= 2;
C2DMessaging.setBackoff(context, backoffTimeMs);
}
} else {
try {
onRegistered(context, registrationId);
C2DMessaging.setRegistrationId(context, registrationId);
} catch (IOException ex) {
Log.e(TAG, "Registration error " + ex.getMessage());
}
}
}
}
Ant this one is my C2DMReceiver:
public class C2DMReceiver extends C2DMBaseReceiver {
public static final String EXTRA_DATETIME = "datetime";
public static final String EXTRA_CAM_ID = "cam_id";
public static final String EXTRA_TYPE = "type";
public static final String GCM_PROJECT_ID = "58312821729";
public static void getC2DMRegistration(Context context){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO){
String id = C2DMessaging.getRegistrationId(context);
if(id.equals(""))
C2DMessaging.register(context, C2DMReceiver.GCM_PROJECT_ID);
else
C2DMReceiver.registerPushDevice(context, id);
Log.d("restored id: " + id);
}
}
public static String getDeviceID(Context context){
String out = null;
try {
TelephonyManager telephonyManager = (TelephonyManager)context.getSystemService(TELEPHONY_SERVICE);
out = telephonyManager.getDeviceId();
} catch (Exception e) {
Log.w("Error getting device uid", e);
}
return out;
}
public static void registerPushDevice(Context context, String registrationId){
try {
CameraManager app = null;
if(context instanceof Activity)
{
app = (CameraManager)(((Activity)context).getApplication());
}
else if(context instanceof Service)
{
app = (CameraManager)(((Service)context).getApplication());
}
else if(context instanceof Application)
{
app = (CameraManager)context;
}
if(app != null && app.isLoggedIn())
{
HashMap<String, String> keyValues = new HashMap<String, String>(app.getUserSessionKeys());
keyValues.put("imei", getDeviceID(context));
keyValues.put("registration_id", registrationId);
keyValues.put("application_id", context.getString(R.string.application_id));
keyValues.put("gcm", "true");
new ServerCall(context, Script.MOBILE, Method.ADD_REGISTRATION_ID, keyValues, null)
.execute();
}
} catch (Exception e) {
Log.e("Failed to register C2DM", e);
}
}
public C2DMReceiver() {
super(GCM_PROJECT_ID);
}
#Override
public void onRegistered(Context context, String registrationId) {
Log.i("onRegistered: " + registrationId);
registerPushDevice(context, registrationId);
}
#Override
public void onUnregistered(Context context) {
Log.i("onUnregistered");
}
#Override
public void onError(Context context, String errorId) {
Log.w("onError: " + errorId);
}
#SuppressWarnings("unchecked")
#Override
protected void onMessage(Context context, Intent receiveIntent){
Bundle extras = receiveIntent.getExtras();
CameraManager app = null;
if(context instanceof Activity)
{
app = (CameraManager)(((Activity)context).getApplication());
}
else if(context instanceof Service)
{
app = (CameraManager)(((Service)context).getApplication());
}
else if(context instanceof Application)
{
app = (CameraManager)context;
}
boolean activateNotificationsphone = app.getUserStorage().getBoolean(Constants.PUSH_NOTIFI_ACTIVATE_FROM_PHONE, true);
if(extras != null && activateNotificationsphone)
{
Log.e(""+extras.keySet());
Iterator<String> i = extras.keySet().iterator();
while(i.hasNext())
{
String key = i.next();
if(key.equalsIgnoreCase(Constants.EXTRA_ALARM_MOTION) || key.equalsIgnoreCase(Constants.EXTRA_CAMERA_DOWN) || key.equalsIgnoreCase(Constants.EXTRA_LSU_DOWN))
{
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.ic_launcher, context.getString(R.string.app_name), System.currentTimeMillis());
Intent notificationIntent = new Intent(context, FragmentTabs.class);
String type = key.toUpperCase();
String value = receiveIntent.getStringExtra(key);
String collapse_key = receiveIntent.getStringExtra("collapse_key");
String message = "";
String[] pair = value.split("[:]");
if(pair.length == 2)
{
notificationIntent
.putExtra(EXTRA_TYPE, type)
.putExtra(EXTRA_CAM_ID, pair[0])
.putExtra(EXTRA_DATETIME, pair[1])
.setAction(collapse_key);
Log.e("Type c2dm:"+type);
Log.e("Cam ID c2dm: " + pair[0]);
Log.e("DateTime c2dm: " + pair[1]);
ArrayList<CamerasFeedItem> cameras = null;
XMLObject settings = null;
ArrayList<EventItem> listEvents = null;
User user = null;
try
{
user = (User)Utils.deserializeObject(new File(getFilesDir(), CameraManager.USER_OBJ_FILE));
cameras = (ArrayList<CamerasFeedItem>)Utils.deserializeObject(new File(getFilesDir(), user.getUserId() + "_" + CameraManager.CAMERAS_OBJ_FILE));
settings = (XMLObject)Utils.deserializeObject(new File(getFilesDir(), user.getUserId() + "_" + CameraManager.SETTINGS_OBJ_FILE));
//List of events:
if(user!=null)
{
listEvents = (ArrayList<EventItem>)Utils.deserializeObject(new File(getFilesDir(), user.getUserId() + "_" + CameraManager.LIST_EVENTS_OBJ_FILE));
}
}
catch (Exception e)
{ }
CamerasFeedItem item = null;
if(settings == null || cameras == null || (item = isItemExists(cameras, pair[0])) == null)
{
return;
}
if(type.equals(Constants.EXTRA_ALARM_MOTION))
{
if(settings.getValue("motion", "no").equals("no"))
{
return;
}
GregorianCalendar curTime = new GregorianCalendar();
long offset = curTime.get(Calendar.ZONE_OFFSET) + curTime.get(Calendar.DST_OFFSET);
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
c.setTimeInMillis(Long.parseLong(pair[1]) + offset);
String when = DateFormat.format("dd-MM-yyyy kk:mm:ss", c).toString();
message = context.getString(R.string.push_motion_on_camera, item.getName(), when);
}
else if(type.equals(Constants.EXTRA_CAMERA_DOWN))
{
if(settings.getValue("cameraDown", "no").equals("no"))
{
return;
}
message = context.getString(R.string.push_camera_is_down, item.getName(), getDownString(pair[1]));
//typeIndex = 1;
}
else if(type.equals(Constants.EXTRA_LSU_DOWN))
{
//typeIndex = 3;
message = "";
}
}
if(!message.equals(""))
{
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_CLEAR_TOP);
notification.defaults |= Notification.DEFAULT_SOUND;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification);
contentView.setTextViewText(R.id.title, context.getString(R.string.app_name));
contentView.setTextViewText(R.id.text, message);
notification.contentView = contentView;
notification.contentIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), notificationIntent, 0);
mNotificationManager.notify(collapse_key, (int)Math.random(), notification);
}
return;
}
}
}
}
private CamerasFeedItem isItemExists(ArrayList<CamerasFeedItem> cameras, String id){
for(CamerasFeedItem item: cameras)
{
if(item.getID().equals(id))
{
return item;
}
if(item.isFolderItem())
{
LSUItem lsu = ((FolderItem)item).getLsuItem();
if(lsu != null && lsu.getID().equals(id))
{
return lsu;
}
CamerasFeedItem result = isItemExists(CamerasFeedItem.parse(item), id);
if(result != null)
{
return result;
}
}
}
return null;
}
private String getDownString(String hours){
StringBuilder out = new StringBuilder();
int total = Integer.parseInt(hours);
int m = total / 720;
total = total % 720;
int w = total / 168;
total = total % 168;
int d = total / 24;
total = total % 24;
if(m > 0)
{
out.append(getResources().getQuantityString(R.plurals.push_month, m, m));
out.append(" ");
}
if(w > 0)
{
out.append(getResources().getQuantityString(R.plurals.push_weeks, w, w));
out.append(" ");
}
if(d > 0)
{
out.append(getResources().getQuantityString(R.plurals.push_days, d, d));
out.append(" ");
}
if(total > 0)
{
out.append(getResources().getQuantityString(R.plurals.push_hours, total, total));
out.append(" ");
}
return out.toString().trim();
}
C2DMBroadcastReceiver.java
public class C2DMBroadcastReceiver extends BroadcastReceiver {
#Override
public final void onReceive(Context context, Intent intent) {
// To keep things in one place.
C2DMBaseReceiver.runIntentInService(context, intent);
setResult(Activity.RESULT_OK, null /* data */, null /* extra */);
}
}
And the last one: C2DMessaging:
public class C2DMessaging {
public static final String EXTRA_SENDER = "sender";
public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
public static final String BACKOFF = "backoff";
public static final String GSF_PACKAGE = "com.google.android.gsf";
// package
static final String PREFERENCE = "com.google.android.c2dm";
private static final long DEFAULT_BACKOFF = 30000;
/**
* Initiate c2d messaging registration for the current application
*/
public static void register(Context context, String senderId) {
try {
Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
registrationIntent.setPackage(GSF_PACKAGE);
registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context, 0, new Intent(), 0));
registrationIntent.putExtra(EXTRA_SENDER, senderId);
context.startService(registrationIntent);
} catch (Exception e) {
Log.w("Couldn't use C2DM, check OS version", e);
}
}
/**
* Unregister the application. New messages will be blocked by server.
*/
public static void unregister(Context context) {
Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
regIntent.setPackage(GSF_PACKAGE);
regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context, 0, new Intent(), 0));
context.startService(regIntent);
}
/**
* Return the current registration id.
*
* If result is empty, the registration has failed.
*
* #return registration id, or empty string if the registration is not complete.
*/
public static String getRegistrationId(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
String registrationId = prefs.getString("dm_registration", "");
return registrationId;
}
public static long getLastRegistrationChange(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
}
static long getBackoff(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
}
static void setBackoff(Context context, long backoff) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putLong(BACKOFF, backoff);
editor.commit();
}
// package
static void clearRegistrationId(Context context) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString("dm_registration", "");
editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
editor.commit();
}
// package
static void setRegistrationId(Context context, String registrationId) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFERENCE,
Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString("dm_registration", registrationId);
editor.commit();
}
}
Your problem is in this method :
static void runIntentInService(Context context, Intent intent) {
if (mWakeLock == null) {
// This is called from BroadcastReceiver, there is no init.
PowerManager pm =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
WAKELOCK_KEY);
}
mWakeLock.acquire();
// Use a naming convention, similar with how permissions and intents are
// used. Alternatives are introspection or an ugly use of statics.
String receiver = context.getPackageName() + ".C2DMReceiver";
intent.setClassName(context, receiver);
context.startService(intent);
}
In the following line context.getPackageName() returns the package of your app (com.app_project.android).
String receiver = context.getPackageName() + ".C2DMReceiver";
However, .C2DMReceiver is located in your library project (com.lib_project.android), and that's why this class is not found when you try to use your library project from your app.
The way to fix it is to refer to the C2DMReceiver class explicitly :
String receiver = C2DMReceiver.class.getName ();
Related
I'm trying to update the progress of a notification during ftp upload handled by a background service, I tried directly from the service and after using a ResultReceiver but when the upload starts, the notification menu is freezed until the upload is done.How can I do this?
FtpService
public class FtpService extends IntentService {
public static final int UPDATE_PROGRESS = 10;
// IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
private static final String ACTION_SEND = "SEND";
private static final String EXTRA_PATH = "PATH";
private static final String PREFERENCES = "virtualtv";
private SharedPreferences mPrefs;
private NotificationManager mNotifyManager;
private NotificationCompat.Builder mBuilder;
/********* work only for Dedicated IP ***********/
private String FTP_HOST= "";
/********* FTP USERNAME ***********/
private String FTP_USER;
/********* FTP PASSWORD ***********/
private String FTP_PASS;
private String FTP_PORT;
private String PATH;
private Context mContext;
public FTPClient ftpClient;
private ResultReceiver mUploadReceiver;
public FtpService() {
super("FtpService");
}
/**
* Starts this service to perform action Foo with the given parameters. If
* the service is already performing a task this action will be queued.
*
* #see IntentService
*/
// TODO: Customize helper method
public static void startActionSend(Context context, String param1) {
Intent intent = new Intent(context, FtpService.class);
intent.setAction(ACTION_SEND);
intent.putExtra(EXTRA_PATH, param1);
intent.putExtra("UPLOAD_RECEIVER", new UploadReceiver(new Handler(), context));
context.startService(intent);
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
mContext = this;
final String action = intent.getAction();
mUploadReceiver = intent.getParcelableExtra("UPLOAD_RECEIVER");
if (ACTION_SEND.equals(action)) {
final String filePath = intent.getStringExtra(EXTRA_PATH);
mPrefs = mContext.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
JsonObject config = Account.getInstance(mContext).getConfig();
if(mPrefs != null && config != null){
FTP_HOST = config.get("ftp_host").getAsString();
FTP_USER = config.get("ftp_username").getAsString();
FTP_PASS = config.get("ftp_password").getAsString();
FTP_PORT = config.get("ftp_port").getAsString();
PATH = config.get("ftp_path").getAsString();
}
handleActionSend(filePath);
}
}
}
/**
* Handle action Foo in the provided background thread with the provided
* parameters.
*/
private void handleActionSend(String filePath) {
File f = new File(filePath);
uploadFile(f);
}
public boolean uploadFile(final File f){
try {
ftpClient = new FTPClient();
ftpClient.connect(FTP_HOST, Integer.valueOf(FTP_PORT));
boolean b = ftpClient.login(FTP_USER, FTP_PASS);
Log.d("Login", "" + b);
b = ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
Log.d("file_type", "" + b);
b = ftpClient.changeWorkingDirectory(PATH);
Log.d("directory", "" + b);
// ftpClient.setFileTransferMode(FTP.BINARY_FILE_TYPE);
//
int reply = ftpClient.getReplyCode();
Log.d("reply", "" + reply);
// if(!FTPReply.isPositiveCompletion(reply)) {
// ftpClient.logout();
// ftpClient.disconnect();
// Log.d("ftp", "FTP server refused connection.");
// return false;
// }
InputStream srcFileStream = new FileInputStream(f);
// BufferedInputStream buffIn = new BufferedInputStream(new FileInputStream(f));
ftpClient.setControlKeepAliveTimeout(10);
ftpClient.enterLocalPassiveMode();
final int fileSize = (int) (FileUtils.sizeOf(f) / 1024);
// dati trasferiti
ftpClient.setCopyStreamListener(new CopyStreamListener() {
#Override
public void bytesTransferred(CopyStreamEvent event) {
}
#Override
public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
Log.d("ftp", "total:" + totalBytesTransferred / 1024 + " trasnferred: " + bytesTransferred + " size:" + fileSize);
Bundle data = new Bundle();
data.putInt("fileSize", fileSize);
data.putInt("kbyte_transferred", (int) totalBytesTransferred / 1024);
mUploadReceiver.send(UPDATE_PROGRESS, data);
}
});
// Log.d("buffer", "" + buffIn.available());
Log.d("ftp", "storing file...");
boolean result = ftpClient.storeFile(f.getName(), srcFileStream);
Log.d("ftp", "result:" + result);
Log.d("ftp", "reply code:" + ftpClient.getReplyCode());
// inputStream.close();
srcFileStream.close();
// buffIn.close();
ftpClient.logout();
ftpClient.disconnect();
return result;
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (CopyStreamException e) {
e.printStackTrace();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch(IOException ioe) {
ioe.printStackTrace();
}
}
}
return false;
}
}
UploadReceiver
class UploadReceiver extends ResultReceiver{
private NotificationManager mNotifyManager;
private NotificationCompat.Builder mBuilder;
private int id = 1;
public UploadReceiver(Handler handler, Context context) {
super(handler);
final int id = 1;
//Gestione notifica
mNotifyManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(context);
mBuilder.setContentTitle("Picture Download")
.setContentText("Download in progress")
.setSmallIcon(R.drawable.ic_launcher);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == FtpService.UPDATE_PROGRESS) {
int fileSize = resultData.getInt("file_size");
int transferred = resultData.getInt("kbyte_transferred");
mBuilder.setProgress(fileSize,transferred, false);
mNotifyManager.notify(id, mBuilder.build());
}
}
}
I solved the issue adding a check with an interval of 1000 milliseconds, if these have gone the UploadReceiver can update the notify and in this way it seems to work fine without freeze.It works also with a lower interval as 500 milliseconds.
Guys is my case these many times progress is rendering but I want only 100 times of progress so this code solve my issue.
Put int oldProgress =1;
int progresss = (int) ((progress.currentBytes / (float) progress.totalBytes) * 100);
if(progresss>oldProgress)
{
oldProgress=progresss;
notificationBuilder.setProgress(100, progresss,false);
notification = notificationBuilder.build();
notificationManager.notify(notificationID,notification);
}
I code a service to subscribe message from mqtt server. Service auto reconnect when internet connection on/off.
This code:
public class NotificationService extends Service{
private Handler connectBrokerHandler;
private ConnectBrokerTimerTask connectBrokerTimerTask;
private Timer connectBrokerTimer;
private MqttAndroidClient androidClient;
private MqttConnectOptions connectOptions;
private MemoryPersistence memPer;
private IMqttToken connectToken;
private CustomActionListener customActionListener;
private Context context;
private String CLIENT_STATUS = CLIENT_INIT;
private static final String TAG = "NotificationService";
private static final String USER_NAME = "chuxuanhy";
private static final String PASS_WORD = "0936160721";
private static final String URL_BROKER = "tcp://ntm.diyoracle.com:1883";
private static final String TOPIC_STAFF = "PROSHIP_STAFF";
private static final String DEVICE_ID = "FUCKYOU_002";
private static final int CONNECTION_TIMEOUT = 60;
private static final int KEEP_ALIVE_INTERVAL = 60*60;
private static final int TRY_CONNECTION_DELAY = 30; //seconds
private static final String CLIENT_INIT = "CLIENT_INIT";
private static final String CLIENT_CONNECTION_BROKER_START = "CLIENT_CONNECTION_BROKER_START";
private static final String CLIENT_CONNECTION_BROKER_FAIL = "CLIENT_CONNECTION_BROKER_FAIL";
private static final String CLIENT_CONNECTION_BROKER_SUCCESS = "CLIENT_CONNECTION_BROKER_SUCCESS";
private static final String CLIENT_LOST_CONNECTION = "CLIENT_LOST_CONNECTION";
private static final String CLIENT_SUBSCRIBE_TOPIC_START = "CLIENT_SUBSCRIBE_TOPIC_START";
private static final String CLIENT_SUBSCRIBE_TOPIC_FAIL = "CLIENT_SUBSCRIBE_TOPIC_FAIL";
private static final String CLIENT_SUBSCRIBE_TOPIC_SUCCESS = "CLIENT_SUBSCRIBE_TOPIC_SUCCESS";
private IntentFilter FilterNetwork;
private BroadcastReceiver ReceiverCheckInternet = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(isOnline()){
if(CLIENT_STATUS == CLIENT_LOST_CONNECTION || CLIENT_STATUS == CLIENT_CONNECTION_BROKER_FAIL || CLIENT_STATUS == CLIENT_SUBSCRIBE_TOPIC_FAIL) {
ConnectBroker();
}
}
}
};
private class ConnectBrokerTimerTask extends TimerTask{
#Override
public void run() {
connectBrokerHandler.post(ConnectBrokerRunable);
}
}
private Runnable ConnectBrokerRunable = new Runnable() {
#Override
public void run() {
ConnectBroker();
}
};
public boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnectedOrConnecting();
}
public void RestartNotificationService(){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartServicePendingIntent);
}
public void createNotification(String title, String body) {
Intent intent = new Intent(this, NotificationReceiver.class);
PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext());
Notification notification = mBuilder.setSmallIcon(R.drawable.train).setTicker(title).setWhen(0)
.setAutoCancel(true)
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle().bigText(body))
.setContentIntent(pIntent)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setVibrate(new long[]{1000, 1000})
.setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_launcher))
.setContentText(body).build();
Random random = new Random();
int m = random.nextInt(9999 - 1000) + 1000;
NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(m, notification);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
try{
RestartNotificationService();
}catch (Exception e){
e.printStackTrace();
}
super.onTaskRemoved(rootIntent);
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(ReceiverCheckInternet);
Log.e(TAG, "onDestroy");
try{
if(androidClient.isConnected()){
this.androidClient.unsubscribe(TOPIC_STAFF);
this.androidClient.disconnect();
this.androidClient = null;
}
}catch (Exception e){
e.printStackTrace();
}
}
private void ConnectBroker(){
try {
CLIENT_STATUS = CLIENT_CONNECTION_BROKER_START;
connectToken = this.androidClient.connect(connectOptions,null,customActionListener);
}catch (Exception e){
//e.printStackTrace();
Log.e(TAG, "ConnectBroker Fail Exception");
}
}
private void SubscribeBrokerTopic(String topic){
try {
CLIENT_STATUS = CLIENT_SUBSCRIBE_TOPIC_START;
androidClient.subscribe(topic, 0, null, customActionListener);
} catch (Exception e) {
//e.printStackTrace();
Log.e(TAG, "SubscribeBrokerTopic Fail Exception");
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("NOTIFI","CALL ME onStartCommand");
this.connectBrokerHandler = new Handler();
this.connectBrokerTimer = new Timer();
this.connectBrokerTimerTask = new ConnectBrokerTimerTask();
this.customActionListener = new CustomActionListener();
this.memPer = new MemoryPersistence();
this.connectOptions = new MqttConnectOptions();
this.connectOptions.setCleanSession(true);
this.connectOptions.setUserName(USER_NAME);
this.connectOptions.setPassword(PASS_WORD.toCharArray());
this.connectOptions.setConnectionTimeout(CONNECTION_TIMEOUT);
this.connectOptions.setKeepAliveInterval(KEEP_ALIVE_INTERVAL);
this.androidClient = new MqttAndroidClient(getApplicationContext(),URL_BROKER,DEVICE_ID,memPer);
this.androidClient.setCallback(new CustomCallBack());
this.androidClient.setTraceCallback(new CustomTraceHandler());
//this.androidClient.
this.FilterNetwork = new IntentFilter();
this.FilterNetwork.addAction("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(ReceiverCheckInternet, FilterNetwork);
//this.androidClient.registerResources(getApplicationContext());
ConnectBroker();
return START_NOT_STICKY;
}
private class CustomTraceHandler implements MqttTraceHandler{
#Override
public void traceDebug(String source, String message) {
Log.e(TAG,"CustomConnectHandler - traceDebug\n"+source+" - "+message);
}
#Override
public void traceError(String source, String message) {
Log.e(TAG, "CustomConnectHandler - traceError\n" + source + " - " + message);
}
#Override
public void traceException(String source, String message, Exception e) {
Log.e(TAG, "CustomConnectHandler - traceException\n" + source + " - " + message);
e.printStackTrace();
}
}
private class CustomCallBack implements MqttCallback{
#Override
public void connectionLost(Throwable throwable) {
CLIENT_STATUS = CLIENT_LOST_CONNECTION;
if(!isOnline()){
Log.e(TAG, "ConnectCallBack connectionLost - No Internet Connection ");
}else {
//throwable.printStackTrace();
}
}
#Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
Log.e(TAG,"ConnectCallBack messageArrived\n"+s+" - "+mqttMessage.toString());
createNotification(s, mqttMessage.toString());
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
Log.e(TAG, "ConnectCallBack deliveryComplete");
}
}
private class CustomActionListener implements IMqttActionListener {
#Override
public void onSuccess(IMqttToken iMqttToken) {
if (CLIENT_STATUS == CLIENT_CONNECTION_BROKER_START) {
CLIENT_STATUS = CLIENT_CONNECTION_BROKER_SUCCESS;
if (iMqttToken.getTopics() == null) {
SubscribeBrokerTopic(TOPIC_STAFF);
}
Log.e(TAG, "CustomActionListener - CONNECT BROKER onSuccess");
} else if (CLIENT_STATUS == CLIENT_SUBSCRIBE_TOPIC_START) {
CLIENT_STATUS = CLIENT_SUBSCRIBE_TOPIC_SUCCESS;
Log.e(TAG, "CustomActionListener - SUBSCRIBE TOPIC onSuccess");
}
}
#Override
public void onFailure(IMqttToken iMqttToken, Throwable throwable) {
if (CLIENT_STATUS == CLIENT_CONNECTION_BROKER_START) {
CLIENT_STATUS = CLIENT_CONNECTION_BROKER_FAIL;
Log.e(TAG, "CustomActionListener - CONNECT BROKER onFailure ");
} else if (CLIENT_STATUS == CLIENT_SUBSCRIBE_TOPIC_START) {
CLIENT_STATUS = CLIENT_SUBSCRIBE_TOPIC_FAIL;
Log.e(TAG, "CustomActionListener - SUBSCRIBE TOPIC onFailure ");
}
if (isOnline()) {
throwable.printStackTrace();
//connectBrokerTimer.scheduleAtFixedRate(connectBrokerTimerTask, 5000, TRY_CONNECTION_DELAY * 1000);
}else {
Log.e(TAG, "CustomActionListener onFailure - No Internet Connection ");
}
}
}
}
But i have a problem, every time internet connection lost, i reconnect server, callback call again.
Example:
Log.e(TAG, "ConnectCallBack connectionLost - No Internet Connection ");
call twice, 3 time, 4 time every disconnect internet
Sorry my english not good, thank for help.
Don't handle the re-connection manually, newer version of paho has option to auto reconnect in MqttConnectOptions class.
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.1'
compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
I'm building a simple chat that connects between an android client and a java server (running on my pc). The user can send and receive messages to/from the android app and the desktop server.
I'm dealing now with the question of how to run the client-socket in a different thread than the UI Thread.
I saw solutions using AsyncTask, but as the user may communicate using the app for a long sequential time, AsyncTask looks like a bad approach.
AsyncTasks should ideally be used for short operations (a few seconds at the most.) API
Because i need the client socket to consistently listen for messages from the desktop server, i thought of creating new Thread receiving a Runnable implementing class.
My questions
1. In which "thread mechanism" to place the client socket rows (Thread, IntentService)?
Socket client = new Socket(host, port);
InputStreamReader inputStreamReader = new InputStreamReader(client.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ((messageFromServer = bufferedReader.readLine()) != null) { //... }
2. How can the client socket (running from a different thread than the main thread) post the messageFromServer to an TextView?
How will i send the user messages from the app to the server (using the client-socket ofcourse), upon user entering text and clicking a button?
Thanks!
I've created a similar app and I used a Service which runs in the background.
I've copied the code from the IntentService clas and updated handleMessage(Message msg) method and removed stopSelf(msg.arg1); line. In this way you have a service that runs in the background. After it I used a Thread for the connection.
You have two choices here. Store the data into the database and the GUI refreshes itself. Or use LocalBroadcastManager.
Here you can also store the data into the db or Start the service with a special intent.
Here is my implementation. I hope you will understand the code.
public class KeepAliveService extends Service {
/**
* The source of the log message.
*/
private static final String TAG = "KeepAliveService";
private static final long INTERVAL_KEEP_ALIVE = 1000 * 60 * 4;
private static final long INTERVAL_INITIAL_RETRY = 1000 * 10;
private static final long INTERVAL_MAXIMUM_RETRY = 1000 * 60 * 2;
private ConnectivityManager mConnMan;
protected NotificationManager mNotifMan;
protected AlarmManager mAlarmManager;
private boolean mStarted;
private boolean mLoggedIn;
protected static ConnectionThread mConnection;
protected static SharedPreferences mPrefs;
private final int maxSize = 212000;
private Handler mHandler;
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(final Looper looper) {
super(looper);
}
#Override
public void handleMessage(final Message msg) {
onHandleIntent((Intent) msg.obj);
}
}
public static void actionStart(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_START)));
}
public static void actionStop(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_STOP)));
}
public static void actionPing(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER)));
}
#Override
public void onCreate() {
Log.i(TAG, "onCreate called.");
super.onCreate();
mPrefs = getSharedPreferences("KeepAliveService", MODE_PRIVATE);
mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
mNotifMan = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
mHandler = new Handler();
final HandlerThread thread = new HandlerThread("IntentService[KeepAliveService]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
// If our process was reaped by the system for any reason we need to
// restore our state with merely a
// call to onCreate.
// We record the last "started" value and restore it here if necessary.
handleCrashedService();
}
#Override
public void onDestroy() {
Log.i(TAG, "Service destroyed (started=" + mStarted + ")");
if (mStarted) {
stop();
}
mServiceLooper.quit();
}
private void handleCrashedService() {
Log.i(TAG, "handleCrashedService called.");
if (isStarted()) {
// We probably didn't get a chance to clean up gracefully, so do it now.
stopKeepAlives();
// Formally start and attempt connection.
start();
}
}
/**
* Returns the last known value saved in the database.
*/
private boolean isStarted() {
return mStarted;
}
private void setStarted(final boolean started) {
Log.i(TAG, "setStarted called with value: " + started);
mStarted = started;
}
protected void setLoggedIn(final boolean value) {
Log.i(TAG, "setLoggedIn called with value: " + value);
mLoggedIn = value;
}
protected boolean isLoggedIn() {
return mLoggedIn;
}
public static boolean isConnected() {
return mConnection != null;
}
#Override
public void onStart(final Intent intent, final int startId) {
final Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
Log.i(TAG, "Service started with intent : " + intent);
onStart(intent, startId);
return START_NOT_STICKY;
}
private void onHandleIntent(final Intent intent) {
if (IntentActions.KEEP_ALIVE_SERVICE_STOP.equals(intent.getAction())) {
stop();
stopSelf();
} else if (IntentActions.KEEP_ALIVE_SERVICE_START.equals(intent.getAction())) {
start();
} else if (IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER.equals(intent.getAction())) {
keepAlive(false);
}
}
#Override
public IBinder onBind(final Intent intent) {
return null;
}
private synchronized void start() {
if (mStarted) {
Log.w(TAG, "Attempt to start connection that is already active");
setStarted(true);
return;
}
try {
registerReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
} catch (final Exception e) {
Log.e(TAG, "Exception occurred while trying to register the receiver.", e);
}
if (mConnection == null) {
Log.i(TAG, "Connecting...");
mConnection = new ConnectionThread(Config.PLUGIN_BASE_HOST, Config.PLUGIN_BASE_PORT);
mConnection.start();
}
}
private synchronized void stop() {
if (mConnection != null) {
mConnection.abort(true);
mConnection = null;
}
setStarted(false);
try {
unregisterReceiver(mConnectivityChanged);
} catch (final Exception e) {
Log.e(TAG, "Exception occurred while trying to unregister the receiver.", e);
}
cancelReconnect();
}
/**
* Sends the keep-alive message if the service is started and we have a
* connection with it.
*/
private synchronized void keepAlive(final Boolean forced) {
try {
if (mStarted && isConnected() && isLoggedIn()) {
mConnection.sendKeepAlive(forced);
}
} catch (final IOException e) {
Log.w(TAG, "Error occurred while sending the keep alive message.", e);
} catch (final JSONException e) {
Log.w(TAG, "JSON error occurred while sending the keep alive message.", e);
}
}
/**
* Uses the {#link android.app.AlarmManager} to start the keep alive service in every {#value #INTERVAL_KEEP_ALIVE} milliseconds.
*/
private void startKeepAlives() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL_KEEP_ALIVE, INTERVAL_KEEP_ALIVE, pi);
}
/**
* Removes the repeating alarm which was started by the {#link #startKeepAlives()} function.
*/
private void stopKeepAlives() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
}
public void scheduleReconnect(final long startTime) {
long interval = mPrefs.getLong("retryInterval", INTERVAL_INITIAL_RETRY);
final long now = System.currentTimeMillis();
final long elapsed = now - startTime;
if (elapsed < interval) {
interval = Math.min(interval * 4, INTERVAL_MAXIMUM_RETRY);
} else {
interval = INTERVAL_INITIAL_RETRY;
}
Log.i(TAG, "Rescheduling connection in " + interval + "ms.");
mPrefs.edit().putLong("retryInterval", interval).apply();
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_RECONNECT), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.set(AlarmManager.RTC_WAKEUP, now + interval, pi);
}
public void cancelReconnect() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_RECONNECT), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
}
private synchronized void reconnectIfNecessary() {
if (mStarted && !isConnected()) {
Log.i(TAG, "Reconnecting...");
mConnection = new ConnectionThread(Config.PLUGIN_BASE_HOST, Config.PLUGIN_BASE_PORT);
mConnection.start();
}
}
private final BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
final NetworkInfo info = mConnMan.getActiveNetworkInfo(); // (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
final boolean hasConnectivity = info != null && info.isConnected();
Log.i(TAG, "Connecting changed: connected=" + hasConnectivity);
if (hasConnectivity) {
reconnectIfNecessary();
} else if (mConnection != null) {
mConnection.abort(false);
mConnection = null;
}
}
};
protected class ConnectionThread extends Thread {
private final Socket mSocket;
private final String mHost;
private final int mPort;
private volatile boolean mAbort = false;
public ConnectionThread(final String host, final int port) {
mHost = host;
mPort = port;
mSocket = new Socket();
}
/**
* Returns whether we have an active internet connection or not.
*
* #return <code>true</code> if there is an active internet connection.
* <code>false</code> otherwise.
*/
private boolean isNetworkAvailable() {
final NetworkInfo info = mConnMan.getActiveNetworkInfo();
return info != null && info.isConnected();
}
#Override
public void run() {
final Socket s = mSocket;
final long startTime = System.currentTimeMillis();
try {
// Now we can say that the service is started.
setStarted(true);
// Connect to server.
s.connect(new InetSocketAddress(mHost, mPort), 20000);
Log.i(TAG, "Connection established to " + s.getInetAddress() + ":" + mPort);
// Start keep alive alarm.
startKeepAlives();
final DataOutputStream dos = new DataOutputStream(s.getOutputStream());
final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
// Send the login data.
final JSONObject login = new JSONObject();
// Send the login message.
dos.write((login.toString() + "\r\n").getBytes());
// Wait until we receive something from the server.
String receivedMessage;
while ((receivedMessage = in.readLine()) != null) {
Log.i(TAG, "Received data: " + receivedMessage);
processMessagesFromServer(dos, receivedMessage);
}
if (!mAbort) {
Log.i(TAG, "Server closed connection unexpectedly.");
}
} catch (final IOException e) {
Log.e(TAG, "Unexpected I/O error.", e);
} catch (final Exception e) {
Log.e(TAG, "Exception occurred.", e);
} finally {
setLoggedIn(false);
stopKeepAlives();
if (mAbort) {
Log.i(TAG, "Connection aborted, shutting down.");
} else {
try {
s.close();
} catch (final IOException e) {
// Do nothing.
}
synchronized (KeepAliveService.this) {
mConnection = null;
}
if (isNetworkAvailable()) {
scheduleReconnect(startTime);
}
}
}
}
/**
* Sends the PING word to the server.
*
* #throws java.io.IOException if an error occurs while writing to this stream.
* #throws org.json.JSONException
*/
public void sendKeepAlive(final Boolean forced) throws IOException, JSONException {
final JSONObject ping = new JSONObject();
final Socket s = mSocket;
s.getOutputStream().write((ping.toString() + "\r\n").getBytes());
}
/**
* Aborts the connection with the server.
*/
public void abort(boolean manual) {
mAbort = manual;
try {
// Close the output stream.
mSocket.shutdownOutput();
} catch (final IOException e) {
// Do nothing.
}
try {
// Close the input stream.
mSocket.shutdownInput();
} catch (final IOException e) {
// Do nothing.
}
try {
// Close the socket.
mSocket.close();
} catch (final IOException e) {
// Do nothing.
}
while (true) {
try {
join();
break;
} catch (final InterruptedException e) {
// Do nothing.
}
}
}
}
public void processMessagesFromServer(final DataOutputStream dos, final String receivedMessage) throws IOException {
}
}
You can start the service by calling KeepAliveService.actionStart() and you can also define custom functions.
Please note that the service will be stopped only if you call KeepAliveService.actionStop(). Otherwise it will run forever. If you call e.g. KeepAliveService.actionSendMessage(String message) then the intent will be passed to the service and you can handle it easily.
EDIT:
The SystemHelper class is only a utility class which contains static methods.
public class SystemHelper {
/**
* Android Lollipop, API 21 introduced a new problem when trying to invoke implicit intent,
* "java.lang.IllegalArgumentException: Service Intent must be explicit"
*
* If you are using an implicit intent, and know only 1 target would answer this intent,
* This method will help you turn the implicit intent into the explicit form.
*
* Inspired from SO answer: http://stackoverflow.com/a/26318757/1446466
* #param context the application context
* #param implicitIntent - The original implicit intent
* #return Explicit Intent created from the implicit original intent
*/
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
Log.i(TAG, "createExplicitFromImplicitIntent ... called with intent: " + implicitIntent);
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
Log.i(TAG, "createExplicitFromImplicitIntent ... resolveInfo is null or there are more than one element.");
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
Log.i(TAG, "createExplicitFromImplicitIntent ... found package name:" + packageName + ", class name: " + className + ".");
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
The Config class.
public class Config {
public static final String PACKAGE_NAME = "com.yourapp.package";
public static final String PLUGIN_BASE_HOST = "test.yoursite.com";
public static final int PLUGIN_BASE_PORT = 10000;
}
And the IntentActions class.
public class IntentActions {
public static final String KEEP_ALIVE_SERVICE_START = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_START";
public static final String KEEP_ALIVE_SERVICE_STOP = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_STOP";
public static final String KEEP_ALIVE_SERVICE_PING_SERVER = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_PING_SERVER";
}
In the AndroidManifest file the service is defined in the following way:
<service android:name="com.yourapp.package.services.KeepAliveService"
android:exported="false">
<intent-filter>
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_START" />
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_STOP" />
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_PING_SERVER" />
</intent-filter>
</service>
I suggest you take a look at the Android documentation for background services. Personally I would use the IntentService as it's a well established pattern within Android.
http://developer.android.com/training/run-background-service/index.html
I tried to send an sms via an Intent with this code:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("smsto:" + phoneNumber));
intent.putExtra("address", phoneNumber);
intent.putExtra("sms_body", messageBody);
intent.putExtra("exit_on_sent", true);
startActivityForResult(intent, CODE);
Then, I want to know if the SMS has been sent or not and I use this code:
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case CODE:
if (resultCode == Activity.RESULT_OK)
{
//Then do...
}
elseif(resultCode == Activity.RESULT_CANCELED)
{
// Do...
}
break;
}
}
The thing is the result is always 0 (Activity.RESULT_CANCELED), even when the SMS has been sent. How can I know if the SMS has been sent or not ? I want to use the SMS default app of the phone, not create an interface that sends SMS.
In the following example, we use a ContentObserver to monitor updates to the SMS Provider. This Observer is created and started before the SMS Intent is fired, and checks the Provider changes against the destination address. The Activity that creates the Observer must implement the SmsSendObserver.SmsSendListener interface to receive the callback.
The Observer's constructor includes a timeout parameter (in milliseconds) to allow the Observer to be properly unregistered if the message is not sent after a reasonable amount of time. This can be set to NO_TIMEOUT if desired. However, the class, as written, is meant for "one shot" use, and it will unregister itself and nullify members upon callback. The stop() method can be used to clean up if no callback occurs. In either case, the instance is no longer usable, and any reference to it should be set to null.
Example Activity:
public class MainActivity extends Activity
implements SmsSendObserver.SmsSendListener {
...
private void sendMessage(String phoneNumber, String messageBody) {
// This example has a timeout set to 15 seconds
new SmsSendObserver(this, phoneNumber, 15000).start();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("smsto:" + phoneNumber));
intent.putExtra("address", phoneNumber);
intent.putExtra("sms_body", messageBody);
intent.putExtra("exit_on_sent", true);
startActivity(intent);
}
public void onSmsSendEvent(boolean sent) {
Toast.makeText(this, sent ? "Message was sent" : "Timed out",
Toast.LENGTH_SHORT).show();
}
}
The SmsSendObserver class:
public class SmsSendObserver extends ContentObserver {
public static final int NO_TIMEOUT = -1;
private static final Handler handler = new Handler();
private static final Uri uri = Uri.parse("content://sms/");
private static final String COLUMN_ADDRESS = "address";
private static final String COLUMN_TYPE = "type";
private static final String[] PROJECTION = { COLUMN_ADDRESS, COLUMN_TYPE };
private static final int MESSAGE_TYPE_SENT = 2;
private Context context = null;
private ContentResolver resolver = null;
private String phoneNumber = null;
private long timeout = NO_TIMEOUT;
private boolean wasSent = false;
private boolean timedOut = false;
public SmsSendObserver(Context context, String phoneNumber, long timeout) {
super(handler);
if (context instanceof SmsSendListener) {
this.context = context;
this.resolver = context.getContentResolver();
this.phoneNumber = phoneNumber;
this.timeout = timeout;
}
else {
throw new IllegalArgumentException(
"Context must implement SmsSendListener interface");
}
}
private Runnable runOut = new Runnable() {
#Override
public void run() {
if (!wasSent) {
timedOut = true;
callBack();
}
}
};
public void start() {
if (resolver != null) {
resolver.registerContentObserver(uri, true, this);
if (timeout > NO_TIMEOUT) {
handler.postDelayed(runOut, timeout);
}
}
else {
throw new IllegalStateException(
"Current SmsSendObserver instance is invalid");
}
}
public void stop() {
if (resolver != null) {
resolver.unregisterContentObserver(this);
resolver = null;
context = null;
}
}
private void callBack() {
((SmsSendListener) context).onSmsSendEvent(wasSent);
stop();
}
#Override
public void onChange(boolean selfChange) {
if (wasSent || timedOut)
return;
Cursor cursor = null;
try {
cursor = resolver.query(uri, PROJECTION, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final String address =
cursor.getString(cursor.getColumnIndex(COLUMN_ADDRESS));
final int type =
cursor.getInt(cursor.getColumnIndex(COLUMN_TYPE));
if (PhoneNumberUtils.compare(address, phoneNumber) &&
type == MESSAGE_TYPE_SENT) {
wasSent = true;
callBack();
}
}
}
finally {
if (cursor != null) {
cursor.close();
}
}
}
public interface SmsSendListener {
// Passes true if the message was sent
// Passes false if timed out
public void onSmsSendEvent(boolean sent);
}
}
I am developing an android app based on GCM. The functionality is working perfectly, but I am running into an issue where when I am using the app and on the list screen, the list does not automatically refresh when the app receives a message from GCM. Notifications work fine, but I do not know how to get the message to appear in the list while I'm viewing it.
Here is my GcmBroadcastReceiver:
public class GcmBroadcastReceiver extends BroadcastReceiver {
static final String TAG = "GCMDemo";
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
private MessageDatabaseHelper MDH;
Context ctx;
#Override
public void onReceive(Context context, Intent intent) {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
ctx = context;
String messageType = gcm.getMessageType(intent);
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
try {
sendNotification("Send error: " + intent.getExtras().toString());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
try {
sendNotification("Deleted messages on server: " +
intent.getExtras().toString());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
try {
if(intent.getExtras().getString("message") != null){
sendNotification(intent.getExtras().getString("message"));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
setResultCode(Activity.RESULT_OK);
}
// Put the GCM message into a notification and post it.
private void sendNotification(String msg) throws JSONException {
mNotificationManager = (NotificationManager)
ctx.getSystemService(Context.NOTIFICATION_SERVICE);
SharedPreferences prefs1 = ctx.getSharedPreferences("UNIQUE", Context.MODE_PRIVATE);
int unique_id = prefs1.getInt("UNIQUE_ID", 0);
SharedPreferences.Editor editor = prefs1.edit();
unique_id++;
editor.putInt("UNIQUE_ID", unique_id);
editor.commit();
Log.v("TEST", "sharedprefs - " + prefs1.getInt("UNIQUE_ID", 0));
JSONObject JSdata = new JSONObject(msg);
String category = "";
String text = "";
String subject = "";
String from = "";
if(JSdata.getString("Category") != null){
Log.v("TEST", "CATEGORY - " + JSdata.getString("Category"));
category = JSdata.getString("Category");
}
if(JSdata.getString("Subject") != null){
Log.v("TEST", "SUBJECT - " + JSdata.getString("Subject"));
subject = JSdata.getString("Subject");
}
if(JSdata.getString("Message") != null){
Log.v("TEST", "Message - " + JSdata.getString("Message"));
text = JSdata.getString("Message");
}
if(JSdata.getString("From") != null){
Log.v("TEST", "FROM - " + JSdata.getString("From"));
from = JSdata.getString("From");
}
String dateTime = "";
Time now = new Time();
now.setToNow();
dateTime = (now.month + 1) + "/" + now.monthDay + " " + now.hour + ":" + now.minute;
Log.v("date", "date - " + dateTime);
Message message = new Message(1, category, text, subject, from, dateTime);
MDH = new MessageDatabaseHelper(ctx);
MDH.addMessage(message);
// new AsyncTask() {
// #Override
// protected String doInBackground(Object... arg0) {
// Intent mainIntent = new Intent(ctx, MainActivity.class);
// mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// ctx.startActivity(mainIntent);
// Log.v("GCMDEMO", "background");
// return "";
// }
// }.execute(null, null, null);
Intent gcmIntent = new Intent(ctx, MessageActivity.class);
gcmIntent.putExtra("message1", message);
PendingIntent contentIntent = PendingIntent.getActivity(ctx, unique_id,
gcmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(ctx)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Choice Cloud Notification")
.setContentText(JSdata.getString("Subject") + " " + JSdata.getString("Category"))
.setAutoCancel(true);
NotificationCompat.InboxStyle big = new NotificationCompat.InboxStyle(
mBuilder);
big.setSummaryText(JSdata.getString("Subject") + " " + JSdata.getString("Category"));
big.addLine(JSdata.getString("Message"));
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(unique_id, big.build());
//mBuilder.setContentIntent(contentIntent);
//mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}
And here is my MainActivity
public class MainActivity extends Activity{
private MessageDatabaseHelper MDH;
private boolean initialLaunch = false;
private StableArrayAdapter adapter;
private ListView listview;
private List<Message> mList;
private Spinner spinner;
public static final String EXTRA_MESSAGE = "message";
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
private static final String SENDER_ID = "469307705305"; //This is the project number under the API
static final String TAG = "GCMDemo";
GoogleCloudMessaging gcm;
AtomicInteger msgId = new AtomicInteger();
SharedPreferences prefs;
Context context;
String regid;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
regid = getRegistrationId(context);
if (regid.length() == 0) {
registerBackground();
}
gcm = GoogleCloudMessaging.getInstance(this);
spinner = (Spinner) findViewById(R.id.category_spinner);
MDH = new MessageDatabaseHelper(this);
//On first launch of app, add some mock messages for testing
if(isInitialLaunch()){
setInitialLaunch(true);
Message m1 = new Message(1, "RateCenter", "rates are low", "Sub1", "From1", "7/31 8:45");
MDH.addMessage(m1);
SharedPreferences prefs1 = getSharedPreferences("UNIQUE", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs1.edit();
editor.putInt("UNIQUE_ID", 0);
editor.commit();
}
//Message m1 = new Message(1, "RateCenter", "rates are low", "Sub1", "From1");
//MDH.addMessage(m1);
//Grab all categories dynamically from database
List<String> cList = new ArrayList<String>();
cList = MDH.getAllCategories();
HashSet hs = new HashSet();
hs.addAll(cList);
cList.clear();
cList.addAll(hs);
//Add those categories to arraylist for use in spinner
List<String> categories = new ArrayList<String>();
//categories.add("Please select a category");
categories.add("All");
for(String s:cList){
categories.add(s);
}
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, categories);
// Specify the layout to use when the list of choices appears
adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Apply the adapter to the spinner
spinner.setAdapter(adapter1);
//add onitemselected listener to spinner
spinner.setOnItemSelectedListener(new OnItemSelectedListener(){
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
//Refresh main activity with correct set of messages pertaining to category selected
String test1 = parent.getItemAtPosition(pos).toString();
String test2 = MyApplication.globalCategory;
if(!(test1.equals(test2))){
MyApplication.sPos = pos;
MyApplication.globalCategory = parent.getItemAtPosition(pos).toString();
Intent intent = getIntent();
finish();
startActivity(intent);
}
}
public void onNothingSelected(AdapterView<?> arg0) {
}});
//Creates a listview to be used to store messages
listview = (ListView) findViewById(R.id.list);
listview.setItemsCanFocus(false);
//grab messages from database for use in listview corresponding to category selected
mList = new ArrayList<Message>();
if(MyApplication.globalCategory.equals("All")){
mList = MDH.getAllMessages();
}
else {
mList = MDH.getMessagesWithCategory(MyApplication.globalCategory);
}
//instantiates adapter and adds it to listview
adapter = new StableArrayAdapter(this, mList);
listview.setAdapter(adapter);
listview.setClickable(true);
//adds click listener to list
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view,
int position, long id) {
Log.v("GCMDemo", "Registration id is - " + getRegistrationId(MainActivity.this));
Message clickedMessage = (Message)parent.getItemAtPosition(position);
clickedMessage.setUnread(false);
MDH.updateUnread(clickedMessage);
//starts new messageactivity, passing the message clicked on as an extra to the intent
listview.invalidateViews();
Intent startMessageActivityIntent = new Intent();
startMessageActivityIntent.putExtra("message1", clickedMessage);
startMessageActivityIntent.setClass(MainActivity.this,
MessageActivity.class);
startActivity(startMessageActivityIntent);
finish();
}
});
}
#SuppressWarnings("unchecked")
private void registerBackground() {
new AsyncTask() {
#Override
protected String doInBackground(Object... arg0) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration id=" + regid;
// Save the regid - no need to register again.
setRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
Log.v("GCMDEMO", "GCMWHAT - " + msg);
return msg;
}
}.execute(null, null, null);
}
private void setRegistrationId(Context context, String regid) {
final SharedPreferences prefs = getGCMPreferences(context);
int appVersion = getAppVersion(context);
Log.v(TAG, "Saving regId on app version " + appVersion);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regid);
editor.putInt(PROPERTY_APP_VERSION, appVersion);
editor.commit();
}
private String getRegistrationId(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.length() == 0) {
Log.v(TAG, "Registration not found.");
return "";
}
// check if app was updated; if so, it must clear registration id to
// avoid a race condition if GCM sends a message
int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int currentVersion = getAppVersion(context);
if (registeredVersion != currentVersion) {
Log.v(TAG, "App version changed");
return "";
}
return registrationId;
}
private int getAppVersion(Context context2) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (NameNotFoundException e) {
// should never happen
throw new RuntimeException("Could not get package name: " + e);
}
}
private SharedPreferences getGCMPreferences(Context context2) {
return getSharedPreferences(MainActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
private class StableArrayAdapter extends ArrayAdapter<Message> {
//adapter class for use with listview, maps items to positions
//HashMap<Message, Integer> hMap = new HashMap<Message, Integer>();
private final Context context1;
private final List<Message> adaptList;
public StableArrayAdapter(Context cont,
List<Message> objects) {
super(context, R.layout.ccm_list_item, objects);
//for (int i = 0; i < objects.size(); i++) {
// hMap.put(objects.get(i), i);
//}
this.context1 = cont;
this.adaptList = new ArrayList<Message>();
this.adaptList.addAll(objects);
}
private class ViewHolder {
public TextView text;
public TextView text2;
public TextView textTime;
public CheckBox checkBox;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
ViewHolder holder = null;
Log.v("ConvertView", String.valueOf(position));
if (convertView == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.ccm_list_item, null);
holder = new ViewHolder();
LayoutInflater inflater = (LayoutInflater) context1
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
holder.text = (TextView) convertView.findViewById(R.id.rowText);
holder.text2 = (TextView) convertView.findViewById(R.id.rowText2);
holder.textTime = (TextView) convertView.findViewById(R.id.rowTextTime);
holder.checkBox = (CheckBox) convertView
.findViewById(R.id.listCheck);
holder.checkBox.setFocusable(false);
holder.checkBox.setFocusableInTouchMode(false);
convertView.setTag(holder);
holder.checkBox.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CheckBox cb = (CheckBox) v;
Message message = (Message)cb.getTag();
Toast.makeText(getApplicationContext(),
"Clicked on Checkbox: " + cb.getText() +
" is " + cb.isChecked(),
Toast.LENGTH_LONG).show();
//country.setSelected(cb.isChecked());
}
});
}
else {
holder = (ViewHolder) convertView.getTag();
}
int count = 0;
Message m1 = new Message();
if(MyApplication.globalCategory.equals("All")){
mList = MDH.getAllMessages();
for (Message m: mList) {
if(count == position){
m1 = m;
}
count++;
}
}
else {
mList = MDH.getMessagesWithCategory(MyApplication.globalCategory);
for (Message m: mList) {
if(count == position){
m1 = m;
}
count++;
}
}
if (m1.getUnread() == false) {
holder.text.setText(m1.toString());
holder.text2.setText(m1.getText());
holder.textTime.setText(m1.getTime());
convertView.setBackgroundColor(Color.LTGRAY);
} else {
holder.text.setText(m1.toString());
holder.text2.setText(m1.getText());
holder.textTime.setText(m1.getTime());
convertView.setBackgroundColor(Color.WHITE);
holder.text.setTypeface(null, Typeface.BOLD);
holder.text2.setTypeface(null, Typeface.BOLD);
}
holder.checkBox.setFocusable(false);
holder.checkBox.setFocusableInTouchMode(false);
holder.text.setFocusable(false);
holder.text.setFocusableInTouchMode(false);
((ViewGroup) convertView).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
return convertView;
}
}
public void setInitialLaunch(boolean initialLaunch) {
SharedPreferences settings = getSharedPreferences("APPLAUNCH", 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("APPLAUNCHKEY", false);
editor.commit();
this.initialLaunch = initialLaunch;
}
public boolean isInitialLaunch() {
SharedPreferences settings = getSharedPreferences("APPLAUNCH", 0);
this.initialLaunch = settings.getBoolean("APPLAUNCHKEY", true);
return initialLaunch;
}
#Override
protected void onStart() {
//on start, grabs message from database and updates adapter with changes to data
super.onStart();
if(MyApplication.globalCategory.equals("All")){
mList = MDH.getAllMessages();
for (Message m: mList) {
}
}
else {
mList = MDH.getMessagesWithCategory(MyApplication.globalCategory);
for (Message m: mList) {
}
}
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
adapter.notifyDataSetChanged() ;
}
});
spinner.setSelection(MyApplication.sPos);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
super.onCreateOptionsMenu(menu);
return true;
}
//option item about added to menu and starts aboutactivity when selected
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_settings) {
Intent startActivityIntent = new Intent(this,
AboutActivity.class);
startActivity(startActivityIntent);
return true;
}
return true;
}
}