Cannot modify managed objects outside of a write transaction - android

I must push a object just with one note and to push his media files, but it do not want to do this. I do not know why is difference , but if i do this without changing notes its work fine , but I need to send just changed notes , please help. What is my problem? I need to understand why this happen
private void pushMediaFiles(Task task,Note newNote, PushTaskListener listener)
{
PushTaskModel pushTaskModel=new PushTaskModel(Realm.getDefaultInstance().copyFromRealm(task));
pushTaskModel.notes.clear();
Note oldNote = new Note();
oldNote.update(newNote);
pushTaskModel.notes.add(oldNote);
service.pushData(pushTaskModel)
.map(taskResponse ->
{
ArrayList<MediaFile> filesToSave = new ArrayList<>();
for (Note note : task.getNotes())
{
for (MediaFile mediaFile : note.getMediaFiles())
{
if (mediaFile.hasChanges() && mediaFile.getFileAbsolutePath() != null)
{
filesToSave.add(Realm.getDefaultInstance().copyFromRealm(mediaFile));
}
}
}
return filesToSave;
})
.subscribe(taskMediaFiles ->
{
Log.d("PUSH_TASK_EVENT","3 GOOD");
if (taskMediaFiles.size() == 0)
{
listener.onSuccess();
return;
}
for (MediaFile mediaFile: taskMediaFiles)
{
saveMediaFile(mediaFile, new OnResponseListener()
{
#Override
public void onStart()
{
}
#Override
public void onComplete(boolean successfully)
{
listener.onSuccess();
}
#Override
public void onError(Throwable throwable)
{
listener.onError(throwable);
}
});
}
});
}

Related

The data is not received after the Internet is recovering

I'm using RxJava3 and Live data.
I'm calling methos getAllMovies and getAllGenres in background thread, then set the received data to MutableLiveData.
In my splash activity I'm calling these methods from viewModel.
I have lottie animation , and each time lottie animation end, I'm checking if data received , if yes , opening another activity, else, waiting again for data receive.
The problem is when I lose internet connection , data is not received after the Internet is recovering.
I'm trying to run app when the wifi and internet is off, and then turning on wifi.
But always getting 0 in methods size.
If I running the app with internet on , data received normally.
That what I have.
App repository:
private MutableLiveData<List<GenreResult>> mGenresResponseMutableLiveData = new MutableLiveData<>();
public MutableLiveData<List<GenreResult>> getGenresResponseMutableLiveData() {
AppService appService = RetrofitInstance.getService();
appService.getAllGenres(mApplication.getResources().getString(R.string.api_key),
mApplication.getResources().getString(R.string.language))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<GenreResponse>() {
#Override
public void accept(GenreResponse genreResponses) throws Throwable {
mGenresResponseMutableLiveData.setValue(genreResponses.getGenreResults());
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Throwable {
Toast.makeText(mApplication.getApplicationContext(), throwable.getMessage(), Toast.LENGTH_LONG).show();
}
});
return mGenresResponseMutableLiveData;
}
ViewModel:
public class GenresViewModel extends AndroidViewModel {
private AppRepository mRepository;
public GenresViewModel(#NonNull Application application) {
super(application);
mRepository = new AppRepository(application);
}
public MutableLiveData<List<GenreResult>> getGenreLiveData() {
return mRepository.getGenresResponseMutableLiveData();
}
}
SplashActivity calling getGenre method:
private void getGenreList() {
mGenreResultArrayList = new ArrayList<>();
mGenresViewModel = new ViewModelProvider.AndroidViewModelFactory(getApplication()).create(GenresViewModel.class);
mGenresViewModel.getGenreLiveData().observe(this, new Observer<List<GenreResult>>() {
#Override
public void onChanged(List<GenreResult> genreResults) {
mGenreResultArrayList = (ArrayList<GenreResult>) genreResults;
}
});
}
SplashActivity checking on lottie listener if data received:
private void initViews() {
mLAVLoader = findViewById(R.id.lavLoader);
mLAVLoader.addAnimatorListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mGenreResultArrayList != null && mGenreResultArrayList.size() > 0
&& mMovieResultArrayList != null && mMovieResultArrayList.size() > 0) {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
intent.putParcelableArrayListExtra("genreList", mGenreResultArrayList);
intent.putParcelableArrayListExtra("moviesList", mMovieResultArrayList);
startActivity(intent);
} else {
mLAVLoader.playAnimation();
Log.d("myDebug", "onAnimationEnd: " + mGenreResultArrayList.size()+ " "+mMovieResultArrayList.size());
}
}
});
}
I fixed it with retryWhen method in Repository:
public MutableLiveData<List<MovieResult>> getAllMoviesMutableLiveData() {
AppService appService = RetrofitInstance.getService();
appService.getAllMovies(mApplication.getResources().getString(R.string.api_key),
mApplication.getResources().getString(R.string.language),
"popularity.desc")
.retryWhen(throwable ->
throwable.delay(5, TimeUnit.SECONDS))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MoviesResponse>() {
#Override
public void accept(MoviesResponse moviesResponse) throws Throwable {
mAllMoviesMutableLiveData.setValue(moviesResponse.getMovieResults());
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Throwable {
}
});
return mAllMoviesMutableLiveData;
}

Realm with Singleton Pattern in Android : Correct structure or not?

Currently I have following singleton structure in my code for managing Realm transactions. I need to know the pros and cons of the following singleton structure. With this approach i will be calling updateClockModel() as RealManager.getInstance().updateClockModel(...) from all my activities and fragments.
public class RealmManager {
private static final String TAG = "RealmManager";
private static RealmManager mInstance = null;
private final ThreadLocal<Realm> localRealm = new ThreadLocal<>();
public static RealmManager getInstance() {
if (mInstance == null)
mInstance = new RealmManager();
return mInstance;
}
public Realm openLocalInstance() {
Realm realm = Realm.getDefaultInstance();
if (localRealm.get() == null) {
localRealm.set(realm);
}
return realm;
}
public Realm getLocalInstance() {
Realm realm = localRealm.get();
if (realm == null) {
throw new IllegalStateException("No open Realms were found on this thread.");
}
return realm;
}
public void closeLocalInstance() {
Realm realm = localRealm.get();
if (realm == null) {
throw new IllegalStateException(
"Cannot close a Realm that is not open.");
}
realm.close();
if (Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) {
localRealm.set(null);
}
}
protected RealmManager() {
}
public void updateClockModel(ClockRLM clockRLM, OnRealmDatabaseListener mRealmListener) {
Realm mRealm = openLocalInstance();
mRealm.executeTransactionAsync(realm -> {
RealmResults<ClockRLM> result = realm.where(ClockRLM.class).equalTo("timeStamp", clockRLM.getTimeStamp()).findAll();
for (ClockRLM clockRLM1 : result) {
clockRLM1.setUploadedSuccess(true);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d("Clocke ", "inserted TimeStamp " + clockRLM.getTimeStamp());
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(clockRLM, true);
closeLocalInstance();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(clockRLM, false);
closeLocalInstance();
}
});
}
public void addClockModel(ClockRLM clockRLM, OnRealmDatabaseListener mRealmListener) {
Realm mRealm = openLocalInstance();
mRealm.executeTransactionAsync(realm -> realm.copyToRealm(clockRLM), new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d("Clocke ", "Inserted TimeStamp " + clockRLM.getTimeStamp());
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(clockRLM, true);
closeLocalInstance();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
closeLocalInstance();
}
});
}
}
It would work, except those methods that do writes cannot be executed on background threads - only on ui thread - so I'd add something like following method
private void executeInTransaction(Realm.Transaction transaction) {
try {
Realm realm = openLocalInstance();
if(!realm.isAutoRefresh()) {
try {
boolean wasInTransaction = realm.isInTransaction();
if(!wasInTransaction) {
realm.beginTransaction();
}
transaction.execute(realm);
if(!wasInTransaction) {
realm.commitTransaction();
}
} catch(Throwable e) {
if(realm.isInTransaction()) {
realm.cancelTransaction();
}
}
} else {
realm.executeTransactionAsync(transaction);
}
} finally {
closeLocalInstance();
}
}
This way you can do batch background operations with manual transaction opening + execute async writes from UI thread.
You need a bit of tweaking to add a "success/failure" listener but the basics are there.

Retrofit Periodic call with Pagination

I am currently using the Retrofit2.0 to poll the server .I am getting the result in x second but the problem is page number is not updating in the API.Lets come to the code for better clarification
private void startPolling() throws Exception {
Log.e("APP CONSTANT","" + current_page);
MrSaferWebService service = ServiceFactory.createRetrofitService(MrSaferWebService.class, AppConstants.BASE_URL);
final Observable<ReportResponse> reportResponseObservable = service.getListOfInciden("get_report", current_page, 5, incident_lat, incident_long);
Observable.interval(0,20,TimeUnit.SECONDS)
.flatMap(new Func1<Long, Observable<ReportResponse>> () {
#Override
public Observable<ReportResponse> call(Long aLong) {
return reportResponseObservable;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<ReportResponse>() {
#Override
public void call(ReportResponse response) {
Log.i("HEARTBEAT_INTERVAL", "Response from HEARTBEAT");
ActivityUtils.showProgress(false, mRootView, mProgressView, mContext);
if (response.getStatus() == 1) {
current_page = current_page + 1;
if (!response.getReportList().isEmpty()) {
addItems(response.getReportList());
}
else{
//do nothing
}
} else {
Log.e("MY ERROR", "" + "SOME ERROR OCCURED");
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
ActivityUtils.showProgress(true, mRootView, mProgressView, mContext);
// TODO: 22/03/16 ADD ERROR HANDLING
}
});
}
As you can see i have incremented the current_page by 1 every time on
SuccessFull Response but when i check the Log the current_page value are increased only once and after that log are not there and hence there value is also not increasing..So it taking the the same page number every time and giving me the Duplicate response.
Please help me to find what i am missing.
After spending more than a day i just changed Action with Subscriber and everything seems to be working .I don't know what happen internally but it works . I am still trying to figure it out what the difference between Action and Subscriber.
Below are my updated code which did the tricks.
private void startPolling() throws Exception {
final MrSaferWebService service = ServiceFactory.createRetrofitService(MrSaferWebService.class, AppConstants.BASE_URL);
Observable
.interval(0,20,TimeUnit.SECONDS)
.flatMap(new Func1<Long, Observable<ReportResponse>>() {
#Override
public Observable<ReportResponse> call(Long aLong) {
Log.e("PAGE", "" + current_page);
return service.getListOfInciden("get_report", current_page, 5, incident_lat, incident_long);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ReportResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
if (mProgressView !=null && mRootView !=null) {
ActivityUtils.showProgress(false, mRootView, mProgressView, mContext);
}
}
#Override
public void onNext(ReportResponse response) {
if (mProgressView !=null && mRootView !=null) {
ActivityUtils.showProgress(false, mRootView, mProgressView, mContext);
}
if (response.getStatus() == 1) {
if (!response.getReportList().isEmpty()){
current_page = current_page + 1;
addItems(response.getReportList());
}
else{
//do nothing
}
} else {
Log.e("MY ERROR", "" + "SOME ERROR OCCURED");
}
}
});
}

Chromecast MediaRouteProviderService

I have strange issue, I am creating mediaprovider for chromecast using following code that works fine for first instance, list of devices is shown and once slected I use router.selectRoute(routeinfo);
but once I exit app this code unable to find Chromecast device, how ever when I remove app from running apps stack this code works fine again and show devices.
If no device is selected and app is exited using back press then also this code works fine
So what I am doing wrong here? what I think is resources are not cleared when my app exit in simple back pressed.
public class ChromecastRouteProviderService extends MediaRouteProviderService {
final String LOGTAG = "Chromecast";
private static final String CONTROL_CATEGORY = CastMediaControlIntent.categoryForCast(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID);
private static final MediaRouteSelector SELECTOR = new MediaRouteSelector.Builder().addControlCategory(CONTROL_CATEGORY)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK).build();
private IntentFilter controlFilter;
public ChromecastRouteProviderService() {
controlFilter = new IntentFilter();
}
public void onCreate() {
super.onCreate();
controlFilter.addCategory(IAppConstants.CATEGORY);
controlFilter.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
}
#Override
public MediaRouteProvider onCreateMediaRouteProvider() {
return new ChromecastRouteProvider(this);
}
class ChromecastRouteProvider extends MediaRouteProvider {
MediaRouter.Callback callback;
Hashtable routes;
public ChromecastRouteProvider(Context context) {
super(context);
routes = new Hashtable();
callback = new CastCallBack();
}
#Nullable
#Override
public RouteController onCreateRouteController(String routeId) {
MediaRouter.RouteInfo routeInfo = (MediaRouter.RouteInfo) routes.get(routeId);
if (routeInfo == null) {
return super.onCreateRouteController(routeId);
} else {
return new ChromecastRouteController(getContext(), routeInfo);
}
}
#Override
public void onDiscoveryRequestChanged(#Nullable MediaRouteDiscoveryRequest request) {
super.onDiscoveryRequestChanged(request);
if (request == null || !request.isActiveScan() || !request.isValid()) {
stopScan();
return;
}
if (!request.getSelector().hasControlCategory(IAppConstants.CATEGORY)) {
Log.i(LOGTAG, "Not scanning for non remote playback");
stopScan();
return;
} else {
Log.i(LOGTAG, "Scanning...");
mediarouter.addCallback(ChromecastRouteProviderService.SELECTOR, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
return;
}
}
void updateDescriptor() {
final MediaRouteProviderDescriptor.Builder descriptor = new MediaRouteProviderDescriptor.Builder();
for (Iterator iterator = routes.values().iterator(); iterator.hasNext(); ) {
MediaRouter.RouteInfo routeinfo = (MediaRouter.RouteInfo) iterator.next();
try {
Bundle bundle = new Bundle();
bundle.putBoolean("has_upsell", true);
descriptor.addRoute(new MediaRouteDescriptor.Builder(routeinfo.getId(), routeinfo.getName())
.addControlFilter(controlFilter).setPlaybackStream(3)
.setDescription(routeinfo.getDescription())
.setEnabled(true).setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
.setVolumeHandling(1).setVolumeMax(100).setVolume(100)
.setExtras(bundle).build());
} catch (Exception e) {
throw new Error("wtf");
}
}
getHandler().post(new Runnable() {
#Override
public void run() {
setDescriptor(descriptor.build());
}
});
}
void stopScan() {
Log.i(LOGTAG, "Stopping scan...");
try {
MediaRouter.getInstance(getContext()).removeCallback(callback);
return;
} catch (Exception exception) {
return;
}
}
class CastCallBack extends MediaRouter.Callback {
void check(MediaRouter mediarouter, MediaRouter.RouteInfo routeinfo) {
Log.i(LOGTAG, new StringBuilder().append("Checking route ").append
(routeinfo.getName()).toString());
CastDevice device = CastDevice.getFromBundle(routeinfo.getExtras());
if (routeinfo.matchesSelector(ChromecastRouteProviderService.SELECTOR)
&& device != null && device.isOnLocalNetwork()) {
routes.put(routeinfo.getId(), routeinfo);
updateDescriptor();
return;
} else {
return;
}
}
public void onRouteAdded(MediaRouter mediarouter, MediaRouter.RouteInfo routeinfo) {
super.onRouteAdded(mediarouter, routeinfo);
check(mediarouter, routeinfo);
}
public void onRouteChanged(MediaRouter mediarouter, MediaRouter.RouteInfo routeinfo) {
super.onRouteChanged(mediarouter, routeinfo);
check(mediarouter, routeinfo);
}
public void onRouteRemoved(MediaRouter mediarouter, MediaRouter.RouteInfo routeinfo) {
super.onRouteRemoved(mediarouter, routeinfo);
if (routeinfo.matchesSelector(ChromecastRouteProviderService.SELECTOR)) ;
}
}
}
}
Ok finally I found answer on my own,
Problem is when any provider is selected it's not added using onRouteAdded why? I really dont understand google logic
So the solution is to unselect the router when you want or better select default route when so that your route is released
MediaRouter.getInstance(this).getDefaultRoute().select();
But again 1 out of 10 times it will not work
Hope will help someone

Show 3dmodel after tracking lost metaio sdk

I am using metaio sdk 6.0.2. i am working on metaio INSTANT_2D_GRAVITY tracking and was able to display 3d model. I want to display same 3d model when tracking is lost.but I am failing to do so. I tried by adding trackingValuesVector in onTrackingEvent of MetaioSDKCallbackHandler with no success. can anyone tell me where am I going wrong?
private TrackingValues mTrackingValues;// declared globally
private IGeometry mModel; // declared globally
private boolean mPreview=true;// declared globally
// start INSTANT_2D_GRAVITY tracking
public void onTakePicture(View v)
{
captureTrackingValues = true;
metaioSDK.startInstantTracking("INSTANT_2D_GRAVITY", new File(""), mPreview);
mPreview = !mPreview;
}
final class MetaioSDKCallbackHandler extends IMetaioSDKCallback
{
#Override
public void onInstantTrackingEvent(final boolean success,final File filePath) {
super.onInstantTrackingEvent(success, filePath);
if(mSurfaceView != null)
{
mSurfaceView.queueEvent(new Runnable() {
#Override
public void run() {
if(success)
{
if(captureTrackingValues == true)
{
metaioSDK.setTrackingConfiguration(filePath);
Log.i("Tracking value success","good");
}
}
else
{
Log.i("Tracking value failure","bad");
}
}
});
}
}
#Override
public void onTrackingEvent(TrackingValuesVector trackingValuesVector) {
super.onTrackingEvent(trackingValuesVector);
if (!trackingValuesVector.isEmpty())
{
for(int i =0;i< trackingValuesVector.size();i++)
{
if(trackingValuesVector.get(i).isTrackingState() && mModel!=null)
{
mTrackingValues = metaioSDK.getTrackingValues(i);
mModel.setCoordinateSystemID(trackingValuesVector.get(i).getCoordinateSystemID());
}
else {
if(mModel!= null && mTrackingValues != null) {
metaioSDK.setCosOffset(1, mTrackingValues);
//mChairModel.setCoordinateSystemID(0);
Log.e("TestAR","isTrackingState is null");
}
}
}
}
else{
if(mModel!= null && mTrackingValues != null) {
metaioSDK.setCosOffset(1, mTrackingValues);
//mModel.setCoordinateSystemID(0);
Log.e("TestAR","trackingValuesVector is null");
}
}
}
}
loading 3d model:
private void loadModel()
{
if (mSurfaceView != null) {
mSurfaceView.queueEvent(new Runnable() {
#Override
public void run() {
File chairModel = AssetsManager.getAssetPathAsFile(getApplicationContext(),"chair.obj");
if (chairModel != null) {
mModel = metaioSDK.createGeometry(chairModel);
mModel.setScale(3f);
mModel.setTranslation(new Vector3d(0f,0f,-60f));
mGestureHandler.addObject(mModel, 1);
mModel.setRotation(new Rotation(0f, 0.5f, 0f));
mModel.setCoordinateSystemID(1);
}
}
});
}
else
{
Log.e("exception", "msurfaceview is null");
}
}
I see that you also tried setting the model to COS 0. This should actually work, if the tracking is lost.
If you do not see the model, you would have to play around with the scale value (i.e. set a low value like 0.01) and with the Z translation value. Set a negative Z value in order to move the model away from the camera clipping plane.

Categories

Resources