Android Wear passing data from Activity back to watchface service - android

I have an Android Wear watch face created, an interactive one. On the face I have a grid area where I listen for an onTapCpmmand:
else if (x >= x6 & x <= x9 & y >= (y5 - (gridYUnit / 2)) & y <= (y8 - (gridYUnit / 2))) {
activityLaunched = new Intent(MyWatchFace.this, ActivityLaunched.class);
activityLaunched .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activityLaunched );
}
The mew activity then takes over. Within this activity, I have three options, basically ImageButtons, with respective onClickHandlers:
optionOne.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
confirmationIntent = new Intent(ActivityLaunched.this, ConfirmationActivity.class);
confirmationIntent .putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE,
ConfirmationActivity.SEARCH_SERVICE);
confirmationIntent .putExtra(ConfirmationActivity.EXTRA_MESSAGE,
getString(R.string.option_one));
currentOption = "1";
broadcastIntent(currentOption);
startActivity(confirmationIntent );
ActivityLaunched.this.finish();
}
});
With broadcastIntent handling the broadcast:
public void broadcastIntent(String currentOption){
Intent optionUpdated = new Intent();
optionUpdated .setAction("com.example.packagename....");
optionUpdated .putExtra("Option", currentOption);
sendBroadcast(optionUpdated );
}
The users selects an options, the activity closes and the flow of control passes to my broadcastReceiver.
Now, I had set up a broadcastReceiver to make a simple toast when an option has been selected. However, I am unable to do any more with this data, other than show a toast.
Within my broadcastReceiver:
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Option Updated." + intent.getStringExtra("Option"), Toast.LENGTH_LONG).show();
currentOption = intent.getStringExtra("Option");
sendOption .setAction("com.example.packagename....");
sendOption tion.putExtra("Option", currentOption );
context.sendBroadcast(sendOption );
Log.d(TAG, "THIS WORKS : " + currentOption );
}
In my WatchFaceService, I have registered the receiver along with the batteryinforeceivers, and any other system ones, as normal. I am receiving the messages within my broadcastReceiver
Back again to my WatchFaceService, it's where I'm getting issues, I'm not receiving any updates:
optionUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle results = getResultExtras(true);
String option = results.getString("Option");
Log.d(TAG, " NOTHING SHOWING HERE " + currentOption + option );
}
};
I have tried using setters and getters, which required a second launch of the activity to get the real value.
Any ideas as to what I am doing wrong?
I've tried following some other answers and ideas here, as well as other external sources. But as it's Android Wear I'm working with, as volatile as the OS is, anything I tried, that was suggested that worked for Android, appears to be ineffective for Android Wear.
Thanks, Emmett

Your watch face service is not guaranteed to be running when there another activity on top of it. It might be preserved, but equally likely it might be torn down. You could try to register your receiver in WatchFaceService.onCreate() and WatchFaceService.onDestroy(), but that's not a way that is guaranteed to work.
Instead, inside the Activity save the information into SharedPreferences and then read the information within you watch face. When your watch face is created, read the value from the prefs (you can also have a listener for the preferences, to update on their change when they change while the watch face is already launched).

I've actually managed to solve it.
I created a second broadcastreceiver, passed this back to the watchface, and then overrode the register / unregister methods to handle the transmission.
Initially, when I had registered the second receiver, it was spamming the log files and crashing the watch. The reason I had to override was to handle the passing of a filter, which cannot be done from within a watchfacecanvas.engine for some strange reason.
Anyway, it's working fine now, but thanks for help

Related

How to utilize Android Nougat's Direct Reply feature with a NotificationListener?

