I have some trouble with the OnQBVideoChatListener in QuickBlox, the two methods onOpponentVideoDataReceive and onOpponentAudioDataReceive are never called. Here is how I implemented the cameraView and the opponentView. It's basically the same as the sample app given here. I have tried to run the sample Video chat app on my phone and tablet however the same issue occures and only the cameraView gets updated. The opponent view is always black. Is there anyone who has experienced the same trouble and have a solution ?
Thanks a bunch !
private void initViews() {
// Setup UI
opponentView = (OpponentGlSurfaceView) findViewById(R.id.opponentView);
cameraView = (CameraView) findViewById(R.id.cameraView);
cameraView.setCameraFrameProcess(true);
// Set VideoChat listener
cameraView.setQBVideoChatListener(qbVideoChatListener);
// Set Camera init callback
cameraView.setFPS(6);
cameraView.setOnCameraViewListener(new OnCameraViewListener() {
#Override
public void onCameraSupportedPreviewSizes(List<Camera.Size> supportedPreviewSizes) {
Camera.Size firstFrameSize = supportedPreviewSizes.get(0);
Camera.Size lastFrameSize = supportedPreviewSizes.get(supportedPreviewSizes.size() - 1);
cameraView.setFrameSize(firstFrameSize.width > lastFrameSize.width ? lastFrameSize : firstFrameSize);
}
});
// VideoChat settings
videoChatConfig = (VideoChatConfig) GlobalVar.getObject(tag);
try {
QBVideoChatController.getInstance().setQBVideoChatListener((QBUser)GlobalVar.getObject(GlobalVar.CURRENT_USER_KEY), qbVideoChatListener);
} catch (XMPPException e) {
e.printStackTrace();
}
}
OnQBVideoChatListener qbVideoChatListener = new OnQBVideoChatListener() {
#Override
public void onCameraDataReceive(byte[] videoData) {
if (videoChatConfig.getCallType() != CallType.VIDEO_AUDIO) {
//...
}
else{
QBVideoChatController.getInstance().sendVideo(videoData);
Log.i(tag,"videoData sent!"); // THIS IS EXECUTED
}
}
#Override
public void onMicrophoneDataReceive(byte[] audioData) {
QBVideoChatController.getInstance().sendAudio(audioData);
Log.i(tag,"AudioData sent!"); //SO IS THIS
}
#Override
public void onOpponentVideoDataReceive(byte[] videoData) {
Log.i(tag,"received img from opponent"); //NOT CALLED
opponentView.loadOpponentImage(videoData);
}
#Override
public void onOpponentAudioDataReceive(byte[] audioData) {
Log.i(tag,"received Audio from opponent"); //NOT CALLED
QBVideoChatController.getInstance().playAudio(audioData);
}
#Override
public void onProgress(boolean progress) {
}
#Override
public void onVideoChatStateChange(CallState callState, VideoChatConfig chat) {
//... STUFF
}
};
Related
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 2 years ago.
I get user current location by using override method
public void onSuccess(LocationEngineResult result) {
location = Point.fromLngLat(result.getLastLocation().getLongitude(),result.getLastLocation().getLatitude());
}
but I don't know how to replace the latitude and longitude on variable
private final Point ROUTE_ORIGIN=Point.fromLngLat(location.longitude(),location.latitude());
with the location i get from public void onSuccess(LocationEngineResult result) is there any solution for this?
The app crashed with error
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'double com.mapbox.geojson.Point.longitude()' on a null object reference
Can anyone give me some idea or solution? I tried to find sources but still can't solve it.
public class ArActivity extends BaseActivity implements RouteListener, ProgressChangeListener, OffRouteListener {
private static final String TAG = ArActivity.class.getSimpleName();
// Handles navigation.
private MapboxNavigation mapboxNavigation;
// Fetches route from points.
private RouteFetcher routeFetcher;
private RouteProgress lastRouteProgress;
private PermissionsManager permissionsManager;
private LocationEngine locationEngine;
private LocationEngineCallback<LocationEngineResult> locationCallback;
public Point location;
private boolean visionManagerWasInit = false;
private boolean navigationWasStarted = false;
TextView tvlocation;
// This dummy points will be used to build route. For real world test this needs to be changed to real values for
// source and target location
private final Point ROUTE_ORIGIN=Point.fromLngLat(location.longitude(),location.latitude());
private final Point ROUTE_DESTINATION = Point.fromLngLat(101.769116, 2.923220);
#Override
protected void initViews() {
setContentView(R.layout.activity_ar_navigation);
tvlocation = findViewById(R.id.location);
}
#Override
protected void onPermissionsGranted() {
startVisionManager();
startNavigation();
}
#Override
protected void onStart() {
super.onStart();
startVisionManager();
startNavigation();
}
#Override
protected void onStop() {
super.onStop();
stopVisionManager();
stopNavigation();
}
private void startVisionManager() {
if (allPermissionsGranted() && !visionManagerWasInit) {
// Create and start VisionManager.
VisionManager.create();
VisionManager.setModelPerformanceConfig(new Merged(new On(ModelPerformanceMode.DYNAMIC, ModelPerformanceRate.LOW)));
VisionManager.start();
VisionManager.setVisionEventsListener(new VisionEventsListener() {
#Override
public void onAuthorizationStatusUpdated(#NotNull AuthorizationStatus authorizationStatus) {
}
#Override
public void onFrameSegmentationUpdated(#NotNull FrameSegmentation frameSegmentation) {
}
#Override
public void onFrameDetectionsUpdated(#NotNull FrameDetections frameDetections) {
}
#Override
public void onFrameSignClassificationsUpdated(#NotNull FrameSignClassifications frameSignClassifications) {
}
#Override
public void onRoadDescriptionUpdated(#NotNull RoadDescription roadDescription) {
}
#Override
public void onWorldDescriptionUpdated(#NotNull WorldDescription worldDescription) {
}
#Override
public void onVehicleStateUpdated(#NotNull VehicleState vehicleState) {
}
#Override
public void onCameraUpdated(#NotNull Camera camera) {
}
#Override
public void onCountryUpdated(#NotNull Country country) {
}
#Override
public void onUpdateCompleted() {
}
});
VisionArView visionArView = findViewById(R.id.mapbox_ar_view);
// Create VisionArManager.
VisionArManager.create(VisionManager.INSTANCE);
visionArView.setArManager(VisionArManager.INSTANCE);
visionArView.setFenceVisible(true);
visionManagerWasInit = true;
}
}
private void stopVisionManager() {
if (visionManagerWasInit) {
VisionArManager.destroy();
VisionManager.stop();
VisionManager.destroy();
visionManagerWasInit = false;
}
}
private void startNavigation() {
if (allPermissionsGranted() && !navigationWasStarted) {
// Initialize navigation with your Mapbox access token.
mapboxNavigation = new MapboxNavigation(
this,
getString(R.string.mapbox_access_token),
MapboxNavigationOptions.builder().build()
);
// Initialize route fetcher with your Mapbox access token.
routeFetcher = new RouteFetcher(this, getString(R.string.mapbox_access_token));
routeFetcher.addRouteListener(this);
locationEngine = LocationEngineProvider.getBestLocationEngine(this);
LocationEngineRequest arLocationEngineRequest = new LocationEngineRequest.Builder(0)
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
.setFastestInterval(1000)
.build();
locationCallback = new LocationEngineCallback<LocationEngineResult> () {
#Override
public void onSuccess(LocationEngineResult result) {
location = Point.fromLngLat(result.getLastLocation().getLongitude(),result.getLastLocation().getLatitude());
}
#Override
public void onFailure(#NonNull Exception exception) {
}
};
try {
locationEngine.requestLocationUpdates(arLocationEngineRequest, locationCallback, Looper.getMainLooper());
} catch (SecurityException se) {
VisionLogger.Companion.e(TAG, se.toString());
}
initDirectionsRoute();
// Route need to be reestablished if off route happens.
mapboxNavigation.addOffRouteListener(this);
mapboxNavigation.addProgressChangeListener(this);
navigationWasStarted = true;
}
}
private void stopNavigation() {
if (navigationWasStarted) {
locationEngine.removeLocationUpdates(locationCallback);
mapboxNavigation.removeProgressChangeListener(this);
mapboxNavigation.removeOffRouteListener(this);
mapboxNavigation.stopNavigation();
navigationWasStarted = false;
}
}
private void initDirectionsRoute() {
// Get route from predefined points.
NavigationRoute.builder(this)
.accessToken(getString(R.string.mapbox_access_token))
.origin(ROUTE_ORIGIN)
.destination(ROUTE_DESTINATION)
.build()
.getRoute(new Callback<DirectionsResponse>() {
#Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
if (response.body() == null || response.body().routes().isEmpty()) {
return;
}
// Start navigation session with retrieved route.
DirectionsRoute route = response.body().routes().get(0);
mapboxNavigation.startNavigation(route);
// Set route progress.
VisionArManager.setRoute(new Route(
getRoutePoints(route),
route.duration().floatValue(),
"",
""
));
}
#Override
public void onFailure(Call<DirectionsResponse> call, Throwable t) {
}
});
}
#Override
public void onErrorReceived(Throwable throwable) {
if (throwable != null) {
throwable.printStackTrace();
}
mapboxNavigation.stopNavigation();
Toast.makeText(this, "Can not calculate the route requested", Toast.LENGTH_SHORT).show();
}
#Override
public void onResponseReceived(#NotNull DirectionsResponse response, RouteProgress routeProgress) {
mapboxNavigation.stopNavigation();
if (response.routes().isEmpty()) {
Toast.makeText(this, "Can not calculate the route requested", Toast.LENGTH_SHORT).show();
} else {
DirectionsRoute route = response.routes().get(0);
mapboxNavigation.startNavigation(route);
// Set route progress.
VisionArManager.setRoute(new Route(
getRoutePoints(route),
(float) routeProgress.durationRemaining(),
"",
""
));
}
}
#Override
public void onProgressChange(Location location, RouteProgress routeProgress) {
lastRouteProgress = routeProgress;
}
#Override
public void userOffRoute(Location location) {
routeFetcher.findRouteFromRouteProgress(location, lastRouteProgress);
}
private RoutePoint[] getRoutePoints(#NotNull DirectionsRoute route) {
ArrayList<RoutePoint> routePoints = new ArrayList<>();
List<RouteLeg> legs = route.legs();
if (legs != null) {
for (RouteLeg leg : legs) {
List<LegStep> steps = leg.steps();
if (steps != null) {
for (LegStep step : steps) {
RoutePoint point = new RoutePoint((new GeoCoordinate(
step.maneuver().location().latitude(),
step.maneuver().location().longitude()
)), mapToManeuverType(step.maneuver().type()));
routePoints.add(point);
List<Point> geometryPoints = buildStepPointsFromGeometry(step.geometry());
for (Point geometryPoint : geometryPoints) {
point = new RoutePoint((new GeoCoordinate(
geometryPoint.latitude(),
geometryPoint.longitude()
)), ManeuverType.None);
routePoints.add(point);
}
}
}
}
}
return routePoints.toArray(new RoutePoint[0]);
}
private List<Point> buildStepPointsFromGeometry(String geometry) {
return PolylineUtils.decode(geometry, Constants.PRECISION_6);
}
private ManeuverType mapToManeuverType(#Nullable String maneuver) {
if (maneuver == null) {
return ManeuverType.None;
}
switch (maneuver) {
case "turn":
return ManeuverType.Turn;
case "depart":
return ManeuverType.Depart;
case "arrive":
return ManeuverType.Arrive;
case "merge":
return ManeuverType.Merge;
case "on ramp":
return ManeuverType.OnRamp;
case "off ramp":
return ManeuverType.OffRamp;
case "fork":
return ManeuverType.Fork;
case "roundabout":
return ManeuverType.Roundabout;
case "exit roundabout":
return ManeuverType.RoundaboutExit;
case "end of road":
return ManeuverType.EndOfRoad;
case "new name":
return ManeuverType.NewName;
case "continue":
return ManeuverType.Continue;
case "rotary":
return ManeuverType.Rotary;
case "roundabout turn":
return ManeuverType.RoundaboutTurn;
case "notification":
return ManeuverType.Notification;
case "exit rotary":
return ManeuverType.RotaryExit;
default:
return ManeuverType.None;
}
}
At the point of instantiating this line (i.e. when the class loads):
private final Point ROUTE_ORIGIN=Point.fromLngLat(location.longitude(),location.latitude());
The location variable you are using is set to nothing:
public Point location;
Aka, it is null and you are getting a NullPointerException.
You should either not try to use the location object until it is instantiated, setting your routeOrigin later on. Or change the ROUTE_ORIGIN to use a static location, the same way you have for ROUTE_DESTINATION.
I made android camera app using RxCamera. But camera screen is narrow.
I can't use standard camera app because I have to customize camera with few features. RxCamera is best for me.
How can I make it like the aspect ratio of a standard camera app on Android phones?
Can anyone help me?
Here is my RxCamera configuration.
private void openCamera() {
int camWidth = getResources().getDisplayMetrics().widthPixels;
int camHeight = getResources().getDisplayMetrics().heightPixels - getStatusBarHeight();
RxCameraConfig config = new RxCameraConfig.Builder()
.useBackCamera()
.setAutoFocus(true)
.setPreferPreviewFrameRate(15, 30)
.setPreferPreviewSize(new Point(camHeight, camWidth), false)
.setMuteShutterSound(true)
.setHandleSurfaceEvent(true)
.build();
RxCamera.open(this, config).flatMap(new Func1<RxCamera, Observable<RxCamera>>() {
#Override
public Observable<RxCamera> call(RxCamera rxCamera) {
return rxCamera.bindTexture(textureView);
}
}).flatMap(new Func1<RxCamera, Observable<RxCamera>>() {
#Override
public Observable<RxCamera> call(RxCamera rxCamera) {
return rxCamera.startPreview();
}
}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<RxCamera>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(final RxCamera rxCamera) {
camera = rxCamera;
}
});
}
Trying to run MIDI on my Android app. I'm following the midisuite example to configure my app and it works fine with the exception of aftertouch. Whenever I try to trigger aftertouch, I run into a threading exception type
InteruptedException. How should I prevent this threading issue? My knowledge on multithreading isn't the best or else I would've figured this out already. All I can really tell right now is that the message is sending too fast and the thread hasn't woken up yet from its sleep call.
I followed the github repo with my code as follows:
MidiReceiver subclass:
#TargetApi(Build.VERSION_CODES.M)
public class MidiEngine extends MidiReceiver {
public AudioActivity activity;
private MidiEventScheduler eventScheduler;
private MidiFramer midiFramer;
private MidiReceiver midiReceiver = new MyReceiver();
private Thread mThread;
private boolean go;
private int mProgram;
public MidiEngine() {
this(new AudioActivity());
}
public MidiEngine(AudioActivity activity) {
this.activity = activity;
midiReceiver = new MyReceiver();
midiFramer = new MidiFramer(midiReceiver);
}
public AudioActivity getActivity() {
return this.activity;
}
/* This will be called when MIDI data arrives. */
#Override
public void onSend(byte[] data, int offset, int count, long timestamp)
throws IOException {
if (eventScheduler != null) {
if (!MidiConstants.isAllActiveSensing(data, offset, count)) {
eventScheduler.getReceiver().send(data, offset, count,
timestamp);
}
}
}
// Custom Listener to send to correct methods
private class MyReceiver extends MidiReceiver {
#Override
public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
byte command = (byte)(msg[0] & MidiConstants.STATUS_COMMAND_MASK);
int channel = (byte)(msg[0] & MidiConstants.STATUS_CHANNEL_MASK);
switch (command) {
case MidiConstants.STATUS_NOTE_ON:
activity.keyDown(i, msg[1], msg[2]);
break;
case MidiConstants.STATUS_NOTE_OFF:
activity.keyUp(channel, msg[1]);
break;
case MidiConstants.STATUS_POLYPHONIC_AFTERTOUCH:
activity.keyDown(channel, msg[1], msg[2]);
break;
case MidiConstants.STATUS_PITCH_BEND:
activity.pitchBendAction(channel, (msg[2] << 7) + msg[1]);
break;
case MidiConstants.STATUS_CONTROL_CHANGE:
activity.ccAction(channel, msg[1], msg[2]);
break;
case MidiConstants.STATUS_PROGRAM_CHANGE:
mProgram = msg[1];
break;
default:
break;
}
}
}
class MyRunnable implements Runnable {
#Override
public void run() {
do {
try {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
processMidiEvents();
}
catch (Exception e) {
Log.e("Java", "SynthEngine background thread exception.", e);
}
}
});
Thread.sleep(100);
}
catch (InterruptedException e) {
Log.e("Java", "Threading exception", e);
}
}
while (go);
}
}
/**
* #throws IOException
*
*/
private void processMidiEvents() throws IOException {
long now = System.nanoTime();
MidiEventScheduler.MidiEvent event = (MidiEventScheduler.MidiEvent) eventScheduler.getNextEvent(now);
while (event != null) {
midiFramer.send(event.data, 0, event.count, event.getTimestamp());
eventScheduler.addEventToPool(event);
event = (MidiEventScheduler.MidiEvent) eventScheduler.getNextEvent(now);
}
}
public void start() {
stop();
go = true;
mThread = new Thread(new MyRunnable());
mThread.setPriority(6);
eventScheduler = new MidiEventScheduler();
mThread.start();
}
public void stop() {
go = false;
if (mThread != null) {
try {
mThread.interrupt();
mThread.join(500);
}
catch (Exception e) {
}
mThread = null;
eventScheduler = null;
}
}
}
Stack Trace Error (line 154 refers to the Thread.sleep part in my custom Runnable class):
Java: Threading exception
java.lang.InterruptedException
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:1031)
at java.lang.Thread.sleep(Thread.java:985)
at com.rfoo.midiapp.communication.MidiEngineInput$MyRunnable.run(MidiEngineInput.java:154)
at java.lang.Thread.run(Thread.java:818)
Thanks!
EDIT: Thread start
Midi Device Service subclass (thread will start whenever a device has connected or disconnected).
#TargetApi(Build.VERSION_CODES.M)
public class MidiSynthDeviceService extends MidiDeviceService {
private static final String TAG = "MidiSynthDeviceService";
private boolean midiStarted = false;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
AudioActivity.midiEngine.stop();
super.onDestroy();
}
#Override
// Declare the receivers associated with your input ports.
public MidiReceiver[] onGetInputPortReceivers() {
return new MidiReceiver[] { AudioActivity.midiEngine };
}
/**
* This will get called when clients connect or disconnect.
* You can use it to turn on your synth only when needed.
*/
#Override
public void onDeviceStatusChanged(MidiDeviceStatus status) {
if (status.isInputPortOpen(0) && !midiStarted) {
AudioActivity.midiEngine.start();
midiStarted = true;
} else if (!status.isInputPortOpen(0) && midiStarted){
AudioActivity.midiEngine.stop();
midiStarted = false;
}
}
}
Activity class:
public class AudioActivity extends AppCompatActivity {
private Thread thread;
public static MidiEngine midiEngine;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Layout inits
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
// Setup MIDI:
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI)) {
Toast.makeText(this, "MIDI not supported!", Toast.LENGTH_LONG).show();
}
else {
midiEngine = new MidiEngine(this);
setupMidi();
}
// Setup audio thread:
if (thread == null) {
thread = new Thread() {
public void run() {
setPriority(Thread.MAX_PRIORITY);
// Runs an Open SL audio thread (C++)
// This generates a waveform.
// AudioEngine is a wrapper class connecting C++ to Java
AudioEngine.runProcess();
}
}
}
}
public void setupMidi() {
if (activity == null) activity = (AudioActivity) getContext();
mMidiManager = (MidiManager) activity.getSystemService(AudioActivity.MIDI_SERVICE);
if (mMidiManager == null) {
Toast.makeText(activity, "MidiManager is null!", Toast.LENGTH_LONG).show();
return;
}
// Get Device Info
MidiDeviceInfo deviceInfo = MidiTools.findDevice(mMidiManager, "RFOO", "AudioApp");
// MIDI Input
portIndex = 0;
inputPortSelector = new MidiOutputPortConnectionSelector(mMidiManager, activity, R.id
.inputListView, deviceInfo, portIndex);
inputPortSelector.setConnectedListener(new MyPortsConnectedListener());
midi_ch_input = 0;
midi_ch_output = 0;
}
// Bunch of UI code here....
}
I've noticed a bug in a basic survey app I'm making to better learn android.
Occasionally I get a W/System.errīš at MainActivity.surveyAvailable(MainActivity.java:40) that points to this line of code:
button.setVisibility(View.GONE);
I've used setVisibility many times before and never had any issues.
Here's the function, this gets called when the user first enters the app, and after they finish taking a survey to check the server and see if there is another survey available for the user:
public void surveyAvailable(boolean surveyIsAvailable) {
Log.d("MainActivity", "App survey is available? " + surveyIsAvailable );
Button button = (Button)findViewById(R.id.takeSurveyButton);
if (surveyIsAvailable) {
button.setVisibility(View.VISIBLE);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
App.getInstance().showSurvey();
}
});
} else {
Log.d("MainActivity", "We hit here");
button.setVisibility(View.GONE);
}
}
When a survey isn't available, the appropriate lines are logged - App survey is available? false and 'We hit here'. But then the button sometimes doesn't get set to View.GONE and I see the System.Err line. But sometimes it works fine and the button's visibility does change. Any idea how to fix that? Or how to get more information on what the System.Err actually means?
EDIT:
I found that by setting Button surveyButton; in my activity and then referencing the button as this.surveyButton seems to get the functionality to work more along the lines of what we'd expect (e.g. when we call button.setVisibility(View.GONE) the view is actually consistently GONE). But it still throws the System.Err line which has me hesitant that things are working correctly.
Edited Activity:
public class MainActivity extends ActionBarActivity implements SurveyListener {
Button surveyButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.surveyButton = (Button)findViewById(R.id.takeSurveyButton);
}
public void surveyAvailable(boolean surveyIsAvailable) {
Log.d("MainActivity", "App survey is available? " + surveyIsAvailable );
if (surveyIsAvailable) {
this.surveyButton.setVisibility(View.VISIBLE);
this.surveyButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
App.getInstance().showSurvey();
}
});
} else {
Log.d("MainActivity", "We hit here");
this.surveyButton.setVisibility(View.GONE);
}
}
}
The activity implements this class:
public abstract interface SurveyListener
{
public abstract void surveyAvailable(boolean surveyAvailable);
}
Main App class that checks for surveys and calls 'surveyAvailable()`:
public class App
{
private static App _instance;
private SurveyListener _eventsHandler;
private String _apiKey = "";
private String _appuserId = "";
private String _surveyUrl = "";
private Activity _parentContext;
private Boolean _surveyAvailable;
public static App initWithApiKeyAndListener(String apiKey, SurveyListener surveyEventsHandler) {
if (_instance == null)
{
_instance = new App();
_instance._parentContext = (Activity) surveyEventsHandler;
_instance.setSurveyListener(surveyEventsHandler);
_instance.setApiKey(apiKey);
String appuserId = PreferenceManager.getDefaultSharedPreferences((Activity) _instance._eventsHandler).getString(tag, "no_appuser");
if (appuserId == "no_appuser") {
_instance._surveyAvailable = true;
_instance.alertAvailability(true);
} else {
_instance.checkForCampaigns();
}
}
return _instance;
}
private void alertAvailability(boolean surveyAvailable) {
App.getInstance()._eventsHandler.surveyAvailable(surveyAvailable);
}
private void checkForCampaigns() {
new CampaignCheck().execute();
}
public static App getInstance()
{
if (_instance == null)
{
_instance = new App();
}
return _instance;
}
public void donePushed()
{
App.getInstance().checkForCampaigns();
}
private class CampaignCheck extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
Boolean surveysAvailable = false;
try {
surveysAvailable = new AppuserConnection().checkCampaigns();
App.getInstance()._surveyAvailable = surveysAvailable;
App.getInstance().alertAvailability(_surveyAvailable);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
}
}
}
You shouldn't modify the UI elements from a different thread. You are doing this by calling App.getInstance().alertAvailability(_surveyAvailable); on a background thread. Move this to the AsyncTask's onPostExecute.
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();
}
});