How can I change text "Default Media Receiver" on Remote Player - java

I use MediaRouter to connect to remote players.Not using Google Cast. I have problem to customize what users see on e.g. TV. ,mainly text : Default Media Receiver.
HOW CAN I CHANGE IT ?
Here is code I have:
In PlayerActivity (MediaRouter Callback, OnCreate method and DiscoveryFragment class):
public static androidx.mediarouter.media.MediaRouter mMediaRouter;
private final androidx.mediarouter.media.MediaRouter.Callback mMediaRouterCB = new androidx.mediarouter.media.MediaRouter.Callback() {
#Override
public void onRouteSelected(#NonNull androidx.mediarouter.media.MediaRouter router, #NonNull androidx.mediarouter.media.MediaRouter.RouteInfo route, int reason) {
super.onRouteSelected(router, route, reason);
if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){
mRoute = route;
isSetRemotePlayer = true;
if (route.isDefault()){
isSetRemotePlayer = false;
}
mMediaRouter.getInstance(PlayerActivity.this);
mMediaRouter.addProvider(new SampleMediaRouteProvider(PlayerActivity.this));
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get the media router service.
mMediaRouter = androidx.mediarouter.media.MediaRouter.getInstance(this);
mMediaRouter.setRouterParams(new MediaRouterParams.Builder().setTransferToLocalEnabled(true).build());
// Create a route selector for the type of routes that we care about.
mSelector =
new MediaRouteSelector.Builder().addControlCategory(MediaControlIntent
.CATEGORY_REMOTE_PLAYBACK).addControlCategory(MediaControlIntent
.CATEGORY_LIVE_AUDIO).addControlCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE)
.build();
// Add a fragment to take care of media route discovery.
FragmentManager fm = getSupportFragmentManager();
fs = new ForegroundService();
DiscoveryFragment fragment =
(PlayerActivity.DiscoveryFragment) fm.findFragmentByTag(DISCOVERY_FRAGMENT_TAG);
if (fragment == null) {
fragment = new DiscoveryFragment();
fragment.setCallback(mMediaRouterCB);
fragment.setRouteSelector(mSelector);
fm.beginTransaction().add(fragment, DISCOVERY_FRAGMENT_TAG).commit();
} else {
fragment.setCallback(mMediaRouterCB);
fragment.setRouteSelector(mSelector);
}
}
public static final class DiscoveryFragment extends MediaRouteDiscoveryFragment {
private static final String TAG = "DiscoveryFragment";
private androidx.mediarouter.media.MediaRouter.Callback mCallback;
public DiscoveryFragment() {
mCallback = null;
}
public void setCallback(androidx.mediarouter.media.MediaRouter.Callback cb) {
mCallback = cb;
}
#Override
public androidx.mediarouter.media.MediaRouter.Callback onCreateCallback() {
return mCallback;
}
#Override
public int onPrepareCallbackFlags() {
// Add the CALLBACK_FLAG_UNFILTERED_EVENTS flag to ensure that we will
// observe and log all route events including those that are for routes
// that do not match our selector. This is only for demonstration purposes
// and should not be needed by most applications.
return super.onPrepareCallbackFlags() | androidx.mediarouter.media.MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS;
}
}
SampleRouteProvider is class provided by android pages(https://developer.android.com/guide/topics/media/mediarouter) and on bottom is Sample (https://github.com/android/media-samples/tree/main/MediaRouter/):
MANY THANKS

Related

Firebase Database query with LiveData and ViewModel returns nothing altough data exists

I have an Android Fragment (in Java) that displays recyclerview. Now I would like to read the data for the items in the recyclerview from a firebase database. They should be stored into an array list orderList. For this purpose, I would like to use LiveData and a ViewModel because I read several times that this is the recommended way of implementing it. Further, I would like the Fragment to update the recyclerview automatically whenever new data is stored in the firebase database.
I tried to follow the steps that are described in the offical Firebase Blog (https://firebase.googleblog.com/2017/12/using-android-architecture-components.html) but unfortunately the result is always an empty list. No elements are being displayed altough there are some relevant entries in the database that should be returned and displayed. The implementation of the recyclerview itself is correct (I checked that by manually adding items into the recylcerview).
Here is my Fragment that holds the recyclerview:
public class FR_Orders extends Fragment {
private FragmentOrdersBinding binding;
//Define variables for the RecyclerView
private RecyclerView recyclerView_Order;
private RV_Adapter_Orders adapter_Order;
private RecyclerView.LayoutManager layoutManager_Order;
private ArrayList<RV_Item_Order> orderList;
public FR_Orders() {
// Required empty public constructor
}
public static FR_Orders newInstance(String param1, String param2) {
FR_Orders fragment = new FR_Orders();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
orderList = new ArrayList<RV_Item_Order>();
// Obtain a new or prior instance of ViewModel_FR_Orders from the ViewModelProviders utility class.
ViewModel_FR_Orders viewModel = new ViewModelProvider(this).get(ViewModel_FR_Orders.class);
LiveData<DataSnapshot> liveData = viewModel.getDataSnapshotLiveData();
liveData.observe(this, new Observer<DataSnapshot>() {
#Override
public void onChanged(#Nullable DataSnapshot dataSnapshot) {
for(DataSnapshot ds: dataSnapshot.getChildren()) {
if (dataSnapshot != null) {
String drinkName = "";
String drinkSize = "";
String orderDate = "";
String orderStatus = "";
int orderDateInMilliseconds = 0;
int orderID = 0;
int quantity = 0;
int tableNumber = 0;
// update the UI here with values in the snapshot
if( dataSnapshot.child("drinkName").getValue(String.class)!=null) {
drinkName = dataSnapshot.child("drinkName").getValue(String.class);
}
if(dataSnapshot.child("drinkSize").getValue(String.class)!=null) {
drinkSize = dataSnapshot.child("drinkSize").getValue(String.class);
}
if(dataSnapshot.child("orderDate").getValue(String.class)!=null) {
orderDate = dataSnapshot.child("orderDate").getValue(String.class);
}
if(dataSnapshot.child("orderStatus").getValue(String.class)!=null) {
orderStatus = dataSnapshot.child("orderStatus").getValue(String.class);
}
if(dataSnapshot.child("orderDateInMilliseconds").getValue(Integer.class)!=null) {
orderDateInMilliseconds = dataSnapshot.child("orderDateInMilliseconds").getValue(Integer.class);
}
if(dataSnapshot.child("quantity").getValue(Integer.class)!=null) {
quantity = dataSnapshot.child("quantity").getValue(Integer.class);
}
if(dataSnapshot.child("orderID").getValue(Integer.class)!=null) {
orderID = dataSnapshot.child("orderID").getValue(Integer.class);
}
if(dataSnapshot.child("tableNumber").getValue(Integer.class)!=null) {
tableNumber = dataSnapshot.child("tableNumber").getValue(Integer.class);
}
orderList.add(new RV_Item_Order(drinkName, drinkSize, orderDateInMilliseconds, orderDate, tableNumber, orderStatus, quantity, orderID));
;
}
}
}
});
}// end onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentOrdersBinding.inflate(inflater, container, false);
buildRecyclerView();
return binding.getRoot();
}
public void buildRecyclerView() {
recyclerView_Order = binding.rvDrinksToBeDisplayed;
recyclerView_Order.setHasFixedSize(true);
layoutManager_Order = new LinearLayoutManager(this.getContext());
adapter_Order = new RV_Adapter_Orders(orderList);
recyclerView_Order.setLayoutManager(layoutManager_Order);
recyclerView_Order.setAdapter(adapter_Order);
adapter_Order.setOnItemClickListener(new RV_Adapter_Orders.OnItemClickListener() {
/*
Define what happens when clicking on an item in the RecyclerView
*/
#Override
public void onItemClick(int position) {
}
});
}//end build recyclerView
}//End class
Here is the LiveData class
public class LiveData_FirebaseOrder extends LiveData<DataSnapshot> {
private static final String LOG_TAG = "LiveData_FirebaseOrder";
private final Query query;
private final MyValueEventListener listener = new MyValueEventListener();
public LiveData_FirebaseOrder(Query query) {
this.query = query;
}
public LiveData_FirebaseOrder(DatabaseReference ref) {
this.query = ref;
}
#Override
protected void onActive() {
Log.d(LOG_TAG, "onActive");
query.addValueEventListener(listener);
}
#Override
protected void onInactive() {
Log.d(LOG_TAG, "onInactive");
query.removeEventListener(listener);
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(LOG_TAG, "Can't listen to query " + query, databaseError.toException());
}
}
}
Here is the ViewModel class (the full name of the database is not given because of privacy issues).
public class ViewModel_FR_Orders extends ViewModel {
private static final DatabaseReference ORDERS_REF =
FirebaseDatabase.getInstance("https://....firebasedatabase.app").getReference("/Orders");
private final LiveData_FirebaseOrder liveData = new LiveData_FirebaseOrder(ORDERS_REF);
#NonNull
public LiveData<DataSnapshot> getDataSnapshotLiveData() {
return liveData;
}
}
And here is a screenshot of the Firebase Database that shows that there are some entries in the node Orders that should be returned
Does anyone have an idea what I am making wrong? I tried to stick exactly to the instructions of the offical Firebase Blog.
Update: I found out that the query itself returns the correct datasnapshots but the recyclerview is not built and updated.

