I'm trying to build a Morse-Code App which can replay Morse with the built in Flashlight. So I tried a few things, one of them slightly worked, but not how it should.
So essentially I type in a Message, let's say "hello". It translates to
.... . .-.. .-.. ---
Then I want to replay that by tapping a Button.
I've tried out different things. This was my first attempt:
public void onPlayflash(View view) throws InterruptedException, CameraAccessException {
if (result == null) {
output.setText("ERROR");
} else {
currentposition = 0;
if (currentposition < result.length()) {
String c = String.valueOf(result.charAt(0));
if (c.equals("-")) {
//timeinmillis = 1000;
//setTimer();
flash.setTorchMode(flash.getCameraIdList()[0], true);
Thread.sleep(2000);
flash.setTorchMode(flash.getCameraIdList()[0], false);
} else if (c.equals(".")) {
//timeinmillis = 500;
//setTimer();
flash.setTorchMode(flash.getCameraIdList()[0], true);
Thread.sleep(1000);
flash.setTorchMode(flash.getCameraIdList()[0], false);
} else {
Thread.sleep(2000);
}
currentposition += 1;
}
}
}
This didn't work. It just said:
I/Choreographer: Skipped (*always a random number over 1000 here*) frames! The application may be doing too much work on its main thread.
Then I tried
public void onPlayflash(View view) throws InterruptedException, CameraAccessException {
if (result == null) {
output.setText("ERROR");
} else {
for (int i = 0; i < result.length(); i++) {
String c = String.valueOf(result.charAt(i));
if (c.equals("_")) {
flash.setTorchMode(flash.getCameraIdList()[0], true);
Thread.sleep(2000);
flash.setTorchMode(flash.getCameraIdList()[0], false);
Thread.sleep(500);
} else if (c.equals(".")) {
flash.setTorchMode(flash.getCameraIdList()[0], true);
Thread.sleep(1000);
flash.setTorchMode(flash.getCameraIdList()[0], false);
Thread.sleep(500);
} else {
Thread.sleep(1500);
}
}
}
}
This kinda worked, but it still says
I/Choreographer: Skipped (*always a random number over 1000 here*) frames! The application may be doing too much work on its main thread.
Actually the replay starts fine, but then it starts to struggle and skips parts of the iteration.
As you can see, I also experimented with android.os.CountDownTimer, but that didn't work out aswell. I only got one flash and then it stopped.
As you might see, I'm not that experienced yet '^^
Hope you can help me. Thanks in advance!
try again with CountDownTimer, but using recursion and passing a list of time to wait
private void test(List<Integer> resultTimeList, Integer count) {
flash.setTorchMode(flash.getCameraIdList()[0], true);
new CountDownTimer(resultTimeList.get(count), 500) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
new CountDownTimer(500, 500) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
flash.setTorchMode(flash.getCameraIdList()[0], false);
test(resultTimeList, count++);
}
}.start();
}
}.start();
}
Related
The problem is that the method runs only after I press button second time, but the goal is to work for both methods one after another when I press button for the first time.
The first method is runThreadWork(); and after that runThreadRest(); when the first is completed.
This is the part for the button.
btnStartTimer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(workMinutes > 0 || workSeconds > 0){
runThreadWork();
}else if(isTimerCompleted){
runThreadRest();
}
}
});
That's is the part for the method, basically both methods are the same in logic, but only change are variables for values, except TextView variable isn't changed for both methods, I want to use one TextView to show the output from both methods one after another.
private void runThreadWork() {
new Thread() {
public void run() {
while (workMinutes > 0 || workSeconds > 0) {
while (workSeconds > 0) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
workSeconds--;
String timeFormatedCountDownWork = String.format("Work time: " + "%02d:%02d", workMinutes, workSeconds);
txtTimeCountDown.setText(timeFormatedCountDownWork);
if(workSeconds == 0 && workMinutes > 0){
workMinutes--;
workSeconds = 60;
}
}
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}.start();
isTimerCompleted = true;
}
I have two handlers. Handler in a handler. Both of them are in a for-loop.
The overview is something like this,
for{
handler.postDelayed(runnableA{
for{
handler2.postDelayed(runnableB{
function();
}, 3000);
}
}, 1000);
}
I wanted to end handlers' work at any time when the user clicks back button. So, I created two Runnable Classes so that I can use something like runnableA.removellbacksAndMessages(null).
Handler messageHandler;
Handler countDownHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, "Start Play in 5 seconds", Toast.LENGTH_SHORT).show();
countDownHandler = new Handler();
for (int i = 7; i >= 0; --i) {
final int idx = i;
Runnable countRunnable = new CountRunnable(idx, countDownView);
countDownHandler.postDelayed(countRunnable, 1000 * (7 - i));
}
}
And this is Runnable Classes.
public class CountRunnable implements Runnable {
int idx;
TextView countDownView;
public CountRunnable(int idx, TextView countDownView) {
this.idx = idx;
this.countDownView = countDownView;
}
#Override
public void run() {
int messageSize = messageItems.size();
for (int j = 0; j < messageSize; j++) {
final int jdx = j;
messageHandler = new Handler();
Runnable messageRunnable = new MessageRunnable(jdx);
messageHandler.postDelayed(messageRunnable, 3000 * jdx);
}
}
}
class MessageRunnable implements Runnable {
int jdx;
public MessageRunnable(int jdx) {
this.jdx = jdx;
}
#Override
public void run() {
addMessageView(messageItems.get(jdx));
}
}
This is onBackPressed():
#Override
public void onBackPressed() {
super.onBackPressed();
Toast.makeText(getApplicationContext(), "All Work Ended.", Toast.LENGTH_SHORT).show();
scrollFlag = true;
try {
messageHandler.removeCallbacksAndMessages(null);
} catch (Exception e) {
Log.d(TAG, "messageHandler never used");
e.printStackTrace();
}
try {
countDownHandler.removeCallbacksAndMessages(null);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addMessageView(String message){
try{
mTextView.setText(message);
}catch(Exception e){
Toast.makeText(getApplicationContext(), "Abnormal End", Toast.LENGTH_SHORT).show();
}
}
But, I keep getting errors because the activity already ended but the handlers can't find the activity. So, Abnormal End Toast message shows as many as the size of inner for loop.
I can ignore this if I don't use the Toast message, but I am afraid of Memory leak or Bad formed Program or something like that.
How can I fix this problem?
The main problem is that you are creating n numbers of CountRunnables and m number MessageRunnables. Despite creating more than one numbers of handlers you are removing callbacks only for the latest-created Hanlder.
Here's what you should do:
Keep a reference of all the Handlers and Runnables and call messageHandler.removeCallbacksAndMessages(null); and countDownHandler.removeCallbacksAndMessages(null); on all of them.
I am trying to use a runnable to count down from 30s to 0, then run a method, then restart. I cannot seem to get this working right. Seems like a very convoluted solution for a simple task...
tvHeatCD is a TextView that should update every sec to show "5s" if 5 seconds remain etc
reduceHeat() is the method that should run every 30 seconds (matching conditions shown)
stopHeatTimer() is just calling handler.removeCallbacks(..) and heatTimerRunning = false
public void checkHeatTimer() {
RealmResults<Heat> heatList = realm.where(Heat.class).equalTo("theheat","theheat").findAll();
ht = heatList.get(0);
if(ht.getHeat() !=0) {
if(!heatTimerRunning) {
final int[] countdown = {30};
tvHeatCD.setVisibility(View.VISIBLE);
heatRunnable = new Runnable() {
#Override
public void run() {
heatTimerRunning = true;
if (!realm.isClosed()) {
countdown[0]--;
if(countdown[0] == 0) {
if (ht.getHeat() != 0) {
Crashlytics.setInt("heat", ht.getHeat());
reduceHeat("normal",20);
stopHeatTimer();
checkHeatTimer();
} else {
Crashlytics.setInt("heat", 0);
tvHeatCD.setVisibility(View.GONE);
stopHeatTimer();
}
}
tvHeatCD.setText(countdown[0] + "s");
heatHandler.postDelayed(heatRunnable, 1000);
} else if (realm.isClosed()) {
recreate();
}
}
};
heatHandler.postDelayed(heatRunnable, 30000);
}
} else {
tvHeatCD.setVisibility(View.GONE);
}
}
Appreciate any help...thank you!
I need to calculate time between two time. System.currentTimeMillis() returns same value everytime when it called in Thread.
My code is:
#Override
protected void onCreate(Bundle savedInstanceState) {
// Other codes..
start_sec = Math.round(System.currentTimeMillis()/1000);
fin = false;
runThread();
// Other codes..
}
private void runThread() {
new Thread() {
public void run() {
while (i++ < 61) {
if (!running) return;
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!fin){
int len = Math.round(System.currentTimeMillis()/1000) - start_sec;
Log.d("current time: ",String.valueOf( Math.round(System.currentTimeMillis()/1000)));
Log.d("difference is: ", String.valueOf(len));
if(len < 0 && len > 58){
fin=true;
}
timerec.getLayoutParams().width = metrics.widthPixels *(60- len)/60;
timerec.requestLayout();
}
else{
end_game();
running= true;
}
}
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
Here is the logs:
...
D/current time:: 1492337024
D/difference is:: 0
D/current time:: 1492337024
D/difference is:: 0
....
It returs same "time". What is the solution?
take time as long. and don't divide it by 1000. the time difference is fraction of seconds.that's why it is showing the same time as you are rounding it.
the difference between two cycle in a while loop is so much less than a second and when your calculating the difference by seconds (you divide current millisecond with 1000) it makes the same second and the difference is 0 seconds.
try to print difference in milliseconds (without dividing).
Try this:
#Override
protected void onCreate(Bundle savedInstanceState) {
// Other codes..
start_sec = System.currentTimeMillis();
fin = false;
runThread();
// Other codes..
}
private void runThread() {
new Thread() {
public void run() {
while (i++ < 61) {
if (!running) return;
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!fin){
int len = System.currentTimeMillis() - start_sec;
Log.d("current time: ",String.valueOf( System.currentTimeMillis()));
Log.d("difference is: ", String.valueOf(len));
if(len < 0 && len > 58){
fin=true;
}
timerec.getLayoutParams().width = metrics.widthPixels *(60- len)/60;
timerec.requestLayout();
}
else{
end_game();
running= true;
}
}
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
Math.round() causes the problem.
long len = System.currentTimeMillis()/1000 - start_sec;
Log.d("current time: ",String.valueOf(System.currentTimeMillis()/1000));
Log.d("difference is: ", String.valueOf(len));
This code works altought dividing.
Im a beginner android developer, so bear with me:
Im getting this error: "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views"
Anyways, i have a media player with two threads, the first one updates a circular progress bar and the second one updates a text view that i want to use to show the time in the mp3 file. The first thread gives me no errors and runs perfectly fine. (I implemented this before the textview update)
The second thread however gives me the error in the title. I've looked into handlers asynctasks and runonuithread but I can't figure out how to utilize any of them since im using a while loop that's constantly updating it.
Also, why is only the second one giving me an error?
new Thread(new Runnable() {
#Override
public void run() {
ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
int currentPosition = 0;
int total = mediaPlayer.getDuration();
myProgress.setMax(total);
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
currentPosition = mediaPlayer.getCurrentPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
myProgress.setProgress(currentPosition);
}
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
TextView currentTime = (TextView) findViewById(R.id.textView9);
int currentPosition = 0;
int total = mediaPlayer.getDuration();
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
currentPosition = mediaPlayer.getCurrentPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
currentTime.setText(getTimeString(currentPosition));
}
}
}).start();
And here's the code for getTimeString:
private String getTimeString(long millis) {
StringBuffer buf = new StringBuffer();
int hours = (int) (millis / (1000*60*60));
int minutes = (int) (( millis % (1000*60*60) ) / (1000*60));
int seconds = (int) (( ( millis % (1000*60*60) ) % (1000*60) ) / 1000);
buf
.append(String.format("%02d", hours))
.append(":")
.append(String.format("%02d", minutes))
.append(":")
.append(String.format("%02d", seconds));
return buf.toString();
}
do
myProgress.setMax(total);
in
runOnUIThread(new Runnable () {
#Override
public void run () {
myProgress.setMax(total);
}
});
Explanation
Views in android are only work/change/created on UI threads only. Other worker thread donot modify UI elements in Android, because Android is single threaded application.
You can also use AsyncTask which have onPreExecute() and onPostExecute() methods which run on UI thread to post updates to UI
As ρяσѕρєя K say, you can't update view in non-ui thread(Main Thread), so you can use a handler to finish this work
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// TODO : change currentTime and myProgress as a class number //
TextView currentTime = (TextView) findViewById(R.id.textView9);
ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
if (msg.what == 0) {
currentTime.setText(getTimeString(msg.arg1));
} else if (msg.what == 1) {
myProgress.setProgress(msg.arg1);
}
}
};
Then in you two thread, replace
// myProgress.setProgress(currentPosition);
handler.obtainMessage(1, currentPosition, 0).sendToTarget();
// currentTime.setText(getTimeString(currentPosition));
// param1 -> msg.what, param2 -> msg.arg1, parm3 -> msg.arg2
handler.obtainMessage(0, currentPosition, 0).sendToTarget();
Do you Ui work in run On Ui thread :
example:
new Thread(new Runnable() {
#Override
public void run() {
int currentPosition = 0;
int total = mediaPlayer.getDuration();
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(1000);
currentPosition = mediaPlayer.getCurrentPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
runOnUIThread(new Runnable () {
#Override
public void run () {
ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
myProgress.setMax(total);
myProgress.setProgress(currentPosition);
}
});
}
}
}).start();