How to send constantly changing sensor data using bluetooth? - java

I am trying to send light data from a light sensor using Bluetooth in Android Studio. Once the user presses the button, the sensor value should be constantly updating and sent to the other phone.
I've modified some Bluetooth code I found online; however, the light sensor value does not constantly update, it just shows the value of the sensor when the button was pressed.
I've tried putting in a timer that runs every 5 seconds after the start flag is set, so that a new value from the light sensor is constantly sent, but it does not work.
Is there a way I can sample the sensor and send the constantly updating sensor data after the button is pressed? Thanks in advance.
This is the code which gets the sensor readings and passes it onto the button listeners code:
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor sensor = sensorEvent.sensor;
if (sensor.getType() == Sensor.TYPE_LIGHT) {
lightText.setText("Light = " + sensorEvent.values[0]);
float lightTemp = sensorEvent.values[0];
implementListeners(lightTemp);
}
}
Button listeners - listens for the user pressing the start button and then writes the sensor reading to the bluetooth part of the code (to then send to the other phone):
private void implementListeners(final float lightTemp) {
.
.
.
.
startBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int startFlag=1;
checkStartFlag(startFlag);
}
private void checkStartFlag(int startFlag) {
if (startFlag==1){
Timer timer = new Timer ();
TimerTask checkSec = new TimerTask () {
#Override
public void run () {
String string= String.valueOf(lightTemp);
sendReceive.write(string.getBytes());
}
};
timer.schedule (checkSec, 0l, 5000); // Check every 1 sec
}
}
});
}

There's a lot going on in your code here, so let me try to explain what's currently happening. When you get a new light value, you change the start button's click listener to start sending the value over bluetooth. However, since you're passing lightTemp into your implementListeners function, it won't get changed when onSensorChanged fires again. Thus, having a timer run every 5 seconds is likely working, but instead of sending a new value it's just sending the old one from before. So, what we need to do is have the data sent whenever the sensor value changes instead of when the button gets pressed.
The best way of going about this would likely be to have a state variable in your class that gets updated when you tap the start button.
private boolean sendData = false;
//...
#Override
protected void onCreate(Bundle savedInstanceState) {
// Your existing code in onCreate goes here
startBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sendData = !sendData; // Toggles whether we're sending data or not.
// You can also just set it to true if you don't
// want toggling behaviour.
}
});
}
Now, we need to actually send the data if we have enabled data sending. We can change your onSensorChanged method to check the state variable we defined before, then send data if our state is true:
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor sensor = sensorEvent.sensor;
if (sensor.getType() == Sensor.TYPE_LIGHT) {
lightText.setText("Light = " + sensorEvent.values[0]);
if(sendData) { // check if we've enabed data sending
String lightTemp = String.valueOf(sensorEvent.values[0]); // get the sensor value as a string
sendReceive.write(lightTemp.getBytes()); // send the string over bluetooth
}
}
}
Your app should now send data over bluetooth every time the light sensor value updates after you tap the start button.

Related

Is there a way to make a Button alter a ToggleButton's state?

I am working on a Tuner app in Android Studio.
I want to have a button that displays "Start Tuning" & when you click it, the ToggleButton above (which has the image of a mic on & mic off) switches from the Muted Mic image to the Unmuted to show that the app is "listening".
As of right now, I have a button that onClick changes the text view from off to on, however there is no way to go back from on to off, and so far I have found no way to make the mic "button" change as well. Is there a way to do this? Here is the Java code I have so far:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
beginTuning = findViewById(R.id.toggleButton);
mic = findViewById(R.id.muteButton);
mic_status = findViewById(R.id.micState);
mic_status.setText("Mic off");
tester = findViewById(R.id.test_button);
tester.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (tester.isPressed() && mic.isChecked()) {
mic_status.setText("on");
} else {
mic_status.setText("off");
}
}
});
you have stored mute status in variable 'mic' so when volume is turned on' set mute-check-status to 'false': You just need to add statements marked 1.. and 2.. to your code;
public void onClick(View view) {
if (tester.isPressed() && mic.isChecked()) {
mic_status.setText("on");
mic.setChecked(false); // 1..add just this line so 'else' part of condition will execute next time
} else {
mic_status.setText("off");
mic.setChecked(ture); // 2..also set mute checked..
}
}
});

Android: SetText in handler doesn't change text in TextView

I have an app that connects to bluetooth and displays information received.
when pressing back, the activity isn't killed and I can go back to the activity and everything is the same. The activity collects the data in the background and everything is okay
I have a button, that when pressed, kills all activities (including the activity that displays the data).
When I try to create that activity again, the data keeps coming, and I can still handle it and write it to a file, but it won't show it in the UI. It seems the setText function in the handler doesn't work after the activity is created the second time (setText does work outside of the handler).
The boolean value synced is true if the activity is created a second time.
This is OnCreate
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_admin);
loading_panel = findViewById(R.id.loadingPanel);
loading_panel.setVisibility(View.GONE);
connectionButton = (Button) findViewById(R.id.connection_button);
gsrTextView = (TextView) findViewById(R.id.gsr_textview);
emgTextView = (TextView) findViewById(R.id.emg_textview);
adxlTextView = (TextView) findViewById(R.id.adxl_textview);
fsrTextView = (TextView) findViewById(R.id.fsr_textview);
textviews_set=true;
if(synced)
{
setConnected(currMac);
manageConnection();
Button sync_button = (Button) findViewById(R.id.sync_button);
sync_button.setText(R.string.synced_text);
}
// more code
}
Implementation of OnBackPressed:
#Override
public void onBackPressed() {
Intent i = new Intent(AdminActivity.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(i);
}
This is (part of) the handler:
#SuppressLint("HandlerLeak")
public final Handler handler = new Handler() {
#Override
public void handleMessage(#NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
//a lot of code for other cases
case READ_SUCCESS:
String s = (String) msg.obj;
showData(s);
break;
}
}
}
This is (part of) showData:
private void showData(String s) {
// todo: 1.parse the json; 2.show data (json components) on the textViews
try {
//Log.d(LOG_TAG, "the new string is " + s);
JSONObject json = new JSONObject(s);
// Collect the sensors measurements
String gsrMeasure = json.getString("gsr");
String emgMeasure = json.getString("emg");
String adxlMeasure = json.getString("adxl");
String fsrMeasure = json.getString("fsr");
String time_str = json.getString("time");
// Show the data
if(textviews_set) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d(LOG_TAG, "ABOUT TO SET TEXT");
gsrTextView.setText(gsrMeasure);
emgTextView.setText(emgMeasure);
adxlTextView.setText(adxlMeasure);
fsrTextView.setText(fsrMeasure);
}
});
}
//more code
}
}
I even made sure that I'm running the setText on UI thread even though it works on the first run of the activity without the runOnUiThread
I also thought that maybe the handler is called before the findViewByIDs are called but I used a boolean variable textviews_set to make sure it doesn't happen
It is worth mentioning that when I create the activity for the first time, the bluetooth device isn't connected and thus the handler isn't running, so there's a really good chance that the problem lies there. I just don't know exactly where, or how to solve it.
I found out what the problem was, if anyone gets stuck with the same issue.
I declared all the textviews as non-static, thus when creating the activity the second time, for some reason the handler still referred to the textviews of the first instance of the activity.
All I had to do was declare the textviews in the class as static, so when I create the activity for the second time, the textviews are already declared and refer to the actual textviews in the xml.
Based on the information provide the follow is the best suggestion.
So try the following in onCreate() before textviews_set=true;
gsrTextView.setText(" ");
emgTextView.setText(" ");
adxlTextView.setText(" ");
fsrTextView.setText(" ");
Hope this solves issue.
Reference:
How do I declare a TextView variable so that I can use it anywhere?

Android SeekBar talkback, talking too much

(Android) On a music player, you update the seekbar as expected with this:
PRECISION_SEEKBAR = 100000;
((SeekBar) findViewById(R.id.seekBar2)).setMax(PRECISION_SEEKBAR);
timerSeekBarUpdate.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
final SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar);
#Override
public void run() {
if (control == null || player == null) {
cancel();
return;
}
seekBar.setProgress((int) (player.getCurrentPosition() * PRECISION_SEEKBAR / player.getDuration()));
...
However, if the focus is on the seek bar, talkback steadily and nonstop gives feedback for the progress. Like "seek control 25%", "seek control 25%", "seek control 25%", "seek control 26%", "seek control 26%", "seek control 27%"
I'm missing sth but couldnot solve the problem. I have set the contentDescription to other than #null. But then it reads the content description this time without stopping.
On Spotify client, I checked, it reads the progress as "xx percent" just once. Despite saving the focus on the seekbar.
When I edit the precision for 1 or 100, then you lose the precision on the seekbar. It looks like there are a few parts in the song. You either play one or another by swiping on the seekbar.
Has anybody experienced sth like this? I couldn't find anything on google docs, stack network or somewhere else.
You can just override sendAccessibilityEvent() so it ignores description updates:
#Override
public void sendAccessibilityEvent(int eventType) {
if (eventType != AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION) {
super.sendAccessibilityEvent(eventType);
}
}
As Altoyyr mentioned, this has the side effect of ignore ALL description updates, including scrolling with volume buttons. So you'll need add back sending the event for volume press actions:
#Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
super.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
}
}
return super.performAccessibilityAction(action, arguments);
}
I had the problem and found that SeekBar reads the percentage on every update.
It helped, that I update the SeekBar only when the percentage changes but still keep a high precision (in my case in ms).
#Override
public void updateSeekBar(final int currentPosInMillis, final int durationInMillis) {
long progressPercent = calculatePercent(currentPosInMillis, durationInMillis);
if (progressPercent != previousProgressPercent) {
seekBar.setMax(durationInMillis);
seekBar.setProgress(currentPosInMillis);
}
previousProgressPercent = progressPercent;
}
private int calculatePercent(int currentPosInMillis, int durationInMillis) {
if(durationInMillis == 0) {
return 0;
}
return (int) (((float)currentPosInMillis / durationInMillis) * 100);
}
previousProgressPercent is initialized to -1.
Please note that this solution is not the same as Spotify does it.
Spotify overrides the message announced by the system when the SeekBar gets selected.
This has following 2 effects:
Updates can be made as often as you want without the percentage beeing repeated
When the percentage changes while the SeekBar is selected then nothing gets announced
Point 2 might me a drawback depending on what you want to achieve.

Recurring background task Android - Handler vs AlarmManager

I am working on an application for research purposes. We want to track some user activity on the phone: if some actions are easily detactable with broadcast receivers, we want also to check current running applications.
This application works only on Android devices that runs android 5.0 or lower.
My problem is that I post a Runnable Obj in a handler, Runnable posts again itself in handler after HALF_SECOND (see code for details). In runnable I get information and send them to IntentService to perform work.
Everything works fine: app starts at boot, handler and runnable do their job in background UNLESS I open the main Activity.
The app is able to keep going for days, but if I open the main Activity and then close it from "recent open activities" with a swipe, or from the memory task manager, handler and runnable stop, even if they are not called/accessed by the activity (they are in a Separate Service).
Moreover, not always a call to onDestroy (of the activity or Service) is made.
Reading online I understand that swipe or task manager remove the app from memory abrouptly thus not always calling onDestory.
What I want to achive is to make the handler start again soon after the main activity is closed.
What I have tried is to put some check in onPause method of the activity, making sure to remove this check if onStart is called again (like in case the the app switches from vertical to horizontal layout, or if home button is pressed and then app is opend again). Also implemented a way to make the handler send "ImAlive" intent to a broadcast receiver, which should restart the service that starts the handler, if intents do not arrive before a count down is finished. Unfortunately, as soon the main activty stops existing, even the broadcast is automatically unregistered and destroyed.
My question is, is there a way to create something that is able to make my handler restart if the activity is closed? Or is there some other pattern that can help me as workaround for what I want to achieve? Because I am polling data every half second I read is better to use handler, because Timer augments small interval to a greater interval, and AlarmManager is not precise enough for very small interval.
What I want to achieve is something similar to Facebook, Instagram, Whatsapp, Telegram app, that are always in memory, and even if you force to terminate them, after a few seconds are back again there... how?
We are not interested in battery issues because of continuous polling to data. As for research purposes we don't mind if the phone on which we are testing last 2 days straight, 1 day or 12 hours or less.
Here the code: OnBootService is started from broadcast receiver, declared in manifest when onBootCompleted and ShutDown actions are received, in order to start and stop handler.
public class OnBootService extends Service{
private static final Handler handler = new Handler();
private final long HALF_SEC = 500;
private RunnableTest r = null;
private Context myContext = this;
private final String TAG = "BootService";
// Extras
public static final String START = "start";
public static final String STOP = "stop";
#Override
public IBinder onBind(Intent intent){
return null;
}
#Override
public int onStartCommand(Intent intent, int flag, int startId){
String action = intent.getAction();
switch(action){
case START: startHandler();
break;
case STOP: stopHandler();
break;
}
return START_NOT_STICKY;
}
private void startHandler(){
if(r == null){
r = new RunnableTest();
handler.post(r);
Log.i(TAG, "----Handler started!");
}
}
private void stopHandler(){
if(r != null){
Log.i(TAG, "----calling STOP");
handler.removeCallbacks(r);
r = null;
}
}
private class RunnableTest implements Runnable {
private String TAG = "RunnableTest";
public RunnableTest(){}
#Override
public void run(){
handler.removeCallbacks(this);
// Do stuff
Intent i = new Intent(myContext, MyIntentService.class);
i.putExtra("addStuff", myStuff);
myContext.startService(i);
handler.postDelayed(this, HALF_SEC);
}
}
Activity is empty: all method overridden just to understand proper Activity lifecycle, but else is empty for now.
public class MainActivity extends AppCompatActivity {
private final String TAG = "Activity";
private Context myContext = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// access a file and get stored information to show
setContentView(R.layout.activity_main);
Toast.makeText(getBaseContext(), "Application open successfully", Toast.LENGTH_LONG).show();
}
#Override
protected void onRestart(){
super.onRestart();
Log.e(TAG, "----onRestart Called");
}
#Override
protected void onStart(){
super.onStart();
Log.e(TAG, "----onSTART Called");
}
#Override
protected void onResume(){
super.onResume();
Log.e(TAG, "----onRESUME Called");
}
#Override
protected void onPause(){
super.onPause();
Log.e(TAG, "----onPAUSE Called");
}
#Override
protected void onStop(){
super.onStop();
Log.e(TAG, "----onSTOP Called");
}
#Override
protected void onDestroy(){
super.onDestroy();
Log.e(TAG, "----onDestroy Called");
}
}
Any help is really appreciated, if you need some more information on the code, I will update the post.
Thank you!
Android system can restart the service if u return START_STICKY inside onStartCommand().
It works perfectly on all lower version than Lollipop in Android.
No need of CountDownTimer.

Configure Back Button to Skip First View

I have an app with a title screen. When the app first starts, I have an onCreate method that contains the following code:
setContentView(R.layout.title_screen);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
setContentView(R.layout.main_screen);
}
}, 2000);
When I run my app and press the back button while on the main_screen layout, it closes the app (as it should). However, when I reopen the app, it displays the title_screen layout for two seconds again even though the app is already running. How can I prevent this?
This will prevent the delay appearing again when resumed:
private static boolean flag = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!flag){
setContentView(R.layout.title_screen);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
setContentView(R.layout.main_screen);
}
}, 2000);
flag = true;
} else {
setContentView(R.layout.main_screen);
}
}
Btw, if your app was on background and it is calling onCreate again while being resumed, it means that it is killed by the OS. Therefore it is normal to have the initial delay again.
What I would do is to implement two different activities first one showing title_screen and the second which is started after 2s should show your main screen.
After looking at your code I can see that you ALWAYS start with title_screen then after 2s, you change to main_screen. Therefore, when you press back, that means you finish your activity. When you re-open your app, onCreated is called again, and it run every line of code as the previous opening.Of course, there's no difference in 2 times you open your app. To overcome it, I recommend to use SharedPreference to store the flag to check main_screen or title_screen.

Categories