Accessing a value from a service to an adapter - java

In Service class :
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
updateprocess(percentage);
}
in adapter
onBindViewHolder
progressBar = new ProgressBar(getContext());
progressBar = parentView.findViewById(R.id.progressbar_process);
now I want to access percentage from service class method to this adapter progress

Use EventBus library
After config that follow this steps
1 : Create servieClass.java
public class serviceClass {
private int percentage;
public serviceClass(int percentage) {
this.percentage = percentage;
}
public int getPercentage() {
return percentage;
}
}
2 : Change service
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
updateprocess(percentage);
EventBus.getDefault().post(new servieClass(percentage));
}
3 : add setPercentage function to your Adapter
public void setPercentage(int percentage){
this.percentage = percentage;
notifyDataSetChanged();
}
4 : Finally add this in fragment that you config EventBus in it
#Subscribe
public void onEvent(BlockedEvent event) {
adapter.setPercentage(percentage);
}
Good luck

In the Service class I wrote this
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
Intent intent = new Intent("PercentageUpdates");
intent.putExtra("percentage", percentage);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
and at the Activity side you have to receive this Broadcast message
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
mMessageReceiver, new IntentFilter("PercentageUpdates"));
By this way you can send percentage to an Activity. here mPercentageReceiver is the class in that class you will perform what ever you want....
private BroadcastReceiver mPercentageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String percentage = intent.getStringExtra("percentage");
if (percentage != null) {
// You can set the percentage here
}
}
};

Related

Understanding the Android issue causing signal 11 (SIGSEGV), code 1 (SEGV_MAPERR) Error after a few seconds