My app is using a NotificationListener to read out messages from various 3rd party apps, for example WhatsApp.
So far I was able to send a reply if only one chat is unread, the code is below.
However, in the case with WhatsApp, getNotification().actions returns a null object when more than two chats are unread, as the messages are bundled together. As you can see in the pictures below, if the notifications are extended there is an option to send a direct reply as well, therefore I am certain that it is possible to utilize this, also I think apps like PushBullet are using this method.
How could I access the RemoteInput of that notification?
public static ReplyIntentSender sendReply(StatusBarNotification statusBarNotification, String name) {
Notification.Action actions[] = statusBarNotification.getNotification().actions;
for (Notification.Action act : actions) {
if (act != null && act.getRemoteInputs() != null) {
if (act.title.toString().contains(name)) {
if (act.getRemoteInputs() != null)
return new ReplyIntentSender(act);
}
}
}
return null;
}
public static class ReplyIntentSender {
[...]
public final Notification.Action action;
public ReplyIntentSender(Notification.Action extractedAction) {
action = extractedAction;
[...]
}
private boolean sendNativeIntent(Context context, String message) {
for (android.app.RemoteInput rem : action.getRemoteInputs()) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putCharSequence(rem.getResultKey(), message);
android.app.RemoteInput.addResultsToIntent(action.getRemoteInputs(), intent, bundle);
try {
action.actionIntent.send(context, 0, intent);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
return false;
}
}
Some explanation how the above code works: Once a notification is received the app tries to get the actions and checks if the name is in the title of a remoteInput (normally it is in the format of "Reply to $NAME"), if that is found the Action is saved into a ReplyIntentSender class, which, when triggered by sendNativeIntent, cycles through all RemoteInputs of that Action and adds the message to the intent. If more than one chat is unread, getNotification().actions returns null.
Below are two screenshots, the first one where it is working without any problems and the second one where it doesn't.
You can consider this as my suggestion. I have done bit research on this and come up with following conclusions.(Also it looks like you have done plenty of research on this so it might be possible that you aware about what I wrote below)
Numerous apps send Wear specific notifications, and many of those contain actions accessible from an Android Wear device. We can grab those Wear notifications on the device, extracting the actions, finding the reply action (if one exists), populating it with our own response and then executing the PendingIntent which sends our response back the original app for it to send on to the recipient.
To do so you can refer this link (A nice workaround by Rob J). You can also refer this link in this context (Great research work done by MichaƂ Tajchert).(You might need to work around with NotificationCompat.isGroupSummary)
This is what I feel(Might be I am totally wrong)
.actions method returns Array of all Notification.Action
structures attached to current notification by addAction(int,
CharSequence, PendingIntent), Here addAction method is deprecated
one so it might not working as intended.
I am not able to test this at my end otherwise I will love to provide a working solution with code.
Hope this will help you. Happy Coding!!!

Get Notification about new/changed/deleted contacts in Android when App is not running

it there a way to get noticed if a new or changed contact is made in Android? I want to get notified when the app starts, if there are any changes. Using a ContentObserver seems to me, that the app must run it in a activity. Or do i have to load all contacts every time from my DB and i am only able to recognize contact changed while my app runs and has an implemented ContentObserver?
i am only able to recognize contact changed while my app runs and has an implemented ContentObserver?
Correct, at least through Android 6.0.
The N Developer Preview has an enhanced JobScheduler that implements a ContentObserver for you, invoking your JobService when a change is detected. Unless there are problems, we can expect that enhanced JobScheduler to ship in the next release of Android, and you can opt into using it on newer Android devices.
Ok, what i did now is: Using a background service and build up an ContentObservice in the onCreate() function. Finally declaring it in the manifest. It will of course not work if the App is totally closed but if it is in background. Thats enough for me. It detects changes to the contacts. Are there any disadvantages in using this approach?
This is the service:
public class ContactsChangeService extends IntentService {
/**
* An IntentService must always have a constructor that calls the super constructor. The
* string supplied to the super constructor is used to give a name to the IntentService's
* background thread.
*/
public ContactsChangeService() {
super("ContactsChangeReceiver");
}
#Override
public void onCreate() {
super.onCreate();
//if created make an Observer
ContactsChangeObserver contentObserver = new ContactsChangeObserver();
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contentObserver);
Log.d(Constants.TAG, "[" + Constants.CONTACTS_OBSERVER_SERVICE + "] " + "started");
}
#Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
//...
// Do work here, based on the contents of dataString
//...
}
}
This is the Observer:
public class ContactsChangeObserver extends ContentObserver{
public ContactsChangeObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(Constants.TAG, "[" + Constants.CONTACTS_OBSERVER_SERVICE + "] " + "Change in Contacts detected");
}
}
And this is the manifest entry:
<service
android:name=".service.ContactsChangeService"
android:exported="true">
</service>

