First of all, I am a great newbie in aAndroid development (and with a English not really perfect).
What is the simplest way to access my layout (simple TextView, here named "text") from onNewToken() method from FirebaseMessagingService class ?
I saw that we regularly talked about BroadcastReceivers but I also read that it was deprecated.
Of course I have errors with findViewById().
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onNewToken(String device_token) {
#SuppressLint("HardwareIds") String device_id = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
DeviceService service = DeviceService.retrofit.create(DeviceService.class);
Call<String> call = service.insertToken(device_token,device_id);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
text.setText("Token registered");
} else {
text.setText("Token registering error");
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
text.setText("Token not transmitted");
}
});
}
}
BroadcastReceiver is not deprecated, but LocalBroadcastManager is deprecated.
you can register receiver without LocalBroadcastManager, using Context.registerReceiver().
And put your response result into SharedPreferences, and use the value of SharedPreferences as LiveData or Flow in your Activity, Fragment or ViewModel.
Or, use event bus with RxJava or Flow whatever
Related
So, I'm trying to connect to Scryfall's API and do an autocomplete call. I've been able to use their other call properly but this one I think where I'm having issue.
Here is the call: https://api.scryfall.com/cards/autocomplete?q=fire
q is the query and it will return a list of up to 20 items that could be auto-completed with the word 'fire'.
{
"object":"catalog",
"total_values":20,
"data": [
"Fire // Ice","Fire Imp","Firefly","Fire Whip","Fire Ants","Firebolt","Fireball","Fire Drake","Fire Snake","Firespout","Firestorm","Fireblast","Fire-Field Ogre","Fire Urchin","Fire Bowman","Fire Dragon","Fire at Will","Fire Ambush","Firemaw Kavu","Fire Juggler"
]
}
I am using retrofit2 for android.
Here is some of my code.
This is my interface for the endpoints
public interface ScryfallEndPoints {
//https://api.scryfall.com/cards/named?fuzzy=
#GET("cards/named")
Call<Card> getCard(
#Query(value=("fuzzy")) String name);
//https://api.scryfall.com/cards/autocomplete?q=
#GET("cards/autocomplete")
Call<Card> getCardAutoComplete(
#Query(value=("q")) String name);
}
This is a method I use in my activity to perform the call.
private void loadCardList()
{
final ScryfallEndPoints apiService =
APIClient.getClient().create(ScryfallEndPoints.class);
Call<Map<String, String>> call = apiService.getCardAutoComplete(str);
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
call.enqueue(new Callback<Map<String, String>>()
{
#Override
public void onResponse(Call<Map<String, String>> call, Response<Map<String, String>> response)
{
Toast.makeText(SuggestionResults.this, "onResponse()", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<Map<String, String>> call, Throwable t)
{
Toast.makeText(SuggestionResults.this, "onFailure()", Toast.LENGTH_SHORT).show();
}
});
//tv.setText(str);
}
Here is a method that is part of my model class.
#SerializedName("data")
private Map<String, String> cardList;
public Map<String, String> getCardList() {return cardList;}
So, I feel like there is definitely something maybe wrong in the way I am trying to access he data in my model class and maybe with the way I have it set up in my interface. When I make the call, it doesn't fail so I don't have error logs to show, i just know that it is going to the onFailure() method and I'm not sure why. I mostly need to figure this out then I can work on getting the list to populate. Also, if there is a way that I can see more of what is going on with the calls that I am making, that would be great too. Thanks!
#GET("cards/autocomplete")
Call<Card> getCardAutoComplete(
#Query(value=("q")) String name);
}
Then the calling part
//Call<Map<String, String>> call = apiService.getCardAutoComplete(str);
//It returns Call of Card type, hence it will be as follows
Call<Card> call = apiService.getCardAutoComplete(str);
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
call.enqueue(new Callback<Card>()
{
#Override
public void onResponse(Call<Card> call, Response<Card> response)
{
Toast.makeText(SuggestionResults.this, "onResponse()", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<Card> call, Throwable t)
{
Toast.makeText(SuggestionResults.this, "onFailure()", Toast.LENGTH_SHORT).show();
}
});
I am implementing MVVM architecture pattern for app development.I have two activities MainActivity and AddUser activity.In MainActivity I am showing list of all notes and in AddUser activity I am inserting user in room database.I am doing all insertion and fetching notes operation in repository class.I am using RxJava Completable operator to insert notes in database.
What I want: After insertion it should redirect to the MainActivity.
Problem: Unable to add Intent in onComplete() method when I am using Intent it is showing error.
Error
Process: com.app.notesreactive, PID: 3970
io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with.
Below is my code:
UserDao.java
#Dao
public interface UserDao {
#Insert
void insert(User user);
#Query("SELECT * FROM Users ORDER BY id DESC")
LiveData<List<User>> getAllUsers();
}
UserRepository.java
public class UserRepository {
private UserDb userDb;
private UserDao userDao;
private LiveData<List<User>> allUsers;
private Context ctx;
public UserRepository(Application application) {
userDb = UserDb.getInstance(application);
userDao = userDb.userDao();
allUsers = userDao.getAllUsers();
ctx = application.getApplicationContext();
}
public void insert(final User user){
Completable.fromAction(() -> userDb.userDao().insert(user))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
Intent i = new Intent(ctx,MainActivity.class);
ctx.startActivity(i);
Toast.makeText(ctx,"Data inserted", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable e) {
Toast.makeText(ctx,e.getMessage(),Toast.LENGTH_SHORT).show();
}
});
}
public LiveData<List<User>> getAllUsers(){
return allUsers;
}
}
Someone please help me out how can I achieve desired result.Any help would be appreciated.
THANKS
Switching the activity from repository class we need to add the following flags to the Intent.
Here is an updated onComplete() method.
#Override
public void onComplete() {
Intent i = new Intent(ctx,MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
ctx.startActivity(i);
Toast.makeText(ctx,"Data inserted", Toast.LENGTH_SHORT).show();
}
Hopefully it will help.
THANKS
Staring an activity in a repository is not good practice. You need to start an activity within an activity. If you want to start activity with application Context you should use Intent.FLAG_ACTIVITY_NEW_TASK. Cause starting an activity from application's Context with default flags brakes the back stack of activities (hierarchy). I suggest to use observables like RxJava's Observable or LiveData from AAC to notify the activity, and start another activity on it. How to do it with LivaData?
Updated insert method as following:
public LiveData<Boolean> insert(final User user){
MutableLiveData<Boolean> isComplated = new MutableLiveData<>();
Completable.fromAction(() -> userDb.userDao().insert(user))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
isComplated.setValue(true);
Toast.makeText(ctx,"Data inserted", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable e) {
Toast.makeText(ctx,e.getMessage(),Toast.LENGTH_SHORT).show();
}
});
return isComplated;
}
Observe this in AddUser activity class:
viewModel.insert(myUser).observe(this, isComplated -> {
if (isComplated != null && isComplated) {
Intent i = new Intent(AddUser.this, MainActivity.class);
ctx.startActivity(i);
}
});
Edit:
The ViewModel class should contain following method:
public LiveData<Boolean> insert(User user) {
return userRepositoryInstance.insert(user);
}
It should help =)
I'm dealing with the following api call https://pokeapi.co/api/v2/pokemon/, the problem is that it seems quite slow and each entry has itself several endpoints. Is it possible to update ListView asynchronously while making the api calls? I feel that it will probably be something like using the endpoint's next and previous keys and triggering listener events that update the Listview from the AsyncTask's doInBackground or onProgressUpdate methods. I'd appreciate any help, I feel that I have the beginning of an idea, but I need help finishing the thought.
You can definitely implement it through AsyncTask but I would rather suggest a solution using RxJava.
You can implement RxJava Chaining.
Sharing a code snippet how you can make a chaining call using RxJava.
private void fetchHackerNewsStoriesChaining() {
StoriesApiInterface storiesApiInterface = HackerNewsApiClient.getStoriesApiInterface();
storiesApiInterface.getTopStories()
.flatMapIterable(new Function<JsonArray, Iterable<?>>() {
#Override
public Iterable<?> apply(JsonArray jsonArray) throws Exception {
Log.d("Count", ""+jsonArray.size());
return jsonArray;
}
})
.flatMap(new Function<Object, ObservableSource<SingleStoryModelResponse>>() {
#Override
public ObservableSource<SingleStoryModelResponse> apply(Object newsId) throws Exception {
return HackerNewsApiClient.getStoryDetailsApiInterface().getNewsStoryDetail(((JsonElement) newsId).getAsLong())
.subscribeOn(Schedulers.io());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<SingleStoryModelResponse>() {
#Override
public void onSubscribe(Disposable d) {
progressBar.setVisibility(View.VISIBLE);
}
#Override
public void onNext(SingleStoryModelResponse singleStoryModelResponse) {
adapterNewsList.addNewsItem(singleStoryModelResponse);
}
#Override
public void onError(Throwable e) {
Log.d("Hacker News", e.getMessage());
}
#Override
public void onComplete() {
progressBar.setVisibility(View.GONE);
}
});
}
I wanted to refactor code for simplicity and readability and that's why I want to move the code outside the class and return a result to class whenever the method is called.
Trying:
ArrayList<MovieReview> movieReview;
public ArrayList<MovieReview> getReviews(String id) {
if (NetworkUtil.isNetworkConnected(getActivity())) {
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<MovieReviewResponse> call = null;
call = apiService.getMovieReviews(id, BuildConfig.THE_MOVIE_DB_API_KEY);
call.enqueue(new Callback<MovieReviewResponse>() {
#Override
public void onResponse(Call<MovieReviewResponse> call, Response<MovieReviewResponse> response) {
movieReview= (ArrayList<MovieReview>) response.body().getMovieReviews();
}
#Override
public void onFailure(Call<MovieReviewResponse> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
}
return movieReview;
}
Output: if I used array list outside the on response gives null value.
but if I called a method from on response and pass the result movieReview, as a parameter, it works fine.
Previously used:
public void getReviews(String id) {
if (NetworkUtil.isNetworkConnected(getActivity())) {
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<MovieReviewResponse> call = null;
call = apiService.getMovieReviews(id, BuildConfig.THE_MOVIE_DB_API_KEY);
call.enqueue(new Callback<MovieReviewResponse>() {
#Override
public void onResponse(Call<MovieReviewResponse> call, Response<MovieReviewResponse> response) {
movieReview = (ArrayList<MovieReview>) response.body().getMovieReviews();
setData(movieReview);
}
#Override
public void onFailure(Call<MovieReviewResponse> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
}
}`
Output: if I used array list outside the on response gives null value
Because that is how asyncronus methods work. Your return happens before onResponse ever happens, so the list object is null.
Tip: In ideal situations, you want to always return an empty list, not null anyway.
Rename your method.
public ArrayList<MovieReview> getReviews(String id)
To Instead
public ArrayList<MovieReview> getReviews(String id, Callback<MovieReviewResponse> callback)
Replace this code
call.enqueue(new Callback<MovieReviewResponse>() {
...
});
With this
call.enqueue(callback);
Wherever you call that method
// In Activity
String id = "X";
api.getReviews(id);
Now do....
// In Activity
String id = "X";
api.getReviews(id, new Callback<MovieReviewResponse>() {
...
});
And now from within onResponse, you can update a ListView adapter, or whatever you need to do
Instead of using only Retrofit make use of RxAndroid. By using this you will get response of Observable<T> which consists of three Override methods onCompleted(), onError() and onNext().
In onNext() method call your specific activity, pass your data through putExtra and get through getExtra.
I have a Service class that I am using to make web service calls using Volley:
public class AccountService {
public static void forgotPassword(Context c, String emailAddress) {
String url = "myUrl";
JsonArrayRequest request = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
// done
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
Volley.newRequestQueue(c).add(request);
}
}
And I'm calling it from an Activity like this:
public class ForgotPasswordActivity extends AppCompatActivity implements View.OnClickListener{
private void submit() {
accountService.forgotPassword();
}
}
When the Volley request is finished, I want to update the UI in my activity. How can I do this considering it is an asynchronous request? Am I able to call a method from the AccountService class in my activity? Thanks for the help
Pass a listener object to the AccountService and then send communication success or error to the activity.
Try as per below (you should not pass the layout to the AccountService which will create some unnecessary issues)
public class ForgotPasswordActivity extends AppCompatActivity
implements View.OnClickListener,
AccountServiceCallback{
private void submit() {
AccountService.forgotPassword("email#123.com", this);
}
#Override
public void onClick(View v) {
}
#Override
public void onResponse(JSONArray response) {
// UPDATE UI as per on response requirement
}
#Override
public void onErrorResponse(VollyError error) {
// UPDATE UI as per response flow
}
}
public class AccountService {
public static void forgotPassword(Context c, String email, final
AccountServiceCallback callback) {
String url = "myUrl";
JsonArrayRequest request = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
// done
callback.onResponse(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.onErrorResponse(error);
}
});
Volley.newRequestQueue(c).add(request);
}
interface AccountServiceCallback {
public void onResponse(JSONArray response);
public void onErrorResponse(VollyError error);
}
}
You will have to find a way to get the result of your background work back onto the main thread so it can make changes to the view hierarchy. There are a LOT of ways to do this, and most of them are not really good.
But before you even do that, there are problems with your code. First, if you are thinking you made an Android Service component, you didn't actually do that. You made a class with the name AccountService, which does nothing special in the Android world.
Second, your call of forgotPassword() passes no arguments, but your definition of forgotPassword() in AccountService has a completely different signature. This wouldn't compile.
You should probably start learning about Android-specific asynchronous programming patterns and develop a strategy for solving your problem before writing code, because you'll almost certainly do it wrong without understanding what you're doing first.