I am programming an android app that does the following (in a nutshell):
Creates an activity that listens to data streaming in via bluetooth
Parses the data streamed in
(Plan) Display the interpreted meaning of the data, like a graph/text
For #3, I am planning to create a new activity that sits on top of the Bluetooth Streaming activity -- after all, the bluetooth matters should be in the background after initial set up.
However, I am having issues sending the data between the BlueTooth activity and the display activity.
I have googled, researched and the best I can come up with is using Intents for this, but its not working.
Here is an example. Say data is coming in and I interpret the data involve body movement:
private BodyMovementPositionStream() {
if (getActivity() != null && getContext() != null && iDebug == null) {
iDebug = new Intent(getActivity(), AGDebug.class);
iDebug.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
new Thread(new Runnable() {
public void run() {
if (theApplication.isD()) {
Log.d(theApplication.getTAG(), "Creating Intent for Position Debuging...");
getContext().registerReceiver(new AGDReceiver(), new IntentFilter("com.ekscsy.sleepstudy.debug.AGDebug"));
getContext().startActivity(iDebug);
}
}
}).start();
}
}
So basically, in that snip-it, I start the activity AGDebug, add the flags for new task, and then I register the receiver in a new thread. This works, I see it on my phone and the Log.d() message shows up.
Later on in my code, I extract the data:
private static final int fSize = 4;
public void unpack(byte[] data) {
int i = -fSize;
accx = Streams.byteArr2ByteBuff(data, i += fSize, fSize).order(null).getFloat();
accy = Streams.byteArr2ByteBuff(data, i += fSize, fSize).order(null).getFloat();
accz = Streams.byteArr2ByteBuff(data, i += fSize, fSize).order(null).getFloat();
gyrox = Streams.byteArr2ByteBuff(data, i += fSize, fSize).order(null).getFloat();
gyroy = Streams.byteArr2ByteBuff(data, i += fSize, fSize).order(null).getFloat();
gyroz = Streams.byteArr2ByteBuff(data, i += fSize, fSize).order(null).getFloat();
temp = Streams.byteArr2ByteBuff(data, i += fSize, fSize).order(null).getFloat();
Intent intent = new Intent("com.ekscsy.sleepstudy.debug.AGDReceiver");
intent.putExtra("com.ekscsy.sleepstudy.debug.AGDebug.accxF", accx);
getContext().sendBroadcast(intent);
}
At the end of the extraction, I make a new intent, .putExtra() the name of the package + a float with some data. I do the .sendBroadcast() but... nothing happens. I have a breakpoint that never gets called:
public class AGDReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
assert(true); //breakpoint here
}
}
Obviously, I am missing steps here, but I can't seem to find any good information on sharing data between activities.
In fact, let me re-iterate, the main goal here is the share data between activities. Data, being floats, ints, etc. I am assuming the "Android" way of doing this is through intents and broadcast, but if there is a better method, I am all ears.
I am looking to make this app compatible with API 8.
EDIT: FYI, I changed the code to take the start of the AGDebug activity out of its own thread. It looks more like this now:
if (getActivity() != null && getContext() != null && iDebug == null) {
iDebug = new Intent(getActivity(), AGDebug.class);
iDebug.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.d(theApplication.getTAG(), "Creating Intent for Position Debuging...");
getContext().registerReceiver(new AGDReceiver(), new IntentFilter("com.ekscsy.sleepstudy.debug.AGDebug"));
getContext().startActivity(iDebug);
}
Still didn't change anything though. I thought maybe there was an issue of running the activity between different manually created threads.
Major Update/New Problem:
I found this guide http://www.sohailaziz.com/2012/04/localbroadcastmanager-intra-application.html and was able to successfully implement its strategy of making the broadcaster work with the LocalBroadcastManager. However... and this is very weird... I am finding the onReceieve() method gets called sometimes or not. IOW: when I run the app, either it will get called each time I say lbm.sendBroadcast(intent) or it won't get called at all.
Very puzzling.
Final Edit:
Okay I got it to work. There is a delicate art of making sure each intent is named correctly and points to the right spot. Once done, it seems to be consistent.
You register the broadcast receiver like this:
getContext().registerReceiver(new AGDReceiver(),
new IntentFilter("com.ekscsy.sleepstudy.debug.AGDebug"));
but you broadcast this Intent:
Intent intent = new Intent("com.ekscsy.sleepstudy.debug.AGDReceiver");
The actions in the Intents are not the same. You could have saved yourself this trouble if you defined a constant for this, ie:
public static final String MY_INTENT_ACTION = "com.ekscsy.sleepstudy.debug.AGDReceiver";
and then used that constant when you create the broadcast intent and when you create the intent filter for registering the broadcast receiver.
Related
I am using the quickstart-android code provided by google but after many attempts I cam unable to find a context that is not returning null. The BarcodeScannerProcessor is not itself an Activity, so I have attempted to create an instance of the LivePreviewActivity and use that as the context in the intent, but it's null.
The goal is to once a valid barcode is recognized I want to open a new activity that allows a user to verify value and on the push of a button call a webservice to post the barcode to a database via API. I am having a hard time finding a valid context and the app is crashing when it trys to execute the Intent.
Starting at line 97-107:
https://github.com/jamiekeefer/quickstart-android/blob/master/mlkit/app/src/main/java/com/google/firebase/samples/apps/mlkit/java/barcodescanning/BarcodeScanningProcessor.java
for (int i = 0; i < barcodes.size(); ++i) {
FirebaseVisionBarcode barcode = barcodes.get(i);
BarcodeGraphic barcodeGraphic = new BarcodeGraphic(graphicOverlay, barcode);
graphicOverlay.add(barcodeGraphic);
System.out.println(barcode.getRawValue());
if (!barcode.getRawValue().equals("") ) {
System.out.println("Got the number:" + barcode.getRawValue() + " Context: " + mContext); //OLD SCHOOL DEBUG OUTPUT
//enter code to start activity
Intent intent = new Intent(mContext, SendScannedBarcode.class);
String message = scannedBarcode;
intent.putExtra(EXTRA_MESSAGE, message);
mContext.startActivity(intent);
}
}
You can back up in the repo to see the instance of the LivePreviewActivity where I trying to get context.
I have tried a number of things and read about Context, Views and Activities and basically have completely confused myself. The only tuts I can find are using Kotlin, which is not helping clarify things.
I appreacite any help in indentifying or contruting a valid Intent from this Context. Thank you.
So I am assuming that in your LivePreviewActivity you are creating an object of the class BarcodeScanningProcessor. What you can do is change the constructor in the BarcodeScanningProcessor class to accept a context and then you pass in your LivePreviewActivity's context.
This is what the code should look like:
In BarcodeScanningProcessor:
public BarcodeScanningProcessor(Context context) {
// Note that if you know which format of barcode your app is dealing with, detection will be
// faster to specify the supported barcode formats one by one, e.g.
// new FirebaseVisionBarcodeDetectorOptions.Builder()
// .setBarcodeFormats(FirebaseVisionBarcode.FORMAT_QR_CODE)
// .build();
detector = FirebaseVision.getInstance().getVisionBarcodeDetector();
this.mContext = context;
}
Then in LivePreviewActivity:
In the particular case of your activity you would do:
case BARCODE_DETECTION:
Log.i(TAG, "Using Barcode Detector Processor");
cameraSource.setMachineLearningFrameProcessor(new BarcodeScanningProcessor(getApplicationContext()));
break;
Or if you just wanted to create an object of the class you could do:
BarcodeScanningProcessor bsp = new BarcodeScanningProcessor(getApplicationContext());
This should now give your BarcodeScanningProcessor class the context of your activity. Now, in BarcodeScanningProcessor, mContext should not be null and will have the context of your activity. I hope this answers your question.
try this create Application class
import android.app.Application;
public class MyApplication extends Application {
static MyApplication instance;
#Override
public void onCreate() {
super.onCreate();
instance=this;
}
public static MyApplication getInstance() {
return instance;
}
}
Register in manifest file
<application
..
android:name="com.yourpackage.MyApplication"
..>
.
.
.
</application>
start activity using this MyApplication.
Intent intent = new Intent(MyApplication.getInstance(), SendScannedBarcode.class);
String message = scannedBarcode;
intent.putExtra(EXTRA_MESSAGE, message);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MyApplication. getInstance().startActivity(intent);
Another way of handling the issue is create new constructor of BarcodeScanningProcessor which takes interface call back and once processing is done pass back result to caller.
public interface BarcodeUpdateListener {
#UiThread
void onBarcodeDetected(Barcode barcode);
}
private BarcodeUpdateListener callback;
public BarcodeScanningProcessor(BarcodeUpdateListener callback){
this.callback = callback;
detector = FirebaseVision.getInstance().getVisionBarcodeDetector();
}
Once you get the result pass result to caller
callback.onBarcodeDetected(<Barcode>)
You can get the context from graphicOverlay:
Context context = graphicOverlay.getContext();
I have a IntentService that queries the MediaStore to get songs, albums, artist, whatnot. that way if someone tries to add an entire artist and there are a lot of albums the IntentService can do the expensive operations of getting all the albums from an artist, then looping through them to get all the songs from each album and it won't take up the ui so the app can still function. My problem/question is, is there a way to send results multiple times from the same service call?
Example: Someone clicks on an artist. That starts the IntentService with the artist_id and the IntentService starts doing its work. What I would like to have happen is, the IntentService gets the first album, then the first song and sends that result back, but it also continues to process the rest of the albums and songs, then when that is done it sends them all back. I don't know if that makes sense or not, so here is an example of my current setup...
BaseActivity...
public GetSongsReceiver receiver;
public GetSongsReceiver.Receiver returnReceiver = new GetSongsReceiver.Receiver() {
#Override
public void onReceiveResult(int resultCode, Bundle resultData) {
if(resultCode == RESULT_OK) {
ArrayList<Song> songs = resultData.getParcelableArrayList("songs");
if(songs != null && songs.size() > 0) {
if(musicBound) {
musicService.addAllSongs(songs);
musicService.playSong();
}
}
}
};
...
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
//processing and such.
if(musicBound) {
musicService.setPlayType(MusicService.PlayType.ARTIST);
Intent i = new Intent(BaseActivity.this, GetPlaylistItemsService.class);
Bundle extras = new Bundle();
extras.putParcelable("key", data.getParcelableExtra("artist"));
extras.putString("service_type", "artist");
extras.putParcelable("receiver", returnReceiver);
i.putExtras(extras);
startService(i);
}
}
GetPlaylistItemsService...
#Override
protected void onHandleIntent(Intent intent) {
ResultReceiver rec = intent.getParcelableExtra("receiver");
Artist artist = intent.getParcelableExtra("key");
ArrayList<Album> albums = getAlbumsFromArtist(artist);
ArrayList<Song> songs = new ArrayList<>();
for(Album album : albums) {
songs.addAll(getSongsFromAlbum(album);
}
Bundle extras = new Bundle();
extras.putParcelableArrayList("songs", songs);
rec.send(Activity.RESULT_OK, extras);
}
What I would like to be able to do is have the "rec.send..." send more than one time. That way I can get the first album and first song, send that result back so the media player can start playing it, then process the rest in the background and add them when they are finished. That would mean the IntentService would need to be able to rec.send more than once. Is that possible or do I need to break this into 2 different IntentService calls, one to get the first item and then another to get the rest?
it won't take up the ui so the app can still function
You do not need an IntentService for this. An ordinary background thread (perhaps tied to a LiveData), AsyncTask, RxJava chain, etc. can handle this.
That would mean the IntentService would need to be able to rec.send more than once. Is that possible
Sure. It would be more efficient to use an ordinary background thread, AsyncTask, RxJava chain, etc. But, you should be able to send() as many times as you like. The results will be handed to onReceiveResult() one at a time (i.e., you call send() 6 times, you get 6 onReceiveResult() calls).
I have a Recycleview of multiple movie's poster. I tried to click each poster to start a new activity with details of the movie, now I've tried to use the details of movie like date and name of movie to be shared in detail activity so I used this code in MainActivity:
#Override
public void onClick(String MovieName, String MovieDate) {
Context context = this;
Class destinationClass = DetailActivity.class;
Intent intentToStartDetailActivity = new Intent(context, destinationClass);
intentToStartDetailActivity.putExtra(Intent.EXTRA_TEXT, String.valueOf(MovieName));
intentToStartDetailActivity.putExtra(Intent.EXTRA_CC, String.valueOf(MovieDate));
startActivity(intentToStartDetailActivity);
}
and in DetailActivity:
mMovieName = (TextView) findViewById(R.id.tv_movie_Name);
mMovieDate = (TextView) findViewById(R.id.tv_movie_date);
Intent intentThatStartedThisActivity = getIntent();
if (intentThatStartedThisActivity != null) {
if (intentThatStartedThisActivity.hasExtra(Intent.EXTRA_TEXT )) {
movieName = intentThatStartedThisActivity.getStringExtra(Intent.EXTRA_TEXT);
mMovieName.setText(movieName);
}
if (intentThatStartedThisActivity.hasExtra(Intent.EXTRA_CC )) {
movieDate = intentThatStartedThisActivity.getStringExtra(Intent.EXTRA_CC);
mMovieDate.setText(movieDate);
}
}
It works fine and I get the information in detail activity but I used Intent.EXTRA_TEXT and Intent.EXTRA_CC and I need to use more. So, my question is can I rename the EXTRA_ with some other word that I choose? Cuz I saw it when I search different names for Extra but don't know how to create a new one like Extra_DESCRIPTION.
And the second question - is it true to use a new if condition with hasExtra() like I did to get information? can't I use only on if condition with hasExtra() to get all information? if so then how to do that?
ofcourse you can use any key you like.
for example
intent.putExtra("username", yourUsername);
intent.putExtra("password", yourPassword);
and it's a good practice to check intent.hasExtra(String key) to avoid null pointer exception.
or you can just directly check if intent has any extra budle by intent.hasExtras()
within my main activity I have the following code:
EditText usernameText;
EditText passwordText;
public void sendLogin (View loginview){
Intent i = new Intent(this, NetworkService.class);
startService(i);
}
Currently, this just sends an intent to the NetworkService, which is handled as follows (truncated):
public class NetworkService extends IntentService {
public NetworkService() {
super("NetworkService");
}
protected void onHandleIntent(Intent i) {
/* HTTP CONNECTION STUFF */
String login = URLEncoder.encode("Username", "UTF-8") + "=" + URLEncoder.encode("XXX", "UTF-8");
login += "&" + URLEncoder.encode("Password", "UTF-8") + "=" + URLEncoder.encode("XXX", "UTF-8");
}
}
Now, what I need to figure out, is how to pass those usernameText and passwordText values through to the NetworkService into the 'XXX', but ALSO within the NetworkService, I intend (no pun intended), to have it handle multiple intents from various places, one from a login, one from retrieving some information on users using the logon token, for instance.
It's where all my networking will be contained. I was instructed this was the best practise within android applications, to keep the networking separate.
My question is: What is the best way of sending those two variables to the NetworkService and also how, within the onHandleIntent of the NetworkService, do I separate the code to only do what I'm asking it to (login, fetch user information, fetch location data etc)?
Sorry if the answer is a simple one, but I'm very new to application programming.
Thanks
public void sendLogin (View loginview){
Intent i = new Intent(this, NetworkService.class);
i.putExtra("username", usernameText.getText().toString());
i.putExtra("password", passwordText.getText().toString());
startService(i);
}
Then in your IntentService:
#Override
protected void onHandleIntent(Intent intent) {
String username = intent.getStringExtra("username");
String password = intent.getStringExtra("password");
...
}
IntentServices are designed to handle several requests sent to it. In other words, if you keep sending intents using startService(intent), your NetworkService will keep getting its onHandleIntent method called. Under the hood, it has a queue of intents that it will work through until it is finished. So if you keep sending intents the way you are currently, but with certain flags set through the putExtra methods, then you can detect what your NetworkService should do and act appropriately. e.g. set a boolean extra to your intent called login, in your intentservice look for that flag being set via intent.getBooleanExtra("login"). If true, do your login stuff, else look for other flags you set.
1. For sending usernameText and passwordText to NetworkService do this....
Intent i = new Intent(Your_Class_Name.this, NetworkService.class);
i.putExtra("username", usernameText.getText().toString());
i.putExtra("password", passwordText.getText().toString());
startService(i);
2. To receive the data in NetworkService do this....
Intent intent = getIntent();
String userName = intent.getExtras().getString("username");
String password = intent.getExtras().getString("password");
I've got two activities, one of them is called MyActivity. I want both of them to be able to use a function located in a class othat we may call MyClass. In MyClass, I try to use an intent to launch the activity AnotherActivity. Since the constructor takes a context as parameter, I simply tried to store a context from the activity in the constructor, and then use it when I try to create my intent.
class MyClass {
private Context cxt;
MyClass(Context cxt) {
this.cxt = cxt;
}
startIntent() {
Intent intent = new Intent(cxt, AnotherActivity.class);
startActivity(intent); // this line throws a NullPointerException
}
}
The code in MyActivity to use the class is shown below:
myClassObject = new MyClass(MyActivity.this);
myClassObject.startIntent();
However, even thought none of the arguments are null (checked that with a simple if-statement), intent seems to be null and a NullPointerException is thrown. Why does it not work, and what can I do to solve the problem?
I'm quite new to Android and Java development, so please explain it as basic as you can.
cxt.startActivity(new Intent(cxt, AnotherActivity.class));
and to be sure that it's intent is NULL, and not something internal in startActivity method, you can add some checks, i.e.
Intent intent = new Intent(cxt, AnotherActivity.class);
Log.d(toString(), "intent = " + intent.toString());
cxt.startActivity(intent);
I've used almost identical code in my applications and it's worked fine.
I suspect there's something else going on that's in code you haven't shown us; I suspect there's some cut-and-paste issues --- e.g. what are you calling startActivity() on in MyClass?