I am trying to use the LocalBroadcastManager inside an android service.
public class WebSocketService extends Service {
private static final String TAG = "WebSocketService";
private WebSocketClient mWebSocketClient;
public WebSocketService() {
connectWebSocket();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void connectWebSocket() {
URI uri;
try {
uri = new URI("wss://echo.websocket.org");
} catch (URISyntaxException e) {
e.printStackTrace();
return;
}
mWebSocketClient = new WebSocketClient(uri) {
#Override
public void onOpen(ServerHandshake serverHandshake) {
Log.i(TAG, "Websocket Opened");
mWebSocketClient.send("Hello from " + Build.MANUFACTURER + " " + Build.MODEL);
}
#Override
public void onMessage(String message) {
Log.i(TAG, "Websocket Received: " + message);
Intent broadcastIntent = new Intent("custom-event");
broadcastIntent.putExtra("message", message);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(broadcastIntent);
}
#Override
public void onClose(int code, String reason, boolean remote) {
Log.i(TAG, "Websocket closed: " + reason);
}
#Override
public void onError(Exception ex) {
Log.i(TAG, "Websocket error");
}
};
}
public void sendMessage(String message) {
mWebSocketClient.send(message);
}
}
The main Activity where the websocket gets created.
public class MainActivity extends Activity {
private WebSocketService webSocketService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webSocketService = new WebSocketService();
}
public void send(View view) {
webSocketService.sendMessage("socket message");
}
}
and its xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:onClick="send"
tools:layout_editor_absoluteX="166dp"
tools:layout_editor_absoluteY="288dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
The above does no compile with exception
After changing LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent); to
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(broadcastIntent);
I get the error
Error Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
My question is whether its possible to pass the context while creating
mWebSocketClient = new WebSocketClient(uri) {
}
such that the LocalBroadcastManager can be used or not?
The question in general is, is it possible to get the applicationContext inside an annoymous namespace.
To get an outerScope this inside an annoymous method / inner class of any java class you can either call WebSocketService.this
LocalBroadcastManager.getInstance(WebSocketService.this).sendBroadcast(broadcastIntent);
or declare a private helper method for the same
private WebSocketService getOuterWebSocketService() {
return this;
}
Now you can use this method inside onMessage as below
#Override
public void onMessage(String message) {
Log.i(TAG, "Websocket Received: " + message);
Intent broadcastIntent = new Intent("custom-event");
broadcastIntent.putExtra("message", message);
LocalBroadcastManager.getInstance(getOuterWebSocketService()).sendBroadcast(broadcastIntent);
}
Related
In the App i am developing i am trying to use IntentService as shown below in the code. the IntentService is declared as shown below in the manifest file.
the issue i am facing now is, when I run App the onHandleIntent is never get called.
I checked some examples in the internt but non of them was helpful, because the recommended hints to solve the issue did not work.
I started the service as follows:
this.mButtonFetchURL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mServiceIntent = new Intent(getApplicationContext(), TwitterTrendsAPIService.class);
mServiceIntent.putExtra(CONST_KEY_REQUEST_URL, BASE_REQUEST_URL);
startService(mServiceIntent);
clearEditText(mEditTextURLContents);
}
});
please let me know how to solve it.
code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.pc_.twittertrendsnearlocation">
<uses-permission android:name="android.permission.INTERNET" />
<application
....
....
....
<service
android:name=".services.TwitterTrendsAPIService"
android:exported="false"
android:enabled="true"/>
</application>
code
public class TwitterTrendsAPIService extends IntentService {
private static final String TAG = TwitterTrendsAPIService.class.getSimpleName();
private boolean mIsFetching = false;
private String mBaseRequestURL = null;
private RequestQueue mRequestQueue = null;
private JsonArrayRequest mJsonArrayRequest = null;
private final static String CONST_KEY_JSON_ARRAY_TRENDS = "trends";
private JSONObject mEntireJSONObject = null;
private JSONArray mEntireTrendsArray = null;
public TwitterTrendsAPIService() {
super(null);
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public TwitterTrendsAPIService(String name) {
super(name);
}
#Override
public void onCreate() {
super.onCreate();
Log.w(TAG, "[onCreate]");
this.setupVolley();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, "[onStartCommand]");
return Service.START_NOT_STICKY;
}
#Override
public void onHandleIntent(Intent intent) {
Log.w(TAG, "[onHandleIntent]");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.w(TAG, "[onDestroy]");
}
private void setupVolley() {
this.mRequestQueue = Volley.newRequestQueue(this);
}
private class ServiceRunnable implements Runnable {
#Override
public void run() {
fetchJSONData();
stopSelf();
}
}
private void fetchJSONData() {
Log.w(TAG, "#fetchJSONData");
this.mJsonArrayRequest = new JsonArrayRequest(Request.Method.GET, this.mBaseRequestURL, null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
mEntireJSONObject = response.getJSONObject(0);
mEntireTrendsArray = mEntireJSONObject.getJSONArray(TwitterTrendsAPIService.CONST_KEY_JSON_ARRAY_TRENDS);
Log.i(TAG, "mEntireTrendsArray.length(): " + mEntireTrendsArray.length());
Log.i(TAG, "mEntireTrendsArray.get(0): " + mEntireTrendsArray.get(0));
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
}
}
Delete onStartCommand(), or chain to the superclass' implementation of onStartCommand(). Right now, you are overriding the built-in implementation, and IntentService uses that to set up the background thread and call onHandleIntent().
I am trying to integrate the Sinch API in android .
My VOIPClient. Java is like this
public class VOIPClient {
private static final String TAG = "VOIPClient";
private SinchClient mSinch;
private TTSHelper mTTS;
private Call mCurrentCall;
private BootService mContext;
private SinchClientBuilder mBuilder;
private NsdController mNsdController;
private final CallListener mCallListener = new CallListener() {
#Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call established at " + " Thusee");
}
#Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established at " + call.getDetails().getEstablishedTime());
mTTS.speak("Call started", TTSHelper.UTTERANCE_VOIP_START);
JsonObject payload = new JsonObject();
payload.addProperty("Status", 0);
payload.addProperty("Call_status", 1);
if (mNsdController != null) {
mNsdController.sendCommand(20, payload);
}
}
#Override
public void onCallEnded(Call call) {
Log.d(TAG, "Call ended at " + call.getDetails().getEndedTime() + "caused by " + call.getDetails().getEndCause().toString());
mTTS.speak("Call ended", TTSHelper.UTTERANCE_VOIP_END);
mCurrentCall.hangup();
JsonObject payload = new JsonObject();
payload.addProperty("Status", 0);
payload.addProperty("Call_status", 1);
if (mNsdController != null) {
mNsdController.sendCommand(21, payload);
}
}
#Override
public void onShouldSendPushNotification(Call call, List<PushPair> list) {
}
};
public VOIPClient(BootService context) {
mContext = context;
mTTS = TTSHelper.getInstance(context);
mBuilder = Sinch.getSinchClientBuilder().context(mContext.getApplicationContext())
.applicationKey(CloudConfig.SINCH_APP_KEY)
.applicationSecret(CloudConfig.SINCH_APP_SECRET)
.environmentHost(CloudConfig.SINCH_ENVIRONMENT);
if (mNsdController != null)
mNsdController.initialize();
}
public void start() {
SharedPreferences prefs = mContext.getPreferences();
int userId = prefs.getInt(MerryClient.PREF_USER_ID, 0);
String mUserId;
if (userId > 0) {
mUserId = String.valueOf(userId);
mSinch = Sinch.getSinchClientBuilder().context(mContext.getApplicationContext()).userId(mUserId)
.applicationKey(CloudConfig.SINCH_APP_KEY)
.applicationSecret(CloudConfig.SINCH_APP_SECRET)
.environmentHost(CloudConfig.SINCH_ENVIRONMENT).build();
mSinch.setSupportCalling(true);
mSinch.setSupportManagedPush(false);
SinchClientListener sinchClientListener = new SinchClientListener() {
#Override
public void onClientStarted(SinchClient sinchClient) {
Log.d(TAG, "Sinch Client starts: " + sinchClient.getLocalUserId());
mTTS.speak("Call ready", TTSHelper.UTTERANCE_VOIP_READY);
}
#Override
public void onClientStopped(SinchClient sinchClient) {
Log.d(TAG, "Sinch Client stops");
}
#Override
public void onClientFailed(SinchClient sinchClient, SinchError sinchError) {
Log.e(TAG, String.format("Sinch Client error %d: %s", sinchError.getCode(), sinchError.getMessage()));
mSinch.terminate();
mTTS.speak("Voice Over IP failed", TTSHelper.UTTERANCE_VOIP_FAIL);
}
#Override
public void onRegistrationCredentialsRequired(SinchClient sinchClient, ClientRegistration clientRegistration) {
Log.d(TAG, "Sinch Client requires registration");
}
#Override
public void onLogMessage(int i, String s, String s1) {
Log.d(TAG, s1);
}
};
mSinch.addSinchClientListener(sinchClientListener);
mSinch.getCallClient().setRespectNativeCalls(false);
mSinch.getCallClient().addCallClientListener(new SinchCallClientListener());
mCurrentCall = null;
mSinch.startListeningOnActiveConnection();
mSinch.start();
}
}
public void tearDown() {
if (mSinch != null) {
mSinch.stopListeningOnActiveConnection();
mSinch.terminate();
mSinch = null;
}
}
public void restart() {
tearDown();
start();
}
public void initiateCall(final String targetUserName) {
new Thread(new Runnable() {
public void run() {
Looper.prepare();
if (targetUserName != null) {
try {
Call call = callUser(targetUserName);
call.addCallListener(mCallListener);
mCurrentCall = call;
} catch (Exception e) {
Log.e(TAG, "Initiate VOIP call failed", e);
}
}
Looper.loop();
}
}).start();
}
public void answerCall() {
if (mCurrentCall != null) {
mCurrentCall.answer();
}
}
public void hangUpCall() {
if (mCurrentCall != null) {
mCurrentCall.hangup();
}
}
private class SinchCallClientListener implements CallClientListener {
#Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d(TAG, "Incoming call");
mTTS.speak("Incoming call from " + call.getRemoteUserId(), TTSHelper.UTTERANCE_VOIP_INCOMING);
call.addCallListener(mCallListener);
mCurrentCall = call;
// For testing only
answerCall();
}
}
public Call callUser(String userId) {
if (mSinch != null && mSinch.isStarted()) {
start();
}
if (mSinch == null) {
return null;
}
return mSinch.getCallClient().callUser(userId);
}
class CallerThread implements Runnable {
public String mtargetUserName;
CallerThread(String targetUserName) {
this.mtargetUserName = targetUserName;
}
#Override
public void run() {
Looper.prepare();
if (mtargetUserName != null) {
try {
Call call = callUser(mtargetUserName);
call.addCallListener(mCallListener);
mCurrentCall = call;
} catch (Exception e) {
Log.e(TAG, "Initiate VOIP call failed", e);
mContext.getAlexa().start();
}
}
Looper.loop();
}
}
}
When I try to call to other device then I am getting these kind of exceptions
Initiate VOIP call failed
java.lang.IllegalStateException: SinchClient not started
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.throwUnlessStarted(Unknown Source)
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.call(Unknown Source)
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.callUser(Unknown Source)
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.callUser(Unknown Source)
at tw.com.test.cloud.VOIPClient.callUser(VOIPClient.java:272)
at tw.com.test.cloud.VOIPClient$CallerThread.run(VOIPClient.java:293)
at java.lang.Thread.run(Thread.java:818)
Also some times I am getting this exception
FATAL EXCEPTION: Thread-75
Process: tw.com.test.wear, PID: 1123
java.lang.IllegalThreadStateException: A Looper must be associated with this thread.
at com.sinch.android.rtc.internal.AndroidLooperCallbackHandler.<init>(Unknown Source)
at com.sinch.android.rtc.internal.client.InternalSinchClientFactory.createSinchClient(Unknown Source)
at com.sinch.android.rtc.DefaultSinchClientBuilder.build(Unknown Source)
at tw.com.test.cloud.VOIPClient.start(VOIPClient.java:109)
at tw.com.test.cloud.VOIPClient$2.onClientStopped(VOIPClient.java:124)
at com.sinch.android.rtc.internal.client.DefaultSinchClient.shutdown(Unknown Source)
at com.sinch.android.rtc.internal.client.DefaultSinchClient.terminate(Unknown Source)
at tw.com.test.cloud.VOIPClient.tearDown(VOIPClient.java:160)
at tw.com.test.nsd.NsdController.messageReceived(NsdController.java:570)
at tw.com.test.nsd.NsdConnection.run(NsdConnection.java:115)
at java.lang.Thread.run(Thread.java:818)
2 Days I tried my self, I can't able to solve this yet,
I am always getting these exception. Sometimes it will work for one time then I need to restart the app.
You have to start SinchClient sinchClient.start(); and take NOTE during production mode do not place in a plain text form your SINCH_APP_SECRET, because its a secret key, hackers will easily read or decompile your code.
public VOIPClient(BootService context) {
mContext = context;
mTTS = TTSHelper.getInstance(context);
mBuilder = Sinch.getSinchClientBuilder().context(mContext.getApplicationContext())
.applicationKey(CloudConfig.SINCH_APP_KEY)
.applicationSecret(CloudConfig.SINCH_APP_SECRET)
.environmentHost(CloudConfig.SINCH_ENVIRONMENT);
sinchClient.setSupportCalling(true);
sinchClient.start();
if (mNsdController != null)
mNsdController.initialize();
}
I am currently using okhttp for network calls. I also need websocket so I made it like this:
public class WebSocketService extends Service implements WebSocketListener {
private static final String TAG = "WebSocketService";
private static final String BASE_URL = "ws://192.168.1.39:8080/websocket";
public static final int MSG_RESPONSE = 1;
public static final int MSG_HELLO = 2;
Messenger msger = new Messenger(new MessageHandler());
private WebSocket webSocket;
private boolean mWebSocketOpened = false;
private Messenger replyToMsngr;
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "service oncreate");
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.NANOSECONDS)
.connectTimeout(15000, TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true)
.build();
Request request = new Request.Builder().url(BASE_URL).build();
WebSocketCall webSocketCall = WebSocketCall.create(okHttpClient, request);
webSocketCall.enqueue(this);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return msger.getBinder(); //using Messenger
}
#Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind");
stopSelf();
return super.onUnbind(intent);
}
#Override
public void onOpen(WebSocket webSocket, Response response) {
Log.i(TAG, "On Open"+response.toString());
this.webSocket = webSocket;
mWebSocketOpened = true;
sendMessage(data);
}
#Override
public void onFailure(IOException e, Response response) {
Log.i(TAG, "onFailure " + e.getMessage());
e.printStackTrace();
mWebSocketOpened = false;
}
#Override
public void onMessage(ResponseBody message) throws IOException {
byte[] bytes = message.bytes();
if(message.contentType()==WebSocket.TEXT){
String newString = new String(bytes);
Message msg = Message.obtain(null, MSG_RESPONSE, 0,0);
msg.replyTo = replyToMsngr;
Bundle bundle = new Bundle();
bundle.putString("rec", newString);
msg.setData(bundle);
try {
replyToMsngr.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
message.close();
}
#Override
public void onPong(Buffer payload) {
Log.i(TAG, "onPong");
}
#Override
public void onClose(int code, String reason) {
mWebSocketOpened = false;
}
public class WebSocketBinder extends Binder{
public WebSocketService getService() {
// Return this instance of LocalService so clients can call public methods
return WebSocketService.this;
}
}
public void sendMessage(String msg){
Log.i(TAG, "sendMessage : "+msg);
if(mWebSocketOpened & webSocket!=null){
try {
RequestBody requestBody = RequestBody.create(WebSocket.TEXT, msg);
webSocket.sendMessage(requestBody);
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}else{
Log.e(TAG, "socket not connected");
}
}
//test func
public int getRandomNumber(){
return new Random().nextInt(100);
}
public class MessageHandler extends android.os.Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESPONSE:
Bundle data = msg.getData();
replyToMsngr = msg.replyTo;
WebSocketService.this.sendMessage(data.getString("rec", "No data"));
break;
case MSG_HELLO:
Toast.makeText(getApplicationContext(), "heelo", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
}
But what I need to do is to be able to use STOMP in the web socket like in the example/library https://github.com/NaikSoftware/StompProtocolAndroid
if there is any way/ example to use okhttp along with stomp, it would help me if you mentioned.
Also if the successful implementation of websocket with above mentioned library is needed here is how I implemented it.
public class TestActivity extends AppCompatActivity {
private static final String TAG = "testactivity";
#BindView(R.id.toolbar)
Toolbar toolbar;
#BindView(R.id.ettext)
EditText ettext;
#BindView(R.id.content_test)
RelativeLayout contentTest;
#BindView(R.id.fab)
FloatingActionButton fab;
private StompClient mStompCLient;
private static final String BASE_URL = "ws://192.168.1.39:8080/websocket";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
ButterKnife.bind(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mStompCLient = Stomp.over(WebSocket.class, BASE_URL);
mStompCLient.topic("/topic/greetings").subscribe(topicMessage -> {
Log.d(TAG, topicMessage.getPayload());
});
mStompCLient.send("/app/hello", "{\"userName\":\"abcd\"}").subscribe();
mStompCLient.lifecycle().subscribe(lifecycleEvent -> {
switch (lifecycleEvent.getType()) {
case OPENED:
Log.d(TAG, "Stomp connection opened");
break;
case ERROR:
Log.e(TAG, "Error", lifecycleEvent.getException());
break;
case CLOSED:
Log.d(TAG, "Stomp connection closed");
break;
}
});
mStompCLient.connect();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
JSONObject object = new JSONObject();
try {
object.put("name", ettext.getText().toString());
} catch (JSONException e) {
e.printStackTrace();
}
Log.i(TAG, "send data : "+ object.toString());
mStompCLient.send("/app/hello", object.toString()).subscribe();
}
});
}
#Override
protected void onStop() {
super.onStop();
disconnectStomp();
}
private void disconnectStomp() {
mStompCLient.disconnect();
}
}
It would also help if anyone can mention the stomp examples using okhttp websocket.
You can now call Stomp.over(okhttp3.WebSocket.class).
I have a regular class (SOAP.java) which I added a function to call a webservice, I have the URL and other informations in res/values/strings.xml.
My Intent Service (which is called every 2 minutes) and a Fragment of my App are using the function in SOAP.java, but I can't get access to the strings, error:
08-19 03:47:19.730 16543-17323/fr.solutis.solutis E/AndroidRuntime﹕
FATAL EXCEPTION: IntentService[EnvoieService]
Process: fr.solutis.solutis, PID: 16543
java.lang.NullPointerException
at fr.solutis.solutis.SOAP.(SOAP.java:22)
at fr.solutis.solutis.notifications.EnvoieService.onHandleIntent(EnvoieService.java:96)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.os.HandlerThread.run(HandlerThread.java:61)
SOAP.java:
public class SOAP {
private Context context;
public SOAP(Context current){
this.context = current;
}
String NAMESPACE = context.getResources().getString(R.string.NAMESPACE);
String URL = context.getResources().getString(R.string.URL);
String SOAP_ACTION = context.getResources().getString(R.string.SOAP_ACTION);
private static String TAG = SOAP.class.getSimpleName();
public Reponse envoieDemande(String method, String xml) {
code
}
}
IntenService:
public class EnvoieService extends IntentService {
DatabaseHandler db = new DatabaseHandler(this);
public EnvoieService() {
super("EnvoieService");
}
#Override
protected void onHandleIntent(Intent intent) {
List<Demande> demandes = db.getAllDemandesRenvoie();
String TAG = EnvoieService.class.getSimpleName();
if (!(demandes.isEmpty())) {
for (Demande cn : demandes) {
...
SOAP soap = new SOAP(this);
Reponse ret = soap.envoieDemande("SendLead", xml);
}
} else {
cancelAlarm();
}
}
public void cancelAlarm() {
Intent intent = new Intent(getApplicationContext(), EnvoieReceiver.class);
final PendingIntent pIntent = PendingIntent.getBroadcast(this, EnvoieReceiver.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pIntent);
}
}
Fragment:
public class DemandeGratuite extends android.support.v4.app.Fragment {
...
{
{
{
{
{
{
{
{
AsyncSoapCall task = new AsyncSoapCall();
task.execute();
getChildFragmentManager().popBackStack();
mListener.onInteraction(6);
} catch (FileNotFoundException e) {
System.err.println("FileNotFoundException: " + e.getMessage());
} catch (IOException e) {
System.err.println("Caught IOException: " + e.getMessage());
}
}
}
}
);
}
private class AsyncSoapCall extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
SOAP soap = new SOAP(getContext());
Reponse ret = soap.envoieDemande("SendLead", xml);
return null;
}
#Override
protected void onPostExecute(Void result) {
Log.i(TAG, "onPostExecute");
}
#Override
protected void onPreExecute() {
Log.i(TAG, "onPreExecute");
}
#Override
protected void onProgressUpdate(Void... values) {
Log.i(TAG, "onProgressUpdate");
}
}
}
Resources cannot be accesed from the context of a service. You must use the context of an activity or u can try to use this class
public class App extends Application {
private static Context mContext;
public static Resources getResources() {
return mContext.getResources();
}
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
}
and then use the function App.getResources().getString(your_string_id) from wherever you want
I am having an app that has multiple activities and uses MQTT.
I am using the paho client in gradle dependencies as follows
compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.0.3-SNAPSHOT'
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.0.3-SNAPSHOT'
I would be using username and password to connect to the broker and some activities would be using diffrent user name and password.
currently I am handling the connect,subscribeand publish tasks in each activity as in the code that I will include below. As the activities get more and more I am having issues. Please suggest me how I can port the code to a service or singleton so that it can be reused and become efficient.
Here is one of the activities
package net.kindows.chitchat;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.pixplicity.easyprefs.library.Prefs;
import net.kindows.SplashScreen;
import net.kindows.common.ApplicationLoader;
import net.kindows.common.utils;
import net.kindows.intlPhone.IntlPhoneInput;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import butterknife.Bind;
import butterknife.ButterKnife;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import static net.kindows.common.ApplicationLoader._toast;
public class LoginActivity extends AppCompatActivity implements MqttCallback {
private static final String TAG = "LoginActivity";
private static final int REQUEST_SIGNUP = 0;
private static final int REQUEST_PAS_RESET = 1;
private static final Integer LOGGED_OUT = 0;
private static final Integer LOGGING_IN = 1;
private static final Integer WAITING_FOR_SING_IN_ACK = 2;
private static final Integer LOGGED_IN = 3;
private static final Integer VERIFICATION_FAILED = 4;
#Bind(R.id.input_password)
EditText _passwordText;
#Bind(R.id.btn_login)
Button _loginButton;
#Bind(R.id.link_signup)
TextView _signupLink;
#Bind(R.id.my_phone_input)
IntlPhoneInput _phoneInputView;
String sUserName = null;
String sPassword = null;
String sDestination = null;
String sMessage = null;
private Integer state;
private Handler han = new Handler();
private MqttConnectOptions connOpt;
private ProgressDialog _progressDialog;
/*
MQTT mqtt = null;
FutureConnection connection = null;*/
private boolean isMinimized = false;
private String clientId;
private Handler loginAgain = new Handler();
private Handler timeout;
private MqttAndroidClient client;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//connect();
_loginButton.setEnabled(false);
// _phoneInputView.setNumber(ApplicationLoader.getSim1number(LoginActivity.this));
_phoneInputView.setOnValidityChange(new IntlPhoneInput.IntlPhoneInputListener() {
#Override
public void done(View view, boolean isValid) {
if (isValid) {
_loginButton.setEnabled(true);
} else _loginButton.setEnabled(false);
}
});
_loginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!ApplicationLoader.isConnected(LoginActivity.this, true)) {
} else login();
}
});
_signupLink.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Start the Signup activity
Prefs.putInt(getString(R.string.key_reset_pass), 2);
Intent intent = new Intent(getApplicationContext(), SignUpActivity.class);
startActivityForResult(intent, REQUEST_SIGNUP);
}
});
state = LOGGED_OUT;
connOpt = new MqttConnectOptions();
connOpt.setCleanSession(true);
connOpt.setKeepAliveInterval(30);
connOpt.setCleanSession(true);
clientId = ApplicationLoader.getClientId(LoginActivity.this);
client = new MqttAndroidClient(this, "tcp://104.131.50.64:1883", clientId, MqttAndroidClient.Ack.AUTO_ACK);//this,"tcp://104.131.50.64:1883", "app1", null);
}
#Override
protected void onStop() {
super.onStop();
isMinimized = true;
// super.onDestroy();
try {
client.close();
} catch (Exception e) {
// client.unregisterResources();
e.printStackTrace();
}
Crouton.cancelAllCroutons();
loginAgain.removeCallbacks(null);
han.removeCallbacks(null);
}
#Override
protected void onStart() {
super.onStart();
// Do not go to splash screen if came from signup activity
if (isMinimized && Prefs.getBoolean(getString(R.string.show_splash), true)) {
isMinimized = false;
han.removeCallbacks(null);
startActivity(new Intent(this, SplashScreen.class));
finish();
}
Prefs.putBoolean(getString(R.string.show_splash), true);
if (utils.getLoginState_login()) {
han.removeCallbacks(null);
utils._startActivity(this, MainActivity.class);
finish();
}
}
#Override
protected void onResume() {
super.onResume();
if (utils.getLoginState_login()) {
han.removeCallbacks(null);
utils._startActivity(this, MainActivity.class);
finish();
}
ApplicationLoader.isConnected(this, true);
}
public void login() {
Log.d(TAG, getString(R.string.login));
_toast(getString(R.string.logging_in), LoginActivity.this);
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
// Disable login button for 5 secs
final boolean lastLoginState = _loginButton.isEnabled();
_loginButton.setEnabled(false);
loginAgain.postDelayed(new Runnable() {
#Override
public void run() {
_loginButton.setEnabled(lastLoginState);
}
}, 5000);
_progressDialog = new ProgressDialog(LoginActivity.this,
R.style.AppTheme_Dark_Dialog);
// String phone = _phoneText.getText().toString();
String password = _passwordText.getText().toString();
String numb = _phoneInputView.getNumber().replace("+", "");
connectMQTT(numb, password);
_progressDialog.setIndeterminate(true);
_progressDialog.setMessage("Authenticating...");
_progressDialog.show();
han.postDelayed(
new Runnable() {
public void run() { // On complete call either onLoginSuccess or onLoginFailed
//onLoginSuccess();
onLoginFailed();
_progressDialog.dismiss();
}
}, ApplicationLoader.timeOUT);
}
private void publish2MQQT(MqttAndroidClient client1, String topic, String msg) throws MqttException {
if (client1 != null) {
MqttMessage msg2 = new MqttMessage();
msg2.setPayload(msg.getBytes());
client1.publish(topic, msg2, this, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
// _sendErrorLog("on sucess of publish");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
// _sendErrorLog("on fail of publish e= " + exception.getMessage());
_progressDialog.dismiss();
_toast((exception != null ? exception.getMessage() : ""), LoginActivity.this);
}
});
// Log.e("mqtt ", "published " + msg);
}
}
private void _sendErrorLog(String s) {
Log.e("LOG", s);
}
private void connectMQTT(final String user, final String pass) {
Log.e("connectMQTT", "1");
try {
connOpt.setUserName(user);
connOpt.setPassword(pass.toCharArray());
_sendErrorLog("on connteing with " + user + " " + pass);
client.connect(connOpt, this, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
_sendErrorLog("on success of connect");
try {
client.subscribe("astr/app/iremote/" + user.replace("+", ""), 0, this, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
_sendErrorLog("on sucess of subscribe");
// TODO: Implement your own authentication logic here.
JsonObject msg = new JsonObject();
msg.addProperty("u", user);
msg.addProperty("P", pass);
sUserName = user;
sPassword = pass;
sDestination = "astr/admin/signin";
sMessage = msg.toString();
state = LOGGING_IN;
try {
Log.e("register", "publishing signin message");
if (client == null) {
Log.e("register", "publishing register message client is null");
}
publish2MQQT(client, sDestination, sMessage);
state = WAITING_FOR_SING_IN_ACK;
} catch (MqttException e) {
e.printStackTrace();
Log.e("register", "got exception in publish " + e.toString());
_progressDialog.dismiss();
_toast(e.getMessage(), LoginActivity.this);
}
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
_sendErrorLog("on failure of subscribe " + exception.getMessage());
_progressDialog.dismiss();
_toast((exception != null ? exception.getMessage() : ""), LoginActivity.this);
}
});
// client.subscribe("astr/app/iremote/" + _num_2b_verified.replace("+", ""));
} catch (MqttException | NullPointerException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
_sendErrorLog("on failure of connect" + exception.getMessage());
han.removeCallbacks(null);
try {
_progressDialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
_toast((exception != null ? exception.getMessage() : ""), LoginActivity.this);
}
});
client.setCallback(this);
} catch (MqttException e) {
e.printStackTrace();
Log.e("connectMQTT", "got exception :: " + e.toString());
}
}
#Override
public void connectionLost(Throwable throwable) {
Log.e("connection", "lost");
//connectMQTT();
}
#Override
protected void onDestroy() {
super.onDestroy();
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
String msgRecived = new String(mqttMessage.getPayload());
/* Log.e("message arrived", "-------------------------------------------------");
Log.e("message arrived", "| Topic:" + s);*/
Log.e("message arrived", "| Message: " + msgRecived);
/*Log.e("message arrived" , "-------------------------------------------------");*/
if (state.equals(WAITING_FOR_SING_IN_ACK)) {
han.removeCallbacks(null);
JsonParser jp = new JsonParser();
JsonObject reply = (JsonObject) jp.parse(msgRecived);
if (reply.get("s").getAsInt() == 200) {
_toast(getString(R.string.logged_in), LoginActivity.this);
_progressDialog.dismiss();
_phoneInputView.setVisibility(View.GONE);
_passwordText.setVisibility(View.VISIBLE);
_loginButton.setText(R.string.logged_in);
_loginButton.setEnabled(true);
state = LOGGED_IN;
utils.storeLoginState(true, sUserName, sPassword);
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
state = LOGGED_IN;
han.removeCallbacks(null);
try {
client.close();
} catch (Exception e) {
e.printStackTrace();
}
startActivity(new Intent(this, MainActivity.class));
//finish();
} else {
state = VERIFICATION_FAILED;
utils.storeLoginState(false, "", "");
onLoginFailed();
}
}
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SIGNUP) {
if (resultCode == RESULT_OK) {
// TODO: Implement successful signup logic here
// By default we just finish the Activity and log them in automatically
this.finish();
}
}
}
#Override
public void onBackPressed() {
// Disable going back to the MainActivity
moveTaskToBack(true);
}
public void onLoginFailed() {
// ApplicationLoader._toast("Login failed",LoginActivity.this);
_toast(getString(R.string.log_in_failed), LoginActivity.this);
_passwordText.setVisibility(View.VISIBLE);
_phoneInputView.setVisibility(View.VISIBLE);
_loginButton.setEnabled(false);
// Enable Login after 5000 ms with editing the number
loginAgain.postDelayed(new Runnable() {
#Override
public void run() {
_loginButton.setEnabled(_phoneInputView.isValid());
}
}, 5000);
}
#Override
public void onPause() {
super.onPause();
//disconnect();
}
public void resetPass(View view) {
// Start the Signup activity
Prefs.putInt(getString(R.string.key_reset_pass), 1);
Intent intent = new Intent(getApplicationContext(), SignUpActivity.class);
startActivityForResult(intent, REQUEST_PAS_RESET);
}
}
I believe the best option for you would be to implement those same functionalities in a Fragment. I won't read through your whole source code because it's enormous and I ain't got time for that, but I'll give you some ideas and directions and you can migrate it yourself.
Step 1: Create a MQTTFragment:
public class MQTTFragment extends Fragment implements MqttCallback {
public static final String TAG = "MQTTFragment.tag";
#Override
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// avoid this frag getting destroyed during rotation
setRetainInstance(true);
}
// DO NOT #Override onCreateView, this fragment have no views
// Put here ALL the code related to MQTT,
// any functionality you want to be able to call from Activity, make public, all the rest is private
// this fragment should also remember the current state of the connection
// this fragment can also have interface and listener pattern in case to past result back to activity.
... your mqtt code
}
Step 2: Every activity u want to use MQTT functionality includes the MQTTFragment
public class MyActivity extends AppCompatActivity {
private MQTTFragment mqttFragment;
// during onCreate you get or create the fragment
#Override public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null) {
mqttFragment = new MQTTFragment();
getSupportFragmentManager()
.beginTransaction()
.add(mqttFragment, MQTTFragment.TAG)
.commit();
} else {
mqttFragment =
(MQTTFragment) getSupportFragmentManager()
.findFragmentByTag(MQTTFragment.TAG);
}
}
// now on this activity you can call anything MQTT related functionality from the Fragment
}