I am posting this question because I couldn't find any satisfactory answers online.
I am developing an Android App in which the data is fetched from the external server(in my case it's localhost MySQL server now) and displayed on the screen.
However, the constraint with this is that the person should always be connected to the internet to get all the data which can be viewed on the phone.
Now, what I would like to achieve is, once the data has been retrieved from the external server it should be stored on the device so that even though the user opens up the app without being connected to the internet, the previously fetched data should be showed to him.
In other words, I would like to have the offline capability.
How can I achieve this?
I have implemented the same for my app. Have a look at the code and you will understand how to do it. I have used Retrofit for the same. I have checked if the nursejson which is in sharedpreference is null. if it is null then continue further to hit API if not then load data from that sharedpreference.
To refresh the list, just check if you have internet connectivity and then delete that sharedpreference and call getnurse method again.
//After Oncreate
pref = getApplicationContext().getSharedPreferences("CachedResponse", 0);
editor = pref.edit();
editor.apply();
// Call getNurses method
getNurses();
//Method to get Nurses
public void getNurses() {
nurseJson = pref.getString("nurseJson", null);
if (nurseJson != null) {
progressBar.setVisibility(View.INVISIBLE);
gson = new Gson();
nurse = gson.fromJson(nurseJson, Nurse.class);
nurseList = nurse.getNurse();
namesArrayList.clear();
for (String nurses : nurseList) {
namesArrayList.add(nurses);
}
namesAdapter.notifyDataSetChanged();
} else {
Call<Nurse> call = apiInterface.getNursesList();
call.enqueue(new Callback<Nurse>() {
#Override
public void onResponse(Call<Nurse> call, Response<Nurse> response) {
progressBar.setVisibility(View.INVISIBLE);
onItemsLoadComplete();
if (response.isSuccessful()) {
nurse = response.body();
nurseJson = new Gson().toJson(nurse);
editor.putString("nurseJson", nurseJson);
editor.commit();
nurseList = nurse.getNurse();
namesArrayList.clear();
for (String nurses : nurseList) {
namesArrayList.add(nurses);
}
namesAdapter.notifyDataSetChanged();
} else {
utility.createSnackbar(coordinatorLayout, "Error Occurred, Please Try Again Later!");
}
}
#Override
public void onFailure(Call<Nurse> call, Throwable t) {
progressBar.setVisibility(View.INVISIBLE);
onItemsLoadComplete();
if (t.getLocalizedMessage() != null) {
if (t.getLocalizedMessage().contains("Unable to resolve host")) {
utility.createSnackbar(coordinatorLayout, "Please Check Internet Connection!");
} else {
utility.createSnackbar(coordinatorLayout, "Error Occurred, Please Try Again Later!");
}
} else {
utility.createSnackbar(coordinatorLayout, "Error Occurred, Please Try Again Later!");
}
}
});
}
}
Related
When I manually add the objects for the data of the recycler view in the required ArrayList, it works fine, but as soon as I fetch the data from my Node JS MongoDB backend, it shows nothing.
The Java part:
Call<ArrayList<BookingDetails>> call = retrofitInterface.executeAdminPendingReq();
call.enqueue(new Callback<ArrayList<BookingDetails>>() {
#Override
public void onResponse(Call<ArrayList<BookingDetails>> call, Response<ArrayList<BookingDetails>> response) {
if(response.code() == 200){
bookingDetails = response.body();
Log.i("Something", bookingDetails.get(0).getPurpose());
Toast.makeText(PendingRequestActivity.this, "Something to show", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(PendingRequestActivity.this, "Nothing to show", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ArrayList<BookingDetails>> call, Throwable t) {
Toast.makeText(PendingRequestActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
The Node JS part:
app.post('/admin_pendingReq', async(req, res) => {
const cursor = bookingReqCollection.find({}, {projection: {_id: 0, confirm: 0}})
const results = await cursor.toArray()
if(results.length > 0){
res.status(200).send(JSON.stringify(results))
}else{
res.status(400).send()
}
})
Also, one thing to note is if I try to log the data fetched from Node in Android Studio (which is present above), it shows the required data.
EDIT: I solved the issue by setting the recycler view adapter and the layout manager inside of the onResponse method after taking it out from the onCreate method. But I would still like to know why this has happened
I am setting up my android application to use authentication and I am following the documentation on AppAuth for Android. So far I have been able to connect and make a request to the identity server and gotten back a response containing most of data I sent in my request and more. I am supposed to exchange my code for an access token. This is my problem. I'm literally copying and pasting the code on the github page,https://github.com/openid/AppAuth-Android, yet it is crashing with the above error. I am relatively new to android and this is my first question here, go easy on me if I am not presenting my question well. Thank you.
Android Studio says the code causing this error is "authService.performTokenRequest()". I have looked around and some solved it by calling "authService.dispose()" in "onDestroy()" but that also crashes with "An error occured while executing doInBackground()". Below is the code causing the error.
authService.performTokenRequest(
resp.createTokenExchangeRequest(),
new AuthorizationService.TokenResponseCallback() {
#Override public void onTokenRequestCompleted(
TokenResponse resp, AuthorizationException ex) {
if (resp != null) {
// exchange succeeded
} else {
// authorization failed, check ex for more details
}
}
});
In my "onCreate()" this is how I call it.
AuthorizationResponse resp = AuthorizationResponse.fromIntent(getIntent());
AuthorizationException ex = AuthorizationException.fromIntent(getIntent());
authState = new AuthState(resp, ex);
authorizationService = new AuthorizationService(this);
authorizationService.performTokenRequest(
resp.createTokenExchangeRequest(),
new AuthorizationService.TokenResponseCallback() {
#Override public void onTokenRequestCompleted(
TokenResponse resp, AuthorizationException ex) {
authState.update(resp, ex);
if (resp != null) {
// exchange succeeded
Log.e("authstate",authState.getAccessToken());
} else {
// authorization failed, check ex for more details
}
}
});
You can dispose your authService onDestroy().
for example you have
AuthorizationService mAuthService = new AuthorizationService(context);
#Override
protected void onDestroy() {
mAuthService.dispose();
mAuthService = null;
}
I have a user signup method that looks like this:
user.signUpInBackground(new SignUpCallback() {
public void done(ParseException e) {
if (e == null) {
// Link user to the 'User' role
ParseQuery<ParseRole> roleQuery = ParseRole.getQuery();
roleQuery.whereEqualTo("name", "User");
roleQuery.getFirstInBackground(new GetCallback<ParseRole>() {
#Override
public void done(ParseRole parseRole, ParseException e) {
if (e == null) {
//final ParseRole tempParseRl = parseRole;
ParseRelation<ParseUser> tempRel = parseRole.getUsers();
Log.i("ParseRole: ", parseRole.getName().toString());
parseRole.getUsers().add(user);
//TODO: 4. Delete reg key used for this user
}
// Error on Role ACL
else {
dMenuVerData.dismiss();
Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
//1. Hide the progress bar
dataVerPBar.setVisibility(View.GONE);
//2. Show okBtn & successTxt
okBtn.setVisibility(View.VISIBLE);
userSuccessTxt.setVisibility(View.VISIBLE);
dataVerTitle.setText("Congratulations!");
//ParseRelation<ParseUser> tempRel = new ParseRelation<ParseUser>();
//tempParseRl.put("users");
} else {
// Dismiss dialog, show Parse error
dMenuVerData.dismiss();
Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
On Parse I have two roles created on the data-browser.
After executing the line:
<role>.getUsers().add(user);
I'm expecting to be able to see the recently signed-up user under the "users , instead this table is empty:
What am I missing? Is the
<role>.getUsers().add(user);
working properly? Thanks.
The solution resides in using <role>.saveInBackground(); method after adding a new user to the ParseRelation, so after the signup succeeded I added:
...
parseRole.getUsers().add(user);
parseRole.saveInBackground();
...
I hope this helps.
you are not saving the role after you changed the users relation
what can i see that you added the user to (the local list you get from parseRole.getUsers() but you didn't saved it (to sync the changes with the remote version)
I use Retrofit library to handle JSON response from my WCF web-service.
RestService.getRestService().search("tools", new Callback<SearchResult>() {
#Override
public void success(SearchResult searchResult, Response response) {
if( searchResult == null) return;
textView.setText(searchResult.toString());
} // end of success(
#Override
public void failure(RetrofitError error) {
showToast(R.string.internet_sikintisi);
}
});
I noticed that i get error if i leave the fragment or activity where i called this function from. Because I set text in textView where the activity or fragment is not already exists.
I modified the code to be like this:
RestService.getRestService().search("tools", new Callback<SearchResult>() {
#Override
public void success(SearchResult searchResult, Response response) {
if( searchResult == null) return;
try{
textView.setText(searchResult.toString());
} catch(Exception e) {}
} // end of success(
#Override
public void failure(RetrofitError error) {
try{
showToast(R.string.internet_sikintisi);
} catch(Exception e) {}
}
});
Basically, the problem solved, the app is not crashing every time the user gets in and out immediately. Still there might be a better solution for this problem.
I checked and Retrofit DOES NOT have cancel request feature!
You need to added checks success and failure methods the same way you would when using an asynctask check if the context still exist or make a isActive field that you set on onResume/onPause of the activity or fragment.
It is possible to cancel Retrofit requests in Retrofit 2(beta is available now.)
call.cancel();// as simple as that
I'm currently building an app that has Parse at its backend. I've realized that a user can log in to his account in multiple devices. Is there a way I could restrict this to only one device at one time? If so can you kindly explain the method.
Thanks in advance.
you need to monitor the application will your web environment (webservice for example) As soon as someone you login you must disconnect the other devices connected to the same user.
You can analyze it by IMEI who made the last login request and send a command to the other devices of the same user to remove access.
I came here looking for a solution and didn't find one. Through trial and error, I have solved it. I am a novice developer and do not know if this is the best practice, but it works. When the user attempts to log in execute the following.
public void login(View v) {
// Create string variables for data.
String username = et_username.getText().toString().toLowerCase().trim();
String password = et_password.getText().toString();
// If a user is already on this device, log them out.
// this will happen when the force log out happens, the device will
// simply catch the invalid session, but still have that user data, just unavailable
user = ParseUser.getCurrentUser();
if (user != null)
user.logOutInBackground();
ParseUser.logInInBackground(username, password, new LogInCallback() {
#Override
public void done(ParseUser user, ParseException e) {
final ParseQuery<ParseObject> query = new ParseQuery<>("_Session");
query.include("user");
query.whereEqualTo("user", ParseUser.getCurrentUser()); // Only get sessions for the specific user.
query.addAscendingOrder("createdAt"); // Place them in chronological order, with the oldest at the top.
query.countInBackground(new CountCallback() {
#Override
public void done(int count, ParseException e) {
if (count > 1) {
try {
query.getFirst().deleteInBackground();
} catch (ParseException e1) {
Toast.makeText(LoginActivity.this, e1.toString(), Toast.LENGTH_LONG).show();
}
}
}
});
if (user != null) {
emailVerified = user.getBoolean("emailVerified");
if (emailVerified) {
// Update the database to track their log in.
user.put("loggedIn", true);
user.saveInBackground();
if (!deliquent) {
// Launch the MainActivity.
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
}
else {
Intent intent = new Intent(LoginActivity.this, CardActivity.class);
startActivity(intent);
}
}
else {
Toast.makeText(LoginActivity.this, getResources().getString(R.string.verify), Toast.LENGTH_SHORT).show();
}
}
else {
Toast.makeText(LoginActivity.this, "From LoginActivity line:213\n" + e.toString(), Toast.LENGTH_SHORT).show();
}
}
});
}