Media Notification not working in Oreo - android

What modifications do i have to do to the below code to make notifications work in API 26+ cause Oreo requires channel and this code is a bit messy. I tried implimenting the channels but didn't work maybe did something wrong so I'm posting the unaltered code without any channel or any other modifications.
public class MediaPlayerService extends Service implements PlayerFragment.onPlayPauseListener {
private MediaSessionManager m_objMediaSessionManager;
private MediaSession m_objMediaSession;
private MediaController m_objMediaController;
private FFmpegMediaPlayer m_objMediaPlayer;
private NotificationManager notificationManager;
private ServiceCallbacks serviceCallbacks;
Intent startIntent;
PlayerFragment pFragment;
private boolean isSwipable = false;
public class LocalBinder extends Binder {
public MediaPlayerService getService() {
// Return this instance of MyService so clients can call public methods
return MediaPlayerService.this;
}
}
public void setCallbacks(ServiceCallbacks callbacks) {
serviceCallbacks = callbacks;
pFragment = serviceCallbacks.getPlayerFragment();
if (pFragment != null)
pFragment.mCallback7 = this;
if (m_objMediaSessionManager == null) {
initMediaSessions();
}
handleIntent(startIntent);
}
#Override
public IBinder onBind(Intent intent) {
startIntent = intent;
return new LocalBinder();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
pFragment = ((HomeActivity) PlayerFragment.ctx).getPlayerFragment();
} catch (Exception e) {
e.printStackTrace();
}
if (pFragment != null)
pFragment.mCallback7 = this;
if (m_objMediaSessionManager == null) {
initMediaSessions();
}
handleIntent(intent);
return super.onStartCommand(intent, flags, startId);
}
private void handleIntent(Intent intent) {
if (intent == null || intent.getAction() == null)
return;
String action = intent.getAction();
if (m_objMediaController != null && action.equalsIgnoreCase(Constants.ACTION_PLAY)) {
m_objMediaController.getTransportControls().play();
} else if (m_objMediaController != null && action.equalsIgnoreCase(Constants.ACTION_PAUSE)) {
m_objMediaController.getTransportControls().pause();
} else if (m_objMediaController != null && action.equalsIgnoreCase(Constants.ACTION_FAST_FORWARD)) {
m_objMediaController.getTransportControls().fastForward();
} else if (m_objMediaController != null && action.equalsIgnoreCase(Constants.ACTION_REWIND)) {
m_objMediaController.getTransportControls().rewind();
} else if (m_objMediaController != null && action.equalsIgnoreCase(Constants.ACTION_PREVIOUS)) {
m_objMediaController.getTransportControls().skipToPrevious();
} else if (m_objMediaController != null && action.equalsIgnoreCase(Constants.ACTION_NEXT)) {
m_objMediaController.getTransportControls().skipToNext();
} else if (m_objMediaController != null && action.equalsIgnoreCase(Constants.ACTION_STOP)) {
m_objMediaController.getTransportControls().stop();
}
}
private void buildNotification(Notification.Action action) {
Notification.MediaStyle style = new Notification.MediaStyle();
style.setShowActionsInCompactView(1);
style.setMediaSession(m_objMediaSession.getSessionToken());
Intent intent = new Intent(getApplicationContext(), MediaPlayerService.class);
intent.setAction(Constants.ACTION_STOP);
PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
String artist;
if (pFragment != null && pFragment.localIsPlaying) {
artist = pFragment.localTrack.getArtist();
} else {
artist = "";
}
Intent notificationIntent = new Intent(this, HomeActivity.class);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Bitmap bmp = null;
try {
bmp = ((BitmapDrawable) pFragment.selected_track_image.getDrawable()).getBitmap();
} catch (Exception e) {
e.printStackTrace();
}
if (bmp == null) {
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_default);
}
Notification notification = new Notification.Builder(this)
.setStyle(style)
.setSmallIcon(R.drawable.ic_notification)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setDeleteIntent(pendingIntent)
.addAction(generateAction(R.drawable.ic_skip_previous_notif, "Previous", Constants.ACTION_PREVIOUS))
.addAction(action)
.addAction(generateAction(R.drawable.ic_skip_next_notif, "Next", Constants.ACTION_NEXT))
.setContentTitle(pFragment.selected_track_title.getText())
.setContentText(artist)
.setLargeIcon(bmp)
.build();
notification.contentIntent = pendingNotificationIntent;
notification.priority = Notification.PRIORITY_MAX;
if (isSwipable || (pFragment.mMediaPlayer != null && pFragment.mMediaPlayer.isPlaying())) {
notification.flags |= Notification.FLAG_ONGOING_EVENT;
}
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
try {
notificationManager.notify(1, notification);
} catch (Exception e) {
e.printStackTrace();
}
updateMediaSession();
}
private Notification.Action generateAction(int icon, String title, String intentAction) {
Intent intent = new Intent(getApplicationContext(), MediaPlayerService.class);
intent.setAction(intentAction);
PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
return new Notification.Action.Builder(icon, title, pendingIntent).build();
}
void updateMediaSession() {
if (pFragment != null) {
m_objMediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
if (pFragment.localIsPlaying) {
if (pFragment.localTrack != null) {
metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, pFragment.localTrack.getTitle());
metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, pFragment.localTrack.getArtist());
}
} else {
if (pFragment.track != null) {
metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, pFragment.track.getTitle());
metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, "");
}
}
if (((BitmapDrawable) pFragment.selected_track_image.getDrawable()).getBitmap() != null) {
try {
metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, ((BitmapDrawable) pFragment.selected_track_image.getDrawable()).getBitmap());
} catch (Exception e) {
e.printStackTrace();
}
}
m_objMediaSession.setMetadata(metadataBuilder.build());
PlaybackState.Builder stateBuilder = new PlaybackState.Builder();
stateBuilder.setActions(PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS);
try {
if (pFragment.mMediaPlayer != null) {
stateBuilder.setState(!pFragment.mMediaPlayer.isPlaying() ? PlaybackState.STATE_PAUSED : PlaybackState.STATE_PLAYING, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
}
} catch (Exception e) {
e.printStackTrace();
}
m_objMediaSession.setPlaybackState(stateBuilder.build());
m_objMediaSession.setActive(true);
}
}
private void initMediaSessions() {
if (pFragment != null) {
pFragment.mCallback7 = this;
m_objMediaPlayer = pFragment.mMediaPlayer;
m_objMediaSessionManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
m_objMediaSession = new MediaSession(getApplicationContext(), "sample session");
m_objMediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
if (pFragment.localIsPlaying) {
if (pFragment.localTrack != null) {
metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, pFragment.localTrack.getTitle());
metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, pFragment.localTrack.getArtist());
}
} else {
if (pFragment.track != null) {
metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, pFragment.track.getTitle());
metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, "");
}
}
if (pFragment.selected_track_image != null && pFragment.selected_track_image.getDrawable() != null) {
if (((BitmapDrawable) pFragment.selected_track_image.getDrawable()).getBitmap() != null) {
metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, ((BitmapDrawable) pFragment.selected_track_image.getDrawable()).getBitmap());
}
}
m_objMediaSession.setMetadata(metadataBuilder.build());
PlaybackState.Builder stateBuilder = new PlaybackState.Builder();
stateBuilder.setActions(PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS);
try {
if (pFragment.mMediaPlayer != null) {
stateBuilder.setState(!pFragment.mMediaPlayer.isPlaying() ? PlaybackState.STATE_PAUSED : PlaybackState.STATE_PLAYING, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
}
} catch (Exception e) {
e.printStackTrace();
}
m_objMediaSession.setPlaybackState(stateBuilder.build());
m_objMediaSession.setActive(true);
m_objMediaController = m_objMediaSession.getController();
m_objMediaSession.setCallback(new MediaSession.Callback() {
#Override
public void onPlay() {
super.onPlay();
try {
Log.d(Constants.LOG_TAG, "onPlay");
PlayerFragment pFrag = pFragment;
if (pFrag != null) {
if (!pFrag.isStart) {
pFrag.togglePlayPause();
}
pFrag.isStart = false;
buildNotification(generateAction(R.drawable.ic_pause_notif, "Pause", Constants.ACTION_PAUSE));
}
} catch (Exception e) {
}
}
#Override
public void onPause() {
super.onPause();
try {
PlayerFragment pFrag = pFragment;
if (pFrag != null) {
pFrag.togglePlayPause();
buildNotification(generateAction(R.drawable.ic_play_notif, "Play", Constants.ACTION_PLAY));
}
} catch (Exception e) {
}
}
#Override
public void onSkipToNext() {
super.onSkipToNext();
try {
if (pFragment != null) {
pFragment.onCallbackCalled(2);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
buildNotification(generateAction(R.drawable.ic_pause_notif, "Pause", Constants.ACTION_PAUSE));
}
}, 100);
}
} catch (Exception e) {
}
}
#Override
public void onSkipToPrevious() {
super.onSkipToPrevious();
try {
if (pFragment != null) {
pFragment.onCallbackCalled(3);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
buildNotification(generateAction(R.drawable.ic_pause_notif, "Pause", Constants.ACTION_PAUSE));
}
}, 100);
}
} catch (Exception e) {
}
}
#Override
public void onFastForward() {
super.onFastForward();
Log.d(Constants.LOG_TAG, "onFastForward");
if (pFragment != null)
pFragment.mCallback.onComplete();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
buildNotification(generateAction(R.drawable.ic_pause_notif, "Pause", Constants.ACTION_PAUSE));
}
}, 100);
}
#Override
public void onRewind() {
super.onRewind();
Log.d(Constants.LOG_TAG, "onRewind");
if (pFragment != null)
pFragment.mCallback.onPreviousTrack();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
buildNotification(generateAction(R.drawable.ic_pause_notif, "Pause", Constants.ACTION_PAUSE));
}
}, 100);
}
#Override
public void onStop() {
super.onStop();
}
#Override
public void onSeekTo(long pos) {
super.onSeekTo(pos);
}
#Override
public void onSetRating(Rating rating) {
super.onSetRating(rating);
}
});
}
}
#Override
public boolean onUnbind(Intent intent) {
m_objMediaSession.release();
return super.onUnbind(intent);
}
#Override
public void onPlayPause() {
if (pFragment.mMediaPlayer != null && pFragment.mMediaPlayer.isPlaying()) {
buildNotification(generateAction(R.drawable.ic_pause_notif, "Pause", Constants.ACTION_PAUSE));
} else {
buildNotification(generateAction(R.drawable.ic_play_notif, "Play", Constants.ACTION_PLAY));
}
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
try {
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(1);
} catch (Exception e) {
e.printStackTrace();
}
}

