Using SharedPreferences data in Application class - java

I am developing an app and I use Socket.io on it, I initialize the socket in a class that extends Application and looks like this:
public class Inicio extends Application{
private Socket mSocket;
private SharedPreferences spref;
#Override
public void onCreate(){
super.onCreate();
try{
spref = getSharedPreferences("accountData", Context.MODE_PRIVATE);
IO.Options op = new IO.Options();
op.forceNew = true;
op.reconnection = true;
op.query = "tok=" + spref.getString("sessiontoken", "") + "&usr=" + spref.getString("userid", "");
mSocket = IO.socket(Constants.serverAddress, op);
}catch(URISyntaxException e){
throw new RuntimeException(e);
}
}
public Socket getmSocket(){
return mSocket;
}
}
So I can get and use the same socket instance in other parts of my application's code calling the following way:
Inicio appClass = (Inicio) getApplication();
mSocket = appClas.getmSocket();
mSocket.connect();
But there is a small problem that motivated me to post this question, can you see when I call to SharedPreferences in the Application class? I do this because I need to send the session token and user account ID to properly start the socket connection with my server, the problem is:
Imagine that a user opens the app for the first time and does not have an account yet, he will login or register and then the session token and user ID will be saved to SharedPreferences, but when the app started and ran the Application class, SharedPreferences was still empty and did not have the required token and user ID to establish the connection, so the user would have to reopen the app now to be able to use the socket successfully.
So I ask you: What are my alternative options for solving the problem? Is there another structure besides the Application class that I could use to not suffer from this problem? Or is there some way to bypass this problem?
What I'm doing to get around the problem for now is to restart the app programmatically when login occurs but I believe this is looks like a sad joke and not the ideal way to do it.
Thanks, I apologize for this long question of mine, but I'll be grateful for any help.

Separate your soket creation logic like below:
private void createSoket() {
spref = getSharedPreferences("accountData", Context.MODE_PRIVATE);
String sessiontoken = spref.getString("sessiontoken", "");
String userId = spref.getString("userid", "");
if(!(TextUtils.isEmpty(sessiontoken) || TextUtils.isEmpty(userId))) {
try {
IO.Options op = new IO.Options();
op.forceNew = true;
op.reconnection = true;
op.query = "tok=" + sessiontoken + "&usr=" + userId;
mSocket = IO.socket(Constants.serverAddress, op);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
And when required soket check null and create before pass the instance.
public Socket getmSocket(){
if(mSoket == null)
createSoket();
return mSocket;
}
N.B: Without valid settionToken and userId, soket is null
This is complete Application class:
public class Inicio extends Application{
private Socket mSocket;
private SharedPreferences spref;
#Override
public void onCreate(){
super.onCreate();
createSoket();
}
private void createSoket() {
spref = getSharedPreferences("accountData", Context.MODE_PRIVATE);
String sessiontoken = spref.getString("sessiontoken", "");
String userId = spref.getString("userid", "");
if(!(TextUtils.isEmpty(sessiontoken) || TextUtils.isEmpty(userId))) {
try {
IO.Options op = new IO.Options();
op.forceNew = true;
op.reconnection = true;
op.query = "tok=" + sessiontoken + "&usr=" + userId;
mSocket = IO.socket(Constants.serverAddress, op);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
public Socket getmSocket(){
if(mSoket == null)
createSoket();
return mSocket;
}
}

Related

BufferingResponseListener and getContentAsString append the previously fetched content

I run a custom WebSocketServlet for Jetty, which sends short text push notifications (for an async mobile and desktop word game) to many platforms (Facebook, Vk.com, Mail.ru, Ok.ru also Firebase and Amazon messaging) using a Jetty HttpClient instance:
public class MyServlet extends WebSocketServlet {
private final SslContextFactory mSslFactory = new SslContextFactory();
private final HttpClient mHttpClient = new HttpClient(mSslFactory);
#Override
public void init() throws ServletException {
super.init();
try {
mHttpClient.start();
} catch (Exception ex) {
throw new ServletException(ex);
}
mFcm = new Fcm(mHttpClient); // Firebase
mAdm = new Adm(mHttpClient); // Amazon
mApns = new Apns(mHttpClient); // Apple
mFacebook = new Facebook(mHttpClient);
mMailru = new Mailru(mHttpClient);
mOk = new Ok(mHttpClient);
mVk = new Vk(mHttpClient);
}
This has worked very good for the past year, but since I have recently upgraded my WAR-file to use Jetty 9.4.14.v20181114 the trouble has begun -
public class Facebook {
private final static String APP_ID = "XXXXX";
private final static String APP_SECRET = "XXXXX";
private final static String MESSAGE_URL = "https://graph.facebook.com/%s/notifications?" +
// the app access token is: "app id | app secret"
"access_token=%s%%7C%s" +
"&template=%s";
private final HttpClient mHttpClient;
public Facebook(HttpClient httpClient) {
mHttpClient = httpClient;
}
private final BufferingResponseListener mMessageListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
LOG.warn("facebook failure: {}", result.getFailure());
return;
}
try {
// THE jsonStr SUDDENLY CONTAINS PREVIOUS CONTENT!
String jsonStr = getContentAsString(StandardCharsets.UTF_8);
LOG.info("facebook success: {}", jsonStr);
} catch (Exception ex) {
LOG.warn("facebook exception: ", ex);
}
}
};
public void postMessage(int uid, String sid, String body) {
String url = String.format(MESSAGE_URL, sid, APP_ID, APP_SECRET, UrlEncoded.encodeString(body));
mHttpClient.POST(url).send(mMessageListener);
}
}
Suddenly the getContentAsString method called for successful HttpClient invocations started to deliver the strings, which were fetched previously - prepended to the the actual result string.
What could it be please, is it some changed BufferingResponseListener behaviour or maybe some non-obvious Java quirk?
BufferingResponseListener was never intended to be reusable across requests.
Just allocate a new BufferingResponseListener for every request/response.

Android: DNS Java SRV lookup fails when network connectivity changes

Happy Friday, everyone!
My Android app relies on DNS Java (http://www.dnsjava.org/doc/) to perform an SRV lookup. We just discovered that if network connectivity changes (Switch from LTE to WiFi or vice versa) each lookup from then on will perpetually timeout regardless of whether internet connectivity is established again on the new network. Sometimes this is resolved by switching back to the old network, but if you change too many times it will not recover. Here is the code we're using below.
If I can provide any other details please let me know. Help is much appreciated!
private static class ConfigUpdater extends AsyncTask<Void, Void, JsonConfig> {
private static final String SRV_RUE_CONFIG_PREFIX = "_rueconfig._tls.";
ConfigListener listener;
String request_url;
String query_url;
String username, password;
String errorMsg;
public ConfigUpdater(String url, String username, String password, ConfigListener listener) {
this.username = username;
this.password = password;
this.listener = listener;
query_url = SRV_RUE_CONFIG_PREFIX + url;
errorMsg = "Failed to Login";
org.xbill.DNS.ResolverConfig.refresh();
}
#Override
protected JsonConfig doInBackground(Void... params) {
Record[] records;// = new Record[0];
try {
Lookup configLookup = new Lookup(query_url, Type.SRV);
configLookup.setCache(null);
records = configLookup.run();
} catch (TextParseException e) {
e.printStackTrace();
return null;
}
if(records != null && records.length > 0) {
for (Record record : records) {
SRVRecord srv = (SRVRecord) record;
String hostname = srv.getTarget().toString().replaceFirst("\\.$", "");
request_url = "https://" + hostname + "/config/v1/config.json";
Log.d("Auto Config request_url: "+request_url);
}
try {
String reponse_str = getFromHttpURLConnection();
Log.d("Auto Config JSON: "+reponse_str);
return parseJson(username, reponse_str, request_url);
} catch (Throwable e){
Log.d("Issue parsing json");
e.printStackTrace();
}
}
return null;
}
For anyone who may ever struggle with SRV lookups failing after a network switch I seem to have found the problem. The issue was that the Resolver property on the Lookup object seems to cache network configuration. If you want to ensure your lookups always have the latest network configuration, simply reinitialize a new Resolver. With the modifications below I was unable to reproduce any of the initial test cases.
Record[] records = null;// = new Record[0];
try {
Lookup configLookup = new Lookup(query_url, Type.SRV);
configLookup.setResolver(new ExtendedResolver()); /** FIX **/
records = configLookup.run();
} catch (TextParseException e) {
e.printStackTrace();
return null;
} catch (UnknownHostException e) {
e.printStackTrace();
}

Difficulty Trying to Install and Update App

I referenced a question here about how one might approach (outside of Google Play) having an app essentially update itself. For testing, I simply wanted to try to see if I could get it to download and install. Unfortunately, I get a parse error.
I would greatly appreciate any help:
A snippet from the class that calls the AsyncTask class:
public class downloadReceiver extends BroadcastReceiver {
private Context context;
private long localUpdate;
private long remoteUpdate = 20;
#Override
public void onReceive(final Context c, Intent i) {
new Thread(new Runnable() {
#Override
public void run() {
SharedPreferences preferences = c.getSharedPreferences("config", c.MODE_PRIVATE);
final String store = preferences.getString("store", "");
final String id = preferences.getString("id", "");
final long lastUpdated = preferences.getLong("updated", 0);
// autoUpdate app
appUpdater updater = new appUpdater(c);
try {
updater.execute(new URL("http://midamcorp.com/myApp.php"));
} catch (Exception e) {Log.e(this.getClass().toString(), " " + e.getMessage()); }
and the appUpdater class:
public class appUpdater extends AsyncTask<URL, String, String> {
private Context c;
public appUpdater(Context context) {
this.c = context;
}
protected String doInBackground(URL... appUrl) {
String location = c.getFilesDir() + "/app.apk";
try {
URL url = appUrl[0];
URLConnection con = url.openConnection();
con.connect();
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(location);
byte[] buffer = new byte[1024];
int read = 0;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.close();
input.close();
} catch(Exception e){
Log.e(this.getClass().toString(), " " + e.getMessage());
}
return location;
}
#Override
protected void onPostExecute(String saveLocation) {
Intent i = new Intent();
i.setAction(Intent.ACTION_VIEW);
Log.i("Location of app is: ", " " + saveLocation);
i.setDataAndType(Uri.fromFile(new File(saveLocation)), "application/vnd.android.package-archive");
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
c.startActivity(i);
}
}
Please note, the URL is linked to a PHP file that forces a download because the server I have it on has trouble with .apk files.
Your primary problem is that the installer does not have access to your portion of internal storage (getFilesDir()). Use external storage.
I also recommend that you call flush(), getFD().sync(), and close() in succession on your FileOutputStream, before trying to install the app.

Using Proxy Behind WebView

its already a week that i search for the way to make webview works with proxy (not using wifi, just mobile ddata). i didnt found any solution yet. can someone give me something to work with it?
i already try all there's that in the SO (maybe not all, if someone have a way please share it)
take a peek at my code
public class WebViewActivity extends Activity {
static WebView web;
String PROXY_IP = "202.58.124.34";
int PROXY_PORT = 8989;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.web_view);
web = (WebView) findViewById(R.id.webView1);
web.setWebViewClient(new MyWebViewClient("username","password"));
web.getSettings().setJavaScriptEnabled(true);
web.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
web.getSettings().setAllowContentAccess(true);
web.getSettings().setAppCacheEnabled(true);
System.getProperties().put("proxySet", "true");
System.getProperties().put(PROXY_IP, "202.58.124.34");
System.getProperties().put(PROXY_PORT, "8989");
Authenticator authenticator = new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
return (new PasswordAuthentication
("username","password".toCharArray()));
}
};
Authenticator.setDefault(authenticator);
web.setHttpAuthUsernamePassword("http://202.58.124.34", "", "username", "password");
web.loadUrl("URL");
}
public static boolean setProxyICSPlus(WebView webview, String host, int port, String exclusionList) {
Log.d("", "Setting proxy with >= 4.1 API.");
try
{
Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
Class params[] = new Class[1];
params[0] = Class.forName("android.net.ProxyProperties");
Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
Class wv = Class.forName("android.webkit.WebView");
Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
Object mWebViewCoreFieldIntance = getFieldValueSafely(mWebViewCoreField, web);
Class wvc = Class.forName("android.webkit.WebViewCore");
Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldIntance);
Class bf = Class.forName("android.webkit.BrowserFrame");
Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
Class ppclass = Class.forName("android.net.ProxyProperties");
Class pparams[] = new Class[3];
pparams[0] = String.class;
pparams[1] = int.class;
pparams[2] = String.class;
Constructor ppcont = ppclass.getConstructor(pparams);
updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance("202.58.124.34", 8989, null));
} catch (Exception ex) {
Log.e("","Setting proxy with >= 4.1 API failed with error: " + ex.getMessage());
return false;
}
Log.d("", "Setting proxy with >= 4.1 API successful!");
return true;
}
private static Object getFieldValueSafely(Field field, Object classInstance) throws IllegalArgumentException, IllegalAccessException {
boolean oldAccessibleValue = field.isAccessible();
field.setAccessible(true);
Object result = field.get(classInstance);
field.setAccessible(oldAccessibleValue);
return result;
}
public void loadUrl(WebView view, String url, String proxyUserName, String proxyPassword){
UsernamePasswordCredentials creds= new UsernamePasswordCredentials("username", "password");
Header credHeader = BasicScheme.authenticate(creds, "UTF-8", true);
Map<String, String> header = new HashMap<String, String>();
header.put(credHeader.getName(), credHeader.getValue());
view.loadUrl(url, header);
}
i already try using the public boolean ICS, using try and catch method. all isnt't showing any result (or i'm doing it wrong?). So please if someone have a way that's work using proxy in webview to show it the way.
thanks.
Note: i'm running using mobile data. not Wifi. so the setting will be just for mobile data.

Android Service-Activity 2 way communication

in my team's Android application I have a service running from boot which communicates with a server to perform operations such as logging in, registering, chatting between phones and updating the phone database.
I need to make my service communicate with the activity bi-directionally: for example I am working on the login activity at the moment and the username and passwords are Strings taken from a text field on the app screen and I have been able to pass them to the service for it to send an authorisation command to the server.
public void loginPressed(View v){
usernameStr = usernameField.getText().toString();
passwordStr = passwordField.getText().toString();
if (!bound) return;
Bundle b = new Bundle();
Message msg = Message.obtain(null, ChatService.LOGIN);
try {
b.putString("username", usernameStr);
b.putString("password", passwordStr);
msg.setData(b);
messenger.send(msg);
}
catch (RemoteException e) {
}
This works as I would have expected. When the server responds with a message saying whether or not the login was sucessful, I need it to pass a message back to the activity so that I can start the main activity if succesful or prompt for re-entry if not.
I tried to use the msg.replyTo field to get the return messenger to send the information back, but when I run the app it force closes with a null pointer exception and I have no idea why this is happening. Here is the code that seems to be the culprit:
private class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
case LOGIN:
Bundle b = msg.getData();
String username = b.getString("username");
String password = b.getString("password");
String loginMessage = TCPCall.login(username, password);
connection.sendMessage(loginMessage);
String loginReturn = connection.retrieveMessage();
Message m;
Scanner s = new Scanner(loginReturn);
s.useDelimiter(",");
String c = s.next();
String status = s.next();
String message = s.next();
if (status.equals("OK")) {
m = Message.obtain(null, LoginActivity.OK);
try {
msg.replyTo.send(m);
} catch (RemoteException e) {}
}
else {
m = Message.obtain(null, LoginActivity.ERR);
try {
msg.replyTo.send(m);
} catch (RemoteException e) {}
}
break;
The null pointer seems to be coming from the
msg.replyTo.send(m);
line of code in both cases (login succesful and login failed)
Any help to fix this problem would be greatly appreciated :)
As Gregg points out in the comments. You need to set msg.replyTo = messenger; int he place where you send the original message.
An example can be found here: http://www.survivingwithandroid.com/2014/01/android-bound-service-ipc-with-messenger.html
I think you forgot to send response to Login Activity by bundle from Service.
So, i made some changes in Messenger Service
define one global variable and made some changes in Incoming Handler
static final int LOGIN_STATUS = 1;
private class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
case LOGIN:
Bundle b = msg.getData();
String username = b.getString("username");
String password = b.getString("password");
String loginMessage = TCPCall.login(username, password);
connection.sendMessage(loginMessage);
String loginReturn = connection.retrieveMessage();
Message m = Message.obtain(null, LOGIN_STATUS);
Scanner s = new Scanner(loginReturn);
s.useDelimiter(",");
String c = s.next();
String status = s.next();
String message = s.next();
if (status.equals("OK")) {
b.putString("responseC",c);
b.putString("responseStatus",status);
b.putString("responseMessage",message)
m.setData(b);
try {
msg.replyTo.send(m);
} catch (RemoteException e) {}
}
else {
/*if something is wrong with username and password you can put
a toast*/
}
break;
Now we have to catch this response in our LoginActivity and
take IncomingHandler in Login Activity also
class IncomingHandler extends Handler{
#Override
public void handleMessage(Message msg) {
switch (msg.what){
case ChatService.LOGIN_STATUS:
String C = msg.getData().getString("responseC");
String Status = msg.getData().getString("responseStatus");
String Message = msg.getData().getString("responseMessage");
//Here is your response in LoginActivity, enjoy!!!
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
public void loginPressed(View v){
usernameStr = usernameField.getText().toString();
passwordStr = passwordField.getText().toString();
if (!bound) return;
Bundle b = new Bundle();
Message msg = Message.obtain(null, ChatService.LOGIN_SATUS,0,0);
try {
b.putString("username", usernameStr);
b.putString("password", passwordStr);
msg.setData(b);
msg.replyTo = mMessenger;
messenger.send(msg);
}
catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
This code is working perfectly, hope it will help you,
Thanks

Categories

Resources