I have implemented MVP pattern in my app. And I'm using WeakReferences to store View's reference in my Presenter. But still my fragments are not being claimed by GC upon destroying. Below is the screenshot of problem. Any idea what is causing this and how to remove this issue?
Below is the code for my Presenter:
public class ProductDetailPresenter implements ProductDetailContract.Presenter {
private final WeakReference<ProductDetailContract.View> view;
private CategoriesDataSource repo;
public ProductDetailPresenter(ProductDetailContract.View view, CategoriesDataSource repo) {
this.view = new WeakReference<>(view);
this.repo = repo;
view.setPresenter(this);
}
#Override
public void start() {
}
#Override
public void submitRating(final Product product, final float mRating) {
final ProductDetailContract.View view =ProductDetailPresenter.this.view.get();
if (view != null) {
repo.submitRating(product.getId(), mRating, true, new CategoriesDataSource.SubmitRatingCallback() {
#Override
public void onRatingSubmitted() {
product.setRating(mRating);
product.setRated(true);
product.setUpdatedAt(new Date(System.currentTimeMillis()));
repo.updateProductInDB(product);
if (!view.isActive()) return;
view.onRatingSubmitted(true, mRating);
}
#Override
public void onError(Throwable throwable) {
if (!view.isActive()) return;
view.onRatingSubmitted(false, 0);
}
});
}
}
#Override
public void onRateKarenClicked() {
ProductDetailContract.View view = this.view.get();
if (view != null) {
view.openDialog();
}
}
#Override
public void onAbhiKhareediyeClicked(Product product) {
EventBus.getDefault().post(
new ProductDetailContract.ContractEventMessages(
ProductDetailContract.ContractEventMessages.EVENT_START_QUANTITY_SCREEN, product));
}
}
This is the problem:
#Override
public void submitRating(final Product product, final float mRating) {
final ProductDetailContract.View view =ProductDetailPresenter.this.view.get(); <-- this is bad
you have a final object that is being passed to the repo. Delete the whole line. You don't need it. Use in the view.get() inside the onRatingSubmitted and onError
Related
I can't solve this problem. I want to write a flutter plugin for connecting to scales via bluetooth, but the scales sdk It's being used through the activity page, so I tried to find information about the activity usage in the flutter plugin, so I tried to run it. ActivityAware But I can't run the SDK and I can't pass values from extend class of sdk into EventChanel and can't send back to stream flutter.
this Logcat Error
2022-09-03 01:35:18.966 7973-7973/com.example.releep_scale_connect_example E/EventChannel#scan_releep_scale: Failed to open event stream
java.lang.IllegalStateException: System services not available to Activities before onCreate()
at android.app.Activity.getSystemService(Activity.java:6916)
at aicare.net.cn.iweightlibrary.bleprofile.BleProfileServiceReadyActivity.isBLEEnabled(BleProfileServiceReadyActivity.java:299)
at aicare.net.cn.iweightlibrary.bleprofile.BleProfileServiceReadyActivity.startScan(BleProfileServiceReadyActivity.java:329)
at com.example.releep_scale_connect.ReleepScaleConnectPlugin.onListen(ReleepScaleConnectPlugin.java:156)
at io.flutter.plugin.common.EventChannel$IncomingStreamRequestHandler.onListen(EventChannel.java:218)
at io.flutter.plugin.common.EventChannel$IncomingStreamRequestHandler.onMessage(EventChannel.java:197)
at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:178)
at io.flutter.embedding.engine.dart.DartMessenger.lambda$handleMessageFromDart$0$DartMessenger(DartMessenger.java:206)
at io.flutter.embedding.engine.dart.-$$Lambda$DartMessenger$6ZD1MYkhaLxyPjtoFDxe45u43DI.run(Unknown Source:12)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:8051)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:620)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1011)
2022-09-03 01:35:18.978 7973-7973/com.example.releep_scale_connect_example W/Looper: PerfMonitor longMsg : seq=135 plan=01:35:17.167 late=0ms wall=1800ms running=24ms runnable=1ms h=android.os.Handler c=io.flutter.embedding.engine.dart.-$$Lambda$DartMessenger$6ZD1MYkhaLxyPjtoFDxe45u43DI procState=2
this my plugin (ReleepScaleConnectPlugin.java)
I want this SDK to be in flutter plugin format.
this github SDK : https://github.com/evanwork1234/AiFitSDK
public class ReleepScaleConnectPlugin extends BleProfileServiceReadyActivity implements FlutterPlugin, EventChannel.StreamHandler, MethodCallHandler, ActivityAware {
private FlutterActivity activity;
private MethodChannel channel;
private EventChannel stream_chanel;
private DeviceDialog devicesDialog;
private BinaryMessenger binaryMessenger;
private String[] permissionArray = new String[] {
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_NETWORK_STATE
};
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding flutterPluginBinding) {
binaryMessenger = flutterPluginBinding.getBinaryMessenger();
}
#Override
public void onMethodCall(#NonNull MethodCall call, #NonNull Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
#Override
public void onDetachedFromEngine(#NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
ArrayList listVal = new ArrayList();
private Gson gson = new Gson();
#Override
public void onListen(Object arguments, EventChannel.EventSink events) {
if (arguments.equals("scan")) {
// devicesDialog.startScan();
startScan();
String json = gson.toJson(listVal);
events.success(json);
}
}
#Override
public void onCancel(Object arguments) {
}
#Override
protected void onError(String s, int i) {
L.e("TAG", "Message = " + s + " errCode = " + i);
// showInfo(getString(R.string.state_error, errMsg, errCode), true);
}
#Override
protected void onGetWeightData(WeightData weightData) {
}
#Override
protected void onGetResult(int i, String s) {
}
#Override
protected void onServiceBinded(WBYService.WBYBinder wbyBinder) {
}
#Override
protected void onServiceUnbinded() {
}
#Override
protected void getAicareDevice(BroadData broadData) {
if (broadData != null) {
L.e("TAG", broadData.toString());
listVal.add(broadData.toString());
}
}
private void startLeScan() {
startScan();
}
private void stopLeScan() {
stopScan();
}
#Override
public void onAttachedToActivity(#NonNull #NotNull ActivityPluginBinding binding) {
L.e("TAG", "onAttachedToActivity");
activity = (FlutterActivity) binding.getActivity();
AiFitSDK.getInstance().init (activity);
channel = new MethodChannel(binaryMessenger, "releep_scale_connect");
channel.setMethodCallHandler(this);
stream_chanel = new EventChannel(binaryMessenger, "scan_releep_scale");
stream_chanel.setStreamHandler(this);
boolean backBoolean = PermissionUtils.checkPermissionArray(activity.getContext(), permissionArray, 3);
// initPermissions();
// Judge whether Bluetooth is on, if you need to change the style, you can do it yourself
if (!isBLEEnabled()) {
showBLEDialog();
}
//startScan();
// devicesDialog = new DeviceDialog(activity.getContext(), this);
}
#Override
public void onDetachedFromActivityForConfigChanges() {
L.e("TAG", "onDetachedFromActivityForConfigChanges");
}
#Override
public void onReattachedToActivityForConfigChanges(#NonNull #NotNull ActivityPluginBinding binding) {
L.e("TAG", "onReattachedToActivityForConfigChanges");
}
#Override
public void onDetachedFromActivity() {
L.e("TAG", "onDetachedFromActivity");
}
}
I want these override methods to be able to pass values back to flutter.
#Override
protected void onGetWeightData(WeightData weightData) {
}
#Override
protected void onGetResult(int i, String s) {
}
#Override
protected void onGetFatData(boolean b, BodyFatData bodyFatData) {
}
I wanted to know how to load more data in recylcer view using firestore.
Query query = FirebaseFirestore.getInstance()
.collection("ie").limit(5);
adapter=new InterviewAdapter(this,query);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
Adapter class looks like this:
public class InterviewAdapter extends FireStoreAdapter<InterviewAdapter.ViewHolder> {
public interface OnInterviewSelectedListener {
void onInterviewSelected(DocumentSnapshot interview);
}
private InterviewAdapter.OnInterviewSelectedListener mListener;
public InterviewAdapter(Query query, OnInterviewSelectedListener listener) {
super(query);
mListener = listener;
}
#Override
public InterviewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new InterviewAdapter.ViewHolder(inflater.inflate(R.layout.ie, parent, false));
}
#Override
public void onBindViewHolder(InterviewAdapter.ViewHolder holder, int position) {
holder.bind(getSnapshot(position), mListener);
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView title,companyName,username,views,isHired;
public ViewHolder(View itemView) {
super(itemView);
title= (TextView) itemView.findViewById(R.id.title);
companyName= (TextView) itemView.findViewById(R.id.companyName);
username= (TextView) itemView.findViewById(R.id.username);
views= (TextView) itemView.findViewById(R.id.views);
isHired= (TextView) itemView.findViewById(R.id.isHired);
}
public void bind(final DocumentSnapshot snapshot,
final OnInterviewSelectedListener listener) {
InterviewExperience experience;
String companyName=snapshot.getString("companyName");
boolean isHired=Boolean.valueOf(snapshot.getBoolean("isHired"));
String username=snapshot.getString("username");
long views=new Double(Double.valueOf(snapshot.getDouble("views"))).longValue();
String id=snapshot.getId();
String title=snapshot.getString("title");
experience=new InterviewExperience(id,title,companyName,username,isHired,views,null,null);
this.title.setText(experience.getTitle());
this.companyName.setText("Company Name: "+experience.getCompanyName());
this.isHired.setText("Hired: "+experience.isHired());
this.views.setText("Views: "+experience.getViews()+"");
this.username.setText("Created By: "+experience.getUsername());
// Click listener
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
listener.onInterviewSelected(snapshot);
}
}
});
}
}
}
public abstract class FireStoreAdapter<VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH>
implements EventListener<QuerySnapshot> {
private static final String TAG = "FirestoreAdapter";
private Query mQuery;
private ListenerRegistration mRegistration;
private ArrayList<DocumentSnapshot> mSnapshots = new ArrayList<>();
public FireStoreAdapter(Query query) {
mQuery = query;
}
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "onEvent:error", e);
onError(e);
return;
}
// Dispatch the event
Log.d(TAG, "onEvent:numChanges:" + documentSnapshots.getDocumentChanges().size());
for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
switch (change.getType()) {
case ADDED:
onDocumentAdded(change);
break;
case MODIFIED:
onDocumentModified(change);
break;
case REMOVED:
onDocumentRemoved(change);
break;
}
}
onDataChanged();
}
public void startListening() {
if (mQuery != null && mRegistration == null) {
mRegistration = mQuery.addSnapshotListener(this);
}
}
public void stopListening() {
if (mRegistration != null) {
mRegistration.remove();
mRegistration = null;
}
mSnapshots.clear();
notifyDataSetChanged();
}
public void setQuery(Query query) {
// Stop listening
stopListening();
// Clear existing data
mSnapshots.clear();
notifyDataSetChanged();
// Listen to new query
mQuery = query;
startListening();
}
#Override
public int getItemCount() {
return mSnapshots.size();
}
protected DocumentSnapshot getSnapshot(int index) {
return mSnapshots.get(index);
}
protected void onDocumentAdded(DocumentChange change) {
mSnapshots.add(change.getNewIndex(), change.getDocument());
notifyItemInserted(change.getNewIndex());
}
protected void onDocumentModified(DocumentChange change) {
if (change.getOldIndex() == change.getNewIndex()) {
// Item changed but remained in same position
mSnapshots.set(change.getOldIndex(), change.getDocument());
notifyItemChanged(change.getOldIndex());
} else {
// Item changed and changed position
mSnapshots.remove(change.getOldIndex());
mSnapshots.add(change.getNewIndex(), change.getDocument());
notifyItemMoved(change.getOldIndex(), change.getNewIndex());
}
}
protected void onDocumentRemoved(DocumentChange change) {
mSnapshots.remove(change.getOldIndex());
notifyItemRemoved(change.getOldIndex());
}
protected void onError(FirebaseFirestoreException e) {};
protected void onDataChanged() {}
}
I used Firestore Adapter code which was given in samples of firestore documentation. Can anyone tell how to use the query object to load more data?
How to load the next 5 items in the recycler view when users scrolls to the end of the list?
You can paginate your Query's result using Query's methods like, startAt(), startAfter(), endAt(), endBefore() with a specified DocumentSnapshot.
If I considered your collection is called "interviews", you can add a method to your FireStoreAdapter like this:
private void paginate(final DocumentSnapshot last, final int limit) {
final Query subset;
if (last == null) {
subset = db.collection("interviews")
.limit(limit);
} else {
subset = db.collection("interviews")
.startAfter(last)
.limit(limit);
}
setQuery(subset);
}
You can perserve the last DocumentSnapshot within onEvent():
final List<DocumentChange> changes = documentSnapshots.getDocumentChanges();
final DocumentSnapshot lastDocument = changes.get(changes.size() - 1).getDocument();
Finally, when users scrolls to the end of the list:
paginate(lastDocument, 5);
And onDocumentAdded() will take care of it. Be carfure NOT to use startAt() because it will not execlude the last one (that already at the end of your list, and will duplicate it).
I'm currently trying to learn how to use MediatorLiveData as described here:
https://developer.android.com/reference/androidx/lifecycle/MediatorLiveData
What I want to do is wait for two livedata object to get an update then do some logic on both.
So in my activity i currently got this. While this works I'm currently only getting Orders while i would like to wait for Orders AND Orderrows to finish and then make some change.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button myButt;
private MainViewModel mvw;
private TextView myView;
private MediatorLiveData data;
#Override
protected void onCreate(Bundle savedInstanceState) {
mvw = new ViewModelProvider(this).get(MainViewModel.class);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myButt = findViewById(R.id.button);
myView = findViewById(R.id.textview);
myButt.setOnClickListener(this);
data = new MediatorLiveData<>();
data.addSource(mvw.getAllOrders(), new Observer<Orders>() {
#Override
public void onChanged(Orders orders) {
data.setValue(orders);
}
});
data.addSource(mvw.getAllOrderRows(), new Observer<OrderRows>() {
#Override
public void onChanged(OrderRows orderRows) {
data.setValue(orderRows);
}
});
data.observe(this, new Observer<Orders>() { //this observers Orders but how do i get orders AND orderrows?
#Override
public void onChanged(Orders order) {
myView.setText(mvw.extractDate(order));
//Here i want to manipulate order and orderrows
Log.i("livedata" , order.getOrders().toString());
}
});
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.button:
Log.i("button","clicked");
mvw.updateOrderData(); // calls for new values to be fetched
mvw.updateOrderRowData();
}
}
}
in my viewmodel:
public class MainViewModel extends ViewModel {
private GetOrder getOrderRepo;
private MutableLiveData<Orders> allOrders;
private MutableLiveData<OrderRows> allOrderRows;
public MainViewModel(){
getOrderRepo = new GetOrder();
}
public MutableLiveData<Orders> getAllOrders() {
if(allOrders == null){
allOrders = new MutableLiveData<>();
allOrders = getOrderRepo.getAllOrders();
}
return allOrders;
}
public MutableLiveData<OrderRows> getAllOrderRows() {
if(allOrderRows == null){
allOrderRows = new MutableLiveData<>();
allOrderRows = getOrderRepo.getAllOrderRows();
}
return allOrderRows;
}
public void updateOrderData(){
Log.i("updating","updating data");
Orders orders = getOrderRepo.getAllOrders().getValue();
allOrders.setValue(orders);
}
public void updateOrderRowData(){
Log.i("updating","updating data");
OrderRows orderRows = getOrderRepo.getAllOrderRows().getValue();
allOrderRows.setValue(orderRows);
}
public String extractDate(Orders orders){
ArrayList<Order> listOfOrders = orders.getOrders();
Log.i("extractDate", ""+(listOfOrders.size()-1));
String date = listOfOrders.get(listOfOrders.size()-1).getOrderTime();
return date;
}
}
In the repostiory.
public class GetOrder {
private ApiService mAPIService;
MutableLiveData<Orders> allOrders;
MutableLiveData<OrderRows> allOrderRows;
public GetOrder(){
mAPIService = ApiUtils.getAPIService();
allOrders = new MutableLiveData<Orders>();
allOrderRows = new MutableLiveData<OrderRows>();
}
public MutableLiveData<Orders> getAllOrders(){
Log.i("func","starting func");
mAPIService.getOrders().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Orders>() {
#Override
public void onCompleted() {
Log.i("func","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("onError",e.toString());
}
#Override
public void onNext(Orders orders) {
Log.i("Repo",orders.toString());
allOrders.setValue(orders);
}
});
return allOrders;
}
public MutableLiveData<OrderRows> getAllOrderRows(){
Log.i("func","starting func");
mAPIService.getOrderRows().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<OrderRows>() {
#Override
public void onCompleted() {
Log.i("func","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("onError",e.toString());
}
#Override
public void onNext(OrderRows orderRows) {
Log.i("Repo",orderRows.toString());
allOrderRows.setValue(orderRows);
}
});
return allOrderRows;
}
}
I am using Sync Adapter along with Dagger 2 for dependency injection. I am stuck since I cannot seem to figure out where should I use XYZ.inject since SyncAdapter class does not provide OnCreate or an Activity to stick to. Can someone suggest how to deal with Dependency injection in case of Sync Adapter alike classes which do not belong to activity/fragment?
PS: I have looked at several similar questions but failed to find a solution to my problem.
SyncAdapter.java
public class SyncAdapter extends AbstractThreadedSyncAdapter {
ContentResolver mContentResolver;
//Injects here
#Inject
SyncCenterPresenter mSyncCenterPresenter;
private final AccountManager mAccountManager;
Context context;
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
mContentResolver = context.getContentResolver();
mAccountManager = AccountManager.get(context);
this.context=context;
}
Account mainAccount;
public static final int SYNC_INTERVAL = 60 * 1;
public static final int SYNC_FLEXTIME = SYNC_INTERVAL/3;
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Log.v("Sync class me","sync adapter on perform sync");
if (mSyncCenterPresenter == null){
Log.v("messsage","null");
} else {
Log.v("messsage","not null");
mSyncCenterPresenter.loadDatabaseCenterPayload();
mSyncCenterPresenter.syncPayload();
}
}
/**
* Helper method to schedule the sync adapter periodic execution
*/
public static void configurePeriodicSync(Context context, int syncInterval, int flexTime) {
Account account = myAccount;
String authority = "com.mifos.provider";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// we can enable inexact timers in our periodic sync
SyncRequest request = new SyncRequest.Builder().
syncPeriodic(syncInterval, flexTime).
setSyncAdapter(account, authority).
setExtras(new Bundle()).build();
ContentResolver.requestSync(request);
} else {
ContentResolver.addPeriodicSync(account,
authority, new Bundle(), syncInterval);
}
}
static Account myAccount;
public static void onAccountCreated(Account newAccount, Context context) {
/*
* Since we've created an account
*/
myAccount = newAccount;
SyncAdapter.configurePeriodicSync(context, SYNC_INTERVAL, SYNC_FLEXTIME);
/*
* Without calling setSyncAutomatically, our periodic sync will not be enabled.
*/
ContentResolver.setSyncAutomatically(newAccount, "com.mifos.provider", true);
/*
* Finally, let's do a sync to get things started
*/
syncImmediately(context);
}
public static Account getSyncAccount(Context context) {
// Create the account type and default account
Account newAccount = new Account(
context.getString(R.string.app_name), "com.mifos");
return newAccount;
}
/**
* Helper method to have the sync adapter sync immediately
* #param context The context used to access the account service
*/
public static void syncImmediately(Context context) {
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
ContentResolver.requestSync(getSyncAccount(context),
"com.mifos.provider", bundle);
}
}
SyncCenterPresenter.java
public class SyncCenterPresenter {
private final DataManagerCenter mDataManagerCenter;
private CompositeSubscription mSubscriptions;
List<CenterPayload> centerPayloads;
int mCenterSyncIndex = 0;
#Inject
public SyncCenterPresenter(DataManagerCenter dataManagerCenter) {
Log.v("messsage","const me");
mDataManagerCenter = dataManagerCenter;
mSubscriptions = new CompositeSubscription();
centerPayloads = new ArrayList<>();
}
public void loadDatabaseCenterPayload() {
Log.v("messsage","load me");
mSubscriptions.add(mDataManagerCenter.getAllDatabaseCenterPayload()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<List<CenterPayload>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<CenterPayload> centerPayloads) {
showCenters(centerPayloads);
}
}));
}
public void syncCenterPayload(CenterPayload centerPayload) {
mSubscriptions.add(mDataManagerCenter.createCenter(centerPayload)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<SaveResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SaveResponse center) {
showCenterSyncResponse();
}
}));
}
public void deleteAndUpdateCenterPayload(int id) {
mSubscriptions.add(mDataManagerCenter.deleteAndUpdateCenterPayloads(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<List<CenterPayload>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<CenterPayload> centerPayloads) {
showPayloadDeletedAndUpdatePayloads(centerPayloads);
}
}));
}
public void showCenters(List<CenterPayload> centerPayload) {
centerPayloads = centerPayload;
}
public void showCenterSyncResponse() {
deleteAndUpdateCenterPayload(centerPayloads
.get(mCenterSyncIndex).getId());
}
public void showPayloadDeletedAndUpdatePayloads(List<CenterPayload> centers) {
mCenterSyncIndex = 0;
this.centerPayloads = centers;
}
public void syncPayload() {
for (int i = 0; i < centerPayloads.size(); ++i) {
if (centerPayloads.get(i).getErrorMessage() == null) {
syncCenterPayload(centerPayloads.get(i));
mCenterSyncIndex = i;
break;
} else {
Log.v("messsage","else block");
}
}
}
}
ActivityComponent
#PerActivity
#Component(dependencies = ApplicationComponent.class, modules =
ActivityModule.class)
public interface ActivityComponent {
void inject(LoginActivity loginActivity);
void inject(PassCodeActivity passCodeActivity);
//other methods
void inject(SyncAdapter syncAdapter);
}
ActivityModule
#Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
mActivity = activity;
}
#Provides
Activity provideActivity() {
return mActivity;
}
#Provides
#ActivityContext
Context providesContext() {
return mActivity;
}
}
EDIT
SyncService.java
public class SyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static SyncAdapter sSyncAdapter = null;
#Override
public void onCreate() {
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}
Can someone please help me to get this working? since I can't figure out where to use the inject and how to do it without an Activity Component? A different and a better approach would be appreciated as well.
Thanks
You can implement the constructor, so please use constructor injection to initialize your adapter.
public class SyncAdapter extends AbstractThreadedSyncAdapter {
// ...
#Inject
public SyncAdapter(Context context, boolean autoInitialize) { /*...*/ }
}
Then you simply inject the service that returns the SyncAdapter like you would anything else...
public class SyncService extends Service {
#Inject SyncAdapter syncAdapter;
#Override
public void onCreate() {
AndroidInjection.inject(this);
// or
DaggerSyncServiceComponent.create().inject(this);
}
#Override
public IBinder onBind(Intent intent) {
return syncAdapter;
}
}
And that's it.
I am implementing an app introduction and waiver that should appear before the user can access the MainActivity of my Android app. If the user has not accepted the waiver or gone through the app introduction, then my IntroNavigator kicks them back to those activities.
How can I rxify my redirectIfNecessary() method in a more functional manner, instead of the imperative approach I implemented below.
IntroNavigatorImpl.java
public class IntroNavigatorImpl implements IntroNavigator {
WeakReference<Activity> activityWeakReference;
CloudPrefsRepo cloudPrefsRepo;
public IntroNavigatorImpl(Activity activity, CloudPrefsRepo cloudPrefsRepo) {
this.activityWeakReference = new WeakReference<>(activity);
this.cloudPrefsRepo = cloudPrefsRepo;
}
#Override
public void redirectIfNecessary() {
final boolean shouldShowAppIntro = cloudPrefsRepo.shouldShowAppIntro()
.toObservable().toBlocking().first();
final boolean shouldShowWaiver = cloudPrefsRepo.shouldShowWaiver()
.toObservable().toBlocking().first();
if (shouldShowAppIntro) {
showAppIntro();
finishActivity();
} else if (shouldShowWaiver) {
showWaiver();
finishActivity();
} else {
//do nothing
}
}
#Override
public void showWaiver() {
//launch waiver activity
}
#Override
public void showAppIntro() {
//launch app intro activity
}
public void finishActivity() {
if (activityWeakReference.get() != null) {
activityWeakReference.get().finish();
}
}
}
CloudPrefsRepo.java
public interface
/**
* Whether to show the app intro.
*/
Single<Boolean> shouldShowAppIntro();
/**
* Whether to show the waiver. If the user has already
* accepted this waiver, then it shouldn't be shown.
*/
Single<Boolean> shouldShowWaiver();
Edit Based on comment:
It's not the prettiest (I'm leaning towards there not being a good reactive way to do this)
final Action0 showInfoAction = new Action0() {
#Override
public void call() {
showAppIntro();
finishActivity();
}
};
final Action0 showWaiverAction = new Action0() {
#Override
public void call() {
showWaiver();
finishActivity();
}
};
final Action0 blankAction = new Action0() {
#Override
public void call() {
}
};
Observable.zip(shouldShowInfo, shouldShowWaiver, new Func2<Boolean, Boolean, Action0>() {
#Override
public Action0 call(Boolean shoudlShowInfo, Boolean shouldShowWaiver) {
if (shoudlShowInfo) {
return showInfoAction;
} else if (shouldShowWaiver) {
return showWaiverAction;
} else {
return blankAction;
}
}
}).subscribe(new Action1<Action0>() {
#Override
public void call(Action0 action0) {
action0.call();
}
});