Android Espresso 2 end phone call

In my app, I have a button that pops up a dialog "Call xxxx-xxxx" Yes / No. After clicking Yes the number shall be called.
This is the test code:
#Test
public void testPhoneButton() {
clickContactTab();
ViewInteraction phoneButtonInteraction = Espresso.onView(ViewMatchers.withId(R.id.button_phone));
phoneButtonInteraction.perform(ViewActions.scrollTo());
phoneButtonInteraction.perform(ViewActions.click());
Espresso.onView(ViewMatchers.withText(R.string.dialog_phone_title)).inRoot(RootMatchers.isDialog()).check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
Espresso.onView(ViewMatchers.withId(android.R.id.button2)).perform(ViewActions.click());
Intents.assertNoUnverifiedIntents();
phoneButtonInteraction.perform(ViewActions.click());
Espresso.onView(ViewMatchers.withId(android.R.id.button1)).perform(ViewActions.click());
Intents.intended(Matchers.allOf(IntentMatchers.hasAction(Intent.ACTION_CALL), IntentMatchers.hasData(Uri.parse("tel:" + tel))));
}
Everything works fine, but how can I cancel the phone call after the test?
yogurtearls answer works for me, thanks:
#Test
public void testPhoneButton() {
clickContactTab();
ViewInteraction phoneButtonInteraction = Espresso.onView(ViewMatchers.withId(R.id.button_phone));
phoneButtonInteraction.perform(ViewActions.scrollTo());
phoneButtonInteraction.perform(ViewActions.click());
Espresso.onView(ViewMatchers.withText(R.string.dialog_phone_title)).inRoot(RootMatchers.isDialog()).check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
Espresso.onView(ViewMatchers.withId(android.R.id.button2)).perform(ViewActions.click());
Intents.assertNoUnverifiedIntents();
phoneButtonInteraction.perform(ViewActions.click());
Intent stubIntent = new Intent();
Instrumentation.ActivityResult stubResult = new Instrumentation.ActivityResult(Activity.RESULT_OK, stubIntent);
Intents.intending(IntentMatchers.hasAction(Intent.ACTION_CALL)).respondWith(stubResult);
Espresso.onView(ViewMatchers.withId(android.R.id.button1)).perform(ViewActions.click());
Intents.intended(Matchers.allOf(IntentMatchers.hasAction(Intent.ACTION_CALL), IntentMatchers.hasData(Uri.parse("tel:" + tel))));
}
You should use Intent stubbing.
You can avoid actually bringing up the dialer, and instead check that the right intent was sent.
Before you click the yes button, call intendING .

Android - Listening to NFC Adapter State Changed

I am trying to build an application which uses NFC. The goal is to display a DialogFragment containing a button link to go the settings and change it manually and when the feature is enabled, disable the DialogFragment.
Problem: If the user enables/disables NFC using the icon in the pull down notifications tray , then the onPause/onResume doesn't get called and misses the condition entirely.
I am sure there is a receiver that I can register to instead and respond appropriately in real time. Any ideas, thoughts or reference will be greatly appreciated!
The following code checks if the state is enabled/disabled. I am also responding to it appropriately in the onResume event.
NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
NfcAdapter adapter = manager.getDefaultAdapter();
if(adapter != null && adapter.isEnabled()) {
detector = new NfcDetector(this);
detector.setListener(this);
onNfcFeatureFound();
}
else {
onNfcFeatureNotFound();
}
For others looking at this post, the code below will take the user directly into settings to enable/disable NFC:
startActivity(new Intent(android.provider.Settings.ACTION_NFC_SETTINGS));
Thought I should post the answer for other people looking for the same problem, since I wasn't able to find one easily.
Add the following code to your activities onCreate() method:
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
this.registerReceiver(mReceiver, filter);
Inner private class declared within your activity (or anywhere else you like):
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) {
final int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
NfcAdapter.STATE_OFF);
switch (state) {
case NfcAdapter.STATE_OFF:
break;
case NfcAdapter.STATE_TURNING_OFF:
break;
case NfcAdapter.STATE_ON:
break;
case NfcAdapter.STATE_TURNING_ON:
break;
}
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
// Remove the broadcast listener
this.unregisterReceiver(mReceiver);
}
// The following check needs to also be added to the onResume
#Override
protected void onResume()
super.onResume();
// Check for available NFC Adapter
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
if(adapter != null && adapter.isEnabled()) {
createNfcDetector();
//NFC is available on device, but disabled
}
else {
//NFC Is available and enabled
}
}
You can use ACTION_ADAPTER_STATE_CHANGED to receive a broadcast message when the state of the adapter changes, but that option is only available in API 18 and above. See this for the documentation.
For prior to 18, I don't know of a way to do this unfortunately.
Also, as an aside, the android.provider.Settings.ACTION_NFC_SETTINGS will work on API levels 16 and above. For prior versions, the NFC settings are under "wireless settings". Take a look at the ensureSensorIsOn method at the bottom of this blog post for a code sample that checks against the API level and redirects to the correct settings pane.

OnRealTimeMessageSent callback not being fired reliably?

With Google Play Game Services, I'm trying to implement a CallBack such that, if there is an issue with sending a message, then I need to resolve it (as each player "passes" their bid to the next player, and all other players need to see what the player that passed bid)
I thought I would try and use the following to instantiate a RealTimeReliableMessageSentListener for that round of messages, so that I can tell if the message was sent and received by everyone:
(I add the tokenID returned by the call to an ArrayList, and then check off remove each tokenID as it comes back in to track when all messages from this round are received)
#Override
public void sendReadyToPlay() {
dLog("Sending ReadyToPlay");
// Broadcast that I'm ready, and see that they are ready
if (!mMultiplayer){
return; // playing against computer
}
// First byte in message indicates whether it's a final score or not
mMsgBuf[0] = (byte) ('R');
readyToPlayTokens.clear();
// Send to every other participant.
for (Participant p : mParticipants) {
dLog("Participant:" + p.getParticipantId());
if (p.getParticipantId().equals(mMyId)) {
continue;}
if (p.getStatus() != Participant.STATUS_JOINED){
continue;
}
readyToPlayTokens.add(mHelper.getGamesClient().sendReliableRealTimeMessage(new RealTimeReliableMessageSentListener() {
#Override
public void onRealTimeMessageSent(int statusCode, int tokenId, String recipientParticipantId){
dLog("onRealTimeMessageSent number two and size is: " + readyToPlayTokens.size());
if(readyToPlayTokens.contains(tokenId)){
readyToPlayTokens.remove(tokenId);
}
dLog("onRealTimeMessageSent number two and size is: " + readyToPlayTokens.size());
if (statusCode != GamesClient.STATUS_OK) {
mGHInterface.onRealTimeMessageReceived("RTPProblem:" + recipientParticipantId);
} else if (readyToPlayTokens.size() == 0) {
mGHInterface.beginRound();
}
}
}, mMsgBuf, mRoomId, p.getParticipantId()));
dLog("sent to:" + p.getParticipantId());
}
}
I can see the messages coming in almost every time from one device to another, so I can see that the messages are going through, BUT, the RealTimeReliableMessageSent listener is only being fired about 50-60 percent of the time, which isn't very reliable! :)
Can anyone see what I might be doing wrong to keep the listener from firing reliably?
may be it happens because you are using anonymous inner class, try to use in different way like implementing your activity RealTimeReliableMessageSentListener

Categories

Resources