I am running a sensor and location service, the data is passed to the TraceManager file which where it is dealt with and passed to the TraceCWrapper to be mapped to a shared C library .so , It seems the sensor and location data is fine and received in TraceManager, it then is passed into TraceCWrapper, however the app crashes after a few seconds, and the only error line i get is:
A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8 in tid 29938 (AsyncTask #1), pid 29870 (pp.traceandroid)
public class TraceManager extends AppCompatActivity {
private String TAG = "TraceManager";
private int phoneAngle = 0;
private double initialStepCalibrationOffset;
private int initialPointingAngleDeg = 0;
private int initialAlignmentMode = 0;
private int startingFloorID = 0;
private LatLng startingLatLong;
private double startingAccuracy = 1.0;
private Context context;
private boolean isMagConsistentAtInit = false;
private boolean isMagValid = true;
private Timer callBackTimer;
private String[] contentsStatic;
private String[] contentsDynamic;
private boolean isRunning = false;
private TraceCWrapper traceCWrapper = new TraceCWrapper();
Handler callbackHandler = new Handler();
Runnable callbackRunnable;
//internal use only
private boolean _traceCDontActuallyUse;
// The interval, in seconds, for providing trace updates.
public ObservableDouble updateCallbackInterval = new ObservableDouble(0){
#Override
public void addOnPropertyChangedCallback(#NonNull OnPropertyChangedCallback callback) {
if(isRunning){
stopCallbackTimer();
startCallbackTimer();
}
super.addOnPropertyChangedCallback(callback);
}
};
private double updateCallBackIntervalValue = updateCallbackInterval.get();
/// A Boolean value
public ObservableBoolean allowsBackgroundExecution = new ObservableBoolean(false){
#Override
public void addOnPropertyChangedCallback(#NonNull OnPropertyChangedCallback callback) {
if(isRunning){
stopUpdatingTrace();
startUpdatingTrace();
}
super.addOnPropertyChangedCallback(callback);
}
};
private boolean allowsBackgroundExecutionValue = allowsBackgroundExecution.get();
public TraceManager(Context context){
this.context=context;
}
public TraceManager(){
}
public void initialiseTrace(String[] mapFloors,
String[] initialDynamicMaps,
int phoneRelativeToBodyDegree, //this comes from onboarding?
double updateCallBackIntervalValue,
boolean allowsBackgroundExecutionValue,
double initialStepCalibrationOffset, //standard
String[] iBeaconUUIDs,
int startingFloorID,
LatLng startingLatLong, //this is form the starting node
double startingAccuracy, //
boolean _traceCDontActuallyUse,
int phoneOrientation,
int phoneOrientationUse,
boolean magntometerValid
){
this.contentsStatic = mapFloors;
this.contentsDynamic = initialDynamicMaps;
this.phoneAngle = phoneRelativeToBodyDegree;
this.initialStepCalibrationOffset = initialStepCalibrationOffset;
this.updateCallbackInterval = updateCallbackInterval;
this.allowsBackgroundExecution = allowsBackgroundExecution;
this.isMagValid = magntometerValid;
if(!(iBeaconUUIDs.length <=0)){
LocationProvider.arrayOfUUIDsToDetect = iBeaconUUIDs;
}else{
Log.i(TAG, "TraceManager.init: ignoring ibeaconUIDs, because it is empty. Default used");
};
this.startingFloorID = startingFloorID;
this.startingLatLong = startingLatLong;
this.startingAccuracy = startingAccuracy;
this.initialPointingAngleDeg = phoneOrientation;
this.initialAlignmentMode = phoneOrientationUse;
//internal use only
this._traceCDontActuallyUse = _traceCDontActuallyUse;
}
//Functions
/// Broadcast Receiver to get readings from MotionProvider/service
public void startUpdatingSensors(){
//Start sensor service
Intent startService = new Intent(TraceManager.this, SensorService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(startService);
} else {
startService(startService);
}
}
/// Starts the generation of trace updates.
public void startUpdatingTrace(){
//Start Sensors
//startUpdatingSensors();
//register for sensorBroadcast
BroadcastReceiver sensorReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "imu Received");
TCIMUEvent tcimuEvent = (TCIMUEvent) intent.getSerializableExtra("imu");
traceCWrapper.provideDeviceMotion(tcimuEvent, 1, 90, RotationMode.PortraitYUp);
}
};
LocalBroadcastManager.getInstance(context).registerReceiver(
sensorReceiver, new IntentFilter("imuCreated")
);
//register for locationBroadcast
//register for sensorBroadcast
BroadcastReceiver locationReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "location Received");
TCLocationEvent tcLocationEvent = (TCLocationEvent) intent.getSerializableExtra("locationCreated");
Log.d(TAG, "Inlocation reciever");
traceCWrapper.provideLocation(tcLocationEvent);
}
};
LocalBroadcastManager.getInstance(context).registerReceiver(
locationReceiver, new IntentFilter("locationCreated")
);
Log.d(TAG, "inside updating trace");
//Start CallbackTimer
startCallbackTimer();
}
private void CallbackUpdate() {
/* callbackRunnable = new Runnable(){
#Override
public void run() {
Log.d(TAG, "calling callback");
traceCWrapper.getLatestTraceResult();
callbackHandler.postDelayed(this, 1000);
}
};*/
}
private void startCallbackTimer(){
Log.d(TAG, "I get in here callback");
callbackRunnable = new Runnable(){
#Override
public void run() {
Log.d(TAG, "calling callback");
traceCWrapper.getLatestTraceResult();
callbackHandler.postDelayed(this, 1000);
}
};
callbackHandler.postDelayed(callbackRunnable, 1000);
}
private void stopCallbackTimer(){
callbackHandler.removeCallbacks(callbackRunnable);
}
//Calls TraceCWrapper upadate maps and passes the dynamic maps
/* public void updateMaps(String[] dynamicMaps){
traceCWrapper.updateMaps(dynamicMaps dynamicmaps){
}
}*/
public void stopUpdatingTrace(){
boolean stopSensors = true;
if(stopSensors){
stopUpdatingSensors();
}
//Callback Timer
stopCallbackTimer();
//State
isRunning = false;
//Trace terminate
if (_traceCDontActuallyUse == false){
traceCWrapper.terminate();
}
}
private void stopUpdatingSensors() {
//todo
//stop the event bus
//stop the service
}
#RequiresApi(api = Build.VERSION_CODES.O)
public void provideManualLocation(TraceManualLocation manualLocation){
if(isRunning){
}else{
Log.e(TAG, "Calling provideManualLocation, but is running is set to false");
}
if(!_traceCDontActuallyUse){
traceCWrapper.provideManualLocation(manualLocation);
}
}
#RequiresApi(api = Build.VERSION_CODES.O)
public void provideManualHeadingCorrection(TraceManualHeading traceManualHeading){
if(isRunning){
}else{
Log.e(TAG, "Calling provideHeadingCorrection, but is running is set to false");
}
if (!_traceCDontActuallyUse){
traceCWrapper.provideManualHeading(traceManualHeading);
}
}
public void updateParameter(TraceCVarParameter traceCVarParameter, double value){
if(isRunning){
}else{
Log.e(TAG, "Calling updateparameter, but is running is set to false");
}
//todo
//callback async
}
//Private [START]
boolean isInitialised = false;
public boolean isInitialised() {
if(!isInitialised){
}else{
//todo
//send to didfinishinitialisation? confirm isMagConsistentAtInit is true
}
return isInitialised;
}
private boolean isMagConsistantAtInit = false;
private Timer callbackTimer;
/* public traceCallBack(int seconds){
callBackTimer = new Timer();
callBackTimer.schedule(new callUpdate(), seconds*1000);
}*/
class callUpdate extends TimerTask{
#Override
public void run() {
//traceCWrapper.getLatestTraceResult();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_trace_manager);
}
}
I do not have enough room to add the TraceCWrapper file however the library is loaded as:
static CLibrary lib = Native.loadLibrary("com.waymap.app.traceandroid", CLibrary.class);
And as the main example the method, traceCWrapper.provideDeviceMotion() is received in TraceCWrapper as:
//Provide Device Motion
public static boolean provideDeviceMotion(TCIMUEvent mTCIMUEvent, int Status, double userHeadingDeg, float rotationMode){
DeviceMotion dM = new DeviceMotion();
dM.setTcimuEvent(mTCIMUEvent);
dM.setStatus(Status);
dM.setUserHeadingDeg(userHeadingDeg);
dM.setRotationMode(rotationMode);
if(isRunning) {
new sendToTraceHandleImuEvent().execute(dM);
isInitalized = true;
return isInitalized;
}else{
Log.i(TAG, "IMU update ignored as not running");
isInitalized = false;
return isInitalized;
}
}
public static class sendToTraceHandleImuEvent extends AsyncTask<DeviceMotion,Void,Void>{
#Override
protected Void doInBackground(DeviceMotion... devicemotions) {
/*public class Arg extends Structure {
public devicemotions[] var1 = new byte[9];
public devicemotions[] var2 = new byte[5];
}*/
Log.d(TAG, "InTraceCwrapper Again, provideIMU");
lib.TraceHandleImuEvent(devicemotions[0].getTcimuEvent(), devicemotions[0].getStatus(), devicemotions[0].getUserHeadingDeg(), devicemotions[0].getRotationMode());
return null;
}
}
You will have to excuse the large amount of Logging and excess code as i have been wrestling with this for a while.
When passing my TCIMUEvent i am using the structure annotation as below:
#Structure.FieldOrder({ "time", "accel", "accelValid", "mag", "magValid", "gyro", "gyroValid", "pressure", "pressureValid", "temperature", "temperatureValid"})
public class TCIMUEvent extends Structure implements Serializable {
public double time;
public float[] accel = new float[3];
public boolean accelValid;
public float[] mag = new float[3];
public boolean magValid;
public float[] gyro = new float[3];
public boolean gyroValid;
public float pressure;
public boolean pressureValid;
public float temperature;
public boolean temperatureValid;
public TCIMUEvent(double time, float[] accel, boolean accelValid, float[] mag, boolean magValid, float[] gyro, boolean gyroValid, float pressure, boolean pressureValid, float temperature, boolean temperatureValid) {
this.time = time;
this.accel = accel;
this.accelValid = accelValid;
this.mag = mag;
this.magValid = magValid;
this.gyro = gyro;
this.gyroValid = gyroValid;
this.pressure = pressure;
this.pressureValid = pressureValid;
this.temperature = temperature;
this.temperatureValid = temperatureValid;
}
}
The Java C Mappings that are required:
My Java Library to map:
void TracehandleLocationEvent(TCLocationEvent tcLocationEvent);
void TracehandleManualLocationEvent(TCManualLocationEvent tcManualLocationEvent);
void TracehandleManualHeadingEvent(TCManualHeadingEvent tcManualHeadingEvent);
void TracehandleManualInitialLocation(TCLocationEvent initialLocationEvent);
void TraceHandleImuEvent(TCIMUEvent tcimuEvent, int Status, double userHeadingDeg, float rotationMode);
void TraceGetResult(Double uptime, Pointer traceResult_out);
-------- These map retrospectively to C:---------
void TraceHandleLocationEvent (const Trace_locationSample_t *locationSample)
void TraceHandleManualLocationEvent(const Trace_manualLocationSample_t
*manualLocationSample)
void TraceHandleManualHeadingEvent(const Trace_manualHeadingSample_t
*manualHeadingSample)
void TraceHandleLocationEvent (const Trace_locationSample_t *locationSample)
void TraceHandleImuEvent(Trace_imuDataSample_t *imuDataSample, int *status,
double *userHeadingDeg, StrapdownStreaming_RotationMode *currentRotateMode)
void TraceGetResult(double time, Trace_Result_t *TraceResult)
The new Mappings look like this, the structures for the objects are all the same format as above in the original question:
void TracehandleLocationEvent(TCLocationEvent tcLocationEvent);
void TracehandleManualLocationEvent(TCManualLocationEvent tcManualLocationEvent);
void TracehandleManualHeadingEvent(TCManualHeadingEvent tcManualHeadingEvent);
void TracehandleManualInitialLocation(TCLocationEvent initialLocationEvent);
void TraceGetResult(DoubleByReference uptime, TCResult traceResult_out);
void TraceHandleImuEvent(TCIMUEvent tcimuEvent, IntByReference status, DoubleByReference heading, FloatByReference rotationMode);
The error being thrown now in relation to the empty constructors in my Structure objects:
java.lang.Error: Structure.getFieldOrder() on class com.dataTypes.TCLocationEvent returns names ([altitude, coordinate, horizontalAccuracy, timestamp, verticalAccuracy]) which do not match declared field names ([])
at com.sun.jna.Structure.getFields(Structure.java:1089)
at com.sun.jna.Structure.deriveLayout(Structure.java:1232)
at com.sun.jna.Structure.calculateSize(Structure.java:1159)
at com.sun.jna.Structure.calculateSize(Structure.java:1111)
at com.sun.jna.Structure.allocateMemory(Structure.java:414)
at com.sun.jna.Structure.<init>(Structure.java:205)
at com.sun.jna.Structure.<init>(Structure.java:193)
at com.sun.jna.Structure.<init>(Structure.java:180)
at com.sun.jna.Structure.<init>(Structure.java:172)
at com.dataTypes.TCLocationEvent.<init>(TCLocationEvent.java:30)
at com.locationGetter.LocationService.<clinit>(LocationService.java:39)
SIGSEGV errors with JNA mappings are frequently caused by accessing native memory you don't own. Problems vary, but the first place to look is your structure type mappings and method/function argument mappings.
As one specific example (there may be more), your code includes this mapping:
void TraceHandleImuEvent(TCIMUEvent tcimuEvent, int Status,
double userHeadingDeg, float rotationMode);
However, the native mapping does not expect an int, double, and float here. It expects pointers:
void TraceHandleImuEvent(Trace_imuDataSample_t *imuDataSample, int *status,
double *userHeadingDeg, StrapdownStreaming_RotationMode *currentRotateMode)
(Structures like TCIMUEvent are automatically mapped to their pointers when passed as arguments, so that one's okay.)
What is happening is that you are passing an int for status (e.g., 8) but the native code is thinking "There's an integer stored at memory location 0x8." You don't own that memory, and thus the error.
IntByReference would be the correct type mapping here, and for many of those function arguments.

Firebase Session Management: Issues with instatiating Global User

I'm a Android Studio coding beginner and currently building a nutrition app to get programming practice. I use Firebase for Authentication and as a database to save User data.
How it works:
My app has a survey built in which asks for body specifics and taste (age, height, liked/disliked ingredients etc.). I have a class GlobalUser with public static attributes to save the answers in the app. When the user registers, he is sent directly to the survey activity. There he answers the questions and the results are written to the Firebase database under his UID (I use a User class with the same attributes as GlobalUser to create an instance and use Firebase's setValue(Object) method). If he signs in (or still is signed in), the LoginRegistrationActivity directly sends him to the MainActivity. There, the GlobalUser class gets instantiated with the data saved under his UID. From the MainActivity, he can navigate to a ProfileActivity where the UI gets updated based on his data. This works quite well. After doing the survey I can find the results in a child node consisting of the UID of the user, the UI gets updated correctly and the sign in/registration process works as intended.
What is wrong:
However, as I was playing around with different designs and constantly restarting the app, it started to crash occasionally. After some testing it showed that the GlobalUser class wasn't updated and thus the ArrayLists were null and caused NullPointerExceptions when I used .size() on them. Since this issue only occurs rarely and seems to be related to restarting the app multiple times I thought it would have something to do with the Activity lifecycle so I also updated the GlobalUser in onStart and onResume but it didn't help. I also tried updating GlobalUser again in the ProfileActivity directly before the ArrayLists were set but it didn't work. I still guess it has something to do with the lifecycle but I have no idea where I should start. Here's the code of the relevant classes/actvitites:
LoginRegistrationActivity:
public class LoginRegistrationActivity extends AppCompatActivity {
private DatabaseReference mRef;
private FirebaseAuth mAuth;
private EditText emailAddress;
private EditText emailPassword;
private Button emailLogin;
private Button emailRegistration;
private TextView forgotPassword;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_registration);
mAuth = FirebaseAuth.getInstance();
if (mAuth.getCurrentUser()!=null){
Intent i = new Intent (LoginRegistrationActivity.this, MainActivity.class);
LoginRegistrationActivity.this.startActivity(i);
}
emailAddress = findViewById(R.id.address_edit);
emailPassword = findViewById(R.id.password_edit);
emailLogin = findViewById(R.id.mail_login_button);
emailRegistration = findViewById(R.id.mail_registration_button);
forgotPassword = findViewById(R.id.forgot_password);
emailRegistration.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String email = emailAddress.getText().toString().trim();
String password = emailPassword.getText().toString().trim();
if (TextUtils.isEmpty(email)){
Toast.makeText(LoginRegistrationActivity.this, "Bitte E-Mail Addresse eingeben!", Toast.LENGTH_LONG).show();
return;
}
if (TextUtils.isEmpty(password)){
Toast.makeText(LoginRegistrationActivity.this, "Bitte Passwort eingeben!", Toast.LENGTH_LONG).show();
return;
}
if (password.length()<6){
Toast.makeText(LoginRegistrationActivity.this, "Passwort muss mindestens sechs Zeichen lang sein!", Toast.LENGTH_LONG).show();
return;
}
mAuth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(LoginRegistrationActivity.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (!task.isSuccessful()){
Toast.makeText(LoginRegistrationActivity.this, "Unbekannter Fehler", Toast.LENGTH_LONG).show();
} else {
Intent i = new Intent (LoginRegistrationActivity.this, SurveyGreetingActivity.class);
LoginRegistrationActivity.this.startActivity(i);
finish();
}
}
});
}
});
emailLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String password = emailPassword.getText().toString();
String email = emailAddress.getText().toString();
if (TextUtils.isEmpty(email)){
Toast.makeText(LoginRegistrationActivity.this, "Bitte E-Mail Addresse eingeben!", Toast.LENGTH_LONG).show();
return;
}
if (TextUtils.isEmpty(password)){
Toast.makeText(LoginRegistrationActivity.this, "Bitte Passwort eingeben!", Toast.LENGTH_LONG).show();
return;
}
if (password.length()<6){
Toast.makeText(LoginRegistrationActivity.this, "Passwort muss mindestens sechs Zeichen haben!", Toast.LENGTH_LONG).show();
return;
}
mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(LoginRegistrationActivity.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (!task.isSuccessful()){
Toast.makeText(LoginRegistrationActivity.this, "Unbekannter Fehler beim Einloggen", Toast.LENGTH_LONG).show();
} else {
Intent i = new Intent (LoginRegistrationActivity.this, MainActivity.class);
LoginRegistrationActivity.this.startActivity(i);
finish();
}
}
});
}
});
}
}
MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final FirebaseAuth mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference mRef = database.getReference().child("users").child("uid").child(mAuth.getCurrentUser().getUid());
//In case the user cancelled the app when filling out the survey for the first time
if (mRef == null){
MainActivity.this.startActivity(new Intent (MainActivity.this, SurveyGreetingActivity.class));
}
//sets GlobalUser to data saved in Firebase Database User object
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user!=null){
GlobalUser.setToUser(user);
GlobalUser.setGlobalUid(mAuth.getCurrentUser().getUid());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(getApplicationContext(), "Database Error", Toast.LENGTH_LONG).show();
}
});
}
}
GlobalUser:
package com.example.andre.valetto02;
import java.util.ArrayList;
public class GlobalUser {
public static String globalUid = null;
public static ArrayList<Ingredient> globalLikes;
public static ArrayList<Ingredient> globalDislikes;
public static int globalAge;
public static int globalWeight;
public static int globalHeight;
public static int globalTrainingGoal;
public static int globalDailyActive;
public static boolean globalIsMale;
public GlobalUser() {
}
public static String getGlobalUid() {
return globalUid;
}
public static void setGlobalUid(String globalUid) {
GlobalUser.globalUid = globalUid;
}
public static ArrayList<Ingredient> getGlobalLikes() {
return globalLikes;
}
public static void setGlobalLikes(ArrayList<Ingredient> globalLikes) {
GlobalUser.globalLikes = globalLikes;
}
public static ArrayList<Ingredient> getGlobalDislikes() {
return globalDislikes;
}
public static void setGlobalDislikes(ArrayList<Ingredient> globalDislikes) {
GlobalUser.globalDislikes = globalDislikes;
}
public static int getGlobalAge() {
return globalAge;
}
public static void setGlobalAge(int globalAge) {
GlobalUser.globalAge = globalAge;
}
public static int getGlobalWeight() {
return globalWeight;
}
public static void setGlobalWeight(int globalWeight) {
GlobalUser.globalWeight = globalWeight;
}
public static int getGlobalHeight() {
return globalHeight;
}
public static void setGlobalHeight(int globalHeight) {
GlobalUser.globalHeight = globalHeight;
}
public static int getGlobalTrainingGoal() {
return globalTrainingGoal;
}
public static void setGlobalTrainingGoal(int globalTrainingGoal) {
GlobalUser.globalTrainingGoal = globalTrainingGoal;
}
public static int getGlobalDailyActive() {
return globalDailyActive;
}
public static void setGlobalDailyActive(int globalDailyActive) {
GlobalUser.globalDailyActive = globalDailyActive;
}
public static boolean isGlobalIsMale() {
return globalIsMale;
}
public static void setGlobalIsMale(boolean globalIsMale) {
GlobalUser.globalIsMale = globalIsMale;
}
public static void setToUser(User user) {
GlobalUser.setGlobalAge(user.getAge());
GlobalUser.setGlobalWeight(user.getWeight());
GlobalUser.setGlobalHeight(user.getHeight());
GlobalUser.setGlobalDailyActive(user.getDailyActive());
GlobalUser.setGlobalTrainingGoal(user.getTrainingGoal());
GlobalUser.setGlobalIsMale(user.getIsMale());
GlobalUser.setGlobalLikes(user.getLikes());
GlobalUser.setGlobalDislikes(user.getDislikes());
}
public static void resetLikesAndDislikes(){
globalLikes = new ArrayList <>();
globalDislikes = new ArrayList<>();
}
public static User globalToUser () {
return new User (globalLikes, globalDislikes, globalWeight, globalHeight, globalAge, globalTrainingGoal, globalDailyActive, globalIsMale);
}
}
User:
package com.example.andre.valetto02;
import java.util.ArrayList;
public class User {
ArrayList<Ingredient> likes;
ArrayList<Ingredient> dislikes;
Boolean isMale;
public Boolean getIsMale(){return isMale;}
public void setIsMale(Boolean b){isMale = b;}
public void setDislikes(ArrayList<Ingredient> dislikes) {
this.dislikes = dislikes;
}
public User (){
likes = new ArrayList<>();
dislikes = new ArrayList<>();
weight = 0;
height = 0;
age = 0;
trainingGoal = 2;
dailyActive = 1;
isMale=true;
}
public User (ArrayList<Ingredient> l, ArrayList<Ingredient> d, int w, int h, int a, int tG, int dA, boolean iM) {
likes = l;
dislikes = d;
weight = w;
height = h;
age = a;
trainingGoal = tG;
dailyActive = dA;
isMale = iM;
}
int age;
public ArrayList<Ingredient> getDislikes() {
return dislikes;
}
public ArrayList<Ingredient> getLikes() {
return likes;
}
public void setLikes (ArrayList<Ingredient> list){
likes = list;
}
public void setDisikes (ArrayList<Ingredient> list){
dislikes = list;
}
public int getAge () {
return age;
}
public void setAge (int i) {
age = i;
}
int weight;
public int getWeight (){
return weight;
}
public void setWeight(int i) {
weight = i;
}
int height;
public int getHeight (){
return height;
}
public void setHeight(int i) {
height = i;
}
int trainingGoal; //trainingGoal = 0 means weight loss, 1 means muscle gain and 2 means healthy living
public void setTrainingGoal(int i) {
trainingGoal = i;
}
public int getTrainingGoal(){
return trainingGoal;
}
int dailyActive; //dailyActive = 0 means wenig, 1 means leicht, 2 means moderat, 3 means sehr and 4 means extrem
public int getDailyActive() {return dailyActive;}
public void setDailyActive(int i) {dailyActive = i;}
public double computeCalorieGoal(){
if (isMale) {
double RMR;
RMR = weight*10 + 6.25*height - 5*age + 5;
if (dailyActive==0) {RMR=RMR*1.2;}
else if (dailyActive==1) {RMR=RMR*1.375;}
else if (dailyActive==2) {RMR=RMR*1.55;}
else if (dailyActive==3) {RMR=RMR*1.725;}
else {RMR=RMR*1.9;}
if (trainingGoal == 0) {RMR = RMR - 400;}
else if (trainingGoal ==1){RMR = RMR + 400;}
return RMR;
} else {
double RMR;
RMR = weight*10 + 6.25*height - 5*age - 161;
if (dailyActive==0) {RMR=RMR*1.2;}
else if (dailyActive==1) {RMR=RMR*1.375;}
else if (dailyActive==2) {RMR=RMR*1.55;}
else if (dailyActive==3) {RMR=RMR*1.725;}
else {RMR=RMR*1.9;}
if (trainingGoal == 0) {RMR = RMR - 300;}
else if (trainingGoal ==1){RMR = RMR + 300;}
return RMR;
}
}
}
Thanks for the help!
I just found the mistake. It has nothing to do with the activity lifecycle and it only indirectly had something to do with restarting the app. The problem was that Firebase's Value Event Listeners are still AsyncTasks. When I started the app and immediately opened the ProfileActivity, the Activity was created before the Firebase AsyncTask could fetch the data from the Database. Thus the ProfileActivity would call the .size() method on the ArrayLists before they were instantiated. In essence, the error occurred when you clicked too quickly through the UI and were faster than the asynchronous data fetching task.
Therefore I moved the session management to the LoginRegistrationActivity like this:
if (mAuth.getCurrentUser()!=null){
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
DatabaseReference mRef = firebaseDatabase.getReference().child("users").child("uid").child(mAuth.getCurrentUser().getUid());
//In case the user cancelled the app when filling out the survey for the first time
if (mRef == null){
LoginRegistrationActivity.this.startActivity(new Intent (LoginRegistrationActivity.this, SurveyGreetingActivity.class));
}
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user!=null) {
GlobalUser.setToUser(user);
GlobalUser.setGlobalUid(mAuth.getCurrentUser().getUid());
}
Intent i = new Intent (LoginRegistrationActivity.this, MainActivity.class);
LoginRegistrationActivity.this.startActivity(i);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
By moving LoginRegistrationActivity.this.startActivity(i) to the onDataChange method, I ensure that the GlobalUser variables get instantiated before the MainActivity is started. There are probably still more elegant ways to do this.

Using Dagger 2 in SyncAdapter class

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.

Parcelable list inside Parcelable class not reading back with Parcel.readTypedList

I have two Activities, A and B. I am trying to send object from Activity A, to Activity B. When in Activity A, I can see that my List contains two items, but when I retrieve it in Activity B, the List contains 7000000+ records.
Here is my Assessment class, that implements Parcelable, and contains an ArrayList<Photo> which should be parcelable as well.
Assessment POJO:
public class Assessment extends BaseObservable implements Parcelable {
public Assessment(){
}
#SerializedName("Vehicle")
private String vehicle;
#SerializedName("Photos")
private List<Photo> photos;
#Bindable
public String getVehicle() {
return vehicle;
}
public void setVehicle(String vehicle) {
this.vehicle = vehicle;
notifyPropertyChanged(BR.vehicle);
}
public List<Photo> getPhotos() {
return photos;
}
public void setPhotos(List<Photo> photos) {
this.photos = photos;
}
protected Assessment(Parcel in) {
vehicle = in.readString();
photos = new ArrayList<Photo>();
in.readTypedList(photos, Photo.CREATOR);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(vehicle);
dest.writeTypedList(photos);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Assessment> CREATOR = new Parcelable.Creator<Assessment>() {
#Override
public Assessment createFromParcel(Parcel in) {
return new Assessment(in);
}
#Override
public Assessment[] newArray(int size) {
return new Assessment[size];
}
};
}
Photo POJO:
public class Photo implements Parcelable {
public Photo(){
}
#SerializedName("PhotoPath")
private String photoPath;
public String getPhotoPath() {
return photoPath;
}
public void setPhotoPath(String photoPath) {
this.photoPath = photoPath;
}
#SerializedName("Base64PhotoString")
private String photoBase64String;
public String getPhotoBase64String() {
return photoBase64String;
}
public void setPhotoBase64String(String photoBase64String) {
this.photoBase64String = photoBase64String;
}
protected Photo(Parcel in) {
photoPath = in.readString();
photoBase64String = in.readString();
}
//region parelable
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(photoPath);
dest.writeString(photoBase64String);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Photo> CREATOR = new Parcelable.Creator<Photo>() {
#Override
public Photo createFromParcel(Parcel in) {
return new Photo(in);
}
#Override
public Photo[] newArray(int size) {
return new Photo[size];
}
};
//endregion
}
Here is how I send the object via Intent from Activity A, to Activity B:
public void OnAdapterItemClicked(View view){
Intent activityIntent = new Intent(this, com.example.andrewkp.gaassessing.DisplayAssessment.class);
Assessment extraAssessment = getAssessmentFromCollection(view); //extraAssessment.getPhotos().size() == 2
activityIntent.putExtra("assessment", extraAssessment);
startActivity(activityIntent);
}
And here is how I read the Parcelable object in Activity B:
assessment = getIntent().getExtras().getParcelable("assessment");
I have looked at the following article, and I follow exactly what they do, but my photos list does not persist through to Activity B:
When I debug the readTypedList method in Parcel class, I can see that it adds 7000000+ records to my ArrayList, but never removes them. Why is this behavior happening?
You are able to put up to 1MB of data in a Bundle encapsulated inside Intent.
You will get bunch of errors when sending PhotoBase64String in Bundle.
However, in order to overcome this issue, I would suggest path/URI of your photo to your second activity. Then in your second activity, read photo from that path, and perform your desired operation.

Concurrent access to static method

I have an activity that contains 2 ArcProgress , both of them get data from seperate childs from Firebase, so I created a static method in a seperate Class that change values of Arcs and then call it on the firebase's onDataChange method. The problem that the arcs shows wrong values and I think that is caused by a concurrent access to that class.
I tried to add synchronized but that doesn't solve the problem.
This is the class that contains the method
public class ArcProgSettings {
private static int diff=0;
private static int i;
private static CountDownTimer waitTimer;
private int previousTemp=0;
private int newT=0;
private ArcProgress arcTemp;
public ArcProgSettings(){
}
public ArcProgSettings(int previousTemp, int newT, final ArcProgress
arcTemp){
this.previousTemp=previousTemp;
this.newT=newT;
this.arcTemp=arcTemp;
}
public synchronized void setProg() {
diff = newT - previousTemp;
i = previousTemp;
if (diff >= 0) {
waitTimer = new CountDownTimer((diff + 2) * 100, 100) {
public void onTick(long millisUntilFinished) {
//called every 300 milliseconds, which could be used to
//send messages or some other action
arcTemp.setProgress(i);
i++;
}
public void onFinish() {
//After 60000 milliseconds (60 sec) finish current
//if you would like to execute something when time
finishes
}
}.start();
} else {
waitTimer = new CountDownTimer(-(diff - 2) * 100, 100) {
public void onTick(long millisUntilFinished) {
arcTemp.setProgress(i);
i--;
}
public void onFinish() {
}
}.start();
}
}
}
and this is the Activity that contains the calls:(setProg)
public class VisualisationActivity extends AppCompatActivity {
private ArcProgress arcTemp;
private DatabaseReference mTempDatabase;
private String MyTemp="0";
int previousTemp=0;
private ArcProgress arcCurrent;
private DatabaseReference mCurrentDatabase;
private String MyCurrent="0";
int previousCurrent=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_visualisation);
arcTemp=(ArcProgress)findViewById(R.id.arc_prog_temp);
mTempDatabase =
FirebaseDatabase.getInstance().getReference().child("temp");
mTempDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
MyTemp =(dataSnapshot.child("value").getValue().toString());
final int newT= Integer.parseInt(MyTemp);
ArcProgSettings arc=new ArcProgSettings(previousTemp,
newT,arcTemp);
arc.setProg();
previousTemp=newT;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
arcCurrent=(ArcProgress)findViewById(R.id.arc_progress_current);
mCurrentDatabase =
FirebaseDatabase.getInstance().getReference().child("current");
mCurrentDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
MyCurrent =(dataSnapshot.child("value").getValue().toString());
final int newC= Integer.parseInt(MyCurrent);
ArcProgSettings arc1=new ArcProgSettings(previousCurrent,
newC,arcCurrent);
arc1.setProg();
previousCurrent=newC;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
If you need separate timers and progress for each listener, why to use static variables? (waitTimer, diff, i). The problem is that both instances of ArcProgress are updating the same values, its not concurrency, is design. Change your variables to make it private only and you'll se a different result.

Categories