I currently have an app setup to receive remote notifications using Azure Notification Hub.
Now, I would like to scan for iBeacons, see if a specific one is close by and if so, the notification should not be shown to the user. However, if the beacon isn't in sight, the user should receive this notification.
Basically I want the beacon to supress the notifications for this app.
How would one go about doing this?
Based on the docs from Azure, when a remote notification comes in, you get a callback like this:
public class MyHandler extends NotificationsHandler {
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
Context ctx;
#Override
public void onReceive(Context context, Bundle bundle) {
ctx = context;
String nhMessage = bundle.getString("message");
sendNotification(nhMessage);
if (MainActivity.isVisible) {
MainActivity.mainActivity.ToastNotify(nhMessage);
}
}
private void sendNotification(String msg) {
// put your notification code here
...
}
}
If you want to filter the notifications based on what beacons are present, you can add that logic to the onReceive method like this:
public void onReceive(Context context, Bundle bundle) {
if (!(MyApplication)this.getApplication()).isBeaconVisible()) {
// Suppress notification by returning early from method
return;
}
...
}
The above isBeaconVisible() could be implemented in a custom Android Application class using the Android Beacon Library with something like below. You'll need to read more about how to set up that library to make this work. You'll also need to register the custom Application class in your AndroidManifest.xml.
public class MyApplication extends Application implements BeaconConsumer, RangeNotifier {
public Collection<Beacon> mVisibleBeacons;
public void onCreate() {
super.onCreate();
BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
// TODO: look up the proper I_BEACON_LAYOUT in a google search
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(I_BEACON_LAYOUT));
beaconManager.addRangeNotifier(this);
}
#Override
public void onBeaconServiceConnect() {
BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
try {
beaconManager.startRangingBeaconsInRegion(new Region("all-beacons", null, null, null));
} catch (RemoteException e) {
e.printStackTrace();
}
}
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
mVisibleBeacons = beacons;
}
public boolean isBeaconVisible() {
return mVisibleBeacons.size() > 0;
}
}
The above logic for isBeaconVisible() returns true if any beacon with any identifier has been seen in the last second. But you can alter this to make it more sophisticated per your requirements.
You can use some opensource library to work with beacons. I used Altbeacon library for example.
Here is the samples : https://altbeacon.github.io/android-beacon-library/samples.html
For your target you need to implement BeaconConsumer interface on Activity or Service. It has a method onBeaconServiceConnect(). Example of implementation:
#Override
public void onBeaconServiceConnect() {
beaconManager.addRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
if (beacons.size() == 0) {
Log.i(TAG, "Show your notification here");
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("someRangingUniqueId", null, null, null));
} catch (RemoteException e) { }
}
Related
How to get result of background process in any Activity?
I'm working with OTA update. App handle that in background with handler. When update is done I have to show alert dialog to the user. I can show it in SettingsActivity where I start with OTA but user can be in any other Activity.
private void checkIsUpdated() {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mDevice.getDevice().read(GattUUID.LS_DEVICE_INFORMATION, GattUUID.LC_FIRMWARE, new
BleDevice.ReadWriteListener() {
#Override
public void onEvent(ReadWriteEvent e) {
if (e.wasSuccess()) {
if (firmwareVersion.equals(e.data_string())) {
showAlertDialog("OTA update failed", "Device is not updated");
} else {
showAlertDialog("OTA update is successful.\nDevice is updated to new " +
"firmware!", "Device is updated");
}
Log.i("OTA", "Read firmware is new success");
} else {
Log.e("OTA", "Read firmware is new success");
}
}
});
}
}, 30000);
}
AlertDialog code
private void showAlertDialog(String message, String title) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.SwipeDialogLight);
builder.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
if (!alert.isShowing()) {
alert.show();
}
}
As #W4R10CK stated, I thought that too. The BroadcastReceiver might not a very good idea. But later, I gave a thought about it and if you call checkIsUpdated function inside a Service and send the Broadcast from it, it might be a feasible solution.
So basically you need a BaseActivity class and in which you need to have a BroadcastReceiver. You need to register it onResume and again unregister it onPause.
And you need to have an abstract method to be overriden in each of your Activity too.
So your BaseActivity may look like this..
public abstract class BaseActivity extends AppCompatActivity {
private final Context context;
public BaseActivity() {
this.context = setContext();
}
protected abstract Context setContext();
#Override
public void onResume() {
super.onResume();
registerBroadcastReceiver();
}
#Override
public void onPause() {
super.onPause();
unRegisterBroadcastReceiver();
}
// Declare your BroadcastReceiver here
private class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
showAlertDialog(context);
}
}
}
As #rogerwar suggested in the comments, the correct approach is a broadcast receiver.
Vogella has a nice tutorial
Since you want to have it in all activities, you can make a base class for all your activities and in this base class you can register the receiver in the onStart and unregister it in the onStop.
I have an app that uses the CastCompanionLibrary and I'm having a weird issue. On a small network with one chromecast device my application is able to detect it and show the MediaRouterItem.
Although, I had a beta tester say that they were not able to get any of their devices detected, so the icon never shows up for them. Come to find out they are connected to a larger, shared network with multiple chromecast devices connected to it. They said that they are able to detect all of the chromecast devices with other apps such as YouTube and Localcast though. Which is weird because that leads me to believe that maybe I'm not doing something right with the discovery process.
Unfortunately, I am not in a position where I can have a network with multiple chromecast devices to debug this issue, so I was just wondering if anyone else had a similar issue? Or is there a certain method that you have to call with the CastCompanionLibrary that I'm missing?
EDIT
I am using the APP_ID that has been published, so I know that it isn't a whitelisting issue.
The code I use for discovery is completed by the CastCompanionLibrary. This is what I use once the onCastDeviceDetected() callback is called:
mCastConsumer = new VideoCastConsumerImpl() {
#Override
public void onFailed(int resourceId, int statusCode) {
}
#Override
public void onConnectionSuspended(int cause) {
Log.d(TAG, "onConnectionSuspended() was called with cause: " + cause);
}
#Override
public void onConnectivityRecovered() {
}
#Override
public void onCastDeviceDetected(final MediaRouter.RouteInfo info) {
if (!MyApplication.isFtuShown(Home.this)) {
MyApplication.setFtuShown(Home.this);
Log.d(TAG, "Route is visible: " + info);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (mediaRouteMenuItem.isVisible()) {
Log.d(TAG, "Cast Icon is visible: " + info.getName());
//showFtu();
}
}
}, 1000);
}
}
};
MyApplication.class:
public static boolean isFtuShown(Context ctx) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(ctx);
return sharedPref.getBoolean(FTU_SHOWN_KEY, false);
}
public static void setFtuShown(Context ctx) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(ctx);
sharedPref.edit().putBoolean(FTU_SHOWN_KEY, true).commit();
}
I want to launch a toast message in a static method of a non-Activity class.
I read a loto of threads about that, but my situation is a bit more complicated, in particular:
I have a service and in OnStartCommand I call with fixed interval a static method of another class, in this called method I want to show in some particular cases a toast message.
I try also to create a support class that extends Application in which I have the application context to get when I need, but nothing to do.
This is the method in the Service class that calls the static method AllInterfacesActived:
public int onStartCommand(Intent intent, int flags, int startId) {
//flag variable that indicates if service is scanning
isScanning = true;
final int result;
turnGPSOn();
/*GET WIFI DATA, we use a thread and with scheduleAtFixedRate we run it repeatedly with the wifi scan interval*/
Wifitimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime());
//we are in wifi case, we set umts string result to "" because we don't perform a umts scansion
String resultUMTS = "";
//call the SCANWIFI METHOD to scan the networks and to obtain their data
String resultScan = scanWifi();
Intent mIntent = new Intent();
mIntent.setAction(INTENT_ACTION);
//put information on wifi and umts in the intent
mIntent.putExtra(INTENT_EXTRA_WIFI, "Waiting for umts scansion...\nWIFI SCAN PERFOMED"+resultScan+"\nUMTS\n"+resultUMTS);
//broadcast of data
Bundle xtra = new Bundle();
sendBroadcast(mIntent);
/*
* when all interfaces are actived we call AllInterfacesActived() of OracoloBrain.java
*/
if(getUseOracolo())
if(isAPNEnabled(getApplicationContext()) && isGpsEnable() && isWifiEnabled()){
OracoloBrain.AllInterfacesActived();
}
}
}, 0,MainActivity.getWifiInterval());
//other code of the onStartCommand method...
In the OracoloBrain class (non activity class) I have the static method AllInterfacesActived.
I leave out the code about this method, but in a particular case I want to show a Toast.
I try to create a another class called MyApplication.java:
public class MyApplication extends Application {
private static Context context;
public void onCreate(){
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
So I try to launch toast using this context but nothing to do.
You could create a handler object on the main thread and then you could use it later to show your toast. Below is a sample code that will help you out.
class MyService extends Service{
Handler handler ;
onStartCommand(intent int , int flags,int startId){
handler = new Handler(); // this will get instantiated on the main thread;
new Thread(new Runnable() {
#Override
public void run() {
dispatchMessage("this is a toast");
}
}).start();
}
public void dispatchMessage(final String message) {
handler.post(new Runnable() {
#Override
public void run() {
System.out.println(message);
Toast.makeText(MyService.this, message, Toast.LENGTH_SHORT).show();
}
});
}
}
You can read more on Handlers to understand what I have done to show Toast on other threads apart from main thread.
You can send context of application to your class:
In your non-activity class:
private static Context c;
public static Context getAndSetMyContext(Context c) {
this.c = c;
}
after that you can:
Toast.makeText (c, "YOUR TEXT", Toast.LENGTH_LONG).show();
And in your activity class:
YourClass.getAndSetMyContext(getBaseContext());
What is the super class of your service? If it's IntentService than Toast is not being showed because you're not posting it on main UI thread. You have to do this in this way:
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable {
#Override
public void run() {
Toast.makeText(context/*Use app context here from your Application subclass*/, "", Toast.SHORT);
});
The question is how to communicate with an Android phone to a server, so that if the Activity is left and the call in the Activity was not successful to repeat the transaction once again automatically. Just now I use the AsyncTask of Android to communicate with the server:
new AsyncTask<String, Void, List<String>>() {
#Override
protected void onPreExecute(
showWaitDialog();
}
#Override
protected void onPostExecute(List<String> msgList) {
//here I put the handling after the POST ie. error and success handling
hideWaitDialog();
if (msgList.isEmpty() {
//success handling --> starting an new Activity
} else {
errorView.setText (...);
errorLayout.setVisibility (View.VISIBLE);
}
}
#Override
protected List<String> doInBackground(String... params) {
List<String> msgs = new ArrayList<String>();
try{
//for example submitting an JSONObject
JSONObject result = HttpUtils.sendHttpPost(
AppConstants.WEB_URL, jsonObject);
//error handling on the result
boolean hasErrors = JsonResult.isOk(result);
if (hasErrors) {
// adding errors to msgs list
String[] errorMessages = JsonResult.getErrorMessages (result,...);
fillList (msgs, errorMessages);
return msgs;
}
} catch (CommunicationError er) {
msgs.add (er...);
}
return msgs;
}
}
The problem with this approach is, that if I don't have a successful transmission of the data I must stay in the same Activity. Until now I show an error message to the user and he is in charge to submit by a button again the results to the server.
What I'm looking for is some Activity that remains persistent in the memory which runs later in the case that the transmission wasn't made.
As an application case I use this to dynamically upload pictures for a Waypoint in a map if I pressed that waypoint. In some case it can happens that the connection to the mobile service provider isn't available (mountains, forest, far apart from antenna). Then I want to leave the map Activity and switch to the detail view of this waypoint. In the success case I put the picture into my model classes and make an serialization. If the user clicks again on the same waypoint the picture is not loaded again. In the non success case I don't want to wait that the user clicks against on the waypoint to retrieve the image. In fact I need a background task, some sort of a queue that pictures of waypoints that are already visited on couldn't be retrieved are loaded until the communication part gives back a positive result and the image can be written into the model. The next time the user is pressing the Waypoint the picture will be then present.
Are there any best practices for making such a code implementation?
Is there any example around?
Is there a better way of doing this?
Yes, you need to Implement Intent Service for this requirement
According to the developers website
The IntentService class provides a straightforward structure for running an operation on a single background thread.
For complete details and working source code, Go through the Android Docs
Thanks to the answer of David.
I just read after the suggestion the tutorial at
[1] http://code.tutsplus.com/tutorials/android-fundamentals-intentservice-basics--mobile-6183
After my tests I prefered a Service (not an IntentService)
and created a service: SubmissionService
public class SubmissionIntentService extends Service {
private List<PendingMessage> pMsgList = new CopyOnWriteArrayList<PendingMessage>();
private Handler handler = new Handler();
private boolean hasAppStopped = false;
private Runnable runner;
public SubmissionIntentService() {
super();
Log.d (TAG, "Service created...");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PendingMessage pMessage = (PendingMessage) intent.getParcelableExtra(AppConstants.MESSAGE_OBJECT);
synchronized (pMsgList) {
pMsgList.add(pMessage);
}
if (runner == null) {
handler.postDelayed(runner = initializeRunnable(), 500);
}
return Service.START_NOT_STICKY;
}
private void runAsLongAppIsActive (Runnable runner) {
if (!hasAppStopped) {
handler.postDelayed (runner, SOME_INTERVAL_CONSTANT);
}
}
private Runnable initializeRunnable() {
Runnable result;
result = new Runnable() {
#Override
public void run() {
if (pMsgList.isEmpty()) {
runAsLongAppIsActive (this);
return;
}
PendingMessage[] pMArray = null;
synchronized(pMsgList) {
pMArray = pMsgList.toArray (new PendingMessage[pMsgList.size()]);
}
if (pMArray==null || pMArray.length==0) {
runAsLongAppIsActive (this);
return;
}
Log.d (TAG, "Message List size is actually :"+pMArray.length);
for (PendingMessage pM: pMArray) {
try {
JSONObject jsonMess = JSONSendMessage.buildOutput (pM);
JSONObject result = HttupUtils.sendHttpPost (WEB_URL, jsonMess);
boolean hasErrors = JSONResult.isOk (result);
if (hasErrors) {
//TODO: error handling in case of transmission
//don't remove the message from the queue
runAsLongAppIsActive(this);
return;
}
//remove pending transmission of the queue if success
synchronized (pMsgList) {
pMsgList.remove (pM);
}
//inform over receiver if activity is shown
Intent broadcastIntent = new Intent();
//put data in intent
sendBroadcast (intent);
//more important
WayPointModel model = ModelInstance.getWayPointModel();
model.addToModel (pM, result);
model.store();
} catch (Exception e) {
continue; //try to send other messages
}
}
runAsLongAppIsActive (this);
}
};
return result;
}
}
#Override
public void onDestroy() {
hasAppStopped = true;
handler.removeCallbacks (runner);
super.onDestroy();
}
}
Further I added a ResponseReceiver:
public class ResponseReceiver extends BroadcastReceiver {
public static final String ACTION_RESP = "MESSAGE_PROCESSED";
#Override
public void onReceive(Context context, Intent intent) {
//work in progress...
}
}
and in the Activity where I want to be informed about events:
public class SomeActivity extends Activity {
private ResponseReceiver receiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter(ResponseReceiver.ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new ResponseReceiver();
registerReceiver(receiver, filter);
...
}
}
and finally to send messages over Http:
Intent msgIntent = new Intent(this, SubmissionIntentService.class);
msgIntent.putExtra(...);
startService(msgIntent);
don't forget to declare the service in your manifest:
<service android:name="ch.xxx.app.service.SubmissionIntentService" />
Observations:
- I called the method startService(...) from different Activities. The constructor is only called once.
==> I have just on instance of the service for all Activities (exactly what I need).
What I don't get until now:
- Putting back data to the Activity. What is if the Activity is at the moment no shown?
I'm trying to create 2 applications that communicate by the GCM service.
Let's assume that I'm trying to sends string from app A to B and then from B to A.
I'm very new to the GCM service and I'm alittle bit confused. Every time you see myApiCode I replaced it in my original code with the api code.Here is the A code:
public class MainActivity extends Activity
{
private final String myApiKey = "903137756997";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, "myApiCode");
} else {
Log.v("INFORMATION", "Already registered");
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
And here is the GCMIntentService:
public class GCMIntentService extends GCMBaseIntentService
{
private final String myApiKey = "903137756997";
public GCMIntentService()
{
super("123");
}
...
#Override
protected void onMessage(Context arg0, Intent arg1)
{
Log.d("GCM", "RECIEVED A MESSAGE");
System.out.println("123123123123");
}
...
}
That code that I've attached would be app A and now I will attach the code of app B:
The following code is a service called from the main activity:
public void onCreate()
{
super.onCreate();
Sender sender = new Sender(myApiKey);
Message message = new Message.Builder().build();
Result result = null;
try {
result = sender.send(message, "123", 5);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (result.getMessageId() != null) {
String canonicalRegId = result.getCanonicalRegistrationId();
if (canonicalRegId != null) {
// same device has more than on registration ID: update database
}
} else {
String error = result.getErrorCodeName();
if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
// application has been removed from device - unregister database
}
}
}
I have to mention that both of the apps are running without exceptions but looks like nothing happens.. I guess I have done something wrong with the keys because I still can't understand how app B will find app A.
You have to override the onRegistered method in GCMIntentService. This will be called when the GCM server returns a registration ID that was prompted by your call to GCMRegistrar.register.
Your implementation of this method should upload the String argument to a server you control, and the server can then send out messages targetting the ID you uploaded.
Also you should not push messages directly between apps this way, because it will require you to ship your private API key out in the app package in order to send messages.