I've been following this tutorial at https://www.sinch.com/tutorials/android-messaging-tutorial-using-sinch-and-parse/#message and I'm having trouble sending the message to another user. When I write a message and send it. I keep getting a toast message that says "Message Failed To Send".
This is my MessageService Activity:
public class MessageService extends Service implements SinchClientListener {
private static final String APP_KEY = "xxxx";
private static final String APP_SECRET = "yyyy";
private static final String ENVIRONMENT = "sandbox.sinch.com";
private final MessageServiceInterface serviceInterface = new MessageServiceInterface();
private SinchClient sinchClient = null;
private MessageClient messageClient = null;
private String currentUserId;
private Intent broadcastIntent = new Intent("com.jordanpeterson.textly.messages.ListUsersActivity");
private LocalBroadcastManager broadCaster;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// get the current user id from parse
currentUserId = ParseUser.getCurrentUser().getObjectId();
if (currentUserId != null && !isSinchClientStarted()) {
startSinchClient(currentUserId);
}
broadCaster = LocalBroadcastManager.getInstance(this);
return super.onStartCommand(intent, flags, startId);
}
private void startSinchClient(String userame) {
sinchClient = Sinch.getSinchClientBuilder().context(this)
.userId(userame).applicationKey(APP_KEY)
.applicationSecret(APP_SECRET).environmentHost(ENVIRONMENT)
.build();
// this client listener requires that you define
// a few methods below
sinchClient.addSinchClientListener(this);
// Messaging is "turned-on", but calling is not
sinchClient.setSupportMessaging(true);
sinchClient.setSupportActiveConnectionInBackground(true);
sinchClient.checkManifest();
sinchClient.start();
}
private boolean isSinchClientStarted() {
return sinchClient != null && sinchClient.isStarted();
}
// The next 5 methods are for the sinch client listener
#Override
public void onClientFailed(SinchClient client, SinchError error) {
broadcastIntent.putExtra("success", false);
broadCaster.sendBroadcast(broadcastIntent);
sinchClient = null;
}
#Override
public void onClientStarted(SinchClient client) {
broadcastIntent.putExtra("success", true);
broadCaster.sendBroadcast(broadcastIntent);
client.startListeningOnActiveConnection();
messageClient = client.getMessageClient();
}
#Override
public void onClientStopped(SinchClient client) {
sinchClient = null;
}
#Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
// No code in here yet
}
#Override
public void onLogMessage(int level, String area, String message) {
// No code in here yet either
}
#Override
public IBinder onBind(Intent intent) {
return serviceInterface;
}
public void sendMessage(String recipientUserId, String textBody) {
if (messageClient != null) {
WritableMessage message = new WritableMessage(recipientUserId,
textBody);
messageClient.send(message);
}
}
public void addMessageClientListener(MessageClientListener listener) {
if (messageClient != null) {
messageClient.addMessageClientListener(listener);
}
}
public void removeMessageClientListener(MessageClientListener listener) {
if (messageClient != null) {
messageClient.removeMessageClientListener(listener);
}
}
#Override
public void onDestroy() {
sinchClient.stopListeningOnActiveConnection();
sinchClient.terminate();
}
// Public interface for ListUsersActivity & MessagingActivity
public class MessageServiceInterface extends Binder {
public void sendMessage(String recipientUserId, String textBody) {
MessageService.this.sendMessage(recipientUserId, textBody);
}
public void addMessageClientListener(MessageClientListener listener) {
MessageService.this.addMessageClientListener(listener);
}
public void removeMessageClientListener(MessageClientListener listener) {
MessageService.this.removeMessageClientListener(listener);
}
public boolean isSinchClientStarted() {
return MessageService.this.isSinchClientStarted();
}
}
}
This is my MessagingActivity:
private String mRecipientId;
private EditText mMessageBodyField;
private String mMessageBody;
private MessageService.MessageServiceInterface messageService;
private String mCurrentUserId;
private ServiceConnection serviceConnection = new MyServiceConnection();
private MessageClientListener messageClientListener = new MyMessageClientListener();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.messaging_sinch);
bindService(new Intent(this, MessageService.class), serviceConnection,
BIND_AUTO_CREATE);
// Get RecipientId from the intent
Intent intent = getIntent();
mRecipientId = intent.getStringExtra("RECIPIENT_ID");
mCurrentUserId = ParseUser.getCurrentUser().getObjectId();
mMessageBodyField = (EditText) findViewById(R.id.messageBodyField);
// Listen for a click on the send button
findViewById(R.id.sendButton).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
// Send The Message!
mMessageBody = mMessageBodyField.getText().toString();
if (mMessageBody.isEmpty()) {
Toast.makeText(MessagingActivity.this,
"Please enter a message", Toast.LENGTH_LONG)
.show();
return;
}
messageService.sendMessage(mRecipientId, mMessageBody);
mMessageBodyField.setText("");
}
});
}
// Unbind the service when the activity is destroyed
#Override
protected void onDestroy() {
messageService.removeMessageClientListener(messageClientListener);
unbindService(serviceConnection);
super.onDestroy();
}
private class MyServiceConnection implements ServiceConnection {
#Override
public void onServiceConnected(ComponentName componentName,
IBinder iBinder) {
messageService = (MessageService.MessageServiceInterface) iBinder;
messageService.addMessageClientListener(messageClientListener);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
messageService = null;
}
}
private class MyMessageClientListener implements MessageClientListener {
#Override
public void onMessageFailed(MessageClient client, Message message,
MessageFailureInfo failureInfo) {
Toast.makeText(MessagingActivity.this, "Message failed to send.",
Toast.LENGTH_LONG).show();
}
#Override
public void onIncomingMessage(MessageClient client, Message message) {
// Display an incoming message
}
#Override
public void onMessageSent(MessageClient client, Message message,
String recipientId) {
// Display the message that was just sent
// Later, I'll show you how to store the
// Message in Parse, so you can retrieve and
// display them every time the conversation is opened
}
#Override
public void onMessageDelivered(MessageClient client,
MessageDeliveryInfo deliveryInfo) {
}
// Don't worry about this right now
#Override
public void onShouldSendPushData(MessageClient client, Message message,
List<PushPair> pushPairs) {
}
}
}
And this is my ListUsersActivity:
public class ListUsersActivity extends Activity {
private String mCurrentUserId;
private ArrayAdapter<String> mNamesArrayAdapter;
private ArrayList<String> mNames;
private ListView mUsersListView;
private Button mLogoutButton;
private ProgressDialog mProgressDialog;
private BroadcastReceiver mReceiver = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_users);
mCurrentUserId = ParseUser.getCurrentUser().getObjectId();
mNames = new ArrayList<String>();
ParseQuery<ParseUser> query = ParseUser.getQuery();
// Don't include yourself!
query.whereNotEqualTo("objectId", mCurrentUserId);
query.findInBackground(new FindCallback<ParseUser>() {
#Override
public void done(List<ParseUser> userList, ParseException e) {
if (e == null) {
for (int i = 0; i < userList.size(); i++) {
mNames.add(userList.get(i).getUsername().toString());
}
mUsersListView = (ListView) findViewById(R.id.usersListView);
mNamesArrayAdapter = new ArrayAdapter<String>(
getApplicationContext(), R.layout.user_list_item,
mNames);
mUsersListView.setAdapter(mNamesArrayAdapter);
mUsersListView
.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a,
View v, int i, long l) {
openConversation(mNames, i);
}
private void openConversation(
ArrayList<String> mNames, int pos) {
ParseQuery<ParseUser> query = ParseUser
.getQuery();
query.whereEqualTo("username",
mNames.get(pos));
query.findInBackground(new FindCallback<ParseUser>() {
#Override
public void done(List<ParseUser> user,
ParseException e) {
if (e == null) {
Intent intent = new Intent(ListUsersActivity.this, MessagingActivity.class);
intent.putExtra("RECIPIENT_ID", user.get(0).getObjectId());
startActivity(intent);
} else {
Toast.makeText(
getApplicationContext(),
"Error finding that user!",
Toast.LENGTH_LONG)
.show();
}
}
});
}
});
} else {
Toast.makeText(ListUsersActivity.this,
"Error loading user list", Toast.LENGTH_LONG)
.show();
}
}
});
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setTitle("Loading");
mProgressDialog.setMessage("Please Wait...");
mProgressDialog.show();
// broadcast receiver to listen for the broadcast
// from MessageService
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Boolean success = intent.getBooleanExtra("success", false);
Toast.makeText(ListUsersActivity.this, "Sinch has started and is working!", Toast.LENGTH_LONG).show();
mProgressDialog.dismiss();
// Show a toast message is the Sinch service failed to start
if (!success) {
Toast.makeText(ListUsersActivity.this,
"Messaging service failed to start",
Toast.LENGTH_LONG).show();
}
}
};
LocalBroadcastManager
.getInstance(this)
.registerReceiver(
mReceiver,
new IntentFilter(
"com.jordanpeterson.textly.messages.ListUsersActivity"));
}
}
Try to print the message error because you may have it for many reasons.
Under onMessageFailed function, add this to see the failure message:
Toast.makeText(MessagingActivity.this,failureInfo.getSinchError().getMessage(), Toast.LENGTH_LONG).show();
Related
Good day everyone, I would like to ask, hat is the cause of that ANR?. In my project I have service which is binded in a activity. Now when I exit in that activity the app is hang for a moment. My thought is that the service is still running though I unbind it in onStop() of the activity.
Here is my service class
public class SpeechService extends Service {
public interface Listener {
/**
* Called when a new piece of text was recognized by the Speech API.
*
* #param text The text.
* #param isFinal {#code true} when the API finished processing audio.
*/
void onSpeechRecognized(String text, boolean isFinal);
}
private static final String TAG = "SpeechService";
private static final String PREFS = "SpeechService";
private static final String PREF_ACCESS_TOKEN_VALUE = "access_token_value";
private static final String PREF_ACCESS_TOKEN_EXPIRATION_TIME = "access_token_expiration_time";
/** We reuse an access token if its expiration time is longer than this. */
private static final int ACCESS_TOKEN_EXPIRATION_TOLERANCE = 30 * 60 * 1000; // thirty minutes
/** We refresh the current access token before it expires. */
private static final int ACCESS_TOKEN_FETCH_MARGIN = 60 * 1000; // one minute
public static final List<String> SCOPE =
Collections.singletonList("https://www.googleapis.com/auth/cloud-platform");
private static final String HOSTNAME = "speech.googleapis.com";
private static final int PORT = 443;
private final SpeechBinder mBinder = new SpeechBinder();
private final ArrayList<Listener> mListeners = new ArrayList<>();
private volatile AccessTokenTask mAccessTokenTask;
private SpeechGrpc.SpeechStub mApi;
private static Handler mHandler;
private final StreamObserver<StreamingRecognizeResponse> mResponseObserver
= new StreamObserver<StreamingRecognizeResponse>() {
#Override
public void onNext(StreamingRecognizeResponse response) {
String text = null;
boolean isFinal = false;
if (response.getResultsCount() > 0) {
final StreamingRecognitionResult result = response.getResults(0);
isFinal = result.getIsFinal();
if (result.getAlternativesCount() > 0) {
final SpeechRecognitionAlternative alternative = result.getAlternatives(0);
text = alternative.getTranscript();
}
}
if (text != null) {
for (Listener listener : mListeners) {
listener.onSpeechRecognized(text, isFinal);
}
}
}
#Override
public void onError(Throwable t) {
Log.e(TAG, "Error calling the API.", t);
}
#Override
public void onCompleted() {
Log.i(TAG, "API completed.");
}
};
private final StreamObserver<RecognizeResponse> mFileResponseObserver
= new StreamObserver<RecognizeResponse>() {
#Override
public void onNext(RecognizeResponse response) {
String text = null;
if (response.getResultsCount() > 0) {
final SpeechRecognitionResult result = response.getResults(0);
if (result.getAlternativesCount() > 0) {
final SpeechRecognitionAlternative alternative = result.getAlternatives(0);
text = alternative.getTranscript();
}
}
if (text != null) {
for (Listener listener : mListeners) {
listener.onSpeechRecognized(text, true);
}
}
}
#Override
public void onError(Throwable t) {
Log.e(TAG, "Error calling the API.", t);
}
#Override
public void onCompleted() {
Log.i(TAG, "API completed.");
}
};
private StreamObserver<StreamingRecognizeRequest> mRequestObserver;
public static SpeechService from(IBinder binder) {
return ((SpeechBinder) binder).getService();
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
fetchAccessToken();
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mFetchAccessTokenRunnable);
mHandler = null;
// Release the gRPC channel.
if (mApi != null) {
final ManagedChannel channel = (ManagedChannel) mApi.getChannel();
if (channel != null && !channel.isShutdown()) {
try {
channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e(TAG, "Error shutting down the gRPC channel.", e);
}
}
mApi = null;
}
}
private void fetchAccessToken() {
if (mAccessTokenTask != null) {
return;
}
mAccessTokenTask = new AccessTokenTask();
mAccessTokenTask.execute();
}
private String getDefaultLanguageCode() {
final Locale locale = Locale.getDefault();
final StringBuilder language = new StringBuilder(locale.getLanguage());
final String country = locale.getCountry();
if (!TextUtils.isEmpty(country)) {
language.append("-");
language.append(country);
}
return language.toString();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void addListener(#NonNull Listener listener) {
mListeners.add(listener);
}
public void removeListener(#NonNull Listener listener) {
mListeners.remove(listener);
}
/**
* Starts recognizing speech audio.
*
* #param sampleRate The sample rate of the audio.
*/
public void startRecognizing(int sampleRate) {
if (mApi == null) {
Log.w(TAG, "API not ready. Ignoring the request.");
return;
}
// Configure the API
mRequestObserver = mApi.streamingRecognize(mResponseObserver);
mRequestObserver.onNext(StreamingRecognizeRequest.newBuilder()
.setStreamingConfig(StreamingRecognitionConfig.newBuilder()
.setConfig(RecognitionConfig.newBuilder()
.setLanguageCode(getDefaultLanguageCode())
.setEncoding(RecognitionConfig.AudioEncoding.LINEAR16)
.setSampleRateHertz(sampleRate)
.build())
.setInterimResults(true)
.setSingleUtterance(true)
.build())
.build());
}
/**
* Recognizes the speech audio. This method should be called every time a chunk of byte buffer
* is ready.
*
* #param data The audio data.
* #param size The number of elements that are actually relevant in the {#code data}.
*/
public void recognize(byte[] data, int size) {
if (mRequestObserver == null) {
return;
}
// Call the streaming recognition API
mRequestObserver.onNext(StreamingRecognizeRequest.newBuilder()
.setAudioContent(ByteString.copyFrom(data, 0, size))
.build());
}
/**
* Finishes recognizing speech audio.
*/
public void finishRecognizing() {
if (mRequestObserver == null) {
return;
}
mRequestObserver.onCompleted();
mRequestObserver = null;
}
/**
* Recognize all data from the specified {#link InputStream}.
*
* #param stream The audio data.
*/
public void recognizeInputStream(InputStream stream) {
try {
mApi.recognize(
RecognizeRequest.newBuilder()
.setConfig(RecognitionConfig.newBuilder()
.setEncoding(RecognitionConfig.AudioEncoding.LINEAR16)
.setLanguageCode("en-US")
.setSampleRateHertz(16000)
.build())
.setAudio(RecognitionAudio.newBuilder()
.setContent(ByteString.readFrom(stream))
.build())
.build(),
mFileResponseObserver);
} catch (IOException e) {
Log.e(TAG, "Error loading the input", e);
}
}
private class SpeechBinder extends Binder {
SpeechService getService() {
return SpeechService.this;
}
}
private final Runnable mFetchAccessTokenRunnable = new Runnable() {
#Override
public void run() {
fetchAccessToken();
}
};
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
#Override
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs =
getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
// Check if the current token is still valid for a while
if (tokenValue != null && expirationTime > 0) {
if (expirationTime
> System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TOLERANCE) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
}
// ***** WARNING *****
// In this sample, we load the credential from a JSON file stored in a raw resource
// folder of this client app. You should never do this in your app. Instead, store
// the file in your server and obtain an access token from there.
// *******************
final InputStream stream = getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream)
.createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
prefs.edit()
.putString(PREF_ACCESS_TOKEN_VALUE, token.getTokenValue())
.putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME,
token.getExpirationTime().getTime())
.apply();
return token;
} catch (IOException e) {
Log.e(TAG, "Failed to obtain access token.", e);
}
return null;
}
#Override
protected void onPostExecute(AccessToken accessToken) {
mAccessTokenTask = null;
final ManagedChannel channel = new OkHttpChannelProvider()
.builderForAddress(HOSTNAME, PORT)
.nameResolverFactory(new DnsNameResolverProvider())
.intercept(new GoogleCredentialsInterceptor(new GoogleCredentials(accessToken)
.createScoped(SCOPE)))
.build();
mApi = SpeechGrpc.newStub(channel);
// Schedule access token refresh before it expires
if (mHandler != null) {
mHandler.postDelayed(mFetchAccessTokenRunnable,
Math.max(accessToken.getExpirationTime().getTime()
- System.currentTimeMillis()
- ACCESS_TOKEN_FETCH_MARGIN, ACCESS_TOKEN_EXPIRATION_TOLERANCE));
}
}
}
/**
* Authenticates the gRPC channel using the specified {#link GoogleCredentials}.
*/
private static class GoogleCredentialsInterceptor implements ClientInterceptor {
private final Credentials mCredentials;
private Metadata mCached;
private Map<String, List<String>> mLastMetadata;
GoogleCredentialsInterceptor(Credentials credentials) {
mCredentials = credentials;
}
#Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
final MethodDescriptor<ReqT, RespT> method, CallOptions callOptions,
final Channel next) {
return new ClientInterceptors.CheckedForwardingClientCall<ReqT, RespT>(
next.newCall(method, callOptions)) {
#Override
protected void checkedStart(Listener<RespT> responseListener, Metadata headers)
throws StatusException {
Metadata cachedSaved;
URI uri = serviceUri(next, method);
synchronized (this) {
Map<String, List<String>> latestMetadata = getRequestMetadata(uri);
if (mLastMetadata == null || mLastMetadata != latestMetadata) {
mLastMetadata = latestMetadata;
mCached = toHeaders(mLastMetadata);
}
cachedSaved = mCached;
}
headers.merge(cachedSaved);
delegate().start(responseListener, headers);
}
};
}
/**
* Generate a JWT-specific service URI. The URI is simply an identifier with enough
* information for a service to know that the JWT was intended for it. The URI will
* commonly be verified with a simple string equality check.
*/
private URI serviceUri(Channel channel, MethodDescriptor<?, ?> method)
throws StatusException {
String authority = channel.authority();
if (authority == null) {
throw Status.UNAUTHENTICATED
.withDescription("Channel has no authority")
.asException();
}
// Always use HTTPS, by definition.
final String scheme = "https";
final int defaultPort = 443;
String path = "/" + MethodDescriptor.extractFullServiceName(method.getFullMethodName());
URI uri;
try {
uri = new URI(scheme, authority, path, null, null);
} catch (URISyntaxException e) {
throw Status.UNAUTHENTICATED
.withDescription("Unable to construct service URI for auth")
.withCause(e).asException();
}
// The default port must not be present. Alternative ports should be present.
if (uri.getPort() == defaultPort) {
uri = removePort(uri);
}
return uri;
}
private URI removePort(URI uri) throws StatusException {
try {
return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), -1 /* port */,
uri.getPath(), uri.getQuery(), uri.getFragment());
} catch (URISyntaxException e) {
throw Status.UNAUTHENTICATED
.withDescription("Unable to construct service URI after removing port")
.withCause(e).asException();
}
}
private Map<String, List<String>> getRequestMetadata(URI uri) throws StatusException {
try {
return mCredentials.getRequestMetadata(uri);
} catch (IOException e) {
throw Status.UNAUTHENTICATED.withCause(e).asException();
}
}
private static Metadata toHeaders(Map<String, List<String>> metadata) {
Metadata headers = new Metadata();
if (metadata != null) {
for (String key : metadata.keySet()) {
Metadata.Key<String> headerKey = Metadata.Key.of(
key, Metadata.ASCII_STRING_MARSHALLER);
for (String value : metadata.get(key)) {
headers.put(headerKey, value);
}
}
}
return headers;
}
}
}
and here is my activity class
public class MainActivity extends AppCompatActivity implements MessageDialogFragment.Listener {
private static final String FRAGMENT_MESSAGE_DIALOG = "message_dialog";
private static final String STATE_RESULTS = "results";
private static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
private SpeechService mSpeechService;
private VoiceRecorder mVoiceRecorder;
private final VoiceRecorder.Callback mVoiceCallback = new VoiceRecorder.Callback() {
#Override
public void onVoiceStart() {
showStatus(true);
if (mSpeechService != null) {
mSpeechService.startRecognizing(mVoiceRecorder.getSampleRate());
}
}
#Override
public void onVoice(byte[] data, int size) {
if (mSpeechService != null) {
mSpeechService.recognize(data, size);
}
}
#Override
public void onVoiceEnd() {
showStatus(false);
if (mSpeechService != null) {
mSpeechService.finishRecognizing();
}
}
};
// Resource caches
private int mColorHearing;
private int mColorNotHearing;
// View references
private TextView mStatus;
private TextView mText, mResult;
private Button editButton, clearButton;
private SharedPreferences settings;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
mSpeechService = SpeechService.from(binder);
mSpeechService.addListener(mSpeechServiceListener);
mStatus.setVisibility(View.VISIBLE);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mSpeechService = null;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
final Resources resources = getResources();
final Resources.Theme theme = getTheme();
mColorHearing = ResourcesCompat.getColor(resources, R.color.status_hearing, theme);
mColorNotHearing = ResourcesCompat.getColor(resources, R.color.status_not_hearing, theme);
mStatus = (TextView) findViewById(R.id.status);
mText = (TextView) findViewById(R.id.text);
mResult = (TextView) findViewById(R.id.resultText);
editButton = (Button)findViewById(R.id.button1);
clearButton = (Button)findViewById(R.id.button2);
settings = getSharedPreferences("MyPreference", Context.MODE_PRIVATE);
clearButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Sounds sounds = new Sounds(getApplicationContext());
if(settings.getBoolean("muteAble", false ) == true){
sounds.playSound();
}
mResult.setText("");
}
});
editButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Sounds sounds = new Sounds(getApplicationContext());
if(settings.getBoolean("muteAble", false ) == true){
sounds.playSound();
}
Intent editIntent = new Intent(MainActivity.this, EditorActivity.class);
String forEditText = mResult.getText().toString();
editIntent.putExtra("forEdit", forEditText);
startActivity(editIntent);
}
});
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if(id == android.R.id.home){
this.finish();
}
return super.onOptionsItemSelected(item);
}
#Override
protected void onStart() {
super.onStart();
// Prepare Cloud Speech API
bindService(new Intent(this, SpeechService.class), mServiceConnection, BIND_AUTO_CREATE);
// Start listening to voices
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
== PackageManager.PERMISSION_GRANTED) {
startVoiceRecorder();
} else if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.RECORD_AUDIO)) {
showPermissionMessageDialog();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO},
REQUEST_RECORD_AUDIO_PERMISSION);
}
}
#Override
protected void onStop() {
// Stop listening to voice
stopVoiceRecorder();
// Stop Cloud Speech API
mSpeechService.removeListener(mSpeechServiceListener);
unbindService(mServiceConnection);
mSpeechService = null;
super.onStop();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
if (permissions.length == 1 && grantResults.length == 1
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startVoiceRecorder();
} else {
showPermissionMessageDialog();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private void startVoiceRecorder() {
if (mVoiceRecorder != null) {
mVoiceRecorder.stop();
}
mVoiceRecorder = new VoiceRecorder(mVoiceCallback);
mVoiceRecorder.start();
}
private void stopVoiceRecorder() {
if (mVoiceRecorder != null) {
mVoiceRecorder.stop();
mVoiceRecorder = null;
}
}
private void showPermissionMessageDialog() {
MessageDialogFragment
.newInstance(getString(R.string.permission_message))
.show(getSupportFragmentManager(), FRAGMENT_MESSAGE_DIALOG);
}
private void showStatus(final boolean hearingVoice) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mStatus.setTextColor(hearingVoice ? mColorHearing : mColorNotHearing);
}
});
}
#Override
public void onMessageDialogDismissed() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO},
REQUEST_RECORD_AUDIO_PERMISSION);
}
private final SpeechService.Listener mSpeechServiceListener =
new SpeechService.Listener() {
#Override
public void onSpeechRecognized(final String text, final boolean isFinal) {
if (isFinal) {
mVoiceRecorder.dismiss();
}
if (mText != null && !TextUtils.isEmpty(text)) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (isFinal) {
mText.setText(null);
mResult.append(" "+text.toString());
} else {
mText.setText(text);
}
}
});
}
}
};
}
Thank in advance for your help
Here I have a query related to connection between android socket and server. I'm following https://socket.io/blog/native-socket-io-and-android/ and found that it is working fine, so I replaced my local server URL with the URL in the tutorial. But here I'm always getting a connection failed or disconnection error. Here is my application class for further clarification.
public class ChatApplication extends Application {
private Socket mSocket;
{
try {
IO.Options options = new IO.Options();
options.forceNew = true;
options.reconnection = true;
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("uid", 1);
jsonObject.put("usid", 6847);
options.query = jsonObject.toString();
} catch (JSONException e) {
e.printStackTrace();
}
Log.e("JSON", jsonObject.toString());
Log.e("OPTIONS", options.toString());
mSocket = IO.socket(Constants.CHAT_SERVER_URL, options);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public Socket getSocket() {
return mSocket;
}
}
And the code of the fragment is below. It keeps calling onDisconnect() whenever I use my local server for it.
public class MainFragment extends Fragment {
private static final String TAG = "MainFragment";
private static final int REQUEST_LOGIN = 0;
private static final int TYPING_TIMER_LENGTH = 600;
private RecyclerView mMessagesView;
private EditText mInputMessageView;
private List<Message> mMessages = new ArrayList<Message>();
private RecyclerView.Adapter mAdapter;
private boolean mTyping = false;
private Handler mTypingHandler = new Handler();
private String mUsername;
private Socket mSocket;
private Boolean isConnected = true;
private Emitter.Listener onConnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (!isConnected) {
if (null != mUsername)
mSocket.emit("add user", mUsername);
Toast.makeText(getActivity().getApplicationContext(),
R.string.connect, Toast.LENGTH_LONG).show();
isConnected = true;
}
}
});
}
};
private Emitter.Listener onDisconnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i(TAG, "diconnected");
isConnected = false;
Toast.makeText(getActivity().getApplicationContext(),
R.string.disconnect, Toast.LENGTH_LONG).show();
}
});
}
};
private Emitter.Listener onConnectError = new Emitter.Listener() {
#Override
public void call(Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e(TAG, "Error connecting");
Toast.makeText(getActivity().getApplicationContext(),
R.string.error_connect, Toast.LENGTH_LONG).show();
}
});
}
};
private Emitter.Listener onNewMessage = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
String message;
try {
username = data.getString("username");
message = data.getString("message");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
removeTyping(username);
addMessage(username, message);
}
});
}
};
private Emitter.Listener onUserJoined = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
int numUsers;
try {
username = data.getString("username");
numUsers = data.getInt("numUsers");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
addLog(getResources().getString(R.string.message_user_joined, username));
addParticipantsLog(numUsers);
}
});
}
};
private Emitter.Listener onUserLeft = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
int numUsers;
try {
username = data.getString("username");
numUsers = data.getInt("numUsers");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
addLog(getResources().getString(R.string.message_user_left, username));
addParticipantsLog(numUsers);
removeTyping(username);
}
});
}
};
private Emitter.Listener onTyping = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
try {
username = data.getString("username");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
addTyping(username);
}
});
}
};
private Emitter.Listener onStopTyping = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
try {
username = data.getString("username");
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
return;
}
removeTyping(username);
}
});
}
};
private Runnable onTypingTimeout = new Runnable() {
#Override
public void run() {
if (!mTyping) return;
mTyping = false;
mSocket.emit("stop typing");
}
};
public MainFragment() {
super();
}
// This event fires 1st, before creation of fragment or any views
// The onAttach method is called when the Fragment instance is associated with an Activity.
// This does not mean the Activity is fully initialized.
#Override
public void onAttach(Context context) {
super.onAttach(context);
mAdapter = new MessageAdapter(context, mMessages);
if (context instanceof Activity) {
//this.listener = (MainActivity) context;
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
ChatApplication app = (ChatApplication) getActivity().getApplication();
mSocket = app.getSocket();
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
mSocket.on("new message", onNewMessage);
mSocket.on("user joined", onUserJoined);
mSocket.on("user left", onUserLeft);
mSocket.on("typing", onTyping);
mSocket.on("stop typing", onStopTyping);
mSocket.connect();
startSignIn();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
#Override
public void onDestroy() {
super.onDestroy();
mSocket.disconnect();
mSocket.off(Socket.EVENT_CONNECT, onConnect);
mSocket.off(Socket.EVENT_DISCONNECT, onDisconnect);
mSocket.off(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.off(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
mSocket.off("new message", onNewMessage);
mSocket.off("user joined", onUserJoined);
mSocket.off("user left", onUserLeft);
mSocket.off("typing", onTyping);
mSocket.off("stop typing", onStopTyping);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mMessagesView = (RecyclerView) view.findViewById(R.id.messages);
mMessagesView.setLayoutManager(new LinearLayoutManager(getActivity()));
mMessagesView.setAdapter(mAdapter);
mInputMessageView = (EditText) view.findViewById(R.id.message_input);
mInputMessageView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int id, KeyEvent event) {
if (id == R.id.send || id == EditorInfo.IME_NULL) {
attemptSend();
return true;
}
return false;
}
});
mInputMessageView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (null == mUsername) return;
if (!mSocket.connected()) return;
if (!mTyping) {
mTyping = true;
mSocket.emit("typing");
}
mTypingHandler.removeCallbacks(onTypingTimeout);
mTypingHandler.postDelayed(onTypingTimeout, TYPING_TIMER_LENGTH);
}
#Override
public void afterTextChanged(Editable s) {
}
});
ImageButton sendButton = (ImageButton) view.findViewById(R.id.send_button);
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
attemptSend();
}
});
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Activity.RESULT_OK != resultCode) {
getActivity().finish();
return;
}
mUsername = data.getStringExtra("username");
int numUsers = data.getIntExtra("numUsers", 1);
addLog(getResources().getString(R.string.message_welcome));
addParticipantsLog(numUsers);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Inflate the menu; this adds items to the action bar if it is present.
inflater.inflate(R.menu.menu_main, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_leave) {
leave();
return true;
}
return super.onOptionsItemSelected(item);
}
private void addLog(String message) {
mMessages.add(new Message.Builder(Message.TYPE_LOG)
.message(message).build());
mAdapter.notifyItemInserted(mMessages.size() - 1);
scrollToBottom();
}
private void addParticipantsLog(int numUsers) {
addLog(getResources().getQuantityString(R.plurals.message_participants, numUsers, numUsers));
}
private void addMessage(String username, String message) {
mMessages.add(new Message.Builder(Message.TYPE_MESSAGE)
.username(username).message(message).build());
mAdapter.notifyItemInserted(mMessages.size() - 1);
scrollToBottom();
}
private void addTyping(String username) {
mMessages.add(new Message.Builder(Message.TYPE_ACTION)
.username(username).build());
mAdapter.notifyItemInserted(mMessages.size() - 1);
scrollToBottom();
}
private void removeTyping(String username) {
for (int i = mMessages.size() - 1; i >= 0; i--) {
Message message = mMessages.get(i);
if (message.getType() == Message.TYPE_ACTION && message.getUsername().equals(username)) {
mMessages.remove(i);
mAdapter.notifyItemRemoved(i);
}
}
}
private void attemptSend() {
if (null == mUsername) return;
if (!mSocket.connected()) return;
mTyping = false;
String message = mInputMessageView.getText().toString().trim();
if (TextUtils.isEmpty(message)) {
mInputMessageView.requestFocus();
return;
}
mInputMessageView.setText("");
addMessage(mUsername, message);
// perform the sending message attempt.
mSocket.emit("new message", message);
}
private void startSignIn() {
// mUsername = null;
Intent intent = new Intent(getActivity(), LoginActivity.class);
startActivityForResult(intent, REQUEST_LOGIN);
}
private void leave() {
mUsername = null;
mSocket.disconnect();
mSocket.connect();
startSignIn();
}
private void scrollToBottom() {
mMessagesView.scrollToPosition(mAdapter.getItemCount() - 1);
}
}
I'm using CHAT_SERVER_URL = "http://192.168.1.14:3000/" to make connection with server but it's not working for me. It works fine when we try to make a web connection through emulator or computer system. Can anyone please give me an idea if I'm doing anything wrong.
Thanks.
After debugging at the server end too, I got to know how to hit my server to make a connection and I got that we don't require IO.Options and Options.query in every case and now it is working fine completely. So for more clarification I'm posting an answer here:
//connect to server
public void startRunning() {
try {
if (baseUrl != null) {
socket = IO.socket("http://" + baseUrl + ":3000?uid=" + userId + "&usid=" + userSessionId);
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
if (socket == null) return;
socket
.on(Socket.EVENT_CONNECT, CONNECTED)
.on(Socket.EVENT_DISCONNECT, DISCONNECTED)
.on("chat-message", CHAT_MESSAGE)
.on("chats-active", CHATS_ACTIVE)
.on("chat-logout", LOGOUT)
.on("messages-read", MESSAGE_READ)
.on("chat-login", CHAT_LOGIN);
socket.connect();
}
I am building Android App which shows Withings user's activity data in my Application.
But when I am trying to call refresh_token url:
https://oauth.withings.com/account/request_token?oauth_callback=******&oauth_consumer_key=******&oauth_nonce=******&oauth_signature=CcMrI7JaI8M5tEenye3s95wx%2BZ4%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1477386344&oauth_version=1.0
Then I am getting Invalid Signature response like below:
{
"status":0,
"message":"Invalid signature :\n CcMrI7JaI8M5tEenye3s95wx+Z4= .. \n{\"oauth_callback\":\"******\",\"oauth_consumer_key\":\"ce54bd6c671546ef8f8d394c0db4bd86688289d5f7fb39f371c5ebce4d01\",\"oauth_nonce\":\"f339febe0fdf4b53b953501e45a049db\",\"oauth_signature\":\"CcMrI7JaI8M5tEenye3s95wx+Z4=\",\"oauth_signature_method\":\"HMAC-SHA1\",\"oauth_timestamp\":\"1477386344\",\"oauth_version\":\"1.0\"}\n{\"base_string\":\"GET&https%3A%2F%2Foauth.withings.com%2Faccount%2Frequest_token&oauth_callback%3D******%26oauth_consumer_key%3D******%26oauth_nonce%3Df339febe0fdf4b53b953501e45a049db%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1477386344%26oauth_version%3D1.0\"}\n{\"key\":\"******\",\"secret\":\"******\",\"callback_url\":null}"
}
First of all you can use the scribe lib
On my sample code I have an Authentication Activity that has an WebView that the user uses to verify the app. Then that Authentication Activity sends back to the MainActivity the response.
On my example I am storing locally on a DB the authenticated user to not ask every time the credentials.
Also I am sending the access token to python server that will get all data stored on Withings Cloud to save it to my Server DB and represent them on a Graph Activity. {I have removed that part}
Because of the copy paste maybe something is missing but most of the code is here
public class WithingsApi extends DefaultApi10a {
private static final String AUTHORIZATION_URL ="https://oauth.withings.com/account/authorize?oauth_token=%s";
private static final String apiKey = "API_KEY";
private static final String apiSecret = "API_SECRET";
#Override
public String getRequestTokenEndpoint() {
return "https://oauth.withings.com/account/request_token";
}
#Override
public String getAccessTokenEndpoint() {
return "https://oauth.withings.com/account/access_token";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return String.format(getAUTHORIZATION_URL(), requestToken.getToken());
}
public static String getKey(){
return apiKey;
}
public static String getSecret(){
return apiSecret;
}
public static String getAUTHORIZATION_URL() {
return AUTHORIZATION_URL;
}
}
#SuppressLint("SetJavaScriptEnabled")
public class AuthenticationActivity extends Activity {
final String LOGTAG = "WITHINGS";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authentication);
final WebView wvAuthorise = (WebView) findViewById(R.id.wvAuthorise);
wvAuthorise.getSettings().setJavaScriptEnabled(true);
wvAuthorise.setWebViewClient(new MyWebViewClient(wvAuthorise));
MainActivity.service = new ServiceBuilder().provider(WithingsApi.class)
.apiKey(WithingsApi.getKey())
.apiSecret(WithingsApi.getSecret())
.build();
new Thread(new Runnable() {
public void run() {
MainActivity.requestToken = MainActivity.service.getRequestToken();
final String authURL = MainActivity.service.getAuthorizationUrl(MainActivity.requestToken);
wvAuthorise.post(new Runnable() {
#Override
public void run() {
wvAuthorise.loadUrl(authURL);
}
});
}
}).start();
}
class MyWebViewClient extends WebViewClient{
WebView wvAuthorise;
MyWebViewClient(WebView wv){
wvAuthorise = wv;
}
#Override
public void onPageFinished(WebView view, String url) {
getUSERID(url);
}
}
private void getUSERID(final String url) {
try {
String divStr = "userid=";
int first = url.indexOf(divStr);
if(first!=-1){
final String userid = url.substring(first+divStr.length());
Intent intent = new Intent();
intent.putExtra("USERID",userid);
setResult(RESULT_OK,intent);
finish();
}
else
{
//...
}
} catch (Exception e) {
Log.e(LOGTAG,e.getMessage());
//...
}
}
}
public class MainActivity extends FragmentActivity {
public static OAuthService service;
public static Token requestToken;
String secret, token;
Token accessToken;
String userId = "";
private UsersDataSource datasource;
private TextView nameTV;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_mainActivity = this;
nameTV = (TextView) findViewById(R.id.nameTitleTextView);
nameTV.setText("--");
getCredentials();
}
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == AUTHENTICATION_REQUEST) {
if (resultCode == RESULT_OK) {
Bundle extras = intent.getExtras();
if (extras != null) {
userId = extras.getString("USERID");
getAccessTokenThread.execute((Object) null);
}
}
}
}
#Override
protected void onResume() {
datasource.open();
super.onResume();
}
#Override
protected void onPause() {
datasource.close();
super.onPause();
}
private void getCredentials() {
try {
datasource = new UsersDataSource(this);
datasource.open();
List<User> users = datasource.getAllUsers();
if (users.isEmpty()) {
startAuthenticationActivity();
} else {
// TODO load all users and if isn't anyone correct
// startAuthenticationActivity
secret = users.get(0).getSecret();
token = users.get(0).getToken();
userId = users.get(0).getUserId();
Log.i(LOGTAG, "secret : " + secret);
Log.i(LOGTAG, "token : " + token);
Log.i(LOGTAG, "userId : " + userId);
try {
service = new ServiceBuilder().provider(WithingsApi.class)
.apiKey(WithingsApi.getKey())
.apiSecret(WithingsApi.getSecret()).build();
accessToken = new Token(token, secret);
loadData();
} catch (Exception ex) {
startAuthenticationActivity();
}
}
} catch (Exception ex) {
Log.e(LOGTAG, "try on create" + ex.getLocalizedMessage());
}
}
private void startAuthenticationActivity() {
Intent intent = new Intent(this,
ics.forth.withings.authentication.AuthenticationActivity.class);
startActivityForResult(intent, AUTHENTICATION_REQUEST);
}
AsyncTask<Object, Object, Object> getAccessTokenThread = new AsyncTask<Object, Object, Object>() {
#Override
protected Object doInBackground(Object... params) {
accessToken = service
.getAccessToken(requestToken, new Verifier(""));
secret = accessToken.getSecret();
token = accessToken.getToken();
return null;
}
#Override
protected void onPostExecute(Object result) {
// authentication complete send the token,secret,userid, to python
datasource.createUser(token, secret, userId);
loadData();
};
};
}
UPDATE
OAuthService class is from Scribe
Token class is from Scribe
UserDataSource class is a DB Helper Class more here
We are using qucikblox sdk 2.0 in online consultation mobile application in both platform ios & android. But recently facing issue that if A user device is locked or A user is not using app and if B user calls A user over audio/video A user will not get call.
Case 1-- Device locked app is in foreground User A will be getting a call from user B but user A will not get to know about a call when user A unlock device then only user A can see call notification.
Case 2-- App is in background & device is unlocked User A will not get a audio.video call, evn push message of chats also not receiving
Create a sticky service which will run in background even activity destroy, this service is responsible for QBsession and all other operations so it will detect call in background so you will create Session in service , not in CallActivity. Iam successfully implements this and iam successfully receiving calls in background by this.
public class BloxService extends Service implements QBRTCClientSessionCallbacks {
private QBChatService chatService;
private volatile boolean resultReceived = true;
static final String APP_ID = "";
static final String AUTH_KEY = "";
static final String AUTH_SECRET = "";
static final String ACCOUNT_KEY = "";
private QBRTCClient rtcClient;
public static BloxService bloxService;
private static final String TAG = "BloxService";
public boolean isSessionRunning;
public boolean isCallRunning;
private Date tokenExpirationDate;
private QBAuth qbAuth;
private AppPrefs prefs;
private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
/*boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
String reason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
boolean isFailover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);*/
NetworkInfo currentNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
//NetworkInfo otherNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
if(currentNetworkInfo!=null && currentNetworkInfo.isConnected()){
if(!QBChatService.getInstance().isLoggedIn()){
if(!isSessionRunning) {
initializeQb();
}
}
}
}
};
#Override
public void onCreate() {
super.onCreate();
// prefs = AppPrefs.getInstance(getApplicationContext()); // for testing we put this line on start
registerReceiver(this.mConnReceiver,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("bloxservice","onDestroy");
try{
unregisterReceiver(mConnReceiver);
}catch (Exception e){
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
prefs = AppPrefs.getInstance(getApplicationContext());
if (prefs.getData(IS_USER_LOGIN, false)) {
Log.d("bloxservice","start");
try {
if (!QBChatService.getInstance().isLoggedIn()) {
initializeQb();
}
}catch (Exception e)
{
initializeQb();
}
bloxService=this;
}
else {
stopSelf();
}
return START_STICKY;
}
public static BloxService getBloxService() {
return bloxService;
}
public void initializeQb(){
QBSettings.getInstance().init(getApplicationContext(), APP_ID, AUTH_KEY, AUTH_SECRET);
QBSettings.getInstance().setAccountKey(ACCOUNT_KEY);
QBChatService.setDebugEnabled(true);
// added on 20 july
QBChatService.setDefaultAutoSendPresenceInterval(60);
QBChatService.ConfigurationBuilder chatServiceConfigurationBuilder = new QBChatService.ConfigurationBuilder();
chatServiceConfigurationBuilder.setSocketTimeout(60); //Sets chat socket's read timeout in seconds
chatServiceConfigurationBuilder.setKeepAlive(true); //Sets connection socket's keepAlive option.
QBChatService.setConfigurationBuilder(chatServiceConfigurationBuilder);
// QBChatService.getInstance().startAutoSendPresence(10);// added on 20 july
chatService = QBChatService.getInstance();
/* tokenExpirationDate = qbAuth.getTokenExpirationDate();
try {
String Token= QBAuth.getSession().toString();
QBAuth.createFromExistentToken()
} catch (QBResponseException e) {
e.printStackTrace();
}
*/
if(AppPrefs.getInstance(this).getData(Constants.PrefsConstatnt.IS_USER_LOGIN,false)){
Log.e("Login Process", "Started");
isSessionRunning=true;
String userId=AppPrefs.getInstance(this).getData(Constants.PrefsConstatnt.USER_ID,"");
String name=AppPrefs.getInstance(this).getData(Constants.PrefsConstatnt.USER_NAME,"");
String picUrl=AppPrefs.getInstance(this).getData(Constants.PrefsConstatnt.USER_IMAGE,"");
String phone=prefs.getData(Constants.PrefsConstatnt.USER_PHONE, "");
if(name.isEmpty()){
name=userId;
}
createAppSession(Integer.parseInt(userId)<10?"0"+userId:userId,name,userId,picUrl,phone);
}
}
private void createAppSession(final String userId, final String name,final String exId,final String picUrl,final String phone) {
QBAuth.createSession(new QBEntityCallback<QBSession>() {
#Override
public void onSuccess(QBSession qbSession, Bundle bundle) {
loadUsers(userId, name, exId,picUrl,phone);
final SharedPreferences prefs = getGCMPreferences(getApplicationContext());
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.isEmpty()) {
}
// Subscribe to Push Notifications
//subscribeToPushNotifications(registrationId);
}
#Override
public void onError(QBResponseException exc) {
exc.printStackTrace();
isSessionRunning=false;
}
});
}
//QBUser users;
public void loadUsers(String userId,String name,String exId,String picUrl,String phone) {
final QBUser userr = new QBUser(userId, DataHolder.PASSWORD);
userr.setFullName(name);
userr.setExternalId(exId);
userr.setCustomData(picUrl);
userr.setPhone(phone);
QBUsers.signUp(userr, new QBEntityCallback<QBUser>() {
#Override
public void onSuccess(QBUser user, Bundle args) {
createSession(userr.getLogin(), userr.getPassword());
}
#Override
public void onError(QBResponseException error) {
error.printStackTrace();
QBUsers.signIn(userr, new QBEntityCallback<QBUser>() {
#Override
public void onSuccess(QBUser user, Bundle args) {
createSession(userr.getLogin(), userr.getPassword());
}
#Override
public void onError(QBResponseException error) {
error.printStackTrace();
isSessionRunning = false;
}
});
}
});
}
private void createSession(final String login, final String password) {
final QBUser user = new QBUser(login, password);
QBAuth.createSession(login, password, new QBEntityCallback<QBSession>() {
#Override
public void onSuccess(QBSession session, Bundle bundle) {
user.setId(session.getUserId());
Log.e("User" + session.getUserId(), "Login");
QBSettings.getInstance().fastConfigInit(APP_ID, AUTH_KEY, AUTH_SECRET);
sendRegistrationToServer(AppPrefs.getInstance(BloxService.this).getData(Constants.PrefsConstatnt.DEVICE_TOKEN, ""));
DataHolder.setLoggedUser(user);
if (chatService.isLoggedIn()) {
resultReceived = true;
initQBRTCClient();
isSessionRunning = false;
} else {
chatService.login(user, new QBEntityCallback<Void>() {
#Override
public void onSuccess(Void result, Bundle bundle) {
initQBRTCClient();
resultReceived = true;
isSessionRunning = false;
}
#Override
public void onError(QBResponseException exc) {
resultReceived = true;
isSessionRunning = false;
}
});
}
/* QBRosterListener rosterListener = new QBRosterListener() {
#Override
public void entriesDeleted(Collection<Integer> userIds) {
Log.d("mayanks","changed");
}
#Override
public void entriesAdded(Collection<Integer> userIds) {
Log.d("mayanks","changed");
}
#Override
public void entriesUpdated(Collection<Integer> userIds) {
Log.d("mayanks","changed");
}
#Override
public void presenceChanged(QBPresence presence) {
Log.d("mayanks","changed");
}
};
QBSubscriptionListener subscriptionListener = new QBSubscriptionListener() {
#Override
public void subscriptionRequested(int userId) {
}
};
QBRoster chatRoster = QBChatService.getInstance().getRoster(QBRoster.SubscriptionMode.mutual, subscriptionListener);
chatRoster.addRosterListener(rosterListener);
Collection<QBRosterEntry> entries = chatRoster.getEntries();
QBPresence presence = chatRoster.getPresence(9);
if (presence!=null) {
if (presence.getType() == QBPresence.Type.online) {
Log.d("mayanks","online");
// User is online
}else{
Log.d("mayanks","offline");
// User is offline
}
}*/
}
#Override
public void onError(QBResponseException exc) {
resultReceived = true;
isSessionRunning = false;
}
});
}
private void sendRegistrationToServer(final String token) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
final SharedPreferences prefs = getGCMPreferences(getApplicationContext());
String deviceID = prefs.getString(PROPERTY_DEVICE_ID, null);
if(deviceID==null)
{
deviceID=DeviceUtils.getDeviceUid();
storeDeviceId(getApplicationContext(),deviceID);
}
QBSubscription qbSubscription = new QBSubscription();
qbSubscription.setNotificationChannel(QBNotificationChannel.GCM);
qbSubscription.setDeviceUdid(deviceID);
qbSubscription.setRegistrationID(token);
qbSubscription.setEnvironment(QBEnvironment.DEVELOPMENT); // Don't forget to change QBEnvironment to PRODUCTION when releasing application
QBPushNotifications.createSubscription(qbSubscription,
new QBEntityCallback<ArrayList<QBSubscription>>() {
#Override
public void onSuccess(ArrayList<QBSubscription> qbSubscriptions, Bundle bundle) {
Log.e(TAG, "Successfully subscribed for QB push messages");
//saveGcmRegIdToPreferences(gcmRegId);
isSessionRunning=false;
}
#Override
public void onError(QBResponseException error) {
Log.e(TAG, "Unable to subscribe for QB push messages; " + error.toString());
isSessionRunning=false;
}
});
}
});
}
#Override
public void onReceiveNewSession(final QBRTCSession qbrtcSession) {
Log.d("bloxservice","CallRecive");
new Handler().post(new Runnable() {
#Override
public void run() {
if(!isCallRunning) {
DataHolder.incomingSession = qbrtcSession;
/* Map<String,String> userInfo = qbrtcSession.getUserInfo();
String s=userInfo.get("mayank");*/
Intent intent = new Intent(BloxService.this, CallActivity.class);
intent.putExtra("incoming", true);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
else{
Log.e("User","Busy");
}
}
});
}
#Override
public void onUserNotAnswer(QBRTCSession qbrtcSession, Integer integer) {
// ToastUtil.showShortToast(this, "no answer");
}
#Override
public void onCallRejectByUser(QBRTCSession qbrtcSession, Integer integer, Map<String, String> map) {
// ToastUtil.showShortToast(this,"rejected");
}
#Override
public void onCallAcceptByUser(QBRTCSession qbrtcSession, Integer integer, Map<String, String> map) {
//ToastUtil.showShortToast(this,"accepted");
}
#Override
public void onReceiveHangUpFromUser(QBRTCSession qbrtcSession, Integer integer, Map<String, String> map) {
}
#Override
public void onUserNoActions(QBRTCSession qbrtcSession, Integer integer) {
// ToastUtil.showShortToast(this,"no Action");
}
#Override
public void onSessionClosed(QBRTCSession qbrtcSession) {
// ToastUtil.showShortToast(this,"onSessionClosed");
}
#Override
public void onSessionStartClose(QBRTCSession qbrtcSession) {
// ToastUtil.showShortToast(this,"onSessionStartClose");
}
private void initQBRTCClient() {
rtcClient = QBRTCClient.getInstance(this);
QBVideoChatWebRTCSignalingManager qbChatService = QBChatService.getInstance().getVideoChatWebRTCSignalingManager();
if (qbChatService != null) {
qbChatService.addSignalingManagerListener(new QBVideoChatSignalingManagerListener() {
#Override
public void signalingCreated(QBSignaling qbSignaling, boolean createdLocally) {
if (!createdLocally) {
rtcClient.addSignaling((QBWebRTCSignaling) qbSignaling);
}
}
});
QBRTCConfig.setMaxOpponentsCount(2);
QBRTCConfig.setDisconnectTime(40);
QBRTCConfig.setAnswerTimeInterval(30l);
QBRTCConfig.setDebugEnabled(true);
rtcClient.addSessionCallbacksListener(this);
rtcClient.prepareToProcessCalls();
QBChatService.getInstance().addConnectionListener(new AbstractConnectionListener() {
#Override
public void connectionClosedOnError(Exception e) {
}
#Override
public void reconnectionSuccessful() {
}
#Override
public void reconnectingIn(int seconds) {
}
});
}
}
public void logout(){
chatService.logout(new QBEntityCallback<Void>() {
#Override
public void onSuccess(Void result, Bundle bundle) {
}
#Override
public void onError(QBResponseException list) {
}
});
}
/* public void subscribeToPushNotifications(String registrationID) {
QBSubscription subscription = new QBSubscription(QBNotificationChannel.GCM);
subscription.setEnvironment(QBEnvironment.DEVELOPMENT);
//
String deviceId;
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(
Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
deviceId = mTelephony.getDeviceId(); /*//*** use for mobiles
} else {
deviceId = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ANDROID_ID); /*//*** use for tablets
}
subscription.setDeviceUdid(deviceId);
//
subscription.setRegistrationID(registrationID);
//
QBPushNotifications.createSubscription(subscription, new QBEntityCallback<ArrayList<QBSubscription>>() {
#Override
public void onSuccess(ArrayList<QBSubscription> subscriptions, Bundle args) {
Log.d("push_send","sucess");
}
#Override
public void onError(QBResponseException error) {
Log.d("push_send","sucess");
}
});
}*/
private SharedPreferences getGCMPreferences(Context context) {
// This sample app persists the registration ID in shared preferences,
// but
// how you store the regID in your app is up to you.
Log.e("getGCMPreferences", "package= " + context.getPackageName());
return getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
}
private void storeDeviceId(Context context, String deviceId) {
final SharedPreferences prefs = getGCMPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_DEVICE_ID, deviceId);
editor.commit();
}
}
I have an iOS app using Sinch which works without a problem, and recently I migrated the app on Android using the very same Sinch credentials(API key and API secret) then I found
12-14 21:57:17.678 17907-17907/com.packagename.android D/SinchClient﹕ onStarted()
12-14 21:57:17.678 17907-17907/com.packagename.android D/SinchClient﹕ mUserAgent.startBroadcastListener()
12-14 21:57:17.678 17907-17907/com.packagename.android D/SinchService﹕ SinchClient started
12-14 21:57:17.738 17907-17907/com.packagename.android I/Timeline﹕ Timeline: Activity_idle id: android.os.BinderProxy#4247f4c8 time:23957901
12-14 21:57:21.458 17907-17913/com.packagename.android I/dalvikvm﹕ Total arena pages for JIT: 11
12-14 21:57:21.458 17907-17913/com.packagename.android I/dalvikvm﹕ Total arena pages for JIT: 12
12-14 21:57:21.458 17907-17913/com.packagename.android I/dalvikvm﹕ Total arena pages for JIT: 13
12-14 21:57:21.458 17907-17913/com.packagename.android I/dalvikvm﹕ Total arena pages for JIT: 14
12-14 21:57:21.608 17907-18645/com.packagename.android E/sinch-android-rtc﹕ WARNING: mxp Ignoring message, reason: 'From local instance'
12-14 21:57:21.608 17907-17907/com.packagename.android W/mxp﹕ Ignoring message, reason: 'From local instance'
As seen above, the Sinch client has been started successfully. But onMessageSent is not called, why?
My questions:
Then why it gave me an error while I was trying to send a message?
What does the error: "Ignoring message, reason: 'From local instance'" mean?
SinchService is running and it is not null, why onMessageSent is not called?
Related Code
SinchService.class
public class SinchService extends Service {
private static final String APP_KEY = "xxx";
private static final String APP_SECRET = "xxx";
private static final String ENVIRONMENT = "clientapi.sinch.com";
private static final String TAG = SinchService.class.getSimpleName();
private final SinchServiceInterface mServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient = null;
private StartFailedListener mListener;
public class SinchServiceInterface extends Binder {
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 void sendMessage(String recipientUserId, String textBody) {
SinchService.this.sendMessage(recipientUserId, textBody);
}
public void receiveMessage(String payload) {
SinchService.this.receiveMessage(payload);
}
public void addMessageClientListener(MessageClientListener listener) {
SinchService.this.addMessageClientListener(listener);
}
public void removeMessageClientListener(MessageClientListener listener) {
SinchService.this.removeMessageClientListener(listener);
}
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return mServiceInterface;
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
public void sendMessage(String recipientUserId, String textBody) {
if (isStarted()) {
WritableMessage message = new WritableMessage(recipientUserId, textBody);
mSinchClient.getMessageClient().send(message);
}
}
public void receiveMessage(String payload) {
if (isStarted()) {
NotificationResult result = mSinchClient.relayRemotePushNotificationPayload(payload);
if (result.isMessage()) {
String msgId = result.getMessageResult().getMessageId();
Log.i(TAG, String.format("Received msg Id %s", msgId));
}
}
}
public void addMessageClientListener(MessageClientListener listener) {
if (mSinchClient != null) {
mSinchClient.getMessageClient().addMessageClientListener(listener);
}
}
public void removeMessageClientListener(MessageClientListener listener) {
if (mSinchClient != null) {
mSinchClient.getMessageClient().removeMessageClientListener(listener);
}
}
private void start(String userName) {
if (mSinchClient == null) {
mSinchClient = Sinch.getSinchClientBuilder()
.context(getApplicationContext())
.userId(userName)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportMessaging(true);
mSinchClient.setSupportPushNotifications(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.checkManifest();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.start();
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.stopListeningOnActiveConnection();
mSinchClient.terminate();
mSinchClient = null;
}
}
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) {
}
}
}
SinchManager.class
public class SinchManager implements MessageClientListener, ServiceConnection {
private SinchCallback callback;
private static final String TAG = SinchManager.class.getSimpleName();
private Context context;
private SinchService.SinchServiceInterface mSinchServiceInterface;
private String chatRecipient;
public SinchManager(Context c, SinchCallback cb) {
this.context = c;
this.callback = cb;
// init Sinch
context.getApplicationContext().bindService(new Intent(context, SinchService.class), this,
context.BIND_AUTO_CREATE);
}
public void sendMessage(String recipient, String msg) {
mSinchServiceInterface.sendMessage(recipient, msg);
}
public void stopSinchClient() {
mSinchServiceInterface.removeMessageClientListener(this);
mSinchServiceInterface.stopClient();
context.stopService(new Intent(context, SinchService.class));
context.unbindService(this);
}
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchService.SinchServiceInterface) iBinder;
mSinchServiceInterface.addMessageClientListener(this);
if (!mSinchServiceInterface.isStarted()) {
mSinchServiceInterface.startClient(username);/// Here username="xxx"
}
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
if (SinchService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = null;
}
}
#Override
public void onIncomingMessage(MessageClient messageClient, Message message) {
callback.onReceiveSinchMessage(message);
}
#Override
public void onMessageSent(MessageClient messageClient, Message message, String s) {
callback.onSentSinchMessage(message, s);
}
#Override
public void onMessageFailed(MessageClient messageClient, Message message, MessageFailureInfo failureInfo) {
callback.onFailedSinchMessage(failureInfo);
}
#Override
public void onMessageDelivered(MessageClient messageClient, MessageDeliveryInfo deliveryInfo) {
callback.onDeliveredSinchMessage(deliveryInfo);
}
#Override
public void onShouldSendPushData(MessageClient messageClient, Message message, List<PushPair> pushPairs) {
//TODO setup offline push notification
Log.d(TAG, "Recipient not online. And it's not in public chat \n Should notify recipient using push");
}
}
On my activity class, I send the message like this:
private void sendMessage() {
String textBody = messageTextBox.getText().toString();
if (chatRecipient.isEmpty()) {
Log.e(TAG, "Recipient is empty, Check Activity Bundle");
return;
}
if (textBody.isEmpty()) {
Toast.makeText(this, "No text message", Toast.LENGTH_SHORT).show();
return;
}
sinchManager.sendMessage(chatRecipient, textBody);
messageTextBox.setText("");
}
Sinch Sample Code
I just tried the sample code Sinch provided and it also shows me the same error code:
12-16 22:48:37.713 7648-8096/com.sinch.android.rtc.sample.messaging E/sinch-android-rtc﹕ WARNING: mxp Ignoring message, reason: 'From local instance'
12-16 22:48:37.713 7648-7648/com.sinch.android.rtc.sample.messaging W/mxp﹕ Ignoring message, reason: 'From local instance'
12-16 22:48:37.863 7648-7648/com.sinch.android.rtc.sample.messaging D/MessageClient﹕ onSentMessage: NativeMessage [id=xxxxx, nativeAddress=xxxx] to recipient with idxxx
However I noticed that the last line of log indicates that the Message was successfully sent.
I am confused right now, is there anyone who has experience on this?
NOTE: the sample code use the same API KEY/SECRET/Environment as the one I showed above.
Download Sinch Sample Code