I use quickblox for create video call
Start call work, but incoming service not work for opponent user
VideoActivity
public class VideoActivity extends E implements QBRTCClientSessionCallbacks,QBRTCClientVideoTracksCallbacks {
private int userid;
private Boolean isOutgoing,micE=true,vidE=true;
private QBRTCSurfaceView surfaceView,remoteview;
private MediaPlayer mp;
private QBRTCSession currentsession;
private QBMediaStreamManager mediaStreamManager;
private ImageView mic,video;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.video);
userid=getIntent().getIntExtra("user", 1);
userid = 39753771;
isOutgoing=getIntent().getBooleanExtra("isoutgoing",false);
isOutgoing = true;
ProcessCalls();
InitSignalling();
if (isOutgoing) {
//CallUser();
SetCallerName(userid);
}
Log.i("errorCheck", "Nz: " + userid);
Log.i("errorCheck", "Na: " + isOutgoing);
if(getIntent().getBooleanExtra("service",false)){
Log.i("errorCheck", "x");
}
mic=(ImageView)findViewById(R.id.mic);
mic.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (micE) {
micE = false;
AudioManage();
} else {
micE = true;
AudioManage();
}
}
});
video=(ImageView)findViewById(R.id.video);
video.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (vidE) {
vidE = false;
VideoManage();
} else {
vidE = true;
VideoManage();
}
}
});
surfaceView =(QBRTCSurfaceView)findViewById(R.id.localView);
surfaceView.setMirror(true);
surfaceView.requestLayout();
remoteview=(QBRTCSurfaceView)findViewById(R.id.opponentView);
remoteview.requestLayout();
}
private void SetCallerName(Integer callerID) {
TextView callerName=(TextView)findViewById(R.id.callername);
TextView callertime=(TextView)findViewById(R.id.callTime);
callerName.setText(callerID + " , You:");
if(isOutgoing){
callertime.setText("Calling...");
}
}
private void InitSignalling() {
final QBChatService chatService = QBChatService.getInstance();
Log.i("errorCheck", "ERR1: " + G.userQB);
chatService.login(G.userQB, new QBEntityCallback() {
#Override
public void onSuccess(Object result, Bundle params) {
Log.i("errorCheck", "R0: " + result);
Log.i("errorCheck", "R1: " + params);
chatService.getVideoChatWebRTCSignalingManager().addSignalingManagerListener(new QBVideoChatSignalingManagerListener() {
#Override
public void signalingCreated(QBSignaling qbSignaling, boolean createdLocally) {
Log.i("errorCheck", "Q1: " + qbSignaling);
Log.i("errorCheck", "Q2: " + createdLocally);
if (!createdLocally) {
QBRTCClient.getInstance(VideoActivity.this).addSignaling((QBWebRTCSignaling) qbSignaling);
}
}
});
QBRTCClient.getInstance(G.currentActivity).addSessionCallbacksListener((QBRTCSessionEventsCallback) G.currentActivity);
QBRTCConfig.setDebugEnabled(true);
QBRTCClient.getInstance(getApplicationContext()).prepareToProcessCalls();
Log.i("errorCheck", "chatService1: " + chatService);
if(G.userMobile.equals("09139479548")) {
CallUser();
}
}
#Override
public void onError(QBResponseException errors) {
Log.i("errorCheck", "E1: " + errors);
}
});
}
private void ProcessCalls() {
QBRTCClient.getInstance(this).prepareToProcessCalls();
}
private void CallUser() {
List<Integer> opponents = new ArrayList<Integer>();
opponents.add(userid);
Map<String, String> userInfo = new HashMap<>();
userInfo.put("key", "value");
QBRTCTypes.QBConferenceType qbConferenceType = QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO;
QBRTCSession session = QBRTCClient.getInstance(G.currentActivity).createNewSessionWithOpponents(opponents, qbConferenceType);
session.addVideoTrackCallbacksListener(this);
session.startCall(userInfo);
Log.i("errorCheck", "QR1: " + session);
SetCallButtonsDialing(session,userInfo);
StartDialRinging();
}
#Override
public void onReceiveNewSession(QBRTCSession qbrtcSession) {
Log.i("errorCheck","x");
qbrtcSession.addVideoTrackCallbacksListener(this);
Map<String,String> userInfo = qbrtcSession.getUserInfo();
SetLayoutForReceiveCall(qbrtcSession,userInfo);
}
private void SetLayoutForReceiveCall(final QBRTCSession qbrtcSession,final Map<String, String> userInfo) {
final FrameLayout receive=(FrameLayout)findViewById(R.id.answerlayout);
receive.setVisibility(View.VISIBLE);
qbrtcSession.addVideoTrackCallbacksListener(this);
final ImageView calll=(ImageView)findViewById(R.id.answerCall);
calll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Accept incoming call
qbrtcSession.acceptCall(userInfo);
receive.setVisibility(View.GONE);
SetCallButtonsDialing(qbrtcSession, userInfo);
StartTimer();
if (mp != null && mp.isPlaying()) {
mp.stop();
}
}
});
findViewById(R.id.rejectcall).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
qbrtcSession.rejectCall(userInfo);
VideoActivity.this.finish();
if(mp!=null&&mp.isPlaying())
{
mp.stop();
}
}
});
}
#Override
public void onUserNoActions(QBRTCSession qbrtcSession, Integer integer) {
Toast.makeText(this, "no action by user", Toast.LENGTH_SHORT).show();
if(mp!=null&&mp.isPlaying())
{
mp.stop();
}
}
#Override
public void onSessionStartClose(QBRTCSession qbrtcSession) {
qbrtcSession.addVideoTrackCallbacksListener(this);
try {
qbrtcSession.getMediaStreamManager().setVideoCapturer(new QBRTCCameraVideoCapturer(this, null));
mediaStreamManager = qbrtcSession.getMediaStreamManager();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onUserNotAnswer(QBRTCSession qbrtcSession, Integer integer) {
Toast.makeText(this, "No answer", Toast.LENGTH_SHORT).show();
if(mp!=null&&mp.isPlaying())
{
mp.stop();
}
finish();
}
#Override
public void onCallRejectByUser(QBRTCSession qbrtcSession, Integer integer, Map<String, String> map) {
Toast.makeText(this, "Call rejected", Toast.LENGTH_SHORT).show();
if(mp!=null&&mp.isPlaying())
{
mp.stop();
}
finish();
}
#Override
public void onCallAcceptByUser(QBRTCSession qbrtcSession, Integer integer, Map<String, String> map) {
qbrtcSession.addVideoTrackCallbacksListener(this);
if(mp!=null&&mp.isPlaying())
{
mp.stop();
}
StartTimer();
}
#Override
public void onReceiveHangUpFromUser(QBRTCSession qbrtcSession, Integer integer, Map<String, String> map) {
if(mp!=null&&mp.isPlaying())
{
mp.stop();
}
Toast.makeText(this, "Call ended by user", Toast.LENGTH_SHORT).show();
finish();
}
#Override
public void onSessionClosed(QBRTCSession qbrtcSession) {
if(mp!=null&&mp.isPlaying())
{
mp.stop();
}
}
#Override
public void onLocalVideoTrackReceive(QBRTCSession qbrtcSession, QBRTCVideoTrack qbrtcVideoTrack) {
Log.i("errorCheck", "WW1: " + qbrtcSession);
qbrtcVideoTrack.addRenderer(new VideoRenderer(surfaceView));
surfaceView.setMirror(true);
surfaceView.requestLayout();
}
#Override
public void onRemoteVideoTrackReceive(QBRTCSession qbrtcSession, QBRTCVideoTrack qbrtcVideoTrack, Integer integer) {
Log.i("errorCheck", "WW2: " + qbrtcSession);
qbrtcVideoTrack.addRenderer(new VideoRenderer(remoteview));
mediaStreamManager = qbrtcSession.getMediaStreamManager();
remoteview.requestLayout();
}
public void StartDialRinging(){
try {
mp = MediaPlayer.create(getApplicationContext(), R.raw.beep);
mp.setLooping(true);
mp.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
VideoCallService
public class VideoCallService extends Service implements QBRTCClientSessionCallbacks {
private Timer mTimer = null;
private Handler mHandler = new Handler();
public static final int notify = 300000;
public VideoCallService() {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
Log.wtf("service", "start");
if (mTimer != null) {
Log.wtf("service", "All ready started");
} else {
mTimer = new Timer(); //recreate new
mTimer.scheduleAtFixedRate(new TimeDisplay(), 0, notify);
LoginChatService();
ProcessCalls();
InitSignalling();
QBRTCClient.getInstance(this).addSessionCallbacksListener(this);
}
}catch (Exception e){
Log.wtf("ex",""+e);
}
return START_NOT_STICKY;
}
private void InitSignalling() {
QBChatService.getInstance().getVideoChatWebRTCSignalingManager()
.addSignalingManagerListener(new QBVideoChatSignalingManagerListener() {
#Override
public void signalingCreated(QBSignaling qbSignaling, boolean createdLocally) {
if (!createdLocally) {
QBRTCClient.getInstance(VideoCallService.this).addSignaling((QBWebRTCSignaling) qbSignaling);
}
}
});
}
private void ProcessCalls() {
QBRTCClient.getInstance(this).prepareToProcessCalls();
}
#Override
public void onReceiveNewSession(QBRTCSession qbrtcSession) {
//DataHolder.getInstance().setServiceData(qbrtcSession,qbrtcSession.getUserInfo());
startActivity(new Intent(this,VideoActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).putExtra("service",true));
}
}
Manifest
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<service android:name=".VideoCallService" />
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.quickblox.sample.groupchatwebrtc" />
</intent-filter>
</receiver>
<service
android:name=".GcmPushListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<service
android:name="com.quickblox.messages.services.gcm.QBGcmPushInstanceIDService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID" />
</intent-filter>
</service>
<meta-data android:name="com.quickblox.messages.TYPE" android:value="GCM" />
<meta-data android:name="com.quickblox.messages.SENDER_ID" android:value="1" />
<meta-data android:name="com.quickblox.messages.QB_ENVIRONMENT" android:value="DEVELOPMENT" />
After start Call, session created for opponent
This my log for user caller
I/org.webrtc.Logging: SurfaceViewRenderer: localView: onMeasure(). New size: 200x200
I/org.webrtc.Logging: EglRenderer: localView: setLayoutAspectRatio: 1.0
I/org.webrtc.Logging: EglRenderer: opponentView: setLayoutAspectRatio: 0.58536583
D/QBASDK: register network listener
D/QBASDK: Connected. Login to chat, currentUser JID: 39753009-65649, resource: android_ffffffff-ac04-0c2d-ffff-ffffd173e4c0
I/errorCheck: R0: null
I/errorCheck: R1: Bundle[{}]
D/RTCClient.: Added session CALLBACK listenerapp.x.y.VideoActivity#4288e7f8
I/errorCheck: chatService1: com.quickblox.chat.QBChatService#42f6b478
D/RTCClient.: Call createNewSessionWithOpponents[39753771]conference type=QBConferenceType{value='1'}
D/RTCClient.QBRTCSession: Create new session
I/errorCheck: Q1: com.quickblox.chat.QBWebRTCSignaling#43156ce0
I/errorCheck: Q2: true
D/RTCClient.: New signalling was added for participant39753771
D/RTCClient.QBRTCSession: Make new channel for oppoennt:39753771com.quickblox.videochat.webrtc.QBPeerChannel#42f23c00
D/RTCClient.QBRTCSession: isInitiator=true
D/RTCClient.QBRTCSession: ADD VideoTrackCallbacksListener app.x.y.VideoActivity#4288e7f8
D/RTCClient.QBRTCSession: startCall
D/RTCClient.PeerFactoryManager: Creating Peer connection factory
D/RTCClient.QBRTCSession.SessionWaitingTimers: Stop WaitTimer
But for opponent not set log ( not incoming notification or ... )
How can I solve this problem?
You may refer with this thread. Check the video-sample. There is implementation of opening IncomingCallFragment by receiving push in this sample. Also, if you have successful subscription, but do not receive pushes, you always can check the following:
Check certificates for pushes: Admin panel ->Push Notification ->Settings -> APNS, GCM etc.
Send push from Admin Panel - > Push notifications - > Send (link)
Also you can check Push Notifications Queue tab in Admin panel, there should be a log of your push. It will help you to understand what's going on with your devices.
Hope this helps!
Related
isGestureDetectionAvailable() always returns false. I looked this issue up here on SO, and all those who faced the same problem in the past few years haven't received an answer or got it solved. Here and Here And everywhere else. There will be a 50 points reward for the correct answer as a token of appreciation.
AndroidManifest.xml
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<service
android:enabled="true"
android:name="helpers.FingerprintSVC"
android:label="#string/app_name"
android:accessibilityFlags="flagDefault|flagRequestFingerprintGestures"
android:canRequestFingerprintGestures="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/accessibility_service_config" />
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
FingerprintSVC
public class FingerprintSVC extends AccessibilityService {
private static final String TAG = "++++";
#Override
public int onStartCommand(Intent aIntent, int aFlags, int aStartId){
super.onStartCommand(aIntent, aFlags, aStartId);
Log.d(TAG, "onStartCommand");
if(!isAccessibilityServiceEnabled(this, this.getClass())){
Toast toast= Toast.makeText(this, "Enable A.SVC", Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP|Gravity.FILL_HORIZONTAL, 0, 0);
toast.show();
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
return START_STICKY;
}
#Override
public void onCreate(){
super.onCreate();
Log.d(TAG, "onCreate");
}
#Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
Log.d(TAG, "onAccessibilityEvent");
}
#Override
public void onInterrupt() {
Log.d(TAG, "onInterrupt");
}
#Override
protected boolean onGesture(int gestureId) {
Log.d(TAG, "onGesture " + gestureId);
return super.onGesture(gestureId);
}
#Override
protected boolean onKeyEvent(KeyEvent event) {
Log.d(TAG, "onKeyEvent " + event.getKeyCode());
return super.onKeyEvent(event);
}
#Override
public void onDestroy() {
Toast.makeText(getApplicationContext(), "onDestroy" , Toast.LENGTH_SHORT).show();
super.onDestroy();
}
#Override
protected void onServiceConnected() {
super.onServiceConnected();
Log.d(TAG, "onServiceConnected");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
FingerprintGestureController gestureController = getFingerprintGestureController();
Toast.makeText(getApplicationContext(), "Is available: " + gestureController.isGestureDetectionAvailable(), Toast.LENGTH_LONG).show();
Log.e(TAG, "Is available: " + gestureController.isGestureDetectionAvailable() );
FingerprintGestureController.FingerprintGestureCallback callback = new
FingerprintGestureController.FingerprintGestureCallback() {
#Override
public void onGestureDetectionAvailabilityChanged(boolean available) {
super.onGestureDetectionAvailabilityChanged(available);
Toast.makeText(getApplicationContext(), "Gesture available change to: " + available, Toast.LENGTH_SHORT).show();
Log.d(TAG, "onGestureDetectionAvailabilityChanged " + available);
}
#Override
public void onGestureDetected(int gesture) {
super.onGestureDetected(gesture);
Toast.makeText(getApplicationContext(), "Gesture: " + gesture, Toast.LENGTH_SHORT).show();
Log.d(TAG, "onGestureDetected " + gesture);
}
};
gestureController.registerFingerprintGestureCallback(callback, new Handler());
}
}
#Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind " );
return super.onUnbind(intent);
}
public boolean isAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
String enabledServicesSetting = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (enabledServicesSetting == null)
return false;
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
colonSplitter.setString(enabledServicesSetting);
while (colonSplitter.hasNext()) {
String componentNameString = colonSplitter.next();
ComponentName enabledService = ComponentName.unflattenFromString(componentNameString);
if (enabledService != null && enabledService.equals(expectedComponentName))
return true;
}
return false;
}
}
build.gradle
android {
compileSdkVersion 30
buildToolsVersion '29.0.3'
defaultConfig {
applicationId "com.xxxx.xxxx"
minSdkVersion 26
targetSdkVersion 30
}
I'm testing on Samsung M11 powered by Android 10. I don't have other devices to test on.
I have a connection with SIP server(Asterisk), but micro and dynamics doesn't work. Asterisk kicks client in 31 seconds(Empty RTP thread). Documentation by Google says:
Android provides an API that supports the Session Initiation Protocol (SIP). This lets you add SIP-based internet telephony features to your applications. Android includes a full SIP protocol stack and integrated call management services that let applications easily set up outgoing and incoming voice calls, without having to manage sessions, transport-level communication, or audio record or playback directly.
Activity + Receiver:
public class MainActivity extends AppCompatActivity {
public String domain = "192.168.10.37";
public String name = "111";
public String password = "123456";
public String sipAddress = "100#192.168.10.37";
public IncomingCallReceiver receiver;
public SipManager sipManager;
public SipProfile sipProfile;
public SipAudioCall call;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.USE_SIP)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.USE_SIP)) {
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.USE_SIP},
0);
}
}
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("zlotnikov.SIPexample.INCOMING_CALL");
receiver = new IncomingCallReceiver();
this.registerReceiver(receiver, intentFilter);
initManager();
}
private void initManager() {
if (sipManager == null) {
sipManager = SipManager.newInstance(this);
}
}
private void initializeLocalProfile() {
if (sipProfile != null) {
closeLocalProfile();
}
try {
SipProfile.Builder builder = new SipProfile.Builder(name, domain);
builder.setPassword(password);
builder.setSendKeepAlive(true);
builder.setAutoRegistration(true);
sipProfile = builder.build();
Intent intent = new Intent();
intent.setAction("zlotnikov.SIPexample.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
SipRegistrationListener listener = new SipRegistrationListener() {
#Override
public void onRegistering(String s) {
System.out.println("voip onRegistering " + s);
}
#Override
public void onRegistrationDone(String s, long l) {
System.out.println("voip onRegistrationDone " + s + " " + l);
//initCall();
}
#Override
public void onRegistrationFailed(String s, int i, String s1) {
System.out.println("voip onRegistrationFailed " + s);
}
};
sipManager.open(sipProfile, pendingIntent, null);
//sipManager.register(sipProfile, 40, listener);
sipManager.setRegistrationListener(sipProfile.getUriString(), listener);
} catch (ParseException e) {
e.printStackTrace();
} catch (SipException e) {
e.printStackTrace();
}
}
#Override
protected void onStart() {
super.onStart();
initializeLocalProfile();
}
#Override
protected void onStop() {
super.onStop();
closeLocalProfile();
}
private void closeLocalProfile() {
try {
if (sipProfile != null) {
sipManager.close(sipProfile.getUriString());
}
} catch (Exception ee) {
ee.printStackTrace();
}
}
private void initCall() {
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
#Override
public void onCallEstablished(SipAudioCall call) {
super.onCallEstablished(call);
System.out.println("voip onCallEstablished");
call.startAudio();
call.setSpeakerMode(true);
}
#Override
public void onCallEnded(SipAudioCall call) {
super.onCallEnded(call);
System.out.println("voip onCallEnded");
}
};
call = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, listener, 30);
} catch (SipException e) {
closeLocalProfile();
call.close();
e.printStackTrace();
System.out.println("voip MainActivity Конец соединения");
}
}
public class IncomingCallReceiver extends BroadcastReceiver {
private MediaPlayer mediaPlayer;
#Override
public void onReceive(Context context, Intent intent) {
SipAudioCall incomingCall = null;
System.out.println("voip Пришел звонок " + intent.toString());
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
#Override
public void onRinging(SipAudioCall call, SipProfile caller) {
System.out.println("voip onRinging()");
try {
startRinging();
} catch (Exception e) {
stopRinging();
System.out.println("voip onRinging exception");
e.printStackTrace();
}
}
#Override
public void onCallEstablished(SipAudioCall call) {
super.onCallEstablished(call);
System.out.println("voip onCallEstablished()");
stopRinging();
}
#Override
public void onCallEnded(SipAudioCall call) {
super.onCallEnded(call);
System.out.println("voip onCallEnded()");
}
};
incomingCall = sipManager.takeAudioCall(intent, listener);
incomingCall.startAudio();
incomingCall.setSpeakerMode(true);
/*if (incomingCall.isMuted()) {
incomingCall.toggleMute();
}*/
//call = incomingCall;
incomingCall.answerCall(30);
} catch (Exception e) {
if (incomingCall != null) {
incomingCall.close();
System.out.println("voip IncomingCallReceiver конец соединения");
}
}
}
private synchronized void startRinging() {
long[] pattern = {0, 1000, 1000};
((Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE)).vibrate(pattern, 0);
Uri defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(getApplicationContext(), RingtoneManager.TYPE_RINGTONE);
mediaPlayer = MediaPlayer.create(getApplicationContext(), defaultRingtoneUri);
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
private synchronized void stopRinging() {
((Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE))
.cancel();
if (mediaPlayer != null || mediaPlayer.isPlaying()) mediaPlayer.stop();
}
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="zlotnikov.sipexample">
<uses-permission android:name="android.permission.USE_SIP" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.sip.voip" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".MainActivity$IncomingCallReceiver" android:label="Call Receiver" />
</application>
we are using Sinch voip api. There is a bound service which starts at app start , we initialize the sinch client in the service and it is always running in the background. I tried putting the code for notification in the call screen activity as this activity will always show to accept the call. My aim is to be able to click on the notification and reopen this call activity like in whatsapp.
public class CallScreenActivity extends BaseActivity {
static final String TAG = CallScreenActivity.class.getSimpleName();
private AudioPlayer mAudioPlayer;
private Timer mTimer;
private UpdateCallDurationTask mDurationTask;
private String mCallId;
String mCaller, mReceiver;
String otherusername, myname;
private long mCallStart = 0;
private TextView mCallDuration;
private TextView mCallState;
private TextView mCallerName;
private ImageView mCallImg;
private String mk, mTimestamp;
private String mProfpic;
Button endCallButton;
Notification notification;
NotificationManager notificationManager;
private class UpdateCallDurationTask extends TimerTask {
#Override
public void run() {
CallScreenActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
updateCallDuration();
}
});
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| +WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| +WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(R.layout.activity_callscreen);
mAudioPlayer = new AudioPlayer(this);
mCallDuration = (TextView) findViewById(R.id.callDuration);
mCallerName = (TextView) findViewById(R.id.remoteUser);
mCallState = (TextView) findViewById(R.id.callState);
mCallImg = (ImageView) findViewById(R.id.imgotherusr);
endCallButton = (Button) findViewById(R.id.hangupButton);
endCallButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
endCall();
}
});
mCallStart = System.currentTimeMillis();
mCallId = getIntent().getStringExtra(SinchCallService.CALL_ID);
UserSession us = new UserSession(this);
mk = us.getUserKey();
myname = us.getUsername();
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
System.out.println(mCaller+"on create call screen activity ongoing call" + mReceiver + otherusername);
showNotification();
}
private void showNotification() {
System.out.println("show notification callscreenactivity");
Intent notificationIntent = new Intent(this, CallScreenActivity.class);
notificationIntent.setAction("ongoingcall");
notificationIntent.putExtra("calleruid", mCaller);
notificationIntent.putExtra("receiveruid", mReceiver);
notificationIntent.putExtra("otherusername", otherusername);
notificationIntent.putExtra("timestamp", mTimestamp);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent hangupintent = new Intent(this, CallScreenActivity.class);
hangupintent.setAction("hangupcall");
hangupintent.setAction("ongoingcall");
hangupintent.putExtra("calleruid", mCaller);
hangupintent.putExtra("receiveruid", mReceiver);
hangupintent.putExtra("otherusername", otherusername);
hangupintent.putExtra("timestamp", mTimestamp);
PendingIntent phangupintent = PendingIntent.getService(this, 0,
hangupintent, 0);
notification = new NotificationCompat.Builder(this)
.setContentTitle("In call with " + otherusername)
.setContentText("Duration " + VoiceCallHelper.formatTimespan(System.currentTimeMillis() - mCallStart))
.setSmallIcon(R.drawable.iconcall)
.setContentIntent(pendingIntent)
.addAction(R.drawable.ic_arrow_back, "Hangup",
phangupintent).build();
notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111 /* ID of notification */, notification);
}
#Override
public void onServiceConnected() {
try {
doStuff();
} catch (NullPointerException e) {
//getSinchServiceInterface() in doStuff below throw null pointer error.
}
}
private void doStuff() {
final Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.addCallListener(new SinchCallListener());
mCallState.setText(call.getState().toString());
DBREF_USER_PROFILES.child(call.getRemoteUserId()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
System.out.println("datasnapshot callscreenactivity otheruser" + dataSnapshot);
User u = User.parse(dataSnapshot);
mCallerName.setText(u.getName());
mProfpic = u.getProfpicurl();
Glide.with(CallScreenActivity.this).load(mProfpic).into(mCallImg);
} else {
mCallerName.setText(call.getHeaders().get("username"));
Glide.with(CallScreenActivity.this).load(R.drawable.whatsapplogo).into(mCallImg);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
} else {
Log.e(TAG, "Started with invalid callId, aborting.");
finish();
}
}
#Override
public void onPause() {
super.onPause();
mDurationTask.cancel();
mTimer.cancel();
}
#Override
public void onResume() {
super.onResume();
mTimer = new Timer();
mDurationTask = new UpdateCallDurationTask();
mTimer.schedule(mDurationTask, 0, 500);
if (getIntent() != null && getIntent().getAction() != null) {
switch (getIntent().getAction()) {
case "ongoingcall":
System.out.println("on resume call screen activity ongoing call" + mCaller + mReceiver + otherusername);
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
break;
case "hangupcall":
System.out.println("on resume call screen activity hangup call");
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
endCallButton.performClick();
break;
}
}
}
#Override
public void onBackPressed() {
startActivity(new Intent(CallScreenActivity.this, MainAct.class));
}
private void endCall() {
if (notification != null) {
System.out.println("cancelling notification in endCAll callscreenactivity");
notificationManager.cancel(111);
}
mAudioPlayer.stopProgressTone();
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.hangup();
}
finish();
}
private void updateCallDuration() {
if (mCallStart > 0) {
mCallDuration.setText(VoiceCallHelper.formatTimespan(System.currentTimeMillis() - mCallStart));
showNotification();
}
}
private class SinchCallListener implements CallListener {
#Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended. Reason: " + cause.toString() + mk + mCaller);
if (mk != null && mCaller != null && mk.matches(mCaller)) {
mAudioPlayer.stopProgressTone();
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
String endMsg = "Call ended: " + call.getDetails().toString();
Long gt = GetTimeStamp.Id();
Toast.makeText(CallScreenActivity.this, endMsg, Toast.LENGTH_LONG).show();
System.out.println(endMsg + "mtimestamp" + mTimestamp);
String cau;
String oth;
if (call.getDetails().getDuration() > 0)
cau = "completed";
else
cau = cause.toString();
CallDetails cd1 = new CallDetails(String.valueOf(call.getDetails().getDuration()), mCaller, mReceiver, cau, String.valueOf(gt), mTimestamp, mProfpic, mCallerName.getText().toString());
CallDetails cd2 = new CallDetails(String.valueOf(call.getDetails().getDuration()), mCaller, mReceiver, cau, String.valueOf(gt), mTimestamp, mProfpic, myname);
System.out.println(mCaller + "end msg callscreenactivity" + mReceiver + " " + String.valueOf(gt));
System.out.println("end msg callscreenactivity" + mReceiver + " " + DBREF.child("VoiceCalls").child(mCaller).child(String.valueOf(gt)));
//setting in mCaller mykey node at voicecalls node firebase
DBREF_CALLS.child(mCaller).child(String.valueOf(gt)).setValue(cd1);
//setting in mReceiver otheruserkey node at voicecalls node firebase
DBREF_CALLS.child(mReceiver).child(String.valueOf(gt)).setValue(cd2);
}
endCall();
}
#Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
mAudioPlayer.stopProgressTone();
mCallState.setText(call.getState().toString());
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
mCallStart = System.currentTimeMillis();
mTimestamp = GetTimeStamp.timeStamp();
}
#Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
mAudioPlayer.playProgressTone();
}
#Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
// Send a push through your push provider here, e.g. GCM
}
}
}
My SinchCallService class is this:
public class SinchCallService extends Service {
private static final String APP_KEY = SINCH_APPLICATION_KEY;
private static final String APP_SECRET = SINCH_SECRET_KEY;
private static final String ENVIRONMENT = "sandbox.sinch.com";
public static final String LOCATION = "LOCATION";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchCallService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private StartFailedListener mListener;
#Override
public void onCreate() {
super.onCreate();
UserSession us = new UserSession(this);
System.out.println("From sinchcall oncreate" + us.getUserKey());
if (!isStarted()) {
System.out.println("sinch not started callservice oncreate " + us.getUserKey());
start(us.getUserKey());
}
}
#Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
public void start(String userName) {
System.out.println("sinch call service start " + userName);
if (mSinchClient == null) {
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.start();
System.out.println(" sinch client started");
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
#Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public SinchCallService getService() {
return SinchCallService.this;
}
public Call callPhoneNumber(String phoneNumber) {
return mSinchClient.getCallClient().callPhoneNumber(phoneNumber);
}
public Call callUser(String userId) {
return mSinchClient.getCallClient().callUser(userId);
}
public Call callUser(String userId, Map<String, String> headers) {
if(!isStarted()){
UserSession us = new UserSession(getApplicationContext());
startClient(us.getUserKey());
}
return mSinchClient.getCallClient().callUser(userId, headers);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchCallService.this.isStarted();
}
public void startClient(String userName) {
System.out.println("startClient called sinchcallservice" + userName);
if (!isStarted()) {
System.out.println("startClient not started callservice " + userName);
start(userName);
}
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
#Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
mSinchClient.terminate();
mSinchClient = null;
}
#Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
}
}
#Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
#Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
#Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
private class SinchCallClientListener implements CallClientListener {
#Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.e(TAG, "Incoming call");
Intent intent = new Intent(SinchCallService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.putExtra(LOCATION, call.getHeaders().get("location"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchCallService.this.startActivity(intent);
}
}
}
Following is my BaseActivity.java :
public abstract class BaseActivity extends FragmentActivity implements ServiceConnection {
private SinchCallService.SinchServiceInterface mSinchServiceInterface;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getApplicationContext().bindService(new Intent(this, SinchCallService.class), this,
BIND_AUTO_CREATE);
}
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchCallService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchCallService.SinchServiceInterface) iBinder;
onServiceConnected();
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
if (SinchCallService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = null;
onServiceDisconnected();
}
}
protected void onServiceConnected() {
// for subclasses
}
protected void onServiceDisconnected() {
// for subclasses
}
protected SinchCallService.SinchServiceInterface getSinchServiceInterface() {
return mSinchServiceInterface;
}
}
I have tried things like setting
android:launchMode="singleTop"
If I backpress on the CallScreenActivity and click on notification, it opens the MainAct not the CallScreenActivity. How to make it open the CallScreenActivity?
I tried a solution with creating a stackbuilder and passing it to pending intent as follows:
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(CallScreenActivity.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent pendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
And also changed the manifest as follows:
<activity
android:launchMode="singleTop"
android:name=".activity.calls.CallScreenActivity"
android:screenOrientation="portrait"
android:parentActivityName=".activity.main.MainAct">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.main.MainAct"/>
</activity>
But the above change leads to app crash with following error:
07-06 23:38:52.353 9182-9182/com.app E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke interface method 'com.sinch.android.rtc.calling.CallClient com.sinch.android.rtc.SinchClient.getCallClient()' on a null object reference
at com.app.services.SinchCallService$SinchServiceInterface.getCall(SinchCallService.java:150)
at com.app.activity.calls.CallScreenActivity.endCall(CallScreenActivity.java:265)
at com.app.activity.calls.CallScreenActivity.access$100(CallScreenActivity.java:51)
at com.app.activity.calls.CallScreenActivity$1.onClick(CallScreenActivity.java:110)
at android.view.View.performClick(View.java:5207)
at android.view.View$PerformClick.run(View.java:21177)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5438)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
So If i am using the code without the stackbuilder and i press home button, then press the notification my CallSCreenACtivity opens, but if i press back button inside the CAllSCreenACtivity and then press home button then i press notification, it opens the main acitivty instead of the CallSCreenACtivity. My call is still actvity even after pressing the backbutton on callscreenactivty.
Also I have noticed that the duration of the call being shown in the notification has stopped refreshing after back press on the CallScreenActivity but the call is still active.
Flow of the app:
For Caller:-
When app starts, MainAct starts,from where a user clicks on someone's profile and is taken to ProfilesAct where he presses the call button to call that person and is taken to the CallScreenActivity.
MainAct->ProfilesACt->CallScreenACt
For Receiver:-
App is in background, the call comes, the SinchCallService makes the IncomingCAllAct to show, when he accepts the call he is taken to the CallScreenActivity.
IncomingCallAct->CallSCreeenACtivity
or could be suppose receiver is already using the app, then
any activity(could be chat activity, main acitivty etc)->IncomingCallAct->CallSCreeenACtivity
Manifest of the app:-
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app.t">
<application
android:name=".TApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/Theme.AppCompat.Light.NoActionBar">
<activity
android:name=".SplashAct"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.userprofile.UserProfileAct"
android:screenOrientation="portrait" />
<activity
android:name=".activity.videocalls.VideoCallScreenActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop"/>
<activity
android:name=".activity.videocalls.IncomingVideoCallScreenActivity"
android:screenOrientation="portrait"
android:noHistory="true"/>
<activity
android:name=".activity.main.MainAct"
android:launchMode="singleTop"
android:screenOrientation="portrait"></activity>
<activity android:name=".activity.chat.ChatActivity" />
<service android:name=".services.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name=".services.MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<activity android:name=".activity.settings.SettingsmainActivity" />
<service android:name=".services.SinchCallService"></service>
<activity
android:name=".activity.calls.CallScreenActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".activity.calls.IncomingCallScreenActivity"
android:noHistory="true"
android:screenOrientation="portrait" />
<activity android:name=".activity.chat.NewChatActivity" />
<activity android:name=".activity.chat.ChatActivity1"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>
</manifest>
There is no need to make the CallScreenActivity in the mainfest as singletask or singletop, just store the call parameters like callid(which helps Sinch api in recognizing the specific call), call start time, etc in sharedpreferences or sqlite db, make the notification intent like following :
Intent notificationIntent = new Intent(getApplicationContext(), CallScreenActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle("In call with " + otherusername)
.setSmallIcon(R.drawable.iconcall)
.setContentIntent(pendingIntent).build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111 /* ID of notification */, notification);
As long as we pass the right callid we will be able to resume call by clicking the notification.
I want to send a messsage to two different Android Smart-Watches.
On the mobile side the Wearable.MessageApi.sendMessage gets called and the sendMessageResult.getStatus().isSuccess() is true, so the sending of the message should work.
The problem now is, that the onMessageReceived method of the MyWearableListenerService never receives a message. What am I doing wrong?
This is the code I'm using:
MainActivity of the Android App.
The left/right methods are sending the messages
public class MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener {
private static final String TAG_CONNECTION = "Connection";
private static final String TAG_NODES = "Nodes";
private static final String START_ACTIVITY_PATH = "/start_MainActivity";
private String leftWatch;
private String rightWatch;
private GoogleApiClient mGoogleApiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Setting up the Wearable API Client
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Wearable.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(#Nullable Bundle bundle) {
Log.e(TAG_CONNECTION, "Connection established");
}
#Override
public void onConnectionSuspended(int i) {
Log.e(TAG_CONNECTION, "Connection suspended");
}
})
.build();
mGoogleApiClient.connect();
// Find the connected watches and store their UUIDs to distinguish at a later moment
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(#NonNull NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
List<Node> nodes = getConnectedNodesResult.getNodes();
if (nodes.isEmpty()) {
Log.e(TAG_NODES, "No Nodes found");
return;
}
rightWatch = nodes.get(0).getId();
Log.e(TAG_NODES,"Node is nearby: "+nodes.get(0).isNearby());
Log.e(TAG_NODES, rightWatch);
leftWatch = nodes.get(1).getId();
Log.e(TAG_NODES,"Node is nearby: "+nodes.get(1).isNearby());
Log.e(TAG_NODES, leftWatch);
}
});
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Executed upon failed connection to Wearable API
// e.g. when Android Wear App is missing
Log.e(TAG_CONNECTION, "Connection failed");
}
public void left(View view) {
Log.e(TAG_CONNECTION, "Trying to send message to: " + leftWatch);
if (!mGoogleApiClient.isConnected()) mGoogleApiClient.connect();
Wearable.MessageApi.sendMessage(mGoogleApiClient, leftWatch, START_ACTIVITY_PATH, new byte[0]).setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e(TAG_CONNECTION, "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
return;
}
Log.e(TAG_CONNECTION, "Message successfully sent");
}
});
}
public void right(View view) {
Log.e(TAG_CONNECTION, "Trying to send message to: " + rightWatch);
if (!mGoogleApiClient.isConnected()) mGoogleApiClient.connect();
Wearable.MessageApi.sendMessage(mGoogleApiClient, rightWatch, START_ACTIVITY_PATH, new byte[0]).setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e(TAG_CONNECTION, "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
return;
}
Log.e(TAG_CONNECTION, "Message successfully sent");
}
});
}
public void both(View view) {
Log.e(TAG_CONNECTION, "Trying to send message to: All currently connected watches");
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(#NonNull NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
List<Node> nodes = getConnectedNodesResult.getNodes();
for (final Node node : nodes) {
Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), START_ACTIVITY_PATH, new byte[0]).setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e(TAG_CONNECTION, "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
return;
}
Log.e(TAG_CONNECTION, "Message successfully sent to: " + node.getId());
}
});
}
}
});
}
#Override
protected void onDestroy() {
mGoogleApiClient.disconnect();
super.onDestroy();
}
}
MyWearableListenerService
public class MyWearableListenerService extends WearableListenerService {
private static final String TAG_SIGNAL = "Signal";
private static final String TAG_CONNECTION = "Connection";
private static final String START_ACTIVITY_PATH = "/start_MainActivity";
#Override
public void onMessageReceived(MessageEvent messageEvent) {
Log.e(TAG_SIGNAL, "Message received: " + messageEvent.getPath());
if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
Intent intent = new Intent(this, MainWearActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else super.onMessageReceived(messageEvent);
}
#Override
public void onPeerConnected(Node node) {
Log.e(TAG_CONNECTION, "Peer connected: " + node.getId());
super.onPeerConnected(node);
}
#Override
public void onPeerDisconnected(Node node) {
Log.e(TAG_CONNECTION, "Peer disconnected: " + node.getId());
super.onPeerDisconnected(node);
}
}
AndroidManifest on the wear side
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="laufnavigation.awp.hska.de.androidwearapp">
<uses-feature android:name="android.hardware.type.watch" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#android:style/Theme.DeviceDefault">
<uses-library
android:name="com.google.android.wearable"
android:required="false" />
<activity android:name=".MainWearActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyWearableListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data android:scheme="wear" android:host="*" />
</intent-filter>
</service>
</application>
</manifest>
I believe you should add a path prefix to you intent filter. In your case in the android manifest of your wearable:
<service
android:name=".MyWearableListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data android:scheme="wear" android:host="*" android:pathPrefix:"/start_MainActivity" />
</intent-filter>
</service>
Let me know if it works!
I am trying to send a message from my mobile app to a service in the wearable app.
The message needs to be send from a BroadcastReceiver. In the BroadcastReceiver the message is sent successfully, at least that is what is said in the log, but the method onMessageReceive is not triggered.
I am not quite sure what the problem might be, I have the same applicationId in both build.gradle files, but packages names are different.
In mobile manifest the receiver has the following declaration:
<receiver android:name="com.app.mobile.NewAlarm"
android:exported="false">
<intent-filter>
<action android:name="com.example.android.support.wearable.notifications.ACTION_EXAMPLE" />
</intent-filter>
</receiver>
the BroadcastReceiver class:
public class TimeAlarm extends BroadcastReceiver {
public static final String TAP_ACTION_PATH = "/tap";
private static final String TAG = "NotificationReceiver";
public void onReceive(Context context, Intent paramIntent) {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(Wearable.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int cause) {
}
}).build();
mGoogleApiClient.connect();
sendMessage();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
private void getNodes() {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
HashSet<String> results = new HashSet<String>();
for (Node node : getConnectedNodesResult.getNodes()) {
results.add(node.getId());
Log.d(TAG,node.getId().toString());
}
Log.d(TAG,results.toString());
sendMessageApi(results);
}
}
);
}
private void sendMessageApi(Collection<String> nodes) {
for (String node : nodes) {
Wearable.MessageApi.sendMessage(
mGoogleApiClient, node, TAP_ACTION_PATH, null).setResultCallback(
new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e(TAG, "ERROR: failed to send Message: " + sendMessageResult.getStatus());
} else {
Log.e(TAG, "Success!");
}
}
}
);
}
}
private void sendMessage() {
getNodes();
}
}
The WearableListenerService is declared this way in wear app manifest:
<service android:name=".DataLayerListenerService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
and finally the DataLayerListenerService:
public class DataLayerListenerService extends WearableListenerService {
public static final String START_ACTIVITY_PATH = "/notice";
public static final String TAP_ACTION_PATH = "/tap";
private static final String TAG = "DataLayerListenerService";
#Override
public void onMessageReceived(MessageEvent messageEvent) {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
Log.d(TAG, "Message Received !!");
if (START_ACTIVITY_PATH.equals(messageEvent.getPath())) {
Log.d(TAG, "Message Received !!");
int vibeTime = 5000;
String strVibeTime = new String(messageEvent.getData());
if (!strVibeTime.equals("") && !strVibeTime.equals("0")) {
vibeTime = (Integer.parseInt(strVibeTime)) * 1000;
}
sleep(1000);
vibrator.vibrate(vibeTime);
} else if (TAP_ACTION_PATH.equals(messageEvent.getPath())) {
Log.d(TAG, "Tapping Received !!");
vibrator.cancel();
}
}
public synchronized void sleep(long msec) {
try
{
wait(msec);
}catch(InterruptedException e){
Log.e(TAG, e.getMessage());
}
}
}
You're trying to send the message before the client is connected, so getConnectedNodesResult.getNodes() is returning an empty list.
Try this instead:
public void onReceive(Context context, Intent paramIntent) {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(Wearable.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
sendMessage();
}
#Override
public void onConnectionSuspended(int cause) {
}
}).build();
mGoogleApiClient.connect();
}