React Native (Android Native Module): Get access to ReactContext in another Class? - java

I want to call a method in MyCustomModuleClass extends ReactContextBaseJavaModule from another Class
MyCustomModule.java
public class MyCustomModule extends ReactContextBaseJavaModule {
private ReactContext mReactContext;
public MyCustomModule(ReactApplicationContext reactContext) {
super(reactContext);
mReactContext = reactContext;
}
#Override
public String getName() {
return "CustomModule";
}
private void sendEvent(String eventName, Object params) {
mReactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}
I'm trying this..
OtherClass.java
public class OtherClass extends AnotherClass {
#Override
protected void eventOccurred(Context context) {
MyCustomModule RNC = new MyCustomModule(?);//-> Can't call without ReactContext
RNC.sendEvent("CustomEvent", ObjectData);
}
}
Sorry, I know nothing about java, could any one please help me out?
Is there any way I can get ReactContext in OtherClass?

Here's how I did it in a FirebaseMessagingService extended class.
public class MyPushListener extends FirebaseMessagingService {
#Override
public void onMessageReceived(JSONObject message, JSONObject content){
MainApplication application = (MainApplication) this.getApplication();
ReactNativeHost reactNativeHost = application.getReactNativeHost();
ReactInstanceManager reactInstanceManager = reactNativeHost.getReactInstanceManager();
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext != null) {
WritableNativeArray params = new WritableNativeArray();
params.pushString(message.toString());
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("EVENT_HAS_TRIGGERED", params);
}
}
}
Source

Related

Getting data from Repository to Viewmodel without passing observer to the activity

