I'm using nearby connections to create a connection between devices. When a connection has been established, the activity is changed. Now if a device disconnects then a callback is called, but its received in the old activity. Now I want to show an AlertDialog, but the dialog is never shown because it shows on the old activity. How can show the dialog on the new activity?
I'm using nearby connections like this: URL
private Activity mActivity;
public RemoteConnection(Activity activity){
mActivity = activity;
}
// For simplicity I did only include this method
#Override
public void onDisconnected(String endpointId) {
// We've been disconnected from this endpoint. No more data can be
// sent or received.
new AlertDialog.Builder(mActivity)
.setTitle("Disconnection")
.setMessage("Other device is disconnected")
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
}
The dialog should be shown on the current activity
Write a method in your activities as:
public void showAlert(activity)
{
new AlertDialog.Builder(activity)
.setTitle("Disconnection")
.setMessage("Other device is disconnected")
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
}
and change following code :
#Override
public void onDisconnected(String endpointId) {
// We've been disconnected from this endpoint. No more data can be
// sent or received.
new AlertDialog.Builder(mActivity)
.setTitle("Disconnection")
.setMessage("Other device is disconnected")
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
}
To:
#Override
public void onDisconnected(String endpointId) {
// We've been disconnected from this endpoint. No more data can be
// sent or received.
if(((YourActivityA)activity) != null)
{
((YourActivityA)activity).showAlert(activity);
}
else if(((YourActivityB)activity) != null)
{
((YourActivityB)activity).showAlert(activity);
}
}
Use an event bus implementation or send the callback using sendBroadcast() and have each Activity register a BroadcastReceiver in onResume() and unregister it in onPause().
Related
I'm building an app that hooks on the stock Dialer (Marshmallow API). My goal is to get incoming and place outgoing calls, while getting a handle on the Connection objects to manipulate the Connection's methods.
I have registered PhoneAccount with the CAPABILITY_CALL_PROVIDER.
PhoneAccount.Builder builder = new PhoneAccount.Builder(phoneAccountHandle, "CustomAccount");
builder.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER);
PhoneAccount phoneAccount = builder.build();
telecomManager.registerPhoneAccount(phoneAccount);
My account is visible inside the stock Dialer app (Settings-> Calls-> Calling Accounts) and I have enabled it.
I have a Service that monitors Phone State and on CALL_STATE_RINGING it calls TelecomManager's addNewIncomingCall() method.
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
Toast.makeText(getApplicationContext(), "Phone Is Ringing",
Toast.LENGTH_SHORT).show();
Bundle extras = new Bundle();
Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, incomingNumber, null);
extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri);
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
telecomManager.addNewIncomingCall(phoneAccountHandle, extras);
}
if (state == TelephonyManager.CALL_STATE_OFFHOOK) {.......}
...
}
My custom Connection Service:
#Override
public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
Toast.makeText(getApplicationContext(), "onCreateIncomingConnection called", Toast.LENGTH_SHORT).show();
Connection incomingCallCannection = createConnection(request);
incomingCallCannection.setRinging();
return incomingCallCannection;
}
#Override
public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
Toast.makeText(getApplicationContext(), "onCreateOutgoingConnection called", Toast.LENGTH_SHORT).show();
Connection outgoingCallConnection = createConnection(request);
outgoingCallConnection.setDialing();
return outgoingCallConnection;
}
private Connection createConnection(ConnectionRequest request) {
mConnection = new Connection() {
#Override
public void onStateChanged(int state) {
super.onStateChanged(state);
}
#Override
public void onDisconnect() {
super.onDisconnect();
mConnection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
mConnectionsAvailableForConference.clear();
mConnection.destroy();
}
#Override
public void onSeparate() {
super.onSeparate();
}
#Override
public void onAbort() {
super.onAbort();
mConnection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
mConnection.destroy();
}
#Override
public void onHold() {
super.onHold();
}
#Override
public void onAnswer() {
super.onAnswer();
mConnection.setActive();
}
#Override
public void onReject() {
super.onReject();
mConnection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
mConnection.destroy();
}
};
mConnection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
mConnection.setExtras(request.getExtras());
return mConnection;
}
Now, both ConnectionService's callback methods get called on incoming and outgoing calls respectively. The problem is, when I go to the Dialer and place an outgoing call (using my PhoneAccount) I get the dialing screen (inCallUI ?), with the right caller info being shown (contact name, tel # etc..), but the line doesn't ring in my earpiece and the call is not established (the telephone number that should be receiving the call doesn't ring).
I tried returning super.onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) in the callback instead of creating my own Connection object, and I get the same behavior.
TLDR: my app communicates with the Dialer, is able to place a call and show the dialing screen, but the phone line doesn't ring and nothing happens.
I have been on this for days finding a solution. But after going through the documentation over again it clearly stated that placing outgoing call with a custom PhoneAccount does not use the phone sim service to make the call, it the app that will handle all the call operation by itself.
CAPABILITY_CALL_PROVIDER: Flag indicating that this PhoneAccount can make phone calls in place of traditional SIM-based telephony
calls.
if you need to transfer data during outgoing call you can use the Bundle to send info to the default call app.
you can read more on the documentation here.
https://developer.android.com/reference/android/telecom/PhoneAccount
https://developer.android.com/guide/topics/connectivity/telecom/selfManaged#outgoing
I'm working on an Android app that communicates with a Cast receiver app.
Connecting to the app works (I can see the app appear on the tv), but I'm having difficulties getting the custom channel to work.
In the onCreate of my Activity I get the CastContext and add my SessionManagerLister.
mCastContext = CastContext.getSharedInstance(this);
mCastContext.getSessionManager().addSessionManagerListener(getSessionManagerListener(), CastSession.class);
getSessionManagerListener() returns the listener where I register my MessageReceivedCallback:
private SessionManagerListener<CastSession> getSessionManagerListener()
{
return new SessionManagerListener<CastSession>()
{
#Override
public void onSessionStarted(CastSession castSession, String s)
{
try
{
castSession.setMessageReceivedCallbacks("urn:x-cast:be.myappname.player.cast.v1", new Cast.MessageReceivedCallback()
{
#Override
public void onMessageReceived(CastDevice castDevice, String s, String s1)
{
System.out.println("never reaches this callback");
}
});
}
catch (IOException e)
{
e.printStackTrace();
}
}
... other methods omitted ...
}
}
When I tap the Toolbar cast button I can select a device, which triggers the onSessionStarted in the SessionManagerListener (this also starts the receiver app on the tv). I then add the MessageReceivedCallback, but its callback never gets called.
Inspecting my Cast device in Chrome does show the data I'm expecting to receive, it just never seems to reach my Android code.
cast_receiver.js:67 [667.202s] [cast.receiver.IpcChannel] IPC message
[667.202s] [cast.receiver.IpcChannel] IPC message sent: {"namespace":"urn:x-cast:be.myappname.player.cast.v1","senderId":"7c442884-74e6-a388-243c-58b4ab3a4527.3471:com.google.sample.cast.refplayer.tutorial-512","data":"{\"type\":\"login request\"}"}
A colleague is working on the iOS app and that one does receive the callback.
Try the following in onSessionStarted
CastContext cc = CastContext.getSharedInstance(this);
SessionManager sm = cc.getSessionManager();
if (sm != null) {
CastSession cs = sm.getCurrentCastSession();
if (cs != null) {
try {
MyCastChannel mcc = new MyCastChannel();
cs.setMessageReceivedCallbacks("urn:x-cast:be.myappname.player.cast.v1",mcc);
}
catch (IOException e) {
}
}
}
public class MyCastChannel implements Cast.MessageReceivedCallback
{
#Override
public void onMessageReceived(CastDevice castDevice, String namespace, String message)
{
// do your thing
}
}
I had the same problem, this is how I managed to get the message to be sent:
context.sendCustomMessage(namespace, undefined, JSON.stringify({
"a": "b"
}));
This is the javascript on the receiver side. So you need the "undefined" param and also use JSON.stringify(), otherwise the message gets silently dropped.
The undefined means "send to all", but you should probably specify sender-id there.
This is in the v3 API.
In my case, it was more subtle.
The callback worked absolutely fine when the cast session was initiated for the first time. When the user presses the cast button the receiver is registered for the message callback.
override fun onSessionStarted(castSession: CastSession?, p1: String?) {
liveViewModel.requestPause()
castSession?.let {
setCastChannelReceiver(it, this#myActivity)
loadRemoteMedia(it, buildChromeCastInfo())
}
}
fun setCastChannelReceiver(castSession: CastSession?, receiver: CastMessageReceiver) {
castSession?.let {
castChannel.addReceiver(receiver, castSession)
it.setMessageReceivedCallbacks(castChannel.nameSpace, castChannel)
}
}
Although when the user use to kill the Activity which initiated the cast session and then after traversing other parts of app use to again visit the Activity, the callback failed to work.
Remember, when the user visits the Activity for the second time, the CastSession is already connected. As a result the onSessionStarted(castSession: CastSession, p1: String) method is never called.
I was under the assumption that once the receiver has been registered for the session, it need not be registered again. But still for some reason the callback never worked.
As a final resort, just to be assured I re-registered the receiver in the OnCreate() of the Activity.
override fun onCreate(out:Bundle){
....
setCastChannelReceiver(castSession, receiver)
....
}
fun setCastChannelReceiver(castSession: CastSession?, receiver: CastMessageReceiver) {
castSession?.let {
castChannel.addReceiver(receiver, castSession)
it.setMessageReceivedCallbacks(castChannel.nameSpace, castChannel)
}
}
And it worked!!
NOTE: For me, the communication between the Sender(Android App) and Cast Receiver only occurred when the string messages were in JSON format.
is it possible to start reading from a nfc-chip after pressing a button. After pushing the button there should be a message like "please hold your nfc chip close to the device..". I only see tutorials, which show how to start the application when holding a nfc chip to the device (onNewIntent).
Second Question. What if the application is already running and i hold the nfc chip next to my device? Is it forcing a destroy and then launches again?
Thanks!
Regarding the first part of your question, you could use a flag within your activity that indicates the state of your application (ready to write/message is showing, not ready to write/message not showing). YOu can find a simple example here:
private static final int DIALOG_WRITE = 1;
private boolean mWrite = false; // write state
public void onCreate(Bundle savedInstanceState) {
[...]
// Set action for "Write to tag..." button:
mMyWriteButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Switch to write mode:
mWrite = true;
// Show a dialog when we are ready to write:
showDialog(DIALOG_WRITE);
}
});
[...]
}
protected Dialog onCreateDialog(int id, Bundle args) {
switch (id) {
case DIALOG_WRITE:
// A dialog that we show when we are ready to write to a tag:
return new AlertDialog.Builder(this)
.setTitle("Write to tag...")
.setMessage("Touch tag to start writing.")
.setCancelable(true)
.setNeutralButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int arg) {
d.cancel();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface d) {
mWrite = false;
}
}).create();
}
return null;
}
// You would call this method from onCreate/onStart/onResume/onNewIntent
// or from whereever you want to process an incoming intent
private void resolveIntent(Intent data, boolean foregroundDispatch) {
String action = data.getAction();
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
// The reference to the tag that invoked us is passed as a parameter (intent extra EXTRA_TAG)
Tag tag = data.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (mWrite) {
// We want to write
mWrite = false;
// TODO: write to tag
} else {
// not in write-mode
// TODO: read tag or do nothing
}
}
}
Regarding the second part of your question, when you want to receive NFC tag discovery events while your activity is already in the foreground, you should register with the NFC foreground dispatch system. See Advanced NFC: Using the NFC Foreground Dispatch System. There is no need to destroy and re-create your activity.
I'm pretty new with Android programming. But I have been working on this for over a week now, and it starts to get booooring.
My idea is that I want to connect two devices using Wifi Direct. But I only want to connect to those which are running my application. Besides, I want the users to be able to see some information of the other devices (such as user name), not just the MAC or the Android_XXXX name included in the WifiP2pDevice. That's why I decided that a device looking for other devices, should both start the application service and search for peers which are also broadcasting this service.
The problem (I'm testing with two real devices) is that, even though they are running exactly the same code, only one of them is getting the service discovery callbacks (the onDnsSd... listeners below). So, one side acts in the proper way, but not the other. Moreover I'm getting "old" services, meaning that apparently each time I start de service (even though I cancel previously started services), that service seems to be still broadcast during at least some minutes.
I include a shortened version of my code:
public class MoveFlufietsDialogFragment extends DialogFragment implements ChannelListener, DeviceActionListener {
public final HashMap<String, FlufietsPeer> mBuddies = new HashMap<String, FlufietsPeer>();
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
...
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
mManager = (WifiP2pManager) getActivity().getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(getActivity(), getActivity().getMainLooper(), null);
...
startRegistration();
discoverFlufietsService();
...
}
public void discoverFlufietsService() {
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
#Override
public void onDnsSdTxtRecordAvailable(String fullDomain, Map record, WifiP2pDevice device) {
// This and the next listener are only called in one of the devices.
String serviceName = (String) record.get("serviceName");
if ((serviceName != null) && (serviceName.equals("flufiets")) {
// I put the record data in the mBuddies HashMap.
...
mBuddies.put(device.deviceAddress, myPeerDataStructure);
}
}
};
DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
#Override
public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice resourceType) {
if (mBuddies.containsKey(resourceType.deviceAddress)) {
FlufietsPeer flufietsPeer = mBuddies.get(resourceType.deviceAddress);
WiFiPeerListAdapter adapter = ((WiFiPeerListAdapter) mFragmentList.getListAdapter());
adapter.add(flufietsPeer);
adapter.notifyDataSetChanged();
}
}
};
mManager.setDnsSdResponseListeners(mChannel, servListener, txtListener);
WifiP2pDnsSdServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
mManager.addServiceRequest(mChannel, serviceRequest, new ActionListener() {
// onSuccess/onFailure toasts.
});
mManager.discoverServices(mChannel, new WifiP2pManager.ActionListener() {
// onSuccess/onFailure toasts.
});
}
public void startRegistration() {
mManager.clearLocalServices(mChannel, new ActionListener() {
// onSuccess/onFailure toasts.
});
Map record = new HashMap();
record.put("serviceName", "flufiets");
...
WifiP2pDnsSdServiceInfo serviceInfo = WifiP2pDnsSdServiceInfo.newInstance(flufietsService, "_tcp", record);
mManager.addLocalService(mChannel, serviceInfo, new ActionListener() {
// onSuccess/onFailure toasts.
});
}
#Override
public void onResume() {
super.onResume();
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
getActivity().registerReceiver(mReceiver, mIntentFilter);
}
#Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mReceiver);
}
#Override
public void onStop() {
super.onStop();
mManager.clearLocalServices(mChannel, new ActionListener() {
// onSuccess/onFailure toasts.
});
}
...
}
The problem doesn't seem to be related with the device itself (sometimes it works, sometimes it doesn't, but always only in one of them). I suspect it has to do with either trying to discover a service that we ourselves are broadcasting, or having the same service being offered by two devices. I have tried changing the names of the service, so each device would offer either a "send" or "receive" service, but it doesn't work. I only get the callbacks called (onDnsSd...) in one of the devices.
And that thing about getting old services, when I always clear them, is weird (I do include a timestamp in the service record data, and I could always discard all but the last, but doesn't seem to be logical).
Any ideas? ANY help would be VERY appreciated, because writing the application is not funny any more (:-)=
Thanks a lot!
You need to wait until the clearLocalService call succeeds before adding the local service later. So put the addLocalService call into the onSuccess callback of the clearLocalServices.
I am developing a Bluetooth chat application with the help of the Bluetooth chat application on the Android developer site.
I did some modifications in existing code as when I send Bluetooth chat requests to another device (assume we are paired previously) and request receiving device resides in any activity.
I want to display an alert to the current displaying screen, but I am getting an alert at a particular activity only. So I am getting stuck. My code is below for the sender and receiver side:
if (FaceCardList.requestConnection &&
FaceCardList.deviceSelected) {
String authorization = "messagetype:startChat,"
+ FaceCardList.ScreenName;
FaceCardList.this.sendMessage(authorization);
}
On the receiving side:
if (readMessage.indexOf("messagetype:startChat") != -1) {
AlertDialog.Builder builder =
new AlertDialog.Builder(FaceCardList.this);
builder.setMessage(
FaceCardList.screennmname + " wants to chat with you")
.setCancelable(false)
.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
Intent serverIntent = new Intent(
FaceCardList.this,
BluetoothChat.class);
startActivity(serverIntent);
String authorization = "messagetype:initiatechat";
FaceCardList.this
.sendMessage(authorization);
}
})
.setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface diaLog,
int which) {
// TODO Auto-generated method stub
String authorization = "messagetype:stopservice";
FaceCardList.this
.sendMessage(authorization);
FaceCardList.mChatService.stop();
FaceCardList.mChatService.start();
}
});
alert = builder.create();
alert.show();
If you have one central communication activity which is receiving requests you may just use a Handler to communicate with any other activity easily.
See here for reference:http://developer.android.com/reference/android/os/Handler.html
There are many tutorials and examples.
Using handlers is extremely easy and yet flexible - it does not block your activity but allows any kind of communication, even passing parameters
You may create a central communicator class with static methods and send all your requests via this central point to dispatch to whatever activity you like depending on a defined logic within this one point.
Here an example for such a method with parameters:
static public void sendMessageToDialogDuringChat(
int msgID, String msgText, boolean statusBool) {
if (theHandlerForScreenUpdate == null) // you need to create the receiving handler within this class beforehand
return;
Message msgForDialogDuringChat= theHandlerForScreenUpdate
.obtainMessage(msgID);
Bundle bundle = new Bundle();
bundle.putBoolean(msgText, statusBool);
msgForDialogDuringChat.setData(bundle);
theHandlerForScreenUpdate
.sendMessage(msgForDialogDuringChat);
}
and here a handler to receive the message. Be sure that within the central dispatcher you have stored the appropriate handler which should receive the message.
public final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (DEBUG)
Log.i(this.getClass().getSimpleName(),
"-> "
+ Thread.currentThread().getStackTrace()[2]
.getMethodName() + " msg.what="
+ msg.what);
switch (msg.what) {
case MESSAGE_CONNECTION_SUCCESSFULL_FLAG: {
boolean connectionOK = msg.getData().getBoolean(
CONNECTION_SUCCESS);
if (connectionOK)
theStatusTV
.setText(getString(R.string.connection_OK));
else {
theStatusTV
.setText(getString(R.string.connection_NOT_OK));
}
}
break;
case MESSAGE_ALERT_FLAG: {
...start your alert here...
Certainly you could do all of that in the receiving class as well - depends how you need to structure your "special" communication messages