I'm playing text with android TTS - android.speech.tts.TextToSpeech
I use: TextToSpeech.speak to speak and .stop to stop. Is there a way to pause the text also?
The TTS SDK doesn't have any pause functionality that I know of. But you could use synthesizeToFile() to create an audio file that contains the TTS output. Then, you would use a MediaPlayer object to play, pause, and stop playing the file. Depending on how long the text string is, it might take a little longer for audio to be produced because the synthesizeToFile() function would have to complete the entire file before you could play it, but this delay should be acceptable for most applications.
I used splitting of string and used playsilence() like below:
public void speakSpeech(String speech) {
HashMap<String, String> myHash = new HashMap<String, String>();
myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "done");
String[] splitspeech = speech.split("\\.");
for (int i = 0; i < splitspeech.length; i++) {
if (i == 0) { // Use for the first splited text to flush on audio stream
textToSpeech.speak(splitspeech[i].toString().trim(),TextToSpeech.QUEUE_FLUSH, myHash);
} else { // add the new test on previous then play the TTS
textToSpeech.speak(splitspeech[i].toString().trim(), TextToSpeech.QUEUE_ADD,myHash);
}
textToSpeech.playSilence(750, TextToSpeech.QUEUE_ADD, null);
}
}
You can make the TTS pause between sentences, or anywhere you want by adding up to three periods (".") all followed by a single space " ". The example below has a long pause at the beginning, and again before the message body. I'm not sure that is what you are after though.
private final BroadcastReceiver SMScatcher = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(
"android.provider.Telephony.SMS_RECEIVED")) {
// if(message starts with SMStretcher recognize BYTE)
StringBuilder sb = new StringBuilder();
/*
* The SMS-Messages are 'hiding' within the extras of the
* Intent.
*/
Bundle bundle = intent.getExtras();
if (bundle != null) {
/* Get all messages contained in the Intent */
Object[] pdusObj = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdusObj.length];
for (int i = 0; i < pdusObj.length; i++) {
messages[i] = SmsMessage
.createFromPdu((byte[]) pdusObj[i]);
}
/* Feed the StringBuilder with all Messages found. */
for (SmsMessage currentMessage : messages) {
// periods are to pause
sb.append("... Message From: ");
/* Sender-Number */
sb.append(currentMessage.getDisplayOriginatingAddress());
sb.append(".. ");
/* Actual Message-Content */
sb.append(currentMessage.getDisplayMessageBody());
}
// Toast.makeText(application, sb.toString(),
// Toast.LENGTH_LONG).show();
if (mTtsReady) {
try {
mTts.speak(sb.toString(), TextToSpeech.QUEUE_ADD,
null);
} catch (Exception e) {
Toast.makeText(application, "TTS Not ready",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
}
}
};
If you omit the space after the last period it will (or may) not work as expected.
In the absence of a pause option, you can add silence for the duration of when you want to delay the TTS Engine speaking. This of course would have to be a predetermined 'pause' and wouldn't help to include functionality of a pause button, for example.
For API < 21 : public int playSilence (long durationInMs, int queueMode, HashMap params)
For > 21 : public int playSilentUtterance (long durationInMs, int queueMode, String utteranceId)
Remember to use TextToSpeech.QUEUE_ADD rather than TextToSpeech.QUEUE_FLUSH otherwise it will clear the previously started speech.
I used a different approach.
Seperate your text into sentences
Speak every sentence one by one and keep track of the spoken sentence
pause will stop the text instantly
resume will start at the beginning of the last spoken sentence
Kotlin code:
class VoiceService {
private lateinit var textToSpeech: TextToSpeech
var sentenceCounter: Int = 0
var myList: List<String> = ArrayList()
fun resume() {
sentenceCounter -= 1
speakText()
}
fun pause() {
textToSpeech.stop()
}
fun stop() {
sentenceCounter = 0
textToSpeech.stop()
}
fun speakText() {
var myText = "This is some text to speak. This is more text to speak."
myList =myText.split(".")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, null, utteranceId)
sentenceCounter++
} else {
var map: HashMap<String, String> = LinkedHashMap<String, String>()
map[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = utteranceId
textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, map)
sentenceCounter++
}
}
override fun onDone(p0: String?) {
if (sentenceCounter < myList.size) {
speakText()
} else {
speakNextText()
}
}
}
I haven't yet tried this, but I need to do the same thing. My thinking is to first split your speech text into an array of words.
Then create a recursive function that plays the next word after the current word is finished, while keeping a counter of the current word.
divide the messages into parts and listen for last utterance by using onutteranceprogress listener
tts.playSilence(1250, TextToSpeech.QUEUE_ADD, null);
It seems that if you put a period after a word AND start the next word with a capital letter, just like a new sentence, like this:
after we came home. We ate dinner.
the "home. We" will then have a pause in it.
This becomes a grammatically strange way of writing it.
So far I have only tested this in my own language, Swedish.
It might be important that the space is there.
Also, an escaped quote (\") seems to have it pause somewhat as well - at least, if you put it around a word it adds space around the word.
This solution is not perfect, but an alternative to #Aaron C's solution may be to create a custom text to speech class like the below. This solution may work well enough if your text is relatively short and spoken words per minute is accurate enough for the language you are using.
private class CustomTextToSpeech extends TextToSpeech {
private static final double WORDS_PER_MS = (double)190/60/1000;
long startTimestamp = 0;
long pauseTimestamp = 0;
private Handler handler;
private Runnable speakRunnable;
StringBuilder textToSpeechBuilder;
private boolean isPaused = false;
public CustomTextToSpeech(Context context, OnInitListener initListener){
super(context, initListener);
setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String arg0) {
Log.d(TAG, "tts done. " + arg0);
startTimestamp = 0;
pauseTimestamp = 0;
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
#Override
public void onError(String arg0) {
Log.e(TAG, "tts error. " + arg0);
}
#Override
public void onStart(String arg0) {
Log.d(TAG, "tts start. " + arg0);
setStartTimestamp(System.currentTimeMillis());
}
});
handler = new Handler();
speakRunnable = new Runnable() {
#Override
public void run() {
speak();
}
};
textToSpeechBuilder = new StringBuilder(getResources().getString(R.string.talkback_tips));
}
public void setStartTimestamp(long timestamp) {
startTimestamp = timestamp;
}
public void setPauseTimestamp(long timestamp) {
pauseTimestamp = timestamp;
}
public boolean isPaused(){
return (startTimestamp > 0 && pauseTimestamp > 0);
}
public void resume(){
if(handler != null && isPaused){
if(startTimestamp > 0 && pauseTimestamp > 0){
handler.postDelayed(speakRunnable, TTS_SETUP_TIME_MS);
} else {
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
}
isPaused = false;
}
public void pause(){
isPaused = true;
if (handler != null) {
handler.removeCallbacks(speakRunnable);
handler.removeMessages(1);
}
if(isSpeaking()){
setPauseTimestamp(System.currentTimeMillis());
}
stop();
}
public void utter(){
if(handler != null){
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
}
public void speak(){
Log.d(TAG, "textToSpeechBuilder: " + textToSpeechBuilder.toString());
if(isPaused()){
String[] words = textToSpeechBuilder.toString().split(" ");
int wordsAlreadySpoken = (int)Math.round((pauseTimestamp - startTimestamp)*WORDS_PER_MS);
words = Arrays.copyOfRange(words, wordsAlreadySpoken-1, words.length);
textToSpeechBuilder = new StringBuilder();
for(String s : words){
textToSpeechBuilder.append(s);
textToSpeechBuilder.append(" ");
}
} else {
textToSpeechBuilder = new StringBuilder(getResources().getString(R.string.talkback_tips));
}
if (tts != null && languageAvailable)
speak(textToSpeechBuilder.toString(), TextToSpeech.QUEUE_FLUSH, new Bundle(), "utter");
}
}
Related
I'm new to android, and get the project that I need to know how fast the the NFC Tag from the phone read the tag with timer. It's been 4 days and I still try to figure it out.
So this is how it works in my mind. TAG > Detected (timer start) > Information From Tag Show Up > Timer Stop.
Is it possible ? I know it will get around 0.1 miliseconds. I already can make the timer start when the tag detected, but it dont stop.
Here the java code :
public class Read extends Activity {
NfcAdapter mAdapter;
Tag mTag;
PendingIntent mPI;
IntentFilter mFilter[];
String userData,yo;
boolean writeMode;
Context context;
TextView tvNFCContent, Timer,Low;
Button start, pause, reset, lap ;
long MillisecondTime, StartTime, TimeBuff, UpdateTime = 0L ;
Handler handler;
int Seconds, Minutes, MilliSeconds ;
ListView listView ;
String[] ListElements = new String[] { };
List<String> ListElementsArrayList ;
ArrayAdapter<String> adapter ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.read);
tvNFCContent = (TextView) findViewById(R.id.data);
Timer = (TextView)findViewById(R.id.timer);
handler = new Handler() ;
mAdapter = NfcAdapter.getDefaultAdapter(this);
mPI = PendingIntent.getActivity(getApplicationContext(), 0,
new Intent(this,getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0 );
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
mFilter = new IntentFilter[]{tagDetected,filter2};
mAdapter = NfcAdapter.getDefaultAdapter(this);
if (mAdapter == null) {
// Stop here, we definitely need NFC
Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
finish();
}
readFromIntent(getIntent());
}
/******************************************************************************
**********************************Read From NFC Tag***************************
******************************************************************************/
private void readFromIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))
{
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] msgs = null;
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
buildTagViews(msgs);
}
}
private void buildTagViews(NdefMessage[] msgs) {
if (msgs == null || msgs.length == 0) return;
String text = "";
// String tagId = new String(msgs[0].getRecords()[0].getType());
byte[] payload = msgs[0].getRecords()[0].getPayload();
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16"; // Get the Text Encoding
int languageCodeLength = payload[0] & 0063; // Get the Language Code, e.g. "en"
// String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
try {
text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
TimeBuff += MillisecondTime;
handler.removeCallbacks(runnable);
} catch (UnsupportedEncodingException e) {
Log.e("UnsupportedEncoding", e.toString());
}
tvNFCContent.setText("NFC Content: " + text);
}
NdefMessage[] getNdefMessage(Intent intent)
{
NdefMessage[] msgs = null;
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if(rawMsgs != null)
{
msgs = new NdefMessage[rawMsgs.length];
for(int i=0; i<rawMsgs.length; i++)
{
msgs[i] = (NdefMessage)rawMsgs[i];
}
}
return msgs;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
protected void onNewIntent(Intent intent) {
// TODO Auto-generated method stub
setIntent(intent);
readFromIntent(intent);
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
StartTime = SystemClock.uptimeMillis();
handler.postDelayed(runnable, 0);
}
super.onNewIntent(intent);
if(intent.getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED))
{
Toast.makeText(getApplicationContext(),"Ndefdiscovered",Toast.LENGTH_SHORT).show();
}else if(intent.getAction().equals(NfcAdapter.ACTION_TAG_DISCOVERED))
{
mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Toast.makeText(getApplicationContext(),"Smartcard detected",Toast.LENGTH_SHORT).show();
NdefMessage[] messages = getNdefMessage(intent);
if(messages == null)
{
Toast.makeText(getApplicationContext(),"There Is No Data",Toast.LENGTH_SHORT).show();
return;
}
byte[] payload = messages[0].getRecords()[0].getPayload();
userData = new String(payload);
}else
{
Toast.makeText(getApplicationContext(),"Undefined smartcard",Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
mAdapter.disableForegroundDispatch(this);
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mAdapter.enableForegroundDispatch(this, mPI, mFilter, null);
}
public Runnable runnable = new Runnable() {
public void run() {
MillisecondTime = SystemClock.uptimeMillis() - StartTime;
UpdateTime = TimeBuff + MillisecondTime;
Seconds = (int) (UpdateTime / 1000);
Minutes = Seconds / 60;
Seconds = Seconds % 60;
MilliSeconds = (int) (UpdateTime % 1000);
Timer.setText("" + Minutes + ":"
+ String.format("%02d", Seconds) + ":"
+ String.format("%03d", MilliSeconds));
handler.postDelayed(this, 0);
}
};
}
And here the code to stop the timer, but I dont know where to put to stop the timer right on time :
TimeBuff += MillisecondTime;
handler.removeCallbacks(runnable);
With that method of reading you don't need to time how long your App spends actually reading the NFC tags as the time will always be exactly zero.
This is because the Android OS has fully read the data from the tag before the result is passed to your App.
There are ways to actually read the tag yourself if you really want to time reading the tag.
Update:
As you have not told me the Type of card you are using it is not possible to write the code to measure the time you want.
Some Background:
All NFC card operations at the low level will do 1 to N number of transceive operations, with each transceive operation sends a byte Array of 1 to N bytes and the returns a byte Array of 0 to N bytes.
For the raw read time you would time the time taken to run the right number of transeive command to read the data.
Timing of higher level operations would also include the time taken to parse the N number of byte Arrays in to a NdefMessage as well as the transceive commands
The code:
So as I don't know the card type the best I can do is get a time it takes to connect to the card and read the data and parse it to an NdefMessage
This is the timing of any operation that can cause RF activity and block further execution of the code.
The code ignores the fact the Android OS has already read the card and has passed you the card data with zero time in your app spent reading the card, it re-reads the card to get the timings. Note that if you take the card away to fast it can generate exceptions and fail to time reading the card.
I could have written the code to use enableReaderMode which is more reliable especial when writing to the card and has many other benefits. But instead I used ForegroundDispatch as the example code was using ForegroundDispatch, so I followed suit.
PS I would not recommend using ForegroundDispatch
package com.test.foregrounddispatch;
import androidx.appcompat.app.AppCompatActivity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
NfcAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = NfcAdapter.getDefaultAdapter(this);
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
mAdapter.disableForegroundDispatch(this);
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter ndefDetected = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndefDetected.addDataType("*/*");
} catch (IntentFilter.MalformedMimeTypeException e) {}
IntentFilter techDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
IntentFilter[] nfcIntentFilter = new IntentFilter[]{ndefDetected,techDetected,tagDetected};
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
if(mAdapter!= null)
mAdapter.enableForegroundDispatch(this, pendingIntent, nfcIntentFilter, null);
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
// While with ForegroundDispatch the NDEF message has already been read
// And passed to us in the intent and thus the time the App spends "read" the NFC card is Zero
// We want to time time the read, so now we have been notified that a NDEF card is in range
// Try and read from it
// Get the Tag from the intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
long startTime = 0;
long endTime = 0;
try {
// Create an NDEF tag object
Ndef ndefTag = Ndef.get(tag);
// This is the I/O operation to read the card and format the result to an NDEF message
// Nothing is done with the result to not add any time to it, as we are timing this
startTime = System.currentTimeMillis();
ndefTag.connect();
ndefTag.getNdefMessage();
endTime = System.currentTimeMillis();
ndefTag.close();
} catch (Exception e) {
Log.e("NFC", e.toString());
}
Log.v("NFC", "Time to read in milliseconds is: " + (endTime - startTime));
}
}
}
For a short "Hello" plain text NDEF record on my phone it produces the log:-
V/NFC: Time to read in milliseconds is: 18
As I have a text longer than the 4000 chars limit for TTS, I divided the string to more parts and I am adding these parts in cycle to tts queue, for example:
int pos = 0;
while(true) {
String var = "";
try {
var = str.substring(pos, 3999);
pos += 3999;
} catch(Exception e) {
var = str.substring(pos, str.length());
break;
}
Bundle params = new Bundle();
params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "");
tts.speak(var, TextToSpeech.QUEUE_ADD, params, "myID");
}
However this works, but after the END of speech I need to change the stop button again for play button.
I am doing this button change in onDone().
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String utteranceId) {
Log.d("Speak", "TTS finished");
runOnUiThread(new Runnable() {
public void run() {
Button view2 = findViewById(R.id.speech);
view2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.play, 0, 0, 0);
}
});
}
#Override
public void onError(String utteranceId) {
}
#Override
public void onStart(String utteranceId) {
}
});
The problem is, the onDone is calling after each Queue finishes. So if there are more parts in the queue, the onDone is called many times. I can't determine, when the last queue is processed so when to change the button.
I think I have found a solution - by sending a different utteranceId in
tts.speak(var, TextToSpeech.QUEUE_ADD, params, id1);
and in next condition id2. Then checking in onDone the utteranceId and according it change the button.
Seems working very well.
I want to add pause and play functionality in tts. what is have done so far is, when i stop tts speaking and want to get the last spoken word but the loop which is running not getting stop. please resolve my problem.
public void toggleAudioMode() {
isPlayerVisible = !isPlayerVisible;
ivToggleAudioModeAnswer.setImageResource(isPlayerVisible ? R.drawable.ic_audio_off : R.drawable.ic_audio_on);
if (isPlayerVisible) {
splitspeech = result.split(" ");
if (j > 0) {
speakSpeech(j, "run");
} else {
speakSpeech(0, "run");
}
} else {
keepGoing = false;
speakSpeech(0, "stop");
}
}
public void speakSpeech(int m, String type) {
myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "done");
while (i < splitspeech.length) {
if (type.equals("run")) {
if (i == 0) { // Use for the first splited text to flush on audio stream
textToSpeech.speak(splitspeech[i].trim(), QUEUE_FLUSH, myHash);
} else { // add the new text on previous then play the TTS
textToSpeech.speak(splitspeech[i].trim(), QUEUE_ADD, myHash);
j = i;
}
}
i++;
}
if (type.equals("stop")) {
textToSpeech.stop();
}
}
i want play and pause functionality.
I have a written a receiver for a NEW_OUTGOING_CALL intent (static receiver). In order not to hold the system, I do the lengthy part of the process in a AsyncTask.
Based on the number dialed, I may or may not start the AsyncTask (and proceed with regular processing). However, the tasks starts on its own, with the right param passed, and I cant figure out how !!
I've grep'ed the project, and there are no other calls to LongOperation other than the one in the CallOneShot function - but the traces surrounding the 'new' statement do not appear.
How can this happen ?
Please find the code attached, sorry for the length, I've tried to cut it down a bit
Thanks for the help
J.
package com.iper.phoneeco;
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "XXBroadcastReceiver";
FileWriter fDevLog;
MyPrefs myprefs=null;
public final static String EXTRA_MESSAGE = "com.iper.phoneeco.msg1";
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase("android.intent.action.NEW_OUTGOING_CALL"))
{
Log.d(TAG,"OUTGOING CALL RECEIVED");
String phoneNumber = getResultData();
if (phoneNumber == null) {
// No reformatted number, use the original
phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
Log.d(TAG,"phone number:"+phoneNumber);
if (IsToProcess(phoneNumber)) {
Log.d (TAG,"Trapping the call");
// Lets Roll
CallOneShot(phoneNumber);
// and prevent other apps from calling as well
setResultData(null);
// abortBroadcast();
}
else {
Log.d (TAG,"Standard processing");
Toast.makeText(context, "standard processing" , Toast.LENGTH_LONG).show();
}
Log.d (TAG,"Finished processing intent");
}
//
// check is number against a list of exceptions, that we dont handle
//
private boolean IsToProcess(String num){
String[] excluded = {"15","17","18","112","911","991","08.*","^\\*.*","^#.*"};
for (String ex : excluded){
Log.d(TAG,"Exclusion test: "+ex + "versus: "+num);
if (num.matches(ex)) {
Log.d(TAG,"Exclusion FOUND: "+ex);
return false;
}
}
if (num.length() < myprefs.minLen) {
Log.d(TAG,"Exclusion FOUND: Numero trop court");
return false;
}
Log.d(TAG,"Exclusion not found: ");
return true;
}
//
// Displays a toast
//
void MyToast(String s, int col, int dur ) {
Toast toast=Toast.makeText(myprefs.ctx, s, dur);
toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0);
toast.getView().setBackgroundColor(col );
LinearLayout toastLayout = (LinearLayout) toast.getView();
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(20);
toast.show();
}
void MyToast(String s, int col) {
MyToast(s,col,Toast.LENGTH_LONG);
}
public void CallOneShot(String phoneNumber) {
Log.d (TAG,"CallOneShot");
MyToast (myprefs.ctx.getResources().getString(R.string.callbackipg)+" "+phoneNumber,Color.BLUE);
new LongOperation().execute(phoneNumber);
}
//
// the meat....
//
public class LongOperation extends AsyncTask<String, Void, String> {
String numToCall;
#Override
protected String doInBackground(String... params) {
int bytesRead;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
String msgres;
String response;
Log.d(TAG, "Clientthread started");
numToCall=params[0];
Log.d(TAG, "numTocall"+numToCall);
// and add to the call log
ContentValues values = new ContentValues();
values.put(CallLog.Calls.NUMBER, numToCall);
values.put(CallLog.Calls.DATE, System.currentTimeMillis());
values.put(CallLog.Calls.DURATION, 0);
values.put(CallLog.Calls.TYPE, CallLog.Calls.OUTGOING_TYPE);
values.put(CallLog.Calls.NEW, 1);
values.put(CallLog.Calls.CACHED_NAME, "");
values.put(CallLog.Calls.CACHED_NUMBER_TYPE, 0);
values.put(CallLog.Calls.CACHED_NUMBER_LABEL, "");
Log.d(TAG, "Inserting call log placeholder for " + numToCall);
ContentResolver resolver = myprefs.ctx.getContentResolver();
resolver.insert(CallLog.Calls.CONTENT_URI, values);
response=myprefs.ctx.getResources().getString(R.string.errundef);
return response;
}
protected void onPostExecute (String s) {
if (!s.equals("ok")) {
Log.d(TAG,"OnPostExecute - failed: "+s);
MyToast (myprefs.ctx.getResources().getString(R.string.errcallback)+"\n"+s,Color.RED);
}
}
}
}
Have you assign value to myprefs. It seems that you have initialized it to null and never assign it to any value
ok - stupid me is the answer - I had changed the name of the package, and an old version of the package was still on the emulator, trapping the intent ! once I removed it, it all went back to normal...
Many thanks for your help anyway
Android question:
When I set text to a view, when the view will update the UI?
Here is my case 1:
for(int i=0;i< 2000; i++){
aTextView.setText("a"+i);
}
aTextView is a MyTextView, that extends from TextView. I overwirte the onDraw as:
public static int counterP = 0;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
counterP++;
Log.d("MYButton", "onDraw: " + counterP);
}
From the log, I can see, the printed counterP is not consequent. In the loop, the onDraw method is called only 2-4 times.
I did another test, case 2:
boolean notInit = true;
List<String> cmdList = null;
long stSelfRefresh = 0;
String contentStr;
TextView selfTv;
public void onSelfRefreshTv(View v) {
if (cmdList == null || cmdList.isEmpty()) {
Log.e(TAG, "empty now, reset");
notInit = true;
}
if (notInit) {
cmdList = new ArrayList<String>();
for (int i = 0; i < 2000; i++) {
cmdList.add("a" + i);
}
stSelfRefresh = SystemClock.uptimeMillis();
notInit = false;
selfTv = (MyTextView) findViewById(R.id.mytv);
}
contentStr = cmdList.remove(0);
Log.d(TAG, "contentStr = " + contentStr);
selfTv.setText(contentStr);
if (!cmdList.isEmpty()) {
if (!mHandler.sendEmptyMessage(99)) {
Log.e(TAG, "SKIP my self");
}
} else {
Log.d(TAG, "Cost time: " + (SystemClock.uptimeMillis() - stSelfRefresh));
}
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 99:
onSelfRefreshTv(null);
break;
default:
break;
}
}
};
The result of case 2 is the counterP is printed out consequent. 1-2000.
I don't know why the textView is updated for each time?
Do you have any idea for this?
Thanks in advance.
******add case 3***********
for(int i=0;i< 2000;i++){
contentStr = cmdList.remove(0);
mHandler.sendEmptyMessage(100);
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 100:
selfTv.setText(contentStr);
break;
default:
break;
}
}
};
******end of case 3**********
The test result for case 3 is similar with case 1.
It looks like you are running your code on the UI Thread.
This means: as long as your code is running, the drawing code can not run. The onDraw() is called after your loop finished counting and only the last value is actually drawn on the display.
It's as simple as that.
If you want something count up on your display, you can use a AsyncTask to move the counting part into a non-UI Thread:
new AsyncTask<Void,Integer, Void>() {
#Override
protected Void doInBackground(Void... voids) {
for (int i =0; i<2000; i++) {
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
aTextView.setText("a"+values[0]);
}
};
Your second case is like this:
You prepare the list with all 2000 elements.
Set the first element of the list to the TextView and remove the item from the list.
Send yourself a message to run on the UI Thread as soon as the system thinks it is appropriate.
Let go the control of the UI Thread.
Your code is actually finished and the system calls onDraw().
The message from 4 gets processed and you start again with 2.
You see that the system gets control over the UI Thread in the middle of your loop. That's why it's counting visibly.
Case 3 is different from 2:
You are sending 2000 messages in on loop without letting the system handle it.