How to reset MutableLiveData list to display new data when using setOnClickListner?

So I have been trying for quite some time to add a search functionality for my app using TMDB API.
What I have managed to do so far is to be able to search for a movie only the first time, so here's the problem when I am trying to search for a movie for the second time it will keep showing me the old movie list from the first search results, never updating the list accordingly to the search.
For example, i searched for a spiderman movie, it will show me every movie that has the keyword spiderman in it, and then when i try to search again for a different movie with a different keyword it will keep showing me the results from spiderman search, so is there any way to reset MutableLiveData list on every setOnClickListner, so I ll be able to search and display new movies on every search?.
Thank you in advance.
Here are the classes:
public interface SearchMovieService {
#GET("search/movie?api_key=&language=en-US&page=1&include_adult=false")
Call<MovieResponse> getMoviesWithSearching(#Query("query") String query);
}
public class SearchAPIManager {
Retrofit retrofit =
new Retrofit
.Builder()
.baseUrl("https://api.themoviedb.org/3/")
.addConverterFactory(GsonConverterFactory.create())
.build();
private final SearchMovieService searchMovieService = retrofit.create(SearchMovieService.class);
public void getMoviesWithSearching(MutableLiveData<List<Movie>> moviesLiveData, String query){
Call<MovieResponse> movieHTTPRequest = searchMovieService.getMoviesWithSearching(query);
movieHTTPRequest.enqueue(new Callback<MovieResponse>() {
#Override
public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
MovieResponse movieResponse = response.body();
if (movieResponse != null) {
ArrayList<Movie> movies = movieResponse.getMovies();
moviesLiveData.postValue(movies);
}
}
#Override
public void onFailure(Call<MovieResponse> call, Throwable t) {
t.getMessage();
}
});
}
}
public class SearchMovieViewModel extends ViewModel {
private final MutableLiveData<List<Genre>> genresNames = new MutableLiveData<>();
private final MutableLiveData<List<Movie>> moviesSearching = new MutableLiveData<>();
public SearchMovieViewModel() {
GenreAPIManager genreManager = new GenreAPIManager();
genreManager.getGenreNames(genresNames);
SearchAPIManager searchManager = new SearchAPIManager();
searchManager.getMoviesWithSearching(moviesSearching, Constants.MOVIE_SEARCH);
}
public MutableLiveData<List<Genre>> getGenresNames() {
return genresNames;
}
public MutableLiveData<List<Movie>> getMoviesWithSearching() {
return moviesSearching;
}
}
public class SearchMovieFragment extends Fragment {
SearchMovieAdapter adapter;
private EditText etSearch;
private ImageButton ibSearch;
private RecyclerView rvMovieSearch;
private SearchMovieViewModel searchMovieViewModel;
public static SearchMovieFragment newInstance() {
return new SearchMovieFragment();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.search_movie_fragment, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
etSearch = view.findViewById(R.id.etSearch);
ibSearch = view.findViewById(R.id.ibSearch);
rvMovieSearch = view.findViewById(R.id.rvMovieSearch);
rvMovieSearch.setLayoutManager(new VegaLayoutManager());
ibSearch.setOnClickListener(v -> {
if (etSearch.getText().toString().equals("")) {
etSearch.setError("Type Something");
return;
}
Constants.MOVIE_SEARCH = etSearch.getText().toString();
searchMovieViewModel = new ViewModelProvider(this).get(SearchMovieViewModel.class);
searchMovieViewModel.getMoviesWithSearching().observe(getViewLifecycleOwner(), (movies -> {
searchMovieViewModel.getGenresNames().observe(getViewLifecycleOwner(), genres -> {
adapter = new SearchMovieAdapter(movies, genres);
rvMovieSearch.setAdapter(adapter);
rvMovieSearch.scheduleLayoutAnimation();
});
}));
});
}
}
You need to change the page number from the query so that you can get more data from the API
Here you return only page=1 for every request
public interface SearchMovieService {
#GET("search/movie?api_key=&language=en-US&page=1&include_adult=false")
Call<MovieResponse> getMoviesWithSearching(#Query("query") String query);
}
But you can make the page as a query parameter
public interface SearchMovieService {
#GET("search/movie?api_key=&language=en-US&include_adult=false")
Call<MovieResponse> getMoviesWithSearching(#Query("query") String query,
(#Query("page") long page);
}
And apply that when you request the service in activity:
long page = 1;
Call<MovieResponse> movieHTTPRequest = searchMovieService.getMoviesWithSearching(query, page);
Now you can increment the page value whenever you want to get new list of data.
UPDATE:
let's say i searched for a spiderman movie, it will show me every movie that has the keyword spiderman in it, and then when i try to search again for a different movie with a different keyword it will keep showing the results from spiderman search
The MutableLiveData didn't actually being triggered for the new query, so its data doesn't change, you need to enqueue the data again in order to make the MutableLiveData updates the underlying data that being observed.
Notice that the ViewModel constructor won't get called again whenever you make a new query, so you need to add searchManager.getMoviesWithSearching() to somewhere else.
To to that you need to add a method in the ViewModel that renew the search
public class SearchMovieViewModel extends ViewModel {
private final MutableLiveData<List<Genre>> genresNames = new MutableLiveData<>();
private final MutableLiveData<List<Movie>> moviesSearching = new MutableLiveData<>();
public SearchMovieViewModel() {
GenreAPIManager genreManager = new GenreAPIManager();
genreManager.getGenreNames(genresNames);
SearchAPIManager searchManager = new SearchAPIManager();
searchManager.getMoviesWithSearching(moviesSearching, Constants.MOVIE_SEARCH);
}
public MutableLiveData<List<Genre>> getGenresNames() {
return genresNames;
}
public MutableLiveData<List<Movie>> getMoviesWithSearching() {
return moviesSearching;
}
public void updateMoviesWithSearching() {
searchManager.getMoviesWithSearching(moviesSearching, Constants.MOVIE_SEARCH);
}
}
And then call updateMoviesWithSearching() whenever you want to update the list. also make sure that you update Constants.MOVIE_SEARCH with the query
And whenever you make a new query: call updateMoviesWithSearching()
ibSearch.setOnClickListener(v -> {
if (etSearch.getText().toString().equals("")) {
etSearch.setError("Type Something");
return;
}
Constants.MOVIE_SEARCH = etSearch.getText().toString();
searchMovieViewModel = new ViewModelProvider(this).get(SearchMovieViewModel.class);
searchMovieViewModel.updateMoviesWithSearching();
searchMovieViewModel.getMoviesWithSearching().observe(getViewLifecycleOwner(), (movies -> {
searchMovieViewModel.getGenresNames().observe(getViewLifecycleOwner(), genres -> {
adapter = new SearchMovieAdapter(movies, genres);
rvMovieSearch.setAdapter(adapter);
rvMovieSearch.scheduleLayoutAnimation();
});
}));
});

Call non-static method in Static method JAVA

EDITED:
The real purpose of that is to have one activity and on class who fetch data and render it to the activity.
The problem is I have dropdown menu. When I clicked on an item of the menu it change my url but it does not load or fetch my data to the activity but when i clicked again it works but with the paramaters selected just before and if I clicked again it still works but still with the previous elements selected.
My "teacher" said I have to call build into my callback method.
But it doesen't work at all. And I still didn't find any solution :/.
As you recommended I changed everything for non-static method
I thought why not saving an history of the dropdown, compare the current value with the saved value and if it's diffrent it means it was changed so reload the app to make new fetch and displyed new data.
But I got :
Here my all code
PhotosActivity
public class PhotosActivity extends AppCompatActivity {
// Local variable
private OkHttpClient httpClient;
private ImageButton home_btn;
private ImageButton favorites_btn;
private ImageButton search_btn;
private ImageButton profil_btn;
// Constante variable
private static final String TAG = "PhotoActivity";
private static final String clientId = "bb0c749c6403fd2";
// Private shared variable
private static List<Photo> mPhotos;
private static JSONArray mItems;
private static String mAccessToken;
private static String userID;
static Activity activity;
// Shared variable
private static String selectedItem;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photos);
this.home_btn = findViewById(R.id.home_button);
this.favorites_btn = findViewById(R.id.favorites_button);
this.search_btn = findViewById(R.id.search_button);
this.profil_btn = findViewById(R.id.profil_button);
final HttpHandler httpHandler = new HttpHandler();
httpHandler.fetchData();
build();
activity = this;
Spinner spinner=(Spinner)findViewById(R.id.spinner);
String[] filters=getResources().getStringArray(R.array.filters);
ArrayAdapter<String> adapter=new ArrayAdapter<String>(this,R.layout.spinner,R.id.text, filters);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
selectedItem = parent.getItemAtPosition(position).toString();
// httpHandler.fetchData();
// build();
}
public void onNothingSelected(AdapterView<?> parent)
{
Log.d("TAG", "Error");
}
});
home_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent next_activity = new Intent(getApplicationContext(), PhotosActivity.class);
finish();
startActivity(next_activity);
}
});
favorites_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent next_activity = new Intent(getApplicationContext(), FavoriteActivity.class);
finish();
startActivity(next_activity);
}
});
search_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent next_activity = new Intent(getApplicationContext(), SearchActivity.class);
finish();
startActivity(next_activity);
}
});
profil_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent next_activity = new Intent(getApplicationContext(), ProfileActivity.class);
finish();
startActivity(next_activity);
}
});
}
public void Filters() {
String hSection;
String hSort;
String hShowV;
hSection = HttpHandler.section ;
hSort = HttpHandler.sort;
hShowV = HttpHandler.showV;
Intent next_activity = new Intent(getApplicationContext(), FavoriteActivity.class);
if(selectedItem != null) {
if (selectedItem.equals("Most Viral")) {
HttpHandler.sort = "viral/";
HttpHandler.section = "hot/";
if ( (!HttpHandler.sort.equals(hSort)) || (!HttpHandler.section.equals(hSection))) {
Log.d("TAG", "most: "+HttpHandler.section);
Log.d("TAG", "H most: "+hSection);
// activity.recreate();
// onRestart();
finish();
startActivity(next_activity);
}
} else if (selectedItem.equals("Newest")) {
HttpHandler.section = "top/";
HttpHandler.sort = "time/";
if ( (!HttpHandler.sort.equals(hSort)) || (!HttpHandler.section.equals(hSection))) {
Log.d("TAG", "new: "+HttpHandler.section);
Log.d("TAG", "H new: "+hSection);
finish();
startActivity(next_activity);
// activity.recreate();
// onRestart();
}
} else if (selectedItem.equals("Rising")) {
HttpHandler.section = "user/";
HttpHandler.sort = "rising/";
HttpHandler.showV = "?showViral=false";
if ( (!HttpHandler.sort.equals(hSort)) || (!HttpHandler.section.equals(hSection))) {
Log.d("TAG", "rising: "+HttpHandler.section);
Log.d("TAG", "H rising: "+hSection);
// onRestart();
// activity.recreate();
finish();
startActivity(next_activity);
}
} else {
Log.d(TAG, "Might be a problem");
}
} else {
activity.recreate();
}
}
public void build () {
try {
for(int i = 0; i < mItems.length(); i++) {
JSONObject item = mItems.getJSONObject(i);
Photo photo = new Photo();
if(item.getBoolean("is_album")) {
photo.id = item.getString("cover");
} else {
photo.id = item.getString("id");
}
photo.title = item.getString("title");
mPhotos.add(photo);
runOnUiThread(new Runnable() {
#Override
public void run() {
render(mPhotos);
}
});
}
} catch (Exception e) {
Log.e("JSONerr" , "Something went wrong.");
}
}
private static class PhotoVH extends RecyclerView.ViewHolder {
ImageView photo;
TextView title;
public PhotoVH(View itemView) {
super(itemView);
}
}
private void render(final List<Photo> photos) {
RecyclerView rv = (RecyclerView)findViewById(R.id.rv_of_photos);
rv.setLayoutManager(new LinearLayoutManager(this));
RecyclerView.Adapter<PhotoVH> adapter = new RecyclerView.Adapter<PhotoVH>() {
#NonNull
#Override
public PhotoVH onCreateViewHolder(ViewGroup parent, int viewType) {
PhotoVH vh = new PhotoVH(getLayoutInflater().inflate(R.layout.item, null));
vh.photo = (ImageView) vh.itemView.findViewById(R.id.photo);
vh.title = (TextView) vh.itemView.findViewById(R.id.title);
return vh;
}
#Override
public void onBindViewHolder(PhotoVH holder, int position) {
Picasso.with(PhotosActivity.this).load("https://i.imgur.com/" +
photos.get(position).id + ".jpg").into(holder.photo);
holder.title.setText(photos.get(position).title);
}
#Override
public int getItemCount() {
return photos.size();
}
};
rv.setAdapter(adapter);
}
public static void getUserID(String UserID) {
Log.d("TAG", UserID);
userID = UserID;
}
public void callBackPhoto( List<Photo> photos, JSONArray items)
{
mPhotos = photos;
mItems = items;
// build();
}
}
HttpHandler
public class HttpHandler {
private static final String TAG = "HttpHandler";
private static String clientId = "bb0c749c6403fd2";
private static OkHttpClient httpClient;
private static String mAccessToken;
// URL BUILDER VARIABLES
public static String section = "hot/";
public static String sort = "viral/";
public static String page;
public static String showV;
public static String mUrl;
public void fetchData() {
httpClient = new OkHttpClient.Builder().build();
photosActivity.Filters();
mUrl = "https://api.imgur.com/3/gallery/" + section + sort;
// Log.d("TAG", "Sort: " + sort + ": URl is" + mUrl);
Request request = new Request.Builder()
.url(mUrl + "0.json" + showV)
.addHeader("Authorization", "Client-ID " + clientId)
.header("User-Agent", "epicture")
.build();
httpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "An error has occurred " + e);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
JSONObject data = new JSONObject(response.body().string());
JSONArray items = data.getJSONArray("data");
final List<Photo> photos = new ArrayList<Photo>();
photosActivity.callBackPhoto(photos, items);
} catch (Exception e) {
Log.e("JSONerr", "Something went wrong.");
}
}
});
}
public static void getLoginData(String accessToken) {
mAccessToken = accessToken;
}
}
It doesn't look like making sense to declare callBackPhoto as a static method. If you have put static keyword accidentally in its declaration, simply remove it to solve your problem i.e. replace
public static void callBackPhoto( List<Photo> photos, JSONArray items)
with
public void callBackPhoto( List<Photo> photos, JSONArray items)
Note that there is no way to call a non-static method from a static one directly (i.e. without calling it on an object/instance). Thus, if for any reason, you can't remove static keyword from the declaration of callBackPhoto, you are left with only two options:
Declare build too as static
Call build on an object/instance e.g. new PhotosActivity().build()
Though any of these two options will solve your problem, I don't think this is how you intend to design your class.
In java, a static method belongs to EVERY object of the class that defines it. Therefore, you can call it from the parent class without creating an object like so:
ParentClass.myMethod;
However, this is not the case the case with instance (non-static) methods. To call this type of method, you need to define it in a class, create an object from that class, and then call it from that object, like this:
//inside ParentClass definition
public void myMethod(){bla bla;}
//In some other class
ParentClass obj = new ParentClass;
obj.myMethod;
Suppose you have code calling a static member of a class without creating an instance of that class. If that method contained a non-static method, there would be no object in memory to call it on. This is why it isn't possible.
Static
The static methods are alive all the time. They live from the class is loaded. They don't need objects to live. I think of them as not really belonging to the class, but the class is just a nice way to organize those methods (the same for variables). The methods could be put in any other class definition and it would still work. But organizing them in classes where they will be used make it easy to prevent access from other parts of the program, like other objects or other static methods. They are called class methods or class variables.
Instance
The non-static "stuff" does not live unless there is an object. That's why you cannot call below methodOne or methodTwo from the static methods. You have to create an object first. They are called instance methods or instance variables, because you need an instance of an object to call them.
Error
error: non-static method <methodname> cannot be referenced from a static context basically means "There's no object"
Example
public class StackOverflowTest {
public static void main(String[] args) { // this is just another static method
// methodOne(); // error. There's no object
StackOverflowTest test = new StackOverflowTest();
test.methodOne(); // method called on object.
}
// methods live outside objects
static void staticMethodOne() {
System.out.println("staticMethodOne");
staticMethodTwo(); // no object required.
}
static void staticMethodTwo() {
System.out.println("staticMethodTwo");
// methodTwo(); // error. There's no object
}
// methods that only live inside objects
void methodOne() { // method can only be invoked if there's an object.
System.out.println("methodOne");
methodTwo(); // no problem. Already inside the object.
}
void methodTwo() {
System.out.println("methodTwo");
staticMethodTwo(); // no problem. Objects can access static methods.
}
}
Your case
So you either have to create a PhotosActivity object inside your build(), or you have to make callBackPhoto a static method. I can't see what your render does, but it's the same principle.

Splash Screen using AsyncTask

I have a splash screen using AsyncTask, it will download some data from database and store the data in ArrayList. This ArrayList will be used for RecyclerView in fragments of MainActivity.class.
The problem is when I run the app from Android Studio to my phone, everything works perfectly. But, when I destroy the app and run it manually from my phone it will display blank white screen and then it will crash. And if I run once again after it crashed, the app will work. So, this app will always work only if I run it from Android Studio or after it crashed.
The error says that it is caused by the empty list. If I'm not mistaken, I think the AsyncTask doesn't seem to work properly after the activity is destroyed. But I don't know how to fix it. Please help me to solve this problem.
SplashScreen.java
public class SplashScreenActivity extends AppCompatActivity {
public static Event event;
private static List<Feed> feedList;
private static List<Event> eventList;
private static List<Event> todayList;
private static List<Event> upcomingList;
private static List<Partner> partnerList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splashscreen);
Time today = new Time(Time.getCurrentTimezone());
today.setToNow();
Config.TODAY_DATE = String.valueOf(today.monthDay) + "-" + String.valueOf(today.month) + "-" + String.valueOf(today.year);
new DownloadData().execute("");
}
class DownloadData extends AsyncTask<String, Integer, String>
{
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
startActivity(new Intent(getBaseContext(), WelcomeActivity.class));
finish();
}
#Override
protected String doInBackground(String... params) {
RequestHandler rh = new RequestHandler();
String JSON_STRING = rh.sendGetRequest(Config.URL_GET_ALL_DATA);
JSONObject jsonObject;
eventList = new ArrayList<>();
todayList = new ArrayList<>();
upcomingList = new ArrayList<>();
partnerList = new ArrayList<>();
feedList = new ArrayList<>();
try {
jsonObject = new JSONObject(JSON_STRING);
JSONArray getEvent = jsonObject.getJSONArray(Config.TAG_JSON_EVENT);
for (int i = 0; i < getEvent.length(); i++) {
int id = getEvent.getJSONObject(i).getInt(Config.TAG_ID);
int eoId = getEvent.getJSONObject(i).getInt(Config.TAG_EO_ID);
String eoName = getEvent.getJSONObject(i).getString(Config.TAG_EO_NAME);
String title = getEvent.getJSONObject(i).getString(Config.TAG_TITLE);
String day = getEvent.getJSONObject(i).getString(Config.TAG_DAY);
String date = getEvent.getJSONObject(i).getString(Config.TAG_DATE);
int price = getEvent.getJSONObject(i).getInt(Config.TAG_PRICE);
event = new Event(id, eoId, eoName, title, day, date, price);
eventList.add(event);
if(Config.TODAY_DATE.equals(event.getDate())){
todayList.add(event);
} else {
upcomingList.add(event);
}
}
JSONArray getPartner = jsonObject.getJSONArray(Config.TAG_JSON_PARTNER);
for (int i = 0; i < getPartner.length(); i++) {
int pId = getPartner.getJSONObject(i).getInt(Config.TAG_ID);
String pName = getPartner.getJSONObject(i).getString(Config.TAG_NAME);
String pEmail = getPartner.getJSONObject(i).getString(Config.TAG_EMAIL);
String pPhone = getPartner.getJSONObject(i).getString(Config.TAG_PHONE);
String pPhoto = getPartner.getJSONObject(i).getString(Config.TAG_PHOTO_URL);
Partner partner = new Partner(pId, pName, pEmail, pPhone, pPhoto);
partnerList.add(partner);
}
JSONArray getArticle = jsonObject.getJSONArray(Config.TAG_JSON_ARTICLE);
for (int i = 0; i < getArticle.length(); i++) {
int feedId = getArticle.getJSONObject(i).getInt(Config.TAG_ID);
String feedAuthor = getArticle.getJSONObject(i).getString(Config.TAG_FEED_AUTHOR);
String feedTitle = getArticle.getJSONObject(i).getString(Config.TAG_FEED_TITLE);
String feedContent = getArticle.getJSONObject(i).getString(Config.TAG_FEED_CONTENT);
String feedDate = getArticle.getJSONObject(i).getString(Config.TAG_FEED_DATE);
String feedThumbnail = getArticle.getJSONObject(i).getString(Config.TAG_FEED_THUMBNAIL);
Feed feed = new Feed(feedId, feedAuthor, feedTitle, feedContent, feedDate, feedThumbnail);
feedList.add(feed);
}
} catch (JSONException e) {
e.printStackTrace();
}
return JSON_STRING;
}
}
public static List<Feed> getFeedList(){ return feedList;}
public static List<Event> getEventList() {return eventList;}
public static List<Event> getTodayList() { return todayList;}
public static List<Event> getUpcomingList() { return upcomingList;}
public static List<Partner> getPartnerList() {return partnerList;}
}
DiscoverFragment.java
public class DiscoverFragment extends Fragment implements ViewPager.OnPageChangeListener, View.OnClickListener {
protected View view;
private LinearLayout pager_indicator;
private int dotsCount;
private ImageView[] dots;
private List<Feed> feedList;
private List<Event> eventList;
private List<Partner> partnerList;
public DiscoverFragment() {}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_discover, container, false);
RecyclerView recyclerViewEvent = (RecyclerView) view.findViewById(R.id.discover_event_recycler_view);
RecyclerView recyclerViewPartner = (RecyclerView) view.findViewById(R.id.discover_partner_recycler_view);
ClickableViewPager intro_images = (ClickableViewPager) view.findViewById(R.id.pager_introduction);
pager_indicator = (LinearLayout) view.findViewById(R.id.viewPagerCountDots);
eventList = SplashScreenActivity.getEventList();
partnerList = SplashScreenActivity.getPartnerList();
feedList = SplashScreenActivity.getFeedList();
EventAdapter eventAdapter = new EventAdapter(getContext(), eventList);
DiscoverPartnerAdapter discoverPartnerAdapter = new DiscoverPartnerAdapter(getContext(), partnerList);
DiscoverFeedAdapter mAdapter = new DiscoverFeedAdapter(getContext(), feedList);
final LinearLayoutManager layoutManagerEvent = new LinearLayoutManager(getContext());
final LinearLayoutManager layoutManagerPartner = new LinearLayoutManager(getContext());
layoutManagerEvent.setOrientation(LinearLayoutManager.HORIZONTAL);
layoutManagerPartner.setOrientation(LinearLayoutManager.HORIZONTAL);
addBottomDots(0);
intro_images.setAdapter(mAdapter);
intro_images.setCurrentItem(0);
intro_images.addOnPageChangeListener(this);
intro_images.setOnItemClickListener(new ClickableViewPager.OnItemClickListener() {
#Override
public void onItemClick(int position) {
Config.FEED_ID = position;
startActivity(new Intent(getContext(), ArticleActivity.class));
}
});
return view;
}
private void addBottomDots(int currentPage) {
dots = new ImageView[feedList.size()]; //the problem
pager_indicator.removeAllViews();
for (int i = 0; i < dots.length; i++) {
dots[i] = new ImageView(getContext());
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(4, 0, 4, 0);
pager_indicator.addView(dots[i], params);
}
if (dots.length > 0)
dots[currentPage].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
}
#Override
public void onClick(View v) {
switch (v.getId()) {
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
addBottomDots(position);
for (int i = 0; i < dotsCount; i++) {
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
}
dots[position].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
}
}
LogCat
01-29 00:40:57.565 32535-32535/com.irmaelita.esodiaapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.irmaelita.esodiaapp, PID: 32535
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
at com.irmaelita.esodiaapp.fragment.DiscoverFragment.addBottomDots(DiscoverFragment.java:181)
at com.irmaelita.esodiaapp.fragment.DiscoverFragment.onCreateView(DiscoverFragment.java:158)
feedList is null. You create feedList instance when DownloadData task is executed. But you call feedList.size() in addBottomDots when fragment view should be created. So, most probably addBottomDots is called before DownloadData task is executed. You need to fix it.
The feedlist in your discover fragment is going empty while initializing. Please set a null check before doing so.It not about running from android studio.If I have understood it correctly you are trying to access a list from splasScreen activity after finishing it. ie in post execute you finish the current activity and the fragment is in main activity,so the list is going null.So if this is the case (and please correct me if not) then either download the data somewhere centrally or best way send it to main activity with intent and use it there. Also when running from android studio kill the app manually and run it again,while the phone is connected and see if it crashes in current scenario.
Send your data from doInBackground to MainActivity with sendBroadcast
Add broadcast method in DownloadData class
private void broadcast(SplashParcel parcel) {
Intent i = new Intent("splash_parcel");
i.putExtra("values", parcel);
sendBroadcast(i);
}
#Override
protected String doInBackground(String... params) {
// your code
// ..
try {
// your code
// ..
// send splashParcel to MainActivity
SplashParcel splashParcel = new SplashParcel(feedList, eventList, todayList, upcomingList, partnerList);
broadcast (splashParcel);
} catch (JSONException e) {
e.printStackTrace();
}
return JSON_STRING;
}
Add new class SplashParcel.java
public class SplashParcel implements Parcelable {
public static final Creator<SplashParcel> CREATOR = new Creator<SplashParcel>() {
#Override
public SplashParcel createFromParcel(Parcel in) {
return new SplashParcel(in);
}
#Override
public SplashParcel[] newArray(int size) {
return new SplashParcel[size];
}
};
private static List<Feed> _feedList;
private static List<Event> _eventList;
private static List<Event> _todayList;
private static List<Event> _upcomingList;
private static List<Partner> _partnerList;
protected SplashParcel(Parcel in) {
_feedList = new ArrayList<Feed>();
in.readList(_feedList, null);
_eventList = new ArrayList<Event>();
in.readList(_eventList, null);
_todayList = new ArrayList<Event>();
in.readList(_todayList, null);
_upcomingList = new ArrayList<Event>();
in.readList(_upcomingList, null);
_partnerList = new ArrayList<Partner>();
in.readList(_partnerList, null);
}
public SplashParcel(List<Feed> feedList, List<Event> eventList, List<Event> todayList, List<Event> upcomingList, List<Partner> partnerList) {
_feedList = feedList;
_eventList = eventList;
_todayList = todayList;
_upcomingList = upcomingList;
_partnerList = partnerList;
}
public SplashParcel() {
}
public List<Feed> getFeedList() {
return _feedList;
}
public void setFeedList(List<Feed> feedList) {
_feedList = feedList;
}
public List<Event> getEventList() {
return _eventList;
}
public void setEventList(List<Event> eventList) {
_eventList = eventList;
}
public List<Event> getTodayList() {
return _todayList;
}
public void setTodayList(List<Event> todayList) {
_todayList = todayList;
}
public List<Event> getUpcomingList() {
return _upcomingList;
}
public void setUpcomingList(List<Event> upcomingList) {
_upcomingList = upcomingList;
}
public List<Partner> getPartnerList() {
return _partnerList;
}
public void setPartnerList(List<Partner> partnerList) {
_partnerList = partnerList;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeList(_feedList);
parcel.writeList(_eventList);
parcel.writeList(_todayList);
parcel.writeList(_upcomingList);
parcel.writeList(_partnerList);
}
}
MainActivity.java
// member variable
private BroadcastReceiver _splashReceiver;
private Bundle _bundle = new Bundle();
#Override
protected void onResume() {
super.onResume();
splashReceiver();
}
// receive splashParcel from SplashScreenActivity
private void splashReceiver() {
if (_splashReceiver == null) {
_splashReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
SplashParcel splashParcel = intent.getParcelableExtra("values");
if (splashParcel != null) {
// save splashParcel into _budle
_bundle.putParcelable("splash_parcel", splashParcel);
}
}
};
registerReceiver(_splashReceiver, new IntentFilter("splash_parcel"));
}
}
//Send _bundle to DiscoverFragment
private void showDiscoverFragment(){
if(_bundle != null) {
// create instance of discoverFragment with passing _bundle as arguments
DiscoverFragment discoverFragment = new DiscoverFragment();
discoverFragment.setArguments(_bundle);
// replace activity_main.xml with discoverFragment
getSupportFragmentManager().beginTransaction().replace(R.id.main_container, discoverFragment).addBackStack(null).commit();
}
}
In onCreateView of DiscoverFragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
SplashParcel splashParcel = getArguments().getParcelable("splash_parcel");
if(splashParcel != null) {
// your splashParcel ready in here
List<Feed> feedList = splashParcel.getFeedList()
List<Event> eventList = splashParcel.getEventList()
List<Event> todayList = splashParcel.getTodayList();
List<Event> upcommingList = splashParcel.getUpcomingList();
List<Partner> partnerList = splashParcel.getPartnerList();
}
}

Dagger injection - when do the provide methods get called

I'm experimenting a bit with Dagger on Android which seems to be a nice tool to isolate dependencies. In the first place I copied the android-activity-graphs example from GitHub: https://github.com/square/dagger/tree/master/examples/android-activity-graphs
I then added a couple of classes to the ActivityModule
#Module(
injects = {
HomeActivity.class,
HomeFragment.class
},
addsTo = AndroidModule.class,
library = true
)
public class ActivityModule {
private static final String TAG = "Activity_Module";
private final DemoBaseActivity activity;
public ActivityModule(DemoBaseActivity activity) {
this.activity = activity;
}
/**
* Allow the activity context to be injected but require that it be annotated with
* {#link ForActivity #ForActivity} to explicitly differentiate it from application context.
*/
#Provides
#Singleton
#ForActivity
Context provideActivityContext() {
return activity;
}
#Provides
#Singleton
ActivityTitleController provideTitleController() {
return new ActivityTitleController(activity);
}
//My addition from here
#Provides
#Singleton
Player providePlayer() {
Log.i(TAG, "in providePlayer()");
return new MyAndroidTestPlayer(activity);
}
#Provides
RandomNumberGenerator provideRandomNumberGenerator() {
Log.i(TAG, "in provideRandomNumberGenerator()");
return new RealRandomNumberGenerator();
}
}
The rest of the graph initialization is identical to the example from github.
The thing that puzzles me is the fact that the injected object are null after the construction of the class they are injected into (HomeFragment)... for a while.
Again HomeFragment is more or less identical to HomeFragment from the examples in github, with a few additions of my own.
If I call whatever on either of the injected Player or RandomNumberGenerator objects in the onCreateView() of the HomeFragment I get an error saying they are null.
However if I call them inside the inner OnClickListener - onClick() they work as expected.
Can anyone point me to the piece of knowledge I am missing to understand what is going on here?
public class HomeFragment extends DemoBaseFragment {
public static final String TAG = "HOME_FRAGMENT";
public static HomeFragment newInstance() {
return new HomeFragment();
}
#Inject
ActivityTitleController titleController;
#Inject
Player player;
#Inject
RandomNumberGenerator randomNumberGenerator;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
TextView tv = new TextView(getActivity());
if (randomNumberGenerator != null) {
Log.i(TAG, "randomNumberGenerator is NOT null");
} else {
Log.e(TAG, "randomNumberGenerator is NULL!");
}
if (player != null) {
Log.i(TAG, "player is NOT null");
} else {
Log.e(TAG, "player is NULL!");
}
//int randomNumber = randomNumberGenerator.getIntegerInRange(48, 50);
//player.playTestNote();
tv.setGravity(CENTER);
tv.setText("Play test note");
tv.setTextSize(40);
tv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
player.playTestNote();
int randomNumber = randomNumberGenerator.getIntegerInRange(48, 50);
Log.i(TAG, "Text view clicked, random number is: " + randomNumber);
}
});
return tv;
}
The classes I'm using to test with are pretty trivial (RandomNumberGenerator more so than the Player class). I'll skip the RandomNumberGenerator. Here is the MyAndroidTestPlayer which implements Player (just one playTestNote() method).
public class MyAndroidTestPlayer implements Player {
SoundPool soundPool;
private static final int MAX_STREAMS = 10;
private static final int DEFAULT_SRC_QUALITY = 0;
private static final int HARDCODED_SOUND_RESOURCE_C3 = R.raw.midi_48_c3;
private static final int DEFAULT_PRIORITY = 1;
private static final String TAG = "MyAndroidTestPlayer";
private Context context;
private boolean isLoaded = false;
private int streamId;
private int soundId;
public MyAndroidTestPlayer(Context context) {
this.context = context;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
createNewSoundPool();
} else {
createOldSoundPool();
}
}
protected void createOldSoundPool() {
soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, DEFAULT_SRC_QUALITY);
Log.i(TAG, "created old sound pool");
loadSoundPool();
}
protected void createNewSoundPool() {
AudioAttributes attributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build();
soundPool = new SoundPool.Builder().setAudioAttributes(attributes).build();
Log.i(TAG, "created new sound pool");
loadSoundPool();
}
private void loadSoundPool() {
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
isLoaded = true;
Log.i(TAG, "Loaded");
Log.i(TAG, "Status: " + status);
}
});
soundId = soundPool.load(context, HARDCODED_SOUND_RESOURCE_C3, DEFAULT_PRIORITY);
}
#Override
public void playTestNote() {
Log.i(TAG, "before loaded check");
if (isLoaded) {
streamId = soundPool.play(soundId, 1, 1, 1, 0, 1f);
Log.i(TAG, "Played Sound");
Log.i(TAG, "streamId: " + streamId);
}
}
}
Thank you in advance.
I think I got it. HomeFragment had to #Override onCreate and inject itself with the following line
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((DemoBaseActivity) getActivity()).inject(this);
}
Then it works for me.
I hope this will help other users on my level of understanding.

Categories

Resources