Im using Mlkit object detection API in my android application to detect objects in realtime. Here is my code
private AtomicBoolean flag = new AtomicBoolean(false);
private void startDetection(){
final FirebaseVisionObjectDetectorOptions options = new FirebaseVisionObjectDetectorOptions.Builder()
.setDetectorMode(FirebaseVisionObjectDetectorOptions.STREAM_MODE)
.enableClassification()
.build();
final FirebaseVisionObjectDetector objectDetector =
FirebaseVision.getInstance().getOnDeviceObjectDetector(options);
timer = new Timer();
timer.schedule(new TimerTask() {
#SuppressLint("SetTextI18n")
#Override
public void run() {
try {
if(flag.compareAndSet(false, true)) {
status.setText("Processing");
result.setText("Starting");
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(textureView.getBitmap());
objectDetector.processImage(image)
.addOnSuccessListener(
new OnSuccessListener<List<FirebaseVisionObject>>() {
#Override
public void onSuccess(List<FirebaseVisionObject> detectedObjects) {
/*StringBuilder wew = new StringBuilder();
for (FirebaseVisionObject obj : detectedObjects) {
Integer id = obj.getTrackingId();
Rect bounds = obj.getBoundingBox();
// If classification was enabled:
int category = obj.getClassificationCategory();
Float confidence = obj.getClassificationConfidence();
wew.append(category);
}
result.setText(wew.toString());*/
result.setText(Integer.toString(detectedObjects.size()));
flag.set(false);
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
result.setText(e.getMessage());
}
});
}
} catch (NullPointerException e) {
e.printStackTrace();
result.setText(e.getMessage());
}
}
},0,2000);
}
The list of objects detection is 0 and i got this from the logcat
ObjectDetectorTask: Object detector pipeline is reset
Why is that happening?
Related
I'm attempting to stream my webcam locally from a webpage on my computer to an Android app (native WebRTC). I'm using WebRTC for the peer connection and NodeJS with Socket.io for signaling. When I initiate the video stream all sdp appears to be set correctly, but no track plays on my Andorid surface view. All I get is a black screen and this console output:
2020-08-19 13:23:19.667 30492-31575/com.example.demowebrtcclient I/org.webrtc.Logging: EglRenderer: video_viewDuration: 4050 ms. Frames received: 0. Dropped: 0. Rendered: 0. Render fps: .0. Average render time: NA. Average swapBuffer time: NA.
Here's my code, sorry there's so much I just don't know where the root of the problem is.
Peer 1 (Webpage, JS)
navigator.mediaDevices.getUserMedia({audio: true, video: true})
.then(function(s) {
stream = s;
video.srcObject = stream;
video.play();
});
function startStream() {
socket.emit('broadcaster');
}
socket.on('answer', function(id, description) {
let RTCDescription = description;
if (isAndroid) {
RTCDescription = new RTCSessionDescription();
RTCDescription.sdp = description;
RTCDescription.type = "answer";
console.log("Answer received");
}
console.log(RTCDescription);
peerConnection.setRemoteDescription(RTCDescription);
});
socket.on('watcher', function(id) {
peerConnection = new RTCPeerConnection(config);
// peerConnections[id] = peerConnection;
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
peerConnection.createOffer()
.then(function(sdp) {
peerConnection.setLocalDescription(sdp);
})
.then(function() {
socket.emit('offer', id, peerConnection.localDescription);
})
peerConnection.onicecandidate = function(event) {
if (event.candidate) {
socket.emit('candidate', id, event.candidate);
}
};
});
socket.on('candidate', function(id, candidate) {
console.log("Candidate recieved");
peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
});
socket.on('close', function(id) {
peerConnection.close();
delete peerConnection;
});
Peer 2 (Android, Java)
private void initializePeerConnectionFactory() {
PeerConnectionFactory.InitializationOptions initOptions = PeerConnectionFactory.InitializationOptions.builder(getApplicationContext())
.setEnableInternalTracer(true)
.setFieldTrials("WebRTC-H264HighProfile/Enabled/")
.createInitializationOptions();
PeerConnectionFactory.initialize(initOptions);
VideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(rootEglBaseContext, /* enableIntelVp8Encoder */true, /* enableH264HighProfile */true);
VideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBaseContext);
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
options.disableEncryption = true;
options.disableNetworkMonitor = true;
factory = PeerConnectionFactory.builder()
.setVideoEncoderFactory(defaultVideoEncoderFactory)
.setVideoDecoderFactory(defaultVideoDecoderFactory)
.setOptions(options)
.createPeerConnectionFactory();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootEglBase = EglBase.create();
rootEglBaseContext = rootEglBase.getEglBaseContext();
videoView = findViewById(R.id.video_view);
videoView.setMirror(true);
videoView.setEnableHardwareScaler(true);
videoView.init(rootEglBaseContext, null);
videoSink = new ProxyVideoSink();
iceServers = new ArrayList<>();
stunServer = (PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
iceServers.add(stunServer);
executor = Executors.newSingleThreadScheduledExecutor();
mediaConstraints = new MediaConstraints();
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on("offer", handleOffer);
mSocket.on("broadcaster", onBroadcast);
mSocket.on("candidate", onCandidate);
mSocket.connect();
initializePeerConnectionFactory();
}
private Emitter.Listener handleOffer = new Emitter.Listener() {
#Override
public void call(final Object... args) {
Log.d("socket", "Offer recieved");
peerConnection = factory.createPeerConnection(iceServers, observer);
String id = (String) args[0];
JSONObject data = (JSONObject) args[1];
peerConnection.setRemoteDescription(new SdpAdapter("setremote") {
#Override
public void onSetSuccess() {
peerConnection.createAnswer(new SdpAdapter("createanswer") {
#Override
public void onCreateSuccess(final SessionDescription sdp) {
super.onCreateSuccess(sdp);
Log.d("socket", "Session description " + sdp.toString() + " created");
SdpAdapter local = new SdpAdapter("setlocal") {
#Override
public void onSetSuccess() {
super.onSetSuccess();
mSocket.emit("answer" , id, peerConnection.getLocalDescription().description);
}
};
peerConnection.setLocalDescription(local, sdp);
}
}, mediaConstraints);
}
}, new SessionDescription(SessionDescription.Type.OFFER, data.optString("sdp")));
}
};
private Emitter.Listener onConnect = new Emitter.Listener() {
#Override
public void call(final Object... args) {
Log.d("socket", "Socket connected");
mSocket.emit("watcher");
}
};
private Emitter.Listener onBroadcast = new Emitter.Listener() {
#Override
public void call(final Object... args) {
Log.d("socket", "Received broadcast request");
mSocket.emit("watcher");
}
};
private Emitter.Listener onCandidate = new Emitter.Listener() {
#Override
public void call(final Object... args) {
Log.d("ice", "New ice candidate recieved");
JSONObject data = (JSONObject) args[1];
IceCandidate candidate = new IceCandidate(data.optString("sdpMid"), Integer.parseInt(data.optString("sdpMLineIndex")), data.optString("candidate"));
peerConnection.addIceCandidate(candidate);
}
};
public class SdpAdapter implements SdpObserver {
private String name;
public SdpAdapter(String name) {
this.name = name;
}
#Override
public void onCreateSuccess(SessionDescription sessionDescription) {
Log.d("socket", "SDP create success for " + name );
}
#Override
public void onSetSuccess() {
Log.d("socket", "SDP set success for " + name);
}
#Override
public void onCreateFailure(String s) {
Log.d("socket", "SDP create failure for" + s);
}
#Override
public void onSetFailure(String s) {
Log.d("socket", "SDP set failure for" + s);
}
};
public static class ProxyVideoSink implements VideoSink {
private VideoSink mTarget;
#Override
synchronized public void onFrame(VideoFrame frame) {
if (mTarget == null) {
Log.d("socket", "Dropping frame in proxy because target is null.");
return;
}
mTarget.onFrame(frame);
}
synchronized void setTarget(VideoSink target) {
this.mTarget = target;
}
}
Observer observer = new Observer() {
#Override
public void onIceCandidate(IceCandidate iceCandidate) {
Log.d("ice", iceCandidate.toString());
mSocket.emit("candidate", iceCandidate);
}
#Override
public void onAddStream(MediaStream stream) {
executor.execute(new Runnable() {
#Override
public void run() {
Log.d("socket", "Stream added");
Log.d("tracks", "Getting tracks");
VideoTrack remoteVideoTrack = (VideoTrack)stream.videoTracks.get(0);
AudioTrack remoteAudioTrack = (AudioTrack)stream.audioTracks.get(0);
Log.d("tracks", "Enabling tracks");
remoteAudioTrack.setEnabled(true);
remoteVideoTrack.setEnabled(true);
Log.d("tracks", "Adding sink");
videoSink.setTarget(videoView);
remoteVideoTrack.addSink(videoSink);
peerConnection.getStats(reports -> {
for (StatsReport report : reports) {
Log.d("Stats", "Stats: " + report.toString());
}
}, null);
}
});
}
};
}
Disabling encryption in Android will break interoperability with the browser, unless you start them in a special (insecure) mode.
options.disableEncryption = true;
A good point to get started with the Android native WebRTC API is by looking at the example App code:
https://chromium.googlesource.com/external/webrtc/+/refs/heads/master/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
I'll recommend to add more logs and extend the description / question.
Recommended procedure when debugging a WebRTC issue:
Did ICE succeed? (iceConnectionState is connected) If not, look at the gathered and received candidates. Check involved firewall configurations, NAT and TURN.
Do you receive packets but no frames can be rendered? Check your codecs and encoders / decoders.
I want to WorkManager startWork() to be called every time the user connects to the internet through wifi or 3g/4g/5g.
It calls only one time at the start where I register it.
enqueuing work when a user signs in.
Worker.startWorkManager(SignInActivity.this);
startActivity(new Intent(SignInActivity.this,UsersActivity.class);
it never calls again whenever the user turns Wifi OFF and ON again regardless app is in foreground or background or app is killed through swiped from recent apps.
I want it to be called every time user turned Wifi OFF and ON in every scenario i.e foreground, background, or app is killed.
Worker.class
public class Worker {
public Worker(Context context, WorkerParameters workerParams) {
}
public static void startWorkManager(Context context) {
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
WorkManager.getInstance(context).enqueueUniqueWork(Constants.TAG_SYNC_DATA, ExistingWorkPolicy.KEEP, new OneTimeWorkRequest.Builder(SyncDataWorker.class)
.addTag(Constants.TAG_SYNC_DATA)
.setConstraints(constraints)
.build());
}
}
SyncDataWorker.class
public class SyncDataWorker extends ListenableWorker {
public SyncDataWorker(
#NonNull Context context,
#NonNull WorkerParameters params) {
super(context, params);
}
#NonNull
#Override
public ListenableFuture<Result> startWork() {
return CallbackToFutureAdapter.getFuture(completer -> {
AsyncCallback callback = new AsyncCallback() {
#Override
public void onFailure(Exception e) {
completer.setException(e);
}
#Override
public void onSuccess() {
completer.set(Result.success());
}
#Override
public void onRetry() {
completer.set(Result.retry());
}
};
new AsyncSyncData(getApplicationContext(), callback).execute();
return callback;
});
}
}
AsyncSynData.class
public class AsyncSyncData extends AsyncTask<Void, Void, Void> {
private final Context context;
ArrayList<message> messageArrayListNotSync;
ArrayList<unread_messages> unreadMessagesArrayList;
String user_id = "";
private AsyncCallback callback = null;
public AsyncSyncData(Context context, AsyncCallback callback) {
this.context = context;
messageArrayListNotSync = new ArrayList<>();
unreadMessagesArrayList = new ArrayList<>();
this.callback = callback;
}
#Override
protected Void doInBackground(Void... voids) {
AppDatabase db = AppDatabase.getAppDatabase(context);
user user = null;
ArrayList<user> userArrayList = new ArrayList<>(db.applicationDao().getAllUsers());
if (userArrayList.size() > 0) {
user = userArrayList.get(0);
}
messageArrayListNotSync = new ArrayList<>(db.applicationDao().getAllMessagesNotSync(!user_id.isEmpty() ? user_id : user.threadId));
unreadMessagesArrayList = new ArrayList<>(db.applicationDao().getUnreadMessageStatus());
System.out.println("messageArrayListNotSync: " + messageArrayListNotSync);
System.out.println("unreadMessagesArrayList: " + unreadMessagesArrayList);
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("user_id", !user_id.isEmpty() ? user_id : user.threadId);
Gson gson = new GsonBuilder().create();
JsonArray json_messages = gson.toJsonTree(messageArrayListNotSync).getAsJsonArray();
JsonArray json_unread_messages = gson.toJsonTree(unreadMessagesArrayList).getAsJsonArray();
jsonObject.put("messages", json_messages);
jsonObject.put("unread_messages", json_unread_messages);
RequestHandler.postRequest("/messages", jsonObject, context, new VolleyCallback() {
#Override
public void onSuccess(JSONObject result) {
final JSONObject finalResult = result;
try {
if (result != null && result.has("success") && result.getBoolean("success")) {
new AsyncDeleteUnreadMessagesList(context, unreadMessagesArrayList, new Callback() {
#Override
public void onCallbackCompleted() {
try {
ArrayList<com.app.amber.internet.DATABASE_OPERATIONS.schema.message> messagesToStore = new ArrayList<>();
JSONObject result = finalResult.getJSONObject("data");
JSONObject last_messages = result.getJSONObject("last_messages");
new AsyncUpdateLastMessage(context, last_messages, true, new Callback() {
#Override
public void onCallbackCompleted() {
try {
JSONArray json_messages_to_store = result.getJSONArray("messages");
JSONArray json_evetns_type_1 = result.getJSONArray("eventsType1");
JSONArray json_evetns_type_2 = result.getJSONArray("eventsType2");
for (int i = 0; i < json_messages_to_store.length(); i++) {
JSONObject data = json_messages_to_store.getJSONObject(i);
String id = data.getString("id"),
sender_id = data.getString("sender_id"),
receiver_id = data.getString("receiver_id"),
msg = data.getString("msg"),
type = data.getString("type"),
path = data.getString("path"),
download_status = data.getString("download"),
group_users = data.getString("group_users"),
group_message_status = data.getString("group_message_status");
boolean is_sender = false;
long data_created = data.getLong("date_created");
int is_read = 0;
com.app.amber.internet.DATABASE_OPERATIONS.schema.message message =
new com.app.amber.internet.DATABASE_OPERATIONS.schema.message(id, sender_id, receiver_id, msg, type, path, is_sender, data_created,
is_read, download_status, sender_id, group_users, group_message_status);
messagesToStore.add(message);
}
ArrayList<String> messageIdsType1 = new ArrayList<>();
ArrayList<String> messageIdsType2 = new ArrayList<>();
for (int i = 0; i < json_evetns_type_1.length(); i++) {
messageIdsType1.add(json_evetns_type_1.getJSONObject(i).getString("id"));
}
for (int i = 0; i < json_evetns_type_2.length(); i++) {
messageIdsType2.add(json_evetns_type_2.getJSONObject(i).getString("id"));
}
new AsyncStoreOldMessagesLocally(context, messagesToStore, new Callback() {
#Override
public void onCallbackCompleted() {
new AsyncUpdateMessageStatus(context, messageIdsType1, 1, new Callback() {
#Override
public void onCallbackCompleted() {
new AsyncUpdateMessageStatus(context, messageIdsType2, 2, new Callback() {
#Override
public void onCallbackCompleted() {
new AsyncUpdateMessageStatusList(context, messageArrayListNotSync, new Callback() {
#Override
public void onCallbackCompleted() {
sendCallBack();
}
}).execute();
}
}).execute();
}
}).execute();
}
}).execute();
} catch (Exception e) {
System.out.println("Exception occurred while getting data from data JSONObject received from service: " + e.toString());
e.printStackTrace();
sendCallBack();
}
}
}).execute();
} catch (Exception e) {
System.out.println("Exception occurred while parsing data JSONObject received from service: " + e.toString());
e.printStackTrace();
sendCallBack();
}
}
}).execute();
} else {
sendCallBack();
}
} catch (Exception e) {
System.out.println("Exception occurred while parsing webservice result: " + e.toString());
sendCallBack();
}
}
});
} catch (Exception e) {
System.out.println("exception occurred while parsing messaging lists: " + e.toString());
sendCallBack();
}
return null;
}
private void sendCallBack() {
if (callback != null) {
callback.onSuccess();
}
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
"WorkManager startWork() never calls when constraints are met"
"It calls only one time at the start where I register it."
The ListenableWorker can be recreated in some situations, a new instance of ListenableWorker with the same first ListenableWorker.id. But for it be recreated, it can't be finished. Here are some situations:
Some of the constraints do not matches anymore and it matches again
System was rebooted
Now here are some situations where it will be finished:
Some Exception was raised without treatment
completer.set(Result.success()) was called
completer.set(Result.failure()) was called
There are some situations in your code that the worker can be finished.
There are lots of calls to AsyncSyncData.sendCallBack, which can causes the call of completer.set(Result.success()) on the ListenableWorker instance. If it happens the ListenableWorker completes the job, so it will not be recreated anymore.
"I want to WorkManager startWork() to be called every time the user connects to the internet through wifi or 3g/4g/5g."
The WorkManager alone won't create a new instance of the ListenableWork every time the user connects to Internet. The WorkManager is a API to schedule tasks, and the constraints defined in the ListenableWork are used to not start it while them are not matched, after the ListenableWork finishes, how was discussed above, this task is finished, so no more to do.
If you want to listen to some connectivity changes, you should use ConnectivityManager.registerNetworkCallback and then when the user connects to, you do what you want. Here are some examples that could help you
to do it.
Hello I have implemented pusher for realtime chat and subscribing to pusher channel , but I have many activities and fragments where i want to listen to pushr events . I have added this code in every activity/fragment but the problem is that it creates multiple subscriptions for every id . I know that i have to use Singleton for this can anyone point me in the right direction to achieve this ?
Here is the code i am writing in every activity/fragment
private PusherOptions options;
private Channel channel;
private Pusher pusher;
options = new PusherOptions();
options.setCluster("ap2");
pusher = new Pusher("afbfc1f591fd7b70190f", options);
pusher.connect();
profile_id = Global.shared().preferences.getString("PROFILE_ID", " ");
channel = pusher.subscribe(profile_id);
channel.bind("message",
new SubscriptionEventListener() {
#Override
public void onEvent(String s, String s1, final String data) {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
JSONObject result = new JSONObject(data);
String message = result.getString("message");
String time = result.getString("time");
String reId = result.getString("recieverId");
new_message = message;
getConvoData(k, message);
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println("DATA ====>>" + data);
}
});
}
});
okay so after trying for a while i figured it out my self i created a global class and just added pusher code to it so that it maintains just one connection for the entire lifecycle of the app
public class Global extends MultiDexApplication {
#Override
public void onCreate() {
super.onCreate();
SharedPreferences preferences = sharedInstance.getSharedPreferences(sharedInstance.getString(R.string.shared_preferences), Context.MODE_PRIVATE);
sharedInstance.preferences = preferences;
connectTopusher();
}
public void connectTopusher() {
PusherOptions options;
Channel channel;
Pusher pusher;
options = new PusherOptions();
options.setCluster("ap2");
pusher = new Pusher("afbfc1f591fd7b70190f", options);
pusher.connect();
String profile = Global.shared().preferences.getString("PROFILE_ID", "");
channel = pusher.subscribe(profile);
channel.bind("message",
new SubscriptionEventListener() {
#Override
public void onEvent(String s, String s1, final String data) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
try {
JSONObject result = new JSONObject(data);
String message = result.getString("message");
String time = result.getString("time");
String reId = result.getString("recieverId");
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println("DATA ====>>" + data);
}
});
}
});
channel.bind("status_change", new SubscriptionEventListener() {
#Override
public void onEvent(String s, String s1, final String data) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
try {
JSONObject result = new JSONObject(data);
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println("DATA ====>>" + data);
}
});
}
});
}
You can expose channel in your Global class. That will allow you to call bind and unbind in your fragments, when they are in the foreground.
connectToPusher should just create a channel and subscribe to it.
In Global.java:
private Channel channel;
public void connectTopusher() {
PusherOptions options;
Pusher pusher;
options = new PusherOptions();
options.setCluster("ap2");
pusher = new Pusher("afbfc1f591fd7b70190f", options);
pusher.connect();
String profile = Global.shared().preferences.getString("PROFILE_ID", "");
this.channel = pusher.subscribe(profile);
}
public Channel getChannel(){
return this.channel;
}
And then in your activity/fragment you can bind/unbind your listeners to when they are resumed/paused - just keep a reference to it like this:
YourActivity.java (could also be your Fragment)
private SubscriptionEventListener messageListener = new SubscriptionEventListener(){
#Override
public void onEvent(String channel, String event, String data) {
//TODO: do something with events
}
}
//Bind when the listener comes into the foreground:
#Override
protected void onResume() {
super.onResume();
((Global) getActivity().getApplication()).getChannel().bind("message", messageListener);
}
//Make sure to unbind the event listener!
#Override
protected void onPause() {
super.onPause();
((Global) getActivity().getApplication()).getChannel().unbind("message", messageListener);
}
I hope this helps :)
Im trying to understand how threading works in Android.
I've created this AsyncTask class, but I still get this warning in my console:
Skipped 295 frames! The application may be doing too much work on its main thread.
LoadAnswersTask class
public class LoadAnswersTask extends AsyncTask<String, Void, ArrayList<MessageItemModel>> {
public interface LoadAnswersEventHandler {
void onLoadFinished(ArrayList<MessageItemModel> answers);
}
protected LoadAnswersEventHandler event;
public LoadAnswersTask(LoadAnswersEventHandler event) {
this.event = event;
}
#Override
protected ArrayList<MessageItemModel> doInBackground(String... params) {
try {
QuestionModel q = QuestionModel.getById(Integer.parseInt(params[0]));
ArrayList<MessageItemModel> items = new ArrayList<>();
for (AnswerModel answer : q.getAnswers()) {
MessageItemModel messageItem = new MessageItemModel();
messageItem.message = answer.getComment();
messageItem.id = answer.getId();
messageItem.parentId = answer.getParentId();
messageItem.gender = answer.getGender();
messageItem.name = answer.getName();
messageItem.reply = (answer.getParentId() > 0);
messageItem.email = answer.getEmail();
messageItem.answer = true;
items.add(messageItem);
}
return items;
} catch (Exception e) {
Log.d(getClass().getName(), "Failed to load question", e);
}
return null;
}
#Override
protected void onPostExecute(ArrayList<MessageItemModel> messageItemModels) {
this.event.onLoadFinished(messageItemModels);
}
}
I also tried this approach, which seems to work - well sort of as I have my items in a Fragment inside a viewpager - and it sometimes didn't load the answers, im suspecting it's because of the WeakReference combined with the viewpager causing event.get() to be null, but i'm really not sure...
private static class LoadAnswersHandler extends Handler {
private WeakReference<LoadAnswersEventHandler> event;
public LoadAnswersHandler(LoadAnswersEventHandler event) {
this.event = new WeakReference<>(event);
}
#Override
public void handleMessage(Message msg) {
if(event.get() != null) {
event.get().onLoadFinished((ArrayList<MessageItemModel>) msg.obj);
}
}
}
private LoadAnswersHandler loadAnswersHandler;
// ...
protected void loadAnswers(final LoadAnswersEventHandler event) {
loadAnswersHandler = new LoadAnswersHandler(event);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
QuestionModel q = QuestionModel.getById(question.getId());
ArrayList<MessageItemModel> items = new ArrayList<>();
for (AnswerModel answer : q.getAnswers()) {
MessageItemModel messageItem = new MessageItemModel();
messageItem.message = answer.getComment();
messageItem.id = answer.getId();
messageItem.parentId = answer.getParentId();
messageItem.gender = answer.getGender();
messageItem.name = answer.getName();
messageItem.reply = (answer.getParentId() > 0);
messageItem.email = answer.getEmail();
messageItem.answer = true;
items.add(messageItem);
}
loadAnswersHandler.sendMessage(Message.obtain(loadAnswersHandler, UPDATE_UI, items));
} catch (Exception e) {
Log.d(getClass().getName(), "Failed to load question", e);
}
}
});
thread.start();
}
Thanks!
- Simon
I am trying to log location during an activity however I am not getting location updates. I have played around with the min refresh interval as well as the distance interval however it still doesn't work. I followed Xamarin's example for location updates but I've been banging my head against the wall as to why location updates arent working!
public class ActivityManager : Java.Lang.Object, ILocationListener, ISensorEventListener
{
private readonly LocationManager _locationManager;
private readonly SensorManager _sensorManager;
private readonly List<Location> _locationCache;
private readonly List<SensorEvent> _sensorCache;
private bool Continous { get; set; }
public ActivityManager(LocationManager locationManager, SensorManager sensorManager)
{
_locationManager = locationManager;
_sensorManager = sensorManager;
_locationCache = new List<Location>();
_sensorCache = new List<SensorEvent>();
Continous = false;
}
public void StartTrackingLocation()
{
const string provider = LocationManager.GpsProvider;
if (_locationManager.IsProviderEnabled(provider))
{
_locationManager.RequestLocationUpdates(provider, 0, 0, this);
}
}
public void StartTrackingAccelerometer()
{
var mHandlerThread = new HandlerThread("sensorThread");
mHandlerThread.Start();
var handler = new Handler(mHandlerThread.Looper);
_sensorManager.RegisterListener(this, _sensorManager.GetDefaultSensor(SensorType.Accelerometer),
SensorDelay.Normal, handler);
}
public void StopTrackingLocation()
{
_locationManager.RemoveUpdates(this);
}
public void StopTrackingAccelerometer()
{
_sensorManager.UnregisterListener(this);
}
public void StartContinousTracking()
{
_locationCache.Clear();
_sensorCache.Clear();
Continous = true;
}
public void StopContinousTracking()
{
_locationCache.Clear();
_sensorCache.Clear();
Continous = false;
}
public void ExportLocationData(string path)
{
var kml = new Kml
{
Feature = new Placemark
{
Geometry = new LineString
{
Coordinates = new CoordinateCollection(_locationCache.Select(l => new Vector {Latitude = l.Latitude, Longitude = l.Longitude}))
}
}
};
var kmlFile = KmlFile.Create(kml, true);
using (var stream = File.OpenWrite(path))
{
kmlFile.Save(stream);
}
}
public void ExportSensorData(string path)
{
var csv = new CsvWriter(new StreamWriter(path));
csv.WriteField("AccX");
csv.WriteField("AccY");
csv.WriteField("AccZ");
csv.NextRecord();
foreach (var s in _sensorCache.ToList())
{
csv.WriteField(s.Values[0]);
csv.WriteField(s.Values[1]);
csv.WriteField(s.Values[2]);
csv.NextRecord();
}
csv.Dispose();
}
public void OnLocationChanged(Location location)
{
_locationCache.Add(location);
if (!Continous) _locationCache.RemoveAll(l => location.Time - l.Time > 120000);
}
public void OnSensorChanged(SensorEvent e)
{
_sensorCache.Add(e);
if (!Continous) _sensorCache.RemoveAll(s => e.Timestamp - s.Timestamp > 120000000000);
}
public void OnProviderDisabled(string provider) { }
public void OnProviderEnabled(string provider) { }
public void OnStatusChanged(string provider, Availability status, Bundle extras) { }
public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy) { }
}
Thanks in advance.
This is the implementation I have used for the Android element of a Xamarin.Forms app:
The communicating class
public class PlatformLocation : ILocation
{
private readonly global::Android.Locations.LocationManager _locationManager;
private readonly string[] _providers;
private readonly TaskCompletionSource<PfgLocationInfo> _tcs;
public PlatformLocation()
{
_locationManager = (global::Android.Locations.LocationManager)Application.Context.GetSystemService(Context.LocationService);
_providers = _locationManager.GetProviders(false).Where(s => s != global::Android.Locations.LocationManager.PassiveProvider).ToArray();
_tcs = new TaskCompletionSource<PfgLocationInfo>();
}
#region Private Methods
Task<PfgLocationInfo> StartUpdatingLocation(int timeout = 0)
{
var lastKnownGpsLocation = _locationManager.GetLastKnownLocation("gps");
if (lastKnownGpsLocation != null)
{
var pfgLocation = new PfgLocationInfo()
{
Longitude = lastKnownGpsLocation.Longitude,
Latitude = lastKnownGpsLocation.Latitude,
Timestamp = DateTime.Now,
Success = true,
Status = PfgLocationStatus.Valid
};
_tcs.TrySetResult(pfgLocation);
return _tcs.Task;
}
var lastKnownNetworkLocation = _locationManager.GetLastKnownLocation("network");
if (lastKnownNetworkLocation != null)
{
var pfgLocation = new PfgLocationInfo()
{
Longitude = lastKnownNetworkLocation.Longitude,
Latitude = lastKnownNetworkLocation.Latitude,
Timestamp = DateTime.Now,
Success = true,
Status = PfgLocationStatus.Valid
};
_tcs.TrySetResult(pfgLocation);
return _tcs.Task;
}
LocationListener listener = null;
listener = new LocationListener(
_providers.Where(_locationManager.IsProviderEnabled),
() =>
{
if (listener.Task.IsCanceled)
{
_locationManager.RemoveUpdates(listener);
}
},
timeout
);
try
{
var looper = Looper.MyLooper() ?? Looper.MainLooper;
var enabled = 0;
for (var i = 0; i < _providers.Length; ++i)
{
if (_locationManager.IsProviderEnabled(_providers[i]))
{
enabled++;
}
_locationManager.RequestLocationUpdates(_providers[i], 0, 0, listener, looper);
}
if (enabled == 0)
{
for (var i = 0; i < _providers.Length; ++i)
{
_locationManager.RemoveUpdates(listener);
}
_tcs.TrySetResult(new PfgLocationInfo{ Timestamp = DateTime.Now, Status = PfgLocationStatus.Restricted, Success = false });
return _tcs.Task;
}
}
catch (TaskCanceledException tcex)
{
_tcs.TrySetResult(new PfgLocationInfo{ Timestamp = DateTime.Now, Status = PfgLocationStatus.Restricted, Success = false });
return _tcs.Task;
}
catch (SecurityException)
{
_tcs.TrySetResult(new PfgLocationInfo{ Timestamp = DateTime.Now, Status = PfgLocationStatus.Restricted, Success = false });
return _tcs.Task;
}
return listener.Task;
}
#endregion
#region ILocation implementation
public Task<PfgLocationInfo> GetLocationAsync()
{
return StartUpdatingLocation();
}
public Task<PfgLocationInfo> GetLocationAsync(int timeout)
{
return StartUpdatingLocation(timeout);
}
#endregion
}
The ILocationListener implementation:
public class LocationListener : Java.Lang.Object, ILocationListener
{
private readonly HashSet<string> _activeProviders;
private readonly TaskCompletionSource<PfgLocationInfo> _completionSource = new TaskCompletionSource<PfgLocationInfo>();
private readonly Action _finishedCallback;
private readonly Timer _timer;
private readonly int _timeout;
private readonly object _locationLock = new object();
public Task<PfgLocationInfo> Task
{
get
{
return _completionSource.Task;
}
}
public LocationListener(IEnumerable<string> providers, Action finishedCallback, int timeout = -1)
{
_activeProviders = new HashSet<string>(providers);
_finishedCallback = finishedCallback;
if (timeout > 0)
{
_timeout = timeout;
_timer = new Timer(TimesUp, null, _timeout * 1000, 0);
}
}
#region Timeout Methods
void TimesUp(object state)
{
lock (_locationLock)
{
if (_completionSource.TrySetCanceled() && _finishedCallback != null)
{
_finishedCallback();
}
}
_timer.Dispose();
}
#endregion
#region ILocationListener implementation
public void OnLocationChanged(global::Android.Locations.Location location)
{
if (location != null)
{
Finish(location);
return;
}
}
public void OnProviderDisabled(string provider)
{
lock (_activeProviders)
{
if (_activeProviders.Remove(provider) && _activeProviders.Count == 0)
{
_completionSource.TrySetResult(new PfgLocationInfo{ Timestamp = DateTime.Now, Status = PfgLocationStatus.Restricted, Success = false });
}
}
}
public void OnProviderEnabled(string provider)
{
lock (_activeProviders)
_activeProviders.Add(provider);
}
public void OnStatusChanged(string provider, Availability status, global::Android.OS.Bundle extras)
{
switch (status)
{
case Availability.Available:
OnProviderEnabled(provider);
break;
case Availability.OutOfService:
OnProviderDisabled(provider);
break;
}
}
#endregion
#region Private Methods
void Finish(global::Android.Locations.Location location)
{
var pfgLocationInfo = new PfgLocationInfo()
{
Longitude = location.Longitude,
Latitude = location.Latitude,
Timestamp = DateTime.Now,
Success = true,
Status = PfgLocationStatus.Valid
};
_completionSource.TrySetResult(pfgLocationInfo);
}
#endregion
}
The PfgLocationInfo is just a simple class for holding the results
This works exactly as required and should be relatively easy to transform to your requirements.
It turns out this was working but very intermittently. The issue appeared to be that another thread that was running was asynchronously to log the sensor data was queueing up operations which were effectively drowning out the location updates.
Removing the sensor data logging started to allow location updates to come through. This seemed strange to me as this seemed to occur despite the sensor data logging being contained in their own thread.