Why i should pass the LiveData to the activity to get the value of allQuestions. I am a little bit confused.
I tried to get the List of allQuestions in QuestionViewModel from the repo directly but it didn't work.
This is what's in my brain.
public class QuestionViewModel extends AndroidViewModel {
private Repository repo;
private List<QuestionModel> allQuestions;
public QuestionViewModel(#NonNull Application application) {
super(application);
repo = new Repository(application);
allQuestions = repo.getAllQuestions().getValue(); //This line did not work for me (︶︹︺)
}}
What i did do to get it work.
ViewModel
public class QuestionViewModel extends AndroidViewModel {
private Repository repo;
private LiveData<List<QuestionModel>> allQuestions;
public List<QuestionModel> CurrentListOfQues;
public QuestionViewModel(#NonNull Application application) {
super(application);
repo = new Repository(application);
allQuestions = repo.getAllQuestions();
}
public LiveData<List<QuestionModel>> GetAllQues() {
if (allQuestions == null) {
allQuestions = new LiveData<List<QuestionModel>>() {
#Override
public void observe(#NonNull LifecycleOwner owner, #NonNull Observer<? super List<QuestionModel>> observer) {
super.observe(owner, observer);
}
};
}
return allQuestions;
}
public void setCurrent(List<QuestionModel> CurrentListOfQues) {
if (this.CurrentListOfQues == null) {
this.CurrentListOfQues = CurrentListOfQues;
}
}
Question Activity
QVM = new ViewModelProvider(this).get(QuestionViewModel.class);
QVM.GetAllQues().observe(this, new Observer<List<QuestionModel>>() {
#Override
public void onChanged(List<QuestionModel> allQuestions) {
QVM.setCurrent(allQuestions);
}
});
Is this the right way to get the list in my viewmodel? Why allQuestions = repo.getAllQuestions().getValue(); is always null

How to pass an intent variable to retrofit using Android Pagination Library

I am implementing android pagination library in my app and would like to pass "id" of an item from my activity to the data source where my network call is made
AddCommentActivity.java
//I want to pass this string to the network call.
String image_id = getIntent().getStringExtra("image_id");
CommentViewModel commentViewModel = new ViewModelProvider(this).get(CommentViewModel.class);
CommentDataSource.java
public class CommentDataSource extends PageKeyedDataSource<Long, Comment> {
public CommentDataSource(){
progress_bar = new MutableLiveData<>();
}
#Override
public void loadInitial(#NonNull final LoadInitialParams<Long> params, #NonNull final LoadInitialCallback<Long, Comment> callback) {
RestApi restApi = RetrofitApi.create();
Call<CommentResponse> call = restApi.getComments(FIRST_PAGE, "I want the image_id from activity here");
call.enqueue(new Callback<CommentResponse>() {
#Override
public void onResponse(Call<CommentResponse> call, Response<CommentResponse> response) {
}
CommentDataSourceFactory.java
public class CommentDataFactory extends DataSource.Factory<Long, Comment> {
public MutableLiveData<CommentDataSource> commentLiveDataSource = new MutableLiveData<>();
public CommentDataFactory() {
}
#Override
public DataSource<Long, Comment> create() {
CommentDataSource commentDataSource = new CommentDataSource();
commentLiveDataSource.postValue(commentDataSource);
return commentDataSource;
}
CommentViewModel.java
public class CommentViewModel extends ViewModel {
public LiveData<PagedList<Comment>> commentPagedList;
public LiveData<CommentDataSource> liveDataSource;
public LiveData progressBar;
public CommentViewModel(){
CommentDataFactory commentDataFactory = new CommentDataFactory();
liveDataSource = commentDataFactory.commentLiveDataSource;
progressBar = Transformations.switchMap(liveDataSource, CommentDataSource::getProgressBar);
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(CommentDataSource.PAGE_SIZE)
.build();
commentPagedList = new LivePagedListBuilder<>(commentDataFactory, config).build();
}
public LiveData<PagedList<Comment>> getCommentData(){
return commentPagedList;
}
public void getRefreshedData(){
getCommentData().getValue().getDataSource().invalidate();
}
}
How to do that.? I checked Passing variable to paging library class which is exactly what I want to do but I dont understand it and the code gives errors. Errors such as
Cannot create an instance of class CommentViewModel
CommentViewModel has no zero argument constructor
Okay do:
commentViewmodel1.getCommentData().observe(this, new Observer<PagedList<Comments>>(){
#Override
public void onChanged(PagedList<Comment>
comments){
adapter.submitList(comments);
}
});

How do I use Repository pattern and Interactor pattern in a Model View Presenter pattern?

I am developing an application with the help of Model View Presenter pattern.
I make use of Retrofit and so I have a ApiClient and ApiInterface with endpoints. I implement the interface in a RemoteDataSource class which I call in the Repository class.
My questions is - how do I make use of an Interactor class to make the repository communicate with the Presenter?
Here is my code until now:
ApiInterface
public interface ApiInterface {
#GET("?")
Call<ArrayList<Movie>> getMoviesByTitle(#Query("t") String title,#Query("apiKey") String apiKey);
}
RemoteDataSource class
private static MovieRemoteDataSource instance;
private final ApiInterface service;
public MovieRemoteDataSource(ApiInterface movieApi) {
service = ApiClient.createService(ApiInterface.class);
}
public static MovieRemoteDataSource getInstance(ApiInterface movieApi) {
if (instance == null) {
instance = new MovieRemoteDataSource(movieApi);
}
return instance;
}
#Override
public void getMovies(String title, String apiKey, final LoadMovieCallBack callback) {
service.getMoviesByTitle(title,apiKey).enqueue(new Callback<ArrayList<Movie>>() {
#Override
public void onResponse(Call<ArrayList<Movie>> call, Response<ArrayList<Movie>> response) {
ArrayList<Movie> movies = response.body();// != null ? //response.body().getTitle() : null;
if (movies != null && !movies.isEmpty()) {
callback.onMoviesLoaded(movies);
} else {
callback.onDataNotAvailable();
}
}
#Override
public void onFailure(Call<ArrayList<Movie>> call, Throwable t) {
callback.onError();
}
});
}
DataSource interface with a callback
public interface MovieDataSource {
interface LoadMovieCallBack{
void onMoviesLoaded(ArrayList<Movie> movies);
void onDataNotAvailable();
void onError();
}
void getMovies(String title, String apiKey,LoadMovieCallBack callback);
}
Repository
private MovieRemoteDataSource movieRemoteDataSource;
public MoviesRepository() {//ApiInterface movieApi) {
//this.service = ApiClient.createService(ApiInterface.class);
}
public static MoviesRepository getInstance(ApiInterface service) {
if (instance == null) {
instance = new MoviesRepository();
}
return instance;
}
public void getMovies(String title, String apiKey ) {
movieRemoteDataSource.getMovies(title,apiKey,this);
}
In MoviesRepository you should declare a function with Callback. Your Presenter
should implement MovieDataSource.LoadMovieCallBack and pass it when you call MoviesRepository
public void getMovies(String title, String apiKey,MovieDataSource.LoadMovieCallBack callback) {
movieRemoteDataSource.getMovies(title,apiKey,callback);
}
Here is Google MVP already done for todo app sample, you can refer it. But now it deprecated because Google recommends MVVM

Android Architecture SingleLiveEvent and EventObserver Practicle Example in Java

I try to make sample login page with two fields (username, password) and save button with android architecture component, using android data binding, validating the data in viewmodel and from view model I make call to repository for remote server call as mentioned in official doc, remote server return me userid with success so how can I start new fragment from view model using this success? I learn something about singleLiveEvent and EventObserver, but I'm not able to find there clear usage example:
LoginViewModel
private MutableLiveData<String> snackbarStringSingleLiveEvent= new MutableLiveData<>();
#Inject
public LoginViewModel(#NonNull AppDatabase appDatabase,
#NonNull JobPortalApplication application,
#NonNull MyApiEndpointInterface myApiEndpointInterface) {
super(application);
loginRepository = new LoginRepository(application, appDatabase, myApiEndpointInterface);
snackbarStringSingleLiveEvent = loginRepository.getLogin(username.get(), password.get(), type.get());
}
public MutableLiveData<String> getSnackbarStringSingleLiveEvent() {
return snackbarStringSingleLiveEvent;
}
Repository
public SingleLiveEvent<String> getLogin(String name, String password, String type) {
SingleLiveEvent<String> mutableLiveData = new SingleLiveEvent<>();
apiEndpointInterface.getlogin(name, password, type).enqueue(new Callback<GenericResponse>() {
#Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
mutableLiveData.setValue(response.body().getMessage());
}
#Override
public void onFailure(Call<GenericResponse> responseCall, Throwable t) {
mutableLiveData.setValue(Constant.FAILED);
}
});
return mutableLiveData;
}
Login Fragment
private void observeViewModel(final LoginViewModel viewModel) {
// Observe project data
viewModel.getSnackbarStringSingleLiveEvent().observe(this, new Observer<String>() {
#Override
public void onChanged(String s) {
}
});
}
How can I use EventObserver in above case? Any practical example?
Check out below example about how you can create single LiveEvent to observe only one time as LiveData :
Create a class called Event as below that will provide our data once and acts as child of LiveData wrapper :
public class Event<T> {
private boolean hasBeenHandled = false;
private T content;
public Event(T content) {
this.content = content;
}
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return content;
}
}
public boolean isHandled() {
return hasBeenHandled;
}
}
Then declare this EventObserver class like below so that we don't end up placing condition for checking about Event handled every time, everywhere :
public class EventObserver<T> implements Observer<Event<T>> {
private OnEventChanged onEventChanged;
public EventObserver(OnEventChanged onEventChanged) {
this.onEventChanged = onEventChanged;
}
#Override
public void onChanged(#Nullable Event<T> tEvent) {
if (tEvent != null && tEvent.getContentIfNotHandled() != null && onEventChanged != null)
onEventChanged.onUnhandledContent(tEvent.getContentIfNotHandled());
}
interface OnEventChanged<T> {
void onUnhandledContent(T data);
}
}
And How you can implement it :
MutableLiveData<Event<String>> data = new MutableLiveData<>();
// And observe like below
data.observe(lifecycleOwner, new EventObserver<String>(data -> {
// your unhandled data would be here for one time.
}));
// And this is how you add data as event to LiveData
data.setValue(new Event(""));
Refer here for details.
Edit for O.P.:
Yes, data.setValue(new Event("")); is meant for repository when you've got response from API (Remember to return same LiveData type you've taken in VM instead of SingleLiveEvent class though).
So, let's say you've created LiveData in ViewModel like below :
private MutableLiveData<Event<String>> snackbarStringSingleLiveEvent= new MutableLiveData<>();
You provide value to this livedata as Single Event from repository like below :
#Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
mutableLiveData.setValue(new Event(response.body().getMessage())); // we set it as Event wrapper class.
}
And observe it on UI (Fragment) like below :
viewModel.getSnackbarStringSingleLiveEvent().observe(this, new EventObserver<String>(data -> {
// your unhandled data would be here for one time.
}));
Event.java
public class Event<T> {
private T content;
private boolean hasBeenHandled = false;
public Event(T content) {
this.content = content;
}
/**
* Returns the content and prevents its use again.
*/
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return content;
}
}
/**
* Returns the content, even if it's already been handled.
*/
public T peekContent() {
return content;
}
}
EventObserver.java
public class EventObserver<T> implements Observer<Event<? extends T>> {
public interface EventUnhandledContent<T> {
void onEventUnhandledContent(T t);
}
private EventUnhandledContent<T> content;
public EventObserver(EventUnhandledContent<T> content) {
this.content = content;
}
#Override
public void onChanged(Event<? extends T> event) {
if (event != null) {
T result = event.getContentIfNotHandled();
if (result != null && content != null) {
content.onEventUnhandledContent(result);
}
}
}
}
Example, In ViewModel Class
public class LoginViewModel extends BaseViewModel {
private MutableLiveData<Event<Boolean>> _isProgressEnabled = new MutableLiveData<>();
LiveData<Event<Boolean>> isProgressEnabled = _isProgressEnabled;
private AppService appService;
private SchedulerProvider schedulerProvider;
private SharedPreferences preferences;
#Inject
LoginViewModel(
AppService appService,
SchedulerProvider schedulerProvider,
SharedPreferences preferences
) {
this.appService = appService;
this.schedulerProvider = schedulerProvider;
this.preferences = preferences;
}
public void login(){
appService.login("username", "password")
.subscribeOn(schedulerProvider.executorIo())
.observeOn(schedulerProvider.ui())
.subscribe(_userLoginDetails::setValue,
_userLoginDetailsError::setValue,
() -> _isProgressEnabled.setValue(new Event<>(false)),
d -> _isProgressEnabled.setValue(new Event<>(true))
)
}
}
In Login Fragment,
viewModel.isProgressEnabled.observe(this, new EventObserver<>(hasEnabled -> {
if (hasEnabled) {
// showProgress
} else {
// hideProgress
}
}));
Using Event and EventObserver class we can achieve the same like SingleLiveEvent class but if you are thinking a lot of boilerplate code just avoid this method. I hope it would help you and give some idea about why we are using SingleEvent in LiveData.
I understand that Google gives the guidelines to use LiveData between the ViewModel and UI but there are edge cases where using LiveData as a SingleLiveEvent is like reinventing the wheel. For single time messaging between the view model and user interface we can use the delegate design pattern. When initializing the view model in the activity we just have to set the activity as the implementer of the interface. Then throughout our view model we can call the delegate method.
Interface
public interface Snackable:
void showSnackbarMessage(String message);
UI
public class MyActivity extends AppCompatActivity implements Snackable {
private MyViewModel myViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
this.myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
this.myViewModel.setListener(this);
}
#Override
public void showSnackbarMessage(String message) {
Toast.makeText(this, "message", Toast.LENGTH_LONG).show();
}
}
View Model
public class MyViewModel extends AndroidViewModel {
private Snackable listener;
public MyViewModel(#NonNull Application application) {
super(application);
}
public void setListener(MyActivity activity){
this.listener = activity;
}
private void sendSnackbarMessage(String message){
if(listener != null){
listener.showSnackbarMessage(message);
}
}
private void anyFunctionInTheViewModel(){
sendSnackbarMessage("Hey I've got a message for the UI!");
}
}