I can confirm that clearing the app cache (I did not clear data, just cache) and restarting your phone restores notifications. I was previously also getting the "enable notifications" screen as mentioned by previous posts. Now notifications work as expected.

Related

Sinch video call not working in background

I implemented Sinch video calling in my current app. When the app is in the foreground everything is OK for the incoming call. But when I closed the app and tried to receive an incoming call, it does not show any incoming call. How to do video Sinch calling when application is closed for incoming calls? following is my FCMMessageReceiverService class.
public class FCMMessageReceiverService extends FirebaseMessagingService implements ServiceConnection {
Context context;
private SinchService.SinchServiceInterface mSinchServiceInterface;
HashMap dataHashMap;
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
context = this;
if (SinchHelpers.isSinchPushPayload(remoteMessage.getData())) {
Map data = remoteMessage.getData();
dataHashMap = (data instanceof HashMap) ? (HashMap) data : new HashMap<>(data);
if (SinchHelpers.isSinchPushPayload(dataHashMap)) {
getApplicationContext().bindService(new Intent(getApplicationContext(), SinchService.class), this, Context.BIND_AUTO_CREATE);
}
} else {
Intent intent;
PendingIntent pendingIntent = null;
if (remoteMessage.getData().size() > 0) {
String identifier = remoteMessage.getData().get("identifier");
if (identifier.equals("0")) {
intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setWhen(System.currentTimeMillis());
notificationBuilder.setContentTitle(remoteMessage.getNotification().getTitle());
notificationBuilder.setContentText(remoteMessage.getNotification().getBody());
notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
notificationBuilder.setDefaults(Notification.DEFAULT_ALL | Notification.DEFAULT_LIGHTS | Notification.FLAG_SHOW_LIGHTS | Notification.DEFAULT_SOUND);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
public static boolean foregrounded() {
ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(appProcessInfo);
return (appProcessInfo.importance == appProcessInfo.IMPORTANCE_FOREGROUND || appProcessInfo.importance == appProcessInfo.IMPORTANCE_VISIBLE);
}
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchService.SinchServiceInterface) iBinder;
}
// it starts incoming call activity which does not show incoming caller name and picture
NotificationResult result = mSinchServiceInterface.relayRemotePushNotificationPayload(dataHashMap);
if (result.isValid() && result.isCall()) {
CallNotificationResult callResult = result.getCallResult();
if (callResult.isCallCanceled() || callResult.isTimedOut()) {
createNotification("Missed Call from : ", callResult.getRemoteUserId());
return;
} else {
if (callResult.isVideoOffered()) {
Intent intent = new Intent(this, IncomingCallScreenActivity.class);
intent.putExtra(SinchService.CALL_ID, callResult.getRemoteUserId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
}
}
} else if (result.isValid() && result.isMessage()) {
//i want to get message content here
MessageNotificationResult notificationResult = result.getMessageResult();
createNotification("Received Message from : ", notificationResult.getSenderId());
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
unbindService(this);
}
private void createNotification(String contentTitle, String userId) {
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext()).setSmallIcon(R.mipmap.ic_launcher).setContentTitle(contentTitle).setContentText(userId);
mBuilder.setContentIntent(contentIntent);
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
NotificationManager mNotificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
}
}
here is my since service code:
public class SinchService extends Service {
private static final String APP_KEY = "***********";
private static final String APP_SECRET = "***********";
private static final String ENVIRONMENT = "clientapi.sinch.com";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private PersistedSettings mSettings;
private StartFailedListener mListener;
#Override
public void onCreate() {
super.onCreate();
mSettings = new PersistedSettings(getApplicationContext());
String userName = mSettings.getUsername();
if (!userName.isEmpty()) {
Intent intent=new Intent(this,placecallActvity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
start(userName);
}
}
#Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
private void start(String userName) {
if (mSinchClient == null) {
mSettings.setUsername(userName);
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName).applicationKey(APP_KEY).applicationSecret(APP_SECRET).environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.checkManifest();
mSinchClient.setSupportActiveConnectionInBackground(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().setRespectNativeCalls(false);
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.getVideoController().setResizeBehaviour(VideoScalingType.ASPECT_FILL);
mSinchClient.start();
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
mSettings.setUsername("");
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
#Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public NotificationResult relayRemotePushNotificationPayload(final Map payload) {
if (mSinchClient == null && !mSettings.getUsername().isEmpty()) {
start(mSettings.getUsername());
} else if (mSinchClient == null && mSettings.getUsername().isEmpty()) {
return null;
}
return mSinchClient.relayRemotePushNotificationPayload(payload);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String 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 VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
}
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.d(TAG, "Incoming call");
Intent intent = new Intent(SinchService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchService.this.startActivity(intent);
}
}
private class PersistedSettings {
private SharedPreferences mStore;
private static final String PREF_KEY = "Sinch";
public PersistedSettings(Context context) {
mStore = context.getSharedPreferences(PREF_KEY, MODE_PRIVATE);
}
public String getUsername() {
return mStore.getString("Username", "");
}
public void setUsername(String username) {
SharedPreferences.Editor editor = mStore.edit();
editor.putString("Username", username);
editor.commit();
}
}
}
add FCM to your project
add following code
public class SinchService extends Service {
static final String APP_KEY = "************";
static final String APP_SECRET = "***********";
static final String ENVIRONMENT = "clientapi.sinch.com";
public static final int MESSAGE_PERMISSIONS_NEEDED = 1;
public static final String REQUIRED_PERMISSION = "REQUIRED_PESMISSION";
public static final String MESSENGER = "MESSENGER";
private Messenger messenger;
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private StartFailedListener mListener;
private PersistedSettings mSettings;
private AudioPlayer audioPlayer;
private Handler handler;
#Override
public void onCreate() {
super.onCreate();
super.onCreate();
mSettings = new PersistedSettings(getApplicationContext());
String userName = mSettings.getUsername();
if (!userName.isEmpty()) {
start(userName);
}
handler = new Handler();
}
private void createClient(String username) {
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(username)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.setPushNotificationDisplayName("User " + username);
}
#Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminateGracefully();
}
super.onDestroy();
}
private boolean hasUsername() {
if (mSettings.getUsername().isEmpty()) {
Log.e(TAG, "Can't start a SinchClient as no username is available!");
return false;
}
return true;
}
private void createClientIfNecessary() {
if (mSinchClient != null)
return;
if (!hasUsername()) {
throw new IllegalStateException("Can't create a SinchClient as no username is available!");
}
createClient(mSettings.getUsername());
}
private void start(String userName) {
boolean permissionsGranted = true;
if (mSinchClient == null) {
mSettings.setUsername(userName);
createClient(userName);
}
createClientIfNecessary();
try {
//mandatory checks
mSinchClient.checkManifest();
if (getApplicationContext().checkCallingOrSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
throw new MissingPermissionException(Manifest.permission.CAMERA);
}
} catch (MissingPermissionException e) {
permissionsGranted = false;
if (messenger != null) {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString(REQUIRED_PERMISSION, e.getRequiredPermission());
message.setData(bundle);
message.what = MESSAGE_PERMISSIONS_NEEDED;
try {
messenger.send(message);
} catch (RemoteException e1) {
e1.printStackTrace();
}
}
}
if (permissionsGranted) {
Log.d(TAG, "Starting SinchClient");
try {
mSinchClient.start();
} catch (IllegalStateException e) {
Log.w(TAG, "Can't start SinchClient - " + e.getMessage());
}
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminateGracefully();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
#Override
public IBinder onBind(Intent intent) {
messenger = intent.getParcelableExtra(MESSENGER);
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public String getUserName() {
return mSettings.getUsername();
}
public void setUsername(String username) {
mSettings.setUsername(username);
}
public void retryStartAfterPermissionGranted() {
SinchService.this.attemptAutoStart();
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String 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 VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
public NotificationResult relayRemotePushNotificationPayload(final Map payload) {
if (!hasUsername()) {
Log.e(TAG, "Unable to relay the push notification!");
return null;
}
createClientIfNecessary();
return mSinchClient.relayRemotePushNotificationPayload(payload);
}
}
private void attemptAutoStart() {
}
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.d(TAG, "onIncomingCall: " + call.getCallId());
audioPlayer = AudioPlayer.getInstance(getApplicationContext());
call.addCallListener(new SinchCallListener());
Intent intent = new Intent(SinchService.this, IncomingCallScreenActivity.class);
intent.putExtra(EXTRA_ID, MESSAGE_ID);
intent.putExtra(CALL_ID, call.getCallId());
boolean inForeground = isAppOnForeground(getApplicationContext());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (inForeground) {
SinchService.this.startActivity(intent);
} else {
if (!audioPlayer.isPlayedRingtone()) {
audioPlayer.playRingtone();
}
((NotificationManager) Objects.requireNonNull(getSystemService(NOTIFICATION_SERVICE))).notify(MESSAGE_ID, createIncomingCallNotification(call.getRemoteUserId(), intent));
}
}
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = null;
if (activityManager != null) {
appProcesses = activityManager.getRunningAppProcesses();
}
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
private Bitmap getBitmap(Context context, int resId) {
int largeIconWidth = (int) context.getResources()
.getDimension(R.dimen.height);
int largeIconHeight = (int) context.getResources()
.getDimension(R.dimen.height);
Drawable d = context.getResources().getDrawable(resId);
Bitmap b = Bitmap.createBitmap(largeIconWidth, largeIconHeight, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
d.setBounds(0, 0, largeIconWidth, largeIconHeight);
d.draw(c);
return b;
}
private PendingIntent getPendingIntent(Intent intent, String action) {
intent.setAction(action);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 111, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
#TargetApi(29)
private Notification createIncomingCallNotification(String userId, Intent fullScreenIntent) {
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 112, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(getApplicationContext(), FcmListenerService.CHANNEL_ID)
.setContentTitle("Incoming call")
.setContentText(userId)
.setLargeIcon(getBitmap(getApplicationContext(), R.drawable.call_normal))
.setSmallIcon(R.drawable.call_pressed)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setFullScreenIntent(pendingIntent, true)
.addAction(R.drawable.button_accept, "Answer", getPendingIntent(fullScreenIntent, ACTION_ANSWER))
.addAction(R.drawable.button_decline, "Ignore", getPendingIntent(fullScreenIntent, ACTION_IGNORE))
.setOngoing(true);
return builder.build();
}
}
private class SinchCallListener implements CallListener {
#Override
public void onCallEnded(Call call) {
cancelNotification();
if (audioPlayer != null && audioPlayer.isPlayedRingtone()) {
audioPlayer.stopRingtone();
}
}
#Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
if (audioPlayer != null && audioPlayer.isPlayedRingtone()) {
audioPlayer.stopRingtone();
}
}
#Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
}
#Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
// no need to implement for managed push
}
}
public void cancelNotification() {
NotificationManager nMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (nMgr != null) {
nMgr.cancel(MESSAGE_ID);
}
}
private static class PersistedSettings {
private SharedPreferences mStore;
private static final String PREF_KEY = "Sinch";
public PersistedSettings(Context context) {
mStore = context.getSharedPreferences(PREF_KEY, MODE_PRIVATE);
}
public String getUsername() {
return mStore.getString("Username", "");
}
public void setUsername(String username) {
SharedPreferences.Editor editor = mStore.edit();
editor.putString("Username", username);
editor.apply();
}
}
}
and crete FcmListenerService and add following code
public class FcmListenerService extends FirebaseMessagingService {
public static String CHANNEL_ID = "Sinch Push Notification Channel";
private final String PREFERENCE_FILE = "com.sinch.android.rtc.sample.push.shared_preferences";
SharedPreferences sharedPreferences;
#Override
public void onMessageReceived(RemoteMessage remoteMessage){
Map data = remoteMessage.getData();
if (SinchHelpers.isSinchPushPayload(data)) {
new ServiceConnection() {
private Map payload;
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Context context = getApplicationContext();
sharedPreferences = context.getSharedPreferences(PREFERENCE_FILE, Context.MODE_PRIVATE);
if (payload != null) {
SinchService.SinchServiceInterface sinchService = (SinchService.SinchServiceInterface) service;
if (sinchService != null) {
NotificationResult result = sinchService.relayRemotePushNotificationPayload(payload);
// handle result, e.g. show a notification or similar
// here is example for notifying user about missed/canceled call:
if (result!=null && result.isValid() && result.isCall()) {
CallNotificationResult callResult = result.getCallResult();
if (callResult != null && result.getDisplayName() != null) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(callResult.getRemoteUserId(), result.getDisplayName());
editor.apply();
}
if (callResult != null && callResult.isCallCanceled()) {
String displayName = result.getDisplayName();
if (displayName == null) {
displayName = sharedPreferences.getString(callResult.getRemoteUserId(), "n/a");
}
createMissedCallNotification(!displayName.isEmpty() ? displayName : callResult.getRemoteUserId());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.deleteSharedPreferences(PREFERENCE_FILE);
}
}
}
}
}
payload = null;
}
#Override
public void onServiceDisconnected(ComponentName name) {}
public void relayMessageData(Map<String, String> data) {
payload = data;
createNotificationChannel(NotificationManager.IMPORTANCE_MAX);
getApplicationContext().bindService(new Intent(getApplicationContext(), SinchService.class), this, BIND_AUTO_CREATE);
}
}.relayMessageData(data);
}
}
private void createNotificationChannel(int importance) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Amar LPG";
String description = "Incoming call notification";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
}
}
private void createMissedCallNotification(String userId) {
createNotificationChannel(NotificationManager.IMPORTANCE_DEFAULT);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0,
new Intent(getApplicationContext(), IncomingCallScreenActivity.class), 0);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
.setSmallIcon(R.drawable.icon)
.setContentTitle("Missed call from ")
.setContentText(userId)
.setContentIntent(contentIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
mNotificationManager.notify(1, builder.build());
}
}
}
in the incoming call screen add the following code
public class IncomingCallScreenActivity extends BaseActivity {
static final String TAG = IncomingCallScreenActivity.class.getSimpleName();
private String mCallId;
private AudioPlayer mAudioPlayer;
public static final String ACTION_ANSWER = "answer";
public static final String ACTION_IGNORE = "ignore";
public static final String EXTRA_ID = "id";
public static int MESSAGE_ID = 14;
private String mAction;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_incoming_call_screen);
Button answer = (Button) findViewById(R.id.answerButton);
answer.setOnClickListener(mClickListener);
Button decline = (Button) findViewById(R.id.declineButton);
decline.setOnClickListener(mClickListener);
mAudioPlayer = new AudioPlayer(this);
mAudioPlayer.playRingtone();
mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
mAction = "";
}
#Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
if (intent != null) {
if (intent.getStringExtra(SinchService.CALL_ID) != null) {
mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
}
final int id = intent.getIntExtra(EXTRA_ID, -1);
if (id > 0) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(id);
}
mAction = intent.getAction();
}
}
#Override
protected void onServiceConnected() {
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.addCallListener(new SinchCallListener());
TextView remoteUser = (TextView) findViewById(R.id.remoteUser);
remoteUser.setText(call.getRemoteUserId());
if (ACTION_ANSWER.equals(mAction)) {
mAction = "";
answerClicked();
} else if (ACTION_IGNORE.equals(mAction)) {
mAction = "";
declineClicked();
}
} else {
Log.e(TAG, "Started with invalid callId, aborting");
finish();
}
}
private void answerClicked() {
if (mAudioPlayer!=null && mAudioPlayer.isPlayedRingtone()){
mAudioPlayer.stopRingtone();
}
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
Log.d(TAG, "Answering call");
call.answer();
Intent intent = new Intent(this, CallScreenActivity.class);
intent.putExtra(SinchService.CALL_ID, mCallId);
startActivity(intent);
} else {
finish();
}
}
private void declineClicked() {
if (mAudioPlayer!=null && mAudioPlayer.isPlayedRingtone()){
mAudioPlayer.stopRingtone();
}
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.hangup();
}
finish();
}
private class SinchCallListener implements VideoCallListener {
#Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended, cause: " + cause.toString());
mAudioPlayer.stopRingtone();
finish();
}
#Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
}
#Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
}
#Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
}
#Override
public void onVideoTrackAdded(Call call) {
// Display some kind of icon showing it's a video call
}
#Override
public void onVideoTrackPaused(Call call) {
}
#Override
public void onVideoTrackResumed(Call call) {
}
}
private View.OnClickListener mClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.answerButton:
answerClicked();
break;
case R.id.declineButton:
declineClicked();
break;
}
}
};
}
in the login activity add the following code.
public class MainActivity extends BaseActivity implements SinchService.StartFailedListener, PushTokenRegistrationCallback, UserRegistrationCallback {
private Button mLoginButton;
private EditText mLoginName;
private ProgressDialog mSpinner;
private String mUserId;
private long mSigningSequence = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.READ_PHONE_STATE},100);
}
mLoginName = (EditText) findViewById(R.id.loginName);
mLoginButton = (Button) findViewById(R.id.loginButton);
mLoginButton.setEnabled(false);
mLoginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
loginClicked();
}
});
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onServiceConnected() {
mLoginButton.setEnabled(true);
getSinchServiceInterface().setStartListener(this);
}
#Override
protected void onPause() {
if (mSpinner != null) {
mSpinner.dismiss();
}
super.onPause();
}
#Override
public void onStartFailed(SinchError error) {
Toast.makeText(this, error.toString(), Toast.LENGTH_LONG).show();
if (mSpinner != null) {
mSpinner.dismiss();
}
}
#Override
public void onStarted() {
openPlaceCallActivity();
}
private void loginClicked() {
String userName = mLoginName.getText().toString();
if (userName.isEmpty()) {
Toast.makeText(this, "Please enter a name", Toast.LENGTH_LONG).show();
return;
}
if (!getSinchServiceInterface().isStarted()) {
getSinchServiceInterface().startClient(userName);
showSpinner();
} else {
openPlaceCallActivity();
}
mUserId = userName;
UserController uc = Sinch.getUserControllerBuilder()
.context(getApplicationContext())
.applicationKey(APP_KEY)
.userId(mUserId)
.environmentHost(ENVIRONMENT)
.build();
uc.registerUser(this, this);
}
private void openPlaceCallActivity() {
Intent mainActivity = new Intent(this, PlaceCallActivity.class);
startActivity(mainActivity);
finish();
}
private void showSpinner() {
mSpinner = new ProgressDialog(this);
mSpinner.setTitle("Logging in");
mSpinner.setMessage("Please wait...");
mSpinner.show();
}
private void dismissSpinner() {
if (mSpinner != null) {
mSpinner.dismiss();
mSpinner = null;
}
}
#Override
public void tokenRegistered() {
dismissSpinner();
startClientAndOpenPlaceCallActivity();
}
private void startClientAndOpenPlaceCallActivity() {
// start Sinch Client, it'll result onStarted() callback from where the place call activity will be started
if (!getSinchServiceInterface().isStarted()) {
getSinchServiceInterface().startClient("");
showSpinner();
}
}
#Override
public void tokenRegistrationFailed(SinchError sinchError) {
dismissSpinner();
Toast.makeText(this, "Push token registration failed - incoming calls can't be received!", Toast.LENGTH_LONG).show();
}
#Override
public void onCredentialsRequired(ClientRegistration clientRegistration) {
String toSign = mUserId + APP_KEY + mSigningSequence + APP_SECRET;
String signature;
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-1");
byte[] hash = messageDigest.digest(toSign.getBytes("UTF-8"));
signature = Base64.encodeToString(hash, Base64.DEFAULT).trim();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e.getCause());
}
clientRegistration.register(signature, mSigningSequence++);
}
#Override
public void onUserRegistered() {
}
#Override
public void onUserRegistrationFailed(SinchError sinchError) {
dismissSpinner();
Toast.makeText(this, "Registration failed!", Toast.LENGTH_LONG).show();
}
}
on our SDK package you will find under the Samples folder multiple sample files, the one called sinch-rtc-sample-video-push does what you wnat.
You should also check our docs here:
https://developers.sinch.com/docs/voice-android-push-notifications
Best regards

Converting my Background Service to Android Oreo 8.0 Compatible

I have a service in my App which handles number of things. Sometimes when device is idle, the app crashes. And I know about the new Android 8.0 guidelines but I am not sure If I should convert my service to JobScheduler or take any other correct way. I can use some suggestions on which will be the best way to convert this service. Here is the code
HERE IS THE SERVICE :
public class ConnectionHolderService extends Service {
private static final String LOG_TAG = ConnectionHolderService.class.getSimpleName();
private SensorManager mSensorManager;
private static ConnectionHolderService instance = null;
public static ConnectionHolderService getInstanceIfRunningOrNull() {
return instance;
}
private class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
//some code
}
}
private Messenger mMessenger = new Messenger(new IncomingHandler());
public ConnectionHolderService() {
}
#Override
public void onCreate() {
super.onCreate();
instance = this;
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, Intent intent) {
//some code
}
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
private void startListeningForShake() {
mShakeEnabled = true;
startServiceToAvoidStoppingWhenNoClientBound(ACTION_STOP_SHAKE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
mSensorManager.registerListener(mSensorListener,
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_UI);
}
}
private void startServiceToAvoidStoppingWhenNoClientBound(String action) {
registerReceiver(mReceiver, new IntentFilter(action));
startService(new Intent(this, ConnectionHolderService.class));
final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, ":Doze lock");
if (!lock.isHeld()) {
lock.acquire();
}
// When the Shake is active, we should not stop when UI unbinds from this service
startNotification();
}
private Notification getShakeServiceForegroundNotification() {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
if (mShakeEnabled) {
notificationBuilder.addAction(0, getString(R.string.shake_turn_off),
PendingIntent.getBroadcast(this,
REQ_CODE_STAY_ON,
new Intent(ACTION_STOP_SHAKE),
PendingIntent.FLAG_UPDATE_CURRENT));
}
if (mPollingEnabled) {
// notificationBuilder.addAction(0, getString(R.string.stop_smart_home_integration),
// PendingIntent.getBroadcast(this,
// REQ_CODE_STAY_ON,
// new Intent(ACTION_STOP_POLLING_SQS),
// PendingIntent.FLAG_UPDATE_CURRENT));
}
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
notificationBuilder
.setSmallIcon(R.drawable.logo)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setUsesChronometer(true)
.setContentIntent(PendingIntent.getActivity(this, REQ_CODE_STAY_ON, intent, PendingIntent.FLAG_UPDATE_CURRENT))
.setContentTitle(getString(R.string.title_notification_running_background))
.setContentText(getString(R.string.description_running_background));
return notificationBuilder.build();
}
private void stopIfNeeded() {
if (!mShakeEnabled && !mPollingEnabled) {
try {
unregisterReceiver(mReceiver);
} catch (Exception e) {
// It can be IllegalStateException
}
stopNotification();
stopSelf();
if (lock != null && lock.isHeld()) {
lock.release();
}
}
}
public void startNotification() {
if (SqsPollManager.sharedInstance().isConnectInBackground() && !MyApp.sharedInstance().isAppInForeground()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
startForeground(ID_SHAKE_SERVICE, getShakeServiceForegroundNotification());
}
}
}
public void stopNotification() {
mNotificationManager.cancel(ID_SHAKE_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
stopForeground(true);
}
}
#Override
public void onDestroy() {
super.onDestroy();
mReceiver = null;
Log.i(LOG_TAG, "onDestroy");
mMessenger = null;
mSensorListener = null;
instance = null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Log.i(LOG_TAG, "onTaskRemoved! Stopping ConnectionHolderService");
try {
unregisterReceiver(mReceiver);
} catch (Exception e) {
}
stopNotification();
stopSelf();
if (lock != null && lock.isHeld()) {
lock.release();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
stopForeground(true);
}
stopSelf();
}
}
HERE IS MY APP CLASS:
public class MyApp {
#Override
public void onCreate() {
super.onCreate();
sendMessageToConnectionHolderService("SomeMessage");
}
public void sendMessageToConnectionHolderService(final int what) {
bindService(new Intent(this, ConnectionHolderService.class), new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(LOG_TAG, "ConnectionHolderService connected!");
Messenger messenger = new Messenger(service);
Message msg = new Message();
msg.what = what;
try {
messenger.send(msg);
Log.i(LOG_TAG, "Message " + what + " has been sent to the service!");
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error sending message " + msg.what + " to ConnectionHolderService", e);
}
final ServiceConnection conn = this;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
unbindService(conn);
}
}, 1000);
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.i(LOG_TAG, "ConnectionHolderService disconnected!");
}
}, Context.BIND_AUTO_CREATE);
}
private Runnable mStartNotificationRunnable = new Runnable() {
#Override
public void run() {
if (SqsPollManager.sharedInstance().isPollingEnabled()
&& SqsPollManager.sharedInstance().isConnectInBackground()
&& !isAppInForeground()) {
if (null != ConnectionHolderService.getInstanceIfRunningOrNull()) {
ConnectionHolderService.getInstanceIfRunningOrNull().startNotification();
}
}
}
};
private Runnable mStopNotificationRunnable = new Runnable() {
#Override
public void run() {
if (null != ConnectionHolderService.getInstanceIfRunningOrNull()) {
ConnectionHolderService.getInstanceIfRunningOrNull().stopNotification();
}
}
};
}
AndroidManifest :
<service
android:name=".ConnectionHolderService"
android:enabled="true"
android:exported="false" />
You should consider using WorkManager. There are a lot of resources on how to use it, including samples, code-labs and blogs.
And if you are interested in JobScheduler in particular because of the Android 8.0 guidelines, I wrote a blog post that provides insights.

Port forwarding service

to be able using a remote service through localhost IP (to hide real address from users in other intents) I am using this service to port-forwarding :
public class PortForward extends Service implements Runnable {
private static final String TAG = "Port Forward";
private int localPort;
private int remotePort;
private String remoteHost;
private boolean running = false;
private int lastUp = -1;
private int lastDown = -1;
private int bUp = 0;
private int bDown = 0;
LocalBroadcastManager bm;
private Thread t;
ServerSocketChannel serverSocketChannel = null;
public Handler sendBroadcastHandler = new Handler() {
public void handleMessage(Message msg) {
Intent i = new Intent().setAction(MainActivity.USAGE_UPDATE);
i.putExtra("bUp", bUp);
i.putExtra("bDown", bDown);
bm.sendBroadcast(i);
}
};
public Handler sendDeathHandler = new Handler() {
public void handleMessage(Message msg) {
Bundle b = msg.getData();
String causeOfDeath = b.getString("causeOfDeath", "unknown");
Notification note = new Notification.Builder(PortForward.this)
.setContentTitle("TCP forwarding thread dead")
.setContentText("Cause of death: " + causeOfDeath)
.setSmallIcon(R.drawable.ic_launcher).build();
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.notify(1338, note);
}
};
private void updateCounts() {
updateCounts(false);
}
private void updateCounts(boolean force) {
if (!force && (bUp - lastUp < 10000 && bDown - lastDown < 10000)) {
return;
}
lastUp = bUp;
lastDown = bDown;
Message msg = sendBroadcastHandler.obtainMessage();
sendBroadcastHandler.sendMessage(msg);
}
#Override
public void onDestroy() {
Log.d(TAG, "Service onDestroy");
if (t != null) {
t.interrupt();
try {
t.join();
} catch (InterruptedException e) {
Log.d(TAG, "couldn't join forwarder-thread");
System.exit(1);
}
}
Log.d(TAG, "Killed it");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service onStart");
if (running){
updateCounts(true);
return START_REDELIVER_INTENT;
}
running = true;
bm = LocalBroadcastManager.getInstance(this);
localPort = intent.getIntExtra("localPort", -1);
remotePort = intent.getIntExtra("remotePort", -1);
remoteHost = intent.getStringExtra("remoteHost");
t = new Thread(this);
t.start();
Log.d(TAG, "launching a thread");
Notification note = new Notification.Builder(this)
.setContentTitle("Forwarding TCP Port")
.setContentText(String.format(
"localhost:%s -> %s:%s", localPort, remoteHost, remotePort))
.setSmallIcon(R.drawable.ic_launcher)
.build();
Intent i = new Intent(this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
note.contentIntent = pi;
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground(1337, note);
Log.d(TAG, "doing startForeground");
updateCounts(true);
return START_REDELIVER_INTENT;
}
private void reportException(Exception e){
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
Message msg = sendDeathHandler.obtainMessage();
Bundle b = msg.getData();
b.putString("causeOfDeath", sw.toString());
sendDeathHandler.sendMessage(msg);
}
private void finish(Selector s){
try {
serverSocketChannel.close();
} catch (IOException e){ }
Set<SelectionKey> selectedKeys = s.keys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
closeConnectionForKey(keyIterator.next());
}
}
private void closeChannel(SocketChannel c){
if (c != null){
try {
if (c != null){
c.close();
}
} catch (IOException e){ }
}
}
private void closeConnectionForKey(SelectionKey key){
PFGroup g = null;
try {
g = (PFGroup)key.attachment();
} catch (Exception e){
return;
}
if (g == null) {return;}
closeChannel(g.iChannel);
closeChannel(g.oChannel);
}
#Override
public void run() {
String causeOfDeath = null;
System.out.println("Server online");
Selector selector = null;
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(localPort));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
reportException(e);
return;
}
System.out.println("Server socket bound.");
while (true) {
System.out.println("Waiting for conn");
updateCounts();
int readyChannels = 0;
try {
readyChannels = selector.select();
} catch (IOException e) {
reportException(e);
continue;
}
if (Thread.currentThread().isInterrupted()) {
finish(selector);
return;
}
if (readyChannels == 0) {
continue;
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
//System.out.println("Ready on " + readyChannels);
SelectionKey key = keyIterator.next();
keyIterator.remove();
if (!key.isValid()) {
continue;
} else if (key.isAcceptable()) {
System.out.println("Acceptable!");
PFGroup g = new PFGroup();
// 512KB buffers
g.iBuffer = ByteBuffer.allocate(512000);
g.oBuffer = ByteBuffer.allocate(512000);
boolean iConnected = false;
try {
g.iChannel = serverSocketChannel.accept();
iConnected = g.iChannel.finishConnect();
if (iConnected){
g.sidesOn++;
}
g.iChannel.configureBlocking(false);
g.iKey = g.iChannel.register(selector, 0, g);
g.oChannel = SocketChannel.open();
g.oChannel.configureBlocking(false);
g.oChannel.connect(new InetSocketAddress(remoteHost, remotePort));
g.oKey =g.oChannel.register(selector, SelectionKey.OP_CONNECT, g);
} catch (IOException e) {
continue;
}
} else if (key.isConnectable()) {
System.out.println("connectable!");
try {
SocketChannel c = (SocketChannel) key.channel();
PFGroup g = (PFGroup)key.attachment();
if (!c.finishConnect()) {
System.out.println("couldn't finish conencting");
continue;
}
g.sidesOn++;
System.out.println("Initilized the bidirectional forward");
key.interestOps(SelectionKey.OP_READ);
g.iKey = g.iChannel.register(selector, SelectionKey.OP_READ, g);
} catch (IOException e) {
continue;
}
} else if (key.isReadable()) {
try {
ByteBuffer b = null;
SocketChannel from = null;
SocketChannel to = null;
PFGroup g = (PFGroup)key.attachment();
String label = null;
if (key.channel() == g.iChannel){
from = g.iChannel;
to = g.oChannel;
b = g.iBuffer;
label = "incoming";
} else if (key.channel() == g.oChannel){
from = g.oChannel;
to = g.iChannel;
b = g.oBuffer;
label = "outgoing";
}
int i = from.read(b);
b.flip();
while (b.hasRemaining()) {
int bytes = to.write(b);
if(label.equals("incoming")){
bUp += bytes;
} else {
bDown += bytes;
}
}
b.clear();
if (i == -1) {
key.cancel();
g.sidesOn--;
if (g.sidesOn == 0){
System.out.println("Done, closing keys");
closeConnectionForKey(key);
}
}
} catch (IOException e){
Log.d(TAG, "closing connection for key.");
closeConnectionForKey(key);
}
}
}
}
}
public class PFGroup {
public ByteBuffer iBuffer;
public ByteBuffer oBuffer;
public SocketChannel iChannel;
public SocketChannel oChannel;
public int sidesOn = 0;
SelectionKey iKey;
SelectionKey oKey;
}
}
and in my main activity i used it like this:
Intent i=new Intent(this, PortForward.class)
.putExtra("localPort", 1195)
.putExtra("remotePort", port)
.putExtra("remoteHost", address);
startService(i);
but it does not work. when app is in background i can not use address:port through 127.0.0.1:1195 .
and also no related log appeasers in logcat.

Android: "unable to add window token is not valid is your activity running?" in my SplashScreenActivity

I am new to android and now I am creating a app with splash screen as a launcher activity.
When I am trying to open the app it shows unable to add window token is not valid is your activity running.But if I open the app second time then it is working fine
My code is here:
public class SplashScreenActivity extends Activity {
private String mSavedEmail;
private String mSavedPassword;
public static SplashScreenActivity sInstance = null;
private User mUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.activity_splash_screen);
SplashScreenActivity.sInstance = SplashScreenActivity.this;
/* Initiate Crittercism */
Crittercism.initialize(SplashScreenActivity.this, Const.CRITTERCISM_APP_ID);
new Thread(new Runnable() {
#Override
public void run() {
new CouchDB();
}
}).start();
// new UsersManagement();
String language = SampleApp.getPreferences().getGuiLanguage();
if (!language.equals("")) {
Locale locale = new Locale(language);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
}
if (SampleApp.hasNetworkConnection()) {
if (checkIfUserSignIn()) {
boolean isLinked = false;
mSavedEmail = SampleApp.getPreferences().getUserEmail();
mSavedPassword = SampleApp.getPreferences().getUserPassword();
if (mSavedPassword.equals("")) {
isLinked = true;
mSavedPassword = SampleApp.getPreferences().getUserLinkedPassword();
}
mUser = new User();
CouchDB.authAsync(mSavedEmail, mSavedPassword, isLinked, new AuthListener(), SplashScreenActivity.this, false);
} else {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startActivity(new Intent(SplashScreenActivity.this,
SignInActivity.class));
//finish();
}
}, 2000);
}
} else {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(SplashScreenActivity.this,
SignInActivity.class);
intent.putExtra(Const.SIGN_IN, true);
SplashScreenActivity.this.startActivity(intent);
Log.v("sample", "SplashScreenActivity starts SignInActivity");
Toast.makeText(SplashScreenActivity.this,
getString(R.string.no_internet_connection),
Toast.LENGTH_LONG).show();
}
}, 2000);
}
}
catch (Exception e){
}
}
#Override
protected void onResume() {
super.onResume();
overridePendingTransition(0, 0);
}
private boolean checkIfUserSignIn() {
boolean isSessionSaved = false;
Preferences prefs = LasteekApp.getPreferences();
if (prefs.getUserEmail() == null
|| (prefs.getUserPassword() == null && prefs.getUserLinkedPassword() == null)) {
isSessionSaved = false;
} else if (prefs.getUserEmail().equals("")
&& (prefs.getUserPassword() != null && prefs.getUserPassword().equals(""))) {
isSessionSaved = false;
}
else if (prefs.getUserEmail().equals("")
&& (prefs.getUserLinkedPassword() != null && prefs.getUserLinkedPassword().equals(""))) {
isSessionSaved = false;
}
else {
isSessionSaved = true;
}
return isSessionSaved;
}
private void signIn(User u) {
UsersManagement.setLoginUser(u);
UsersManagement.setToUser(u);
UsersManagement.setToGroup(null);
Log.d("User", "GetEmail "+u.getEmail());
boolean openPushNotification = getIntent().getBooleanExtra(
Const.PUSH_INTENT, false);
Intent intent = new Intent(SplashScreenActivity.this,
MyProfileActivity.class);
if (openPushNotification) {
intent = getIntent();
intent.setClass(SplashScreenActivity.this,
MyProfileActivity.class);
}
//parse URI hookup://user/[ime korisnika] and hookup://group/[ime grupe]
Uri userUri = getIntent().getData();
//If opened from link
if (userUri != null) {
String scheme = userUri.getScheme(); // "hookup"
String host = userUri.getHost(); // "user" or "group"
if (host.equals("user")) {
List<String> params = userUri.getPathSegments();
String userName = params.get(0); // "ime korisnika"
intent.putExtra(Const.USER_URI_INTENT, true);
intent.putExtra(Const.USER_URI_NAME, userName);
} else if (host.equals("group")) {
List<String> params = userUri.getPathSegments();
String groupName = params.get(0); // "ime grupe"
intent.putExtra(Const.GROUP_URI_INTENT, true);
intent.putExtra(Const.GROUP_URI_NAME, groupName);
}
}
intent.putExtra(Const.SIGN_IN, true);
try{
if(u.getName().toString().equals("")||u.getName().toString()==null){
Log.v("lasteek", "SplashScreenActivity starts EditProfileActivity 1");
intent.putExtra(Const.NEWLY_SIGNED_UP, true);
SplashScreenActivity.this.startActivity(intent);
} else if (u.getTitlePosition().toString().equals("")||u.getTitlePosition().toString()==null) {
Log.v("sample", "SplashScreenActivity starts EditProfileActivity 2");
intent.putExtra(Const.NEWLY_SIGNED_UP, true);
SplashScreenActivity.this.startActivity(intent);
} else if (u.getFunction()==null) {
Log.v("sample", "SplashScreenActivity starts EditProfileActivity 3");
intent.putExtra(Const.NEWLY_SIGNED_UP, true);
SplashScreenActivity.this.startActivity(intent);
} else if (u.getIndustry()==null) {
Log.v("sample", "SplashScreenActivity starts EditProfileActivity 4");
intent.putExtra(Const.NEWLY_SIGNED_UP, true);
SplashScreenActivity.this.startActivity(intent);
} else {
Log.v("sample", "SplashScreenActivity starts MyProfileActivity");
SplashScreenActivity.this.startActivity(intent);
}
} catch (Exception e){
Log.e("Exception",e.toString());
}
//finish();
}
private void checkPassProtect (User user) {
if (sample.getPreferences().getPasscodeProtect())
{
Intent passcode = new Intent(SplashScreenActivity.this,
PasscodeActivity.class);
passcode.putExtra("protect", true);
SplashScreenActivity.this.startActivityForResult(passcode, 0);
}
else
{
signIn(user);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
signIn(mUser);
}
else {
startActivity(new Intent(SplashScreenActivity.this,
SignInActivity.class));
//finish();
}
super.onActivityResult(requestCode, resultCode, data);
}
private boolean authentificationOk(User user) {
boolean authentificationOk = false;
if (user.getEmail() != null && !user.getEmail().equals("")) {
if (user.getEmail().equals(mSavedEmail)) {
authentificationOk = true;
}
}
return authentificationOk;
}
private class AuthListener implements ResultListener<String>
{
#Override
public void onResultsSucceded(String result) {
boolean tokenOk = result.equals(Const.LOGIN_SUCCESS);
mUser = UsersManagement.getLoginUser();
if (tokenOk && mUser!=null) {
if (authentificationOk(mUser)) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
checkPassProtect(mUser);
}
}, 2000);
}
} else {
SideBarActivity.appLogout(false, false, true, false);
}
}
#Override
public void onResultsFail() {
SideBarActivity.appLogout(false, false, false, true);
}
}
public void onStart() {
super.onStart();
try {
if (SharedData.runningActivities == 0) {
// app enters foreground
}
SharedData.runningActivities++;
Log.v("sample.activitymonitor", "SplashScreenActivity enters foreground");
Log.v("sample.activitymonitor", "runningActivities=" + SharedData.runningActivities);
}
catch (Exception e){
}
}
public void onStop() {
super.onStop();
SharedData.runningActivities--;
if (SharedData.runningActivities == 0) {
// app goes to background
}
Log.v("sample.activitymonitor", "SplashScreenActivity enters background");
Log.v("sample.activitymonitor", "runningActivities=" + SharedData.runningActivities);
}
}

WebSocketClient Fails Only on Mobile Network

My Android app is maintaining a socket connection with the server and this connection works fine when the device is connected to Wi-Fi and fails only when the device is connected to the mobile network. Error trace is as follows
org.java_websocket.exceptions.InvalidFrameException: bad rsv 3
03-08 14:56:40.909 20527-21343/com.mydomain.myapp.staging W/System.err: at org.java_websocket.drafts.Draft_10.translateSingleFrame(Draft_10.java:308)
03-08 14:56:40.909 20527-21343/com.mydomain.myapp.staging W/System.err: at org.java_websocket.drafts.Draft_10.translateFrame(Draft_10.java:285)
03-08 14:56:40.909 20527-21343/com.mydomain.myapp.staging W/System.err: at org.java_websocket.WebSocketImpl.decodeFrames(WebSocketImpl.java:321)
03-08 14:56:40.909 20527-21343/com.mydomain.myapp.staging W/System.err: at org.java_websocket.WebSocketImpl.decode(WebSocketImpl.java:164)
03-08 14:56:40.909 20527-21343/com.mydomain.myapp.staging W/System.err: at org.java_websocket.client.WebSocketClient.run(WebSocketClient.java:185)
03-08 14:56:40.909 20527-21343/com.mydomain.myapp.staging W/System.err: at java.lang.Thread.run(Thread.java:841)
03-08 14:56:40.914 20527-21343/com.mydomain.myapp.staging D/WebSocketService: Socket closed: bad rsv 3
03-08 14:56:40.914 20527-21343/com.mydomain.myapp.staging D/WebSocketService: Socket closed- code: 1002, reason: bad rsv 3
My code is as follows,
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
public class WebSocketService extends Service {
public static final String WEB_SOCKET_SERVICE_RECEIVER = "com.mydomain.myapp.WebSocketServiceReceiver";
public static final String EXTRA_COMMAND = "extraCommand";
public static final String EXTRA_MESSAGE = "extraMessage";
public static final int COMMAND_STOP = -2;
public static final int COMMAND_SEND_MESSAGE = -3;
public static void stopService(Context context, String message) {
Intent intent = new Intent(WEB_SOCKET_SERVICE_RECEIVER);
intent.putExtra(EXTRA_COMMAND, COMMAND_STOP);
intent.putExtra(EXTRA_MESSAGE, message);
context.sendBroadcast(intent);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("WebSocketService", "onStartCommand");
if (!mStarted) {
mStarted = true;
mWebSocketClosed = false;
mWakeLock = ((PowerManager) getSystemService(POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"WebSocketServiceWakeLock");
pingPackets = new Stack<PingPacket>();
registerReceiver(mReceiver, new IntentFilter(
WEB_SOCKET_SERVICE_RECEIVER));
try {
startWebSocket();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("WebSocketService", "onDestroy");
if (mFallbackHandler != null) {
mFallbackHandler.removeCallbacksAndMessages(null);
mFallbackHandler = null;
}
if (mListenForConnectivity) {
mListenForConnectivity = false;
try {
unregisterReceiver(mProviderChangedListener);
} catch (Exception e) {
e.printStackTrace();
}
}
mStarted = false;
try {
unregisterReceiver(mReceiver);
} catch (Exception e) {
e.printStackTrace();
}
if (mWebSocketClient != null && mWebSocketClient.isOpen()) {
stopWebSocket();
}
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
.cancel(AppConstants.CONNECTION_ERROR_NOTIFICATION_ID);
if (mWakeLock.isHeld()) {
mWakeLock.release();
Log.d("WebSocketService", "WakeLock released");
}
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
if (mFallbackHandler != null) {
mFallbackHandler.removeCallbacksAndMessages(null);
mFallbackHandler = null;
}
if (mListenForConnectivity) {
mListenForConnectivity = false;
try {
unregisterReceiver(mProviderChangedListener);
} catch (Exception e) {
e.printStackTrace();
}
}
mStarted = false;
try {
unregisterReceiver(mReceiver);
} catch (Exception e) {
e.printStackTrace();
}
if (mWebSocketClient != null && mWebSocketClient.isOpen()) {
stopWebSocket();
}
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
.cancel(AppConstants.CONNECTION_ERROR_NOTIFICATION_ID);
if (mWakeLock.isHeld()) {
mWakeLock.release();
Log.d("WebSocketService", "WakeLock released");
}
}
protected void startWebSocket() throws URISyntaxException {
Log.d("WebSocketServce", "startWebSocket");
String webSocketBaseURL = BuildSpecificConstants.WEB_SOCKET_BASE_URL;
mWebSocketClient = new WebSocketClient(new URI(webSocketBaseURL
+ "?auth_token="
+ SessionManager.getInstance(getApplicationContext())
.getAPIKey())) {
#Override
public void onOpen(ServerHandshake handshakedata) {
Log.d("WebSocketServce", "onOpen");
if (!mWakeLock.isHeld()) {
mWakeLock.acquire();
Log.d("WebSocketService", "WakeLock acquired");
}
startPingProtocol();
if (mPendingMessage != null) {
sendMessage(mPendingMessage);
}
Editor editor = getSharedPreferences(
SharedPrefKeys.APP_PREFERENCES, MODE_PRIVATE).edit();
editor.putBoolean(SharedPrefKeys.WEB_SOCKET_ERROR, false);
editor.commit();
Intent intent = new Intent(
BaseActivity.BROADCAST_RECEIVER_INTENT_FILTER);
intent.putExtra(BaseActivity.BROADCAST_EXTRA_TYPE,
BaseActivity.BROADCAST_TYPE_WEB_SOCKET_CONNECTED);
sendBroadcast(intent);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
.cancel(AppConstants.CONNECTION_ERROR_NOTIFICATION_ID);
}
#Override
public void onWebsocketPong(WebSocket conn, Framedata frame) {
super.onWebsocketPong(conn, frame);
try {
if (frame.getOpcode() == Opcode.PONG) {
if (!pingPackets.isEmpty()) {
PingPacket returnedPing = pingPackets.get(0);
if (returnedPing != null) {
returnedPing.consume();
pingPackets.remove(0);
Log.d("WebSocketService", returnedPing.id
+ " pong received");
failedPingCount = 0;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onMessage(String message) {
try {
JSONObject eventJson = new JSONObject(message);
Log.d("WebSocketService", "Received: " + message);
String eventType = eventJson
.getString(IncomingMessageParams.EVENT);
if (eventType.equals(EventTypes.NEW_BOOKING)) {
Trip trip = Trip
.decodeJSON(
eventJson
.getJSONObject(BookingsResponseParams.KEY_BOOKING),
eventJson.getJSONObject("passenger"),
eventJson.getJSONArray("locations"),
eventJson.optJSONObject("coupon"),
SessionManager
.getInstance(WebSocketService.this));
Intent intent = new Intent(WebSocketService.this,
IncomingBookingActivity.class);
intent.putExtra(IncomingBookingActivity.EXTRA_BOOKING,
trip);
intent.putExtra(
MainActivity.EXTRA_COUNT_DOWN_TIME,
eventJson
.getInt(IncomingMessageParams.LOOKUP_TIME));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
SessionManager.getInstance(getApplicationContext())
.setStatus(DriverStatus.BUSY);
LocationUpdateService
.sendStatusChange(WebSocketService.this);
}
} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onError(Exception ex) {
ex.printStackTrace();
Log.d("WebSocketService", "Socket closed: " + ex.getMessage());
if (!Helper.checkNetworkStatus(WebSocketService.this)) {
mListenForConnectivity = true;
registerReceiver(mProviderChangedListener,
new IntentFilter(
ConnectivityManager.CONNECTIVITY_ACTION));
if (mWakeLock.isHeld()) {
mWakeLock.release();
Log.d("WebSocketService", "WakeLock released");
}
} else {
if (!mWebSocketClosed) {
reconnect();
}
}
if (!mWebSocketClosed) {
showErrorNotification();
Editor editor = getSharedPreferences(
SharedPrefKeys.APP_PREFERENCES, MODE_PRIVATE)
.edit();
editor.putBoolean(SharedPrefKeys.WEB_SOCKET_ERROR, true);
editor.commit();
Intent intent = new Intent(
BaseActivity.BROADCAST_RECEIVER_INTENT_FILTER);
intent.putExtra(BaseActivity.BROADCAST_EXTRA_TYPE,
BaseActivity.BROADCAST_TYPE_WEB_SOCKET_DISCONNECTED);
sendBroadcast(intent);
}
}
#Override
public void onClose(int code, String reason, boolean remote) {
Log.d("WebSocketService", "Socket closed- code: " + code
+ ", reason: " + reason);
if (code != CloseFrame.NORMAL) {
if (!Helper.checkNetworkStatus(WebSocketService.this)) {
mListenForConnectivity = true;
registerReceiver(
mProviderChangedListener,
new IntentFilter(
ConnectivityManager.CONNECTIVITY_ACTION));
if (mWakeLock.isHeld()) {
mWakeLock.release();
Log.d("WebSocketService", "WakeLock released");
}
} else {
reconnect();
}
showErrorNotification();
Editor editor = getSharedPreferences(
SharedPrefKeys.APP_PREFERENCES, MODE_PRIVATE)
.edit();
editor.putBoolean(SharedPrefKeys.WEB_SOCKET_ERROR, true);
editor.commit();
Intent intent = new Intent(
BaseActivity.BROADCAST_RECEIVER_INTENT_FILTER);
intent.putExtra(BaseActivity.BROADCAST_EXTRA_TYPE,
BaseActivity.BROADCAST_TYPE_WEB_SOCKET_DISCONNECTED);
sendBroadcast(intent);
}
}
};
if (BuildConfig.FLAVOR.equals("production")) {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
#Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
SSLSocketFactory factory = sslContext.getSocketFactory();
try {
mWebSocketClient.setSocket(factory.createSocket());
} catch (IOException e) {
e.printStackTrace();
}
}
mWebSocketClient.connect();
}
protected void startPingProtocol() {
pingCounter = 0;
failedPingCount = 0;
pingRunning = true;
if (pingThread == null || !pingThread.isAlive()) {
pingThread = new Thread(new Runnable() {
#Override
public void run() {
while (pingRunning) {
if (failedPingCount < 3) {
PingPacket packet = new PingPacket(pingCounter);
Log.d("WebSocketService", "sending ping "
+ pingCounter);
pingPackets.add(packet);
packet.sendPing(mWebSocketClient);
pingCounter++;
pingCounter %= Byte.MAX_VALUE;
} else {
stopPingProtocol();
Log.d("WebSocketService", "connection lost");
}
try {
Thread.sleep(AppConstants.PING_SPAWN_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
}
private void stopPingProtocol() {
if (pingThread != null && pingThread.isAlive()) {
pingRunning = false;
pingThread.interrupt();
for (int i = 0; i < pingPackets.size(); i++) {
pingPackets.get(i).consume();
}
pingPackets.clear();
}
}
protected void reconnect() {
if (mConnectionAttempt < AppConstants.MAX_WEB_SOCKET_RECONNECTS) {
mFallbackHandler = new Handler(Looper.getMainLooper());
mFallbackHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (mFallbackHandler != null) {
mFallbackHandler.removeCallbacks(this);
mFallbackHandler = null;
}
try {
stopWebSocket();
startWebSocket();
} catch (URISyntaxException e) {
e.printStackTrace();
}
mConnectionAttempt++;
}
}, AppConstants.WEB_SOCKET_BASE_FALLBACK_TIME * mConnectionAttempt);
} else {
if (mWakeLock.isHeld()) {
mWakeLock.release();
Log.d("WebSocketService", "WakeLock released");
}
}
}
protected void sendMessage(String message) {
if (mWebSocketClient != null && mWebSocketClient.isOpen()) {
mWebSocketClient.send(message);
mConnectionAttempt = 1;
Log.d("WebSocketService", "Send: " + message);
} else {
mPendingMessage = message;
}
}
protected void stopWebSocket() {
Log.d("WebSocketService", "Closing");
mWebSocketClosed = true;
mWebSocketClient.close();
stopPingProtocol();
Editor editor = getSharedPreferences(SharedPrefKeys.APP_PREFERENCES,
MODE_PRIVATE).edit();
editor.putBoolean(SharedPrefKeys.WEB_SOCKET_ERROR, false);
editor.commit();
Intent intent = new Intent(
BaseActivity.BROADCAST_RECEIVER_INTENT_FILTER);
intent.putExtra(BaseActivity.BROADCAST_EXTRA_TYPE,
BaseActivity.BROADCAST_TYPE_WEB_SOCKET_CONNECTED);
sendBroadcast(intent);
}
private WakefulBroadcastReceiver mProviderChangedListener = new WakefulBroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (Helper.checkNetworkStatus(context)) {
mListenForConnectivity = false;
try {
unregisterReceiver(this);
} catch (Exception e1) {
e1.printStackTrace();
}
try {
startWebSocket();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
};
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int command = intent.getIntExtra(EXTRA_COMMAND, -1);
switch (command) {
case COMMAND_STOP:
sendMessage(intent.getStringExtra(EXTRA_MESSAGE));
stopSelf();
break;
case COMMAND_SEND_MESSAGE:
sendMessage(intent.getStringExtra(EXTRA_MESSAGE));
break;
default:
break;
}
}
};
Private class,
private class PingPacket { protected byte id;
protected Timer timer;
protected PingPacket(byte id) {
this.id = id;
timer = new Timer();
}
protected void sendPing(WebSocketClient client) {
FramedataImpl1 frame = new FramedataImpl1(Opcode.PING);
frame.setFin(true);
client.sendFrame(frame);
Log.d("WebSocketService", id + " ping sent");
timer.schedule(new TimerTask() {
#Override
public void run() {
failedPingCount++;
Log.d("WebSocketService", "Ping " + id + " failed");
}
}, AppConstants.PING_EXPIRE_DELAY);
}
protected void consume() {
timer.cancel();
timer.purge();
}
}
}
What could possibly be causing this?
The InvalidFrameException with a message "bad rsv 3" implies that the protocol exchanged in the WebSocket connection is wrong. Your stack trace implies that Draft_10 is used, but it is too old.
FYI: You can find other WebSocket client libraries for Android in "Which WebSocket library to use in Android app?".

Categories

Resources