How to Inject a SharedPreferences Class as DataManager into app?

I'm getting this error while i'm trying to Build/Rebuild :
error: ir.atlaspio.roshdbookstore.DI.Components.ApplicationComponent scoped with #ir.atlaspio.roshdbookstore.DI.Scopes.ApplicationScope may not reference bindings with different scopes:
#Singleton class ir.atlaspio.roshdbookstore.Data.Prefs.AtlasPreferencesHelper
I'm trying to improve my code base on some projects and tutorials, right now i'm trying to doing dependency injection for SharedPreferences, which i've got above problem. It's not long time since i'm messing with Dagger 2 And DI, so excuse my beginner question and help me about this to find out where i'm doing this wrong. also there are some codes which they will inject SharedPrefereces but what i want is to use My own DataManager to do it right.
So here's how i created my project(Which is base on some tutorials):
Explaining Section(Which could be obvious for you guys): adding some method to set data into sharedpref.
AtlasPreferencesHelper :
#Singleton
public class AtlasPreferencesHelper implements PreferencesHelper {
private static final String PREF_KEY_USER_LOGGED_IN_MODE = "PREF_KEY_USER_LOGGED_IN_MODE";
private static final String PREF_KEY_FIRST_TIME_RUN_MODE = "PREF_KEY_FIRST_TIME_RUN_MODE";
private final SharedPreferences atlasPrefs;
#Inject
public AtlasPreferencesHelper(#ApplicationContext Context context,
#PreferenceInfo String prefFileName) {
atlasPrefs = context.getSharedPreferences(prefFileName, Context.MODE_PRIVATE);
}
#Override
public int getCurrentUserLoggedInMode() {
return atlasPrefs.getInt(PREF_KEY_USER_LOGGED_IN_MODE,
DataManager.LoggedInMode.LOGGED_IN_MODE_LOGGED_OUT.getType());
}
#Override
public void setCurrentUserLoggedInMode(DataManager.LoggedInMode mode) {
atlasPrefs.edit().putInt(PREF_KEY_USER_LOGGED_IN_MODE, mode.getType()).apply();
}
#Override
public int getCurrentFirstTimeStat() {
return atlasPrefs.getInt(PREF_KEY_FIRST_TIME_RUN_MODE,
DataManager.FirstTimeRun.FIRST_TIME_RUN_TRUE.getFrType());
}
#Override
public void setCurrentFirstTimeStat(DataManager.FirstTimeRun fMode) {
atlasPrefs.edit().putInt(PREF_KEY_FIRST_TIME_RUN_MODE, fMode.getFrType()).apply();
}
}
Explains : interface to be implemented in AtlasPreferencesHelper.
PreferenceHelper :
public interface PreferencesHelper {
int getCurrentUserLoggedInMode();
void setCurrentUserLoggedInMode(DataManager.LoggedInMode mode);
int getCurrentFirstTimeStat();
void setCurrentFirstTimeStat(DataManager.FirstTimeRun fMode);
}
Explains: Doing Job and getting access to activity to use methods.
AppDataManager :
public class AppDataManager implements DataManager {
private static final String TAG = "AppDataManager";
private final Context mContext;
private final PreferencesHelper mPrefencesHelper;
#Inject
public AppDataManager(#ApplicationContext Context context, PreferencesHelper prefencesHelper) {
mContext = context;
mPrefencesHelper = prefencesHelper;
}
#Override
public void setUserAssLoggedOut() {
setCurrentUserLoggedInMode(LoggedInMode.LOGGED_IN_MODE_LOGGED_OUT);
}
#Override
public int getCurrentUserLoggedInMode() {
return mPrefencesHelper.getCurrentUserLoggedInMode();
}
#Override
public void setCurrentUserLoggedInMode(LoggedInMode mode) {
mPrefencesHelper.setCurrentUserLoggedInMode(mode);
}
#Override
public int getCurrentFirstTimeStat() {
return mPrefencesHelper.getCurrentFirstTimeStat();
}
#Override
public void setCurrentFirstTimeStat(FirstTimeRun fMode) {
mPrefencesHelper.setCurrentFirstTimeStat(fMode);
}
}
Explains : DataManager interface implemented to AppDataManager
DataManager :
public interface DataManager extends PreferencesHelper {
void setUserAssLoggedOut();
enum LoggedInMode {
LOGGED_IN_MODE_LOGGED_OUT(0),
LOGGED_IN_MODE_SERVER(1);
private final int mType;
LoggedInMode(int type)
{
mType = type;
}
public int getType()
{
return mType;
}
}
enum FirstTimeRun {
FIRST_TIME_RUN_FALSE(0),
FIRST_TIME_RUN_TRUE(1);
private final int frType;
FirstTimeRun(int rType){
frType = rType;
}
public int getFrType()
{
return frType;
}
}
}
Explains : Components which, Context provide context, Retrofit Provide retrofit and SharedPref will provide Datamanager and SharedPref
ApplicationComponent :
#ApplicationScope
#Component(modules = {ContextModule.class,
RetrofitModule.class,
SharedPrefModule.class})
public interface ApplicationComponent {
MyAtlasAPI getApiReference();
DataManager getDataManager();
#ApplicationContext
Context getContext();
void injectApplication(AtlasApplication atlasApplication);
}
Explains : Module that will be implement in component for datamanager and sharedpref
SharedPrefModule :
#Module
public class SharedPrefModule {
#Provides
#PreferenceInfo
String providePreferenceName() {
return AppConstants.PREF_NAME;
}
#Provides
#Singleton
DataManager provideDataManager(AppDataManager appDataManager)
{
return appDataManager;
}
#Provides
#Singleton
PreferencesHelper providePreferencesHelper(AtlasPreferencesHelper atlasPreferencesHelper)
{
return atlasPreferencesHelper;
}
}
The error you're getting is telling it all. You're trying to reference to a module with a different scope than your component.
Looking at you ApplicationComponent:
#ApplicationScope
#Component(modules = {ContextModule.class,
RetrofitModule.class,
SharedPrefModule.class})
public interface ApplicationComponent { }
It's using the ApplicationScope
Your ApplicationComponent is then referring to SharedPrefModule.
If we take a look at that class:
#Module
public class SharedPrefModule {
#Provides
#Singleton
PreferencesHelper providePreferencesHelper(AtlasPreferencesHelper atlasPreferencesHelper) {
return atlasPreferencesHelper;
}
}
This module is using the Singleton scope.
Finally your AtlasPreferencesHelper is also scoped with Singleton:
#Singleton
public class AtlasPreferencesHelper {}
What you wanna do is align these scopes.
So either annotate the ApplicationComponent with #Singleton or use the #Application for all the modules within your ApplicationComponent

Categories