UtteranceProgressListener not being reliably called - android

I am writing an Android app to fetch the latest email from a folder and play it using TTS. I want to be able to use it whilst driving so it has to be mostly automatic. Everything so far is working fine until I attempt to capture when the TextToSpeech has finished speaking so we can move on to the next email.
Here is the complete MainActivity.java file:
package uk.co.letsdelight.emailreader;
import android.os.AsyncTask;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.MimeBodyPart;
public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {
public TextToSpeech tts;
private Bundle ttsParam = new Bundle();
public UtteranceProgressListener utListener;
private boolean isPlaying = false;
private Properties imap = new Properties();
private String textToSpeak = "";
#Override
public void onInit(int ttsStatus) {
if (ttsStatus == TextToSpeech.SUCCESS) {
utListener = new UtteranceProgressListener() {
#Override
public void onStart(String s) {
TextView status = findViewById(R.id.status);
status.setText("started reading (Listener)");
}
#Override
public void onDone(String s) {
Toast.makeText(getApplicationContext(), "Done Event Listener", Toast.LENGTH_LONG).show();
TextView status = findViewById(R.id.status);
status.setText("finished reading (Listener)");
/*ImageButton i = findViewById(R.id.playButton);
i.setImageResource(R.drawable.button_play);*/
isPlaying = false;
}
#Override
public void onStop(String s, boolean b) {
Toast.makeText(getApplicationContext(), "Stop Event Listener", Toast.LENGTH_LONG).show();
TextView status = findViewById(R.id.status);
status.setText("stopped reading (Listener)");
/*ImageButton i = findViewById(R.id.playButton);
i.setImageResource(R.drawable.button_play);*/
isPlaying = false;
}
#Override
public void onError(String s) {
Toast.makeText(getApplicationContext(), "Error Event Listener", Toast.LENGTH_LONG).show();
TextView status = findViewById(R.id.status);
status.setText("Error reading email");
ImageButton i = findViewById(R.id.playButton);
i.setImageResource(R.drawable.button_play);
isPlaying = false;
}
};
tts.setOnUtteranceProgressListener(utListener);
TextView status = findViewById(R.id.status);
status.setText("initialised");
} else {
TextView status = findViewById(R.id.status);
status.setText("failed to initialise");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
imap.setProperty("mail.store.protocol", "imap");
imap.setProperty("mail.imaps.port", "143");
tts = new TextToSpeech(this,this);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void restartPressed(View v) {
if (isPlaying) {
tts.stop();
speak();
}
}
public void playPressed(View v) {
ImageButton i = (ImageButton) v;
if (isPlaying) {
isPlaying = false;
i.setImageResource(R.drawable.button_play);
TextView status = findViewById(R.id.status);
status.setText("");
if (tts != null) {
tts.stop();
}
} else {
isPlaying = true;
i.setImageResource(R.drawable.button_stop);
new Reader().execute();
}
}
class Reader extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
TextView status = findViewById(R.id.status);
status.setText("fetching email");
}
#Override
protected String doInBackground(String... params) {
String toRead = "nothing to fetch";
try {
Session session = Session.getDefaultInstance(imap, null);
Store store = session.getStore();
store.connect(getText(R.string.hostname).toString(), getText(R.string.username).toString(), getText(R.string.password).toString());
Folder inbox = store.getFolder("INBOX.Articles.listen");
if (inbox.exists() && inbox.getMessageCount() > 0) {
inbox.open(Folder.READ_ONLY);
Message msg = inbox.getMessage(inbox.getMessageCount() - 6);
if (msg.getContentType().contains("multipart")) {
Multipart multiPart = (Multipart) msg.getContent();
MimeBodyPart part = (MimeBodyPart) multiPart.getBodyPart(multiPart.getCount() - 1);
toRead = part.getContent().toString();
} else {
toRead = msg.getContent().toString();
}
} else {
toRead = "The folder is empty or doesn't exist";
}
} catch (Throwable ex) {
toRead = "Error fetching email - " + ex.toString();
}
return toRead;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
String body;
TextView status = findViewById(R.id.status);
status.setText("");
try {
Document doc = Jsoup.parse(s);
body = doc.body().text();
} catch (Throwable ex) {
body = "Error parsing email - " + ex.toString();
}
status.setText("email successfully fetched");
textToSpeak = body;
if (isPlaying) {
speak();
}
}
}
private void speak() {
int maxLength = TextToSpeech.getMaxSpeechInputLength();
if (textToSpeak.length() > maxLength) {
textToSpeak = "The email text is too long! The maximum length is " + maxLength + " characters";
}
ttsParam.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "EmailReader");
tts.speak(textToSpeak, TextToSpeech.QUEUE_FLUSH, ttsParam, "EmailReader");
}
#Override
protected void onDestroy() {
if (tts != null) {
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
}
The inner class Reader works fine. doInBackground fetches the email and onPostExec strips out any HTML to leave the actual text content of the email. This is passed to the speak() method which does the actual speaking and works.
The issue is with the onUtteranceProgressListener.
Sometimes the onStart(String s) method is called, sometimes it isn't! It seems to never be called the first time the email read out. Mostly it is called for subsequent calls to speak() but not always. About 1 in 5 times it fails to get called. If the listener is called the status displays 'started reading (Listener)' otherwise it shows 'email successfully fetched'.
onDone, onError and onStop are never called.
I have tried using different utteranceID and Bundle values in the tts.speak() call but this makes to difference.
When the app is started, the first status display is 'initialised' which means that the onUtteranceListener must have been set within the onInit method. The TextToSpeech object is instantiated within the activity's onCreate method.
I have looked through all the information I can find which has mostly suggested getting the utteranceID correct. What else can I try in order to get a better understanding of this problem please?

The problem is that the onDone() method (and in fact any of the progress callbacks) is run on a background thread, and therefore the Toast is not going to work, and any code that accesses your UI such as setText(...) may or may not work.
So... the methods probably are being called, but you just can't see that.
The solution to this would be to surround the code in your callbacks with runOnUiThread() like this:
#Override
public void onDone(String s) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Done Event Listener", Toast.LENGTH_LONG).show();
TextView status = findViewById(R.id.status);
status.setText("finished reading (Listener)");
/*ImageButton i = findViewById(R.id.playButton);
i.setImageResource(R.drawable.button_play);*/
isPlaying = false;
}
});
}
Note: It's probably best to initialize your TextView in onCreate() along with everything else instead of in the progress callbacks.
Also, the purpose of the utteranceID is to give each call to speak() a unique identifier that is then passed back to you as the "String s" argument in the progress callbacks.
It's a good idea to give each call to speak a new ("most recent") ID using some kind of random number generator, and then checking it in the progress callbacks.
You can see a similar question and answer regarding this here.
Side note:
Since you have a "restart" button, you should know that on APIs <23, calls to TextToSpeech.stop() will cause the onDone() in your progress listener to be called. On APIs 23+, it calls onStop() instead.

First, make sure you actually have a problem and not a race between who sets the text in what order. Use log statements to make sure it is not actually called.
Try setting queueMode to QUEUE_ADD like:
tts.speak(textToSpeak, TextToSpeech.QUEUE_ADD, ttsParam, "EmailReader");
maybe subsequent calls are cancelling the listener's events from previous texts inputs, as QUEUE_FLUSH suggests.
Also the bundle isn't really needed there, you can set it to null.
Hope any of these helps.

Related

Firing an event as soon as activity is visible to the user

I wanted to create countdown as soon as the user sees an activity. However, there does not seem to be an appropriate callback for that. Neither onResume nor onWindowFocusChanged seem to be the correct callbacks, because the whole code is executed before the user even see anything. As a result I end up with "Go!" on the screen right from the start.
In a nutshell:
Do you have any idea how to implement a countdown without any user interaction as soon as the activity is visible to the user?
EDIT:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.util.concurrent.TimeUnit;
import android.widget.TextView;
public class ChallengeModeTutorial extends AppCompatActivity {
private void delayOneSec()
{
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
assert true;
}
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_challenge_mode_tutorial);
}
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
TextView readySteadyGo = (TextView) findViewById(R.id.challengeModeTutorialReadySteadyGoTextView);
// TextView tutorialText = (TextView) findViewById(R.id.challengeModeTutorialTextTextView);
TextView timeUntilStart = (TextView) findViewById(R.id.challengeModeTutorialReadyTimeTextView);
readySteadyGo.setText("");
timeUntilStart.setText("5");
delayOneSec();
timeUntilStart.setText("4");
delayOneSec();
timeUntilStart.setText("3");
delayOneSec();
readySteadyGo.setText("Ready!");
timeUntilStart.setText("2");
delayOneSec();
readySteadyGo.setText("Steady!");
timeUntilStart.setText("1");
delayOneSec();
readySteadyGo.setText("");
readySteadyGo.setText("Go!");
}
}
The problem is that you're blocking the UI thread. When you call setText(String) it doesn't immediately gets drawn. The TextView gets invalidated and it will be draw on the next draw phase. But if you block the thread, this will never happen. You have to use a postDelayed() to execute the next setText(String) a second later.
I'm not sure if this works, but you could try using a ViewTreeObserver in onCreate. It gets called once the layout is drawn. (Replace LinearLayout with whatever Layout your "activity_challenge_mode_tutorial" actually is.)
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_challenge_mode_tutorial);
final LinearLayout layout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);
ViewTreeObserver vto = layout.getViewTreeObserver();
vto.addOnGlobalLayoutListener (new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Remove listener to prevent repeat calls.
layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Start countdown here.
}
});
}
Code is taken from this answer: https://stackoverflow.com/a/7735122/8118328
You can start your countdown, inside of your onStart method, which you need to overwrite.
The visible lifetime of an activity happens between a call to onStart() until a corresponding call to onStop(). During this time the user can see the activity on-screen, though it may not be in the foreground and interacting with the user.
From:
https://developer.android.com/reference/android/app/Activity
#Override
protected void onStart() {
super.onStart();
}
I finally got it to work like this:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class ChallengeModeTutorial extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_challenge_mode_tutorial);
}
#Override
protected void onStart()
{
super.onStart();
final TextView readySteadyGo = (TextView) findViewById(R.id.challengeModeTutorialReadySteadyGoTextView);
final TextView timeUntilStart = (TextView) findViewById(R.id.challengeModeTutorialReadyTimeTextView);
readySteadyGo.setText("");
Thread t=new Thread(){
#Override
public void run(){
while(continueThread){
try {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
if(currentCount > 0)
{
timeUntilStart.setText(String.valueOf(currentCount));
}
else
{
timeUntilStart.setText("Go!");
}
switch (currentCount)
{
case 2: readySteadyGo.setText("Ready!"); break;
case 1: readySteadyGo.setText("Steady!"); break;
default: readySteadyGo.setText(""); break;
}
currentCount--;
if (currentCount == 0)
{
continueThread = false;
}
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
private boolean continueThread = true;
private int currentCount = 5;
}

when using a void in other class, get error on a null object reference

When I'm trying to use other RFID SDK to get Tag Code. And send to the server to fetch the tag information. But I only can get the RFID SDK demon working. So I just try to add HTTP request method into the demon. but I always get an error about I'm calling a null object reference. when I use
IAcitivity.IMakeHttpCall();
Inventory Activity
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkError;
import com.android.volley.NoConnectionError;
import com.android.volley.ParseError;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.ServerError;
import com.android.volley.TimeoutError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.Volley;
import com.uk.tsl.rfid.ModelBase;
import com.uk.tsl.rfid.TSLBluetoothDeviceActivity;
import com.uk.tsl.rfid.WeakHandler;
import com.uk.tsl.rfid.asciiprotocol.AsciiCommander;
import com.uk.tsl.rfid.asciiprotocol.DeviceProperties;
import com.uk.tsl.rfid.asciiprotocol.commands.FactoryDefaultsCommand;
import com.uk.tsl.rfid.asciiprotocol.enumerations.QuerySession;
import com.uk.tsl.rfid.asciiprotocol.enumerations.TriState;
import com.uk.tsl.rfid.asciiprotocol.parameters.AntennaParameters;
import com.uk.tsl.rfid.asciiprotocol.responders.LoggerResponder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class InventoryActivity extends TSLBluetoothDeviceActivity {
// Debug control
private static final boolean D = BuildConfig.DEBUG;
private RequestQueue mVolleyQueue;
// OkHttpClient httpClient = new OkHttpClient();
// The list of results from actions
private ArrayAdapter<String> mResultsArrayAdapter;
private ListView mResultsListView;
private ArrayAdapter<String> mBarcodeResultsArrayAdapter;
private ListView mBarcodeResultsListView;
// The text view to display the RF Output Power used in RFID commands
private TextView mPowerLevelTextView;
// The seek bar used to adjust the RF Output Power for RFID commands
private SeekBar mPowerSeekBar;
// The current setting of the power level
private int mPowerLevel = AntennaParameters.MaximumCarrierPower;
// Error report
private TextView mResultTextView;
// Custom adapter for the session values to display the description rather than the toString() value
public class SessionArrayAdapter extends ArrayAdapter<QuerySession> {
private final QuerySession[] mValues;
public SessionArrayAdapter(Context context, int textViewResourceId, QuerySession[] objects) {
super(context, textViewResourceId, objects);
mValues = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView)super.getView(position, convertView, parent);
view.setText(mValues[position].getDescription());
return view;
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView)super.getDropDownView(position, convertView, parent);
view.setText(mValues[position].getDescription());
return view;
}
}
// The session
private QuerySession[] mSessions = new QuerySession[] {
QuerySession.SESSION_0,
QuerySession.SESSION_1,
QuerySession.SESSION_2,
QuerySession.SESSION_3
};
// The list of sessions that can be selected
private SessionArrayAdapter mSessionArrayAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inventory);
mVolleyQueue = Volley.newRequestQueue(this);
mResultsArrayAdapter = new ArrayAdapter<String>(this,R.layout.result_item);
mBarcodeResultsArrayAdapter = new ArrayAdapter<String>(this,R.layout.result_item);
mResultTextView = (TextView)findViewById(R.id.resultTextView);
// Find and set up the results ListView
mResultsListView = (ListView) findViewById(R.id.resultListView);
mResultsListView.setAdapter(mResultsArrayAdapter);
mResultsListView.setFastScrollEnabled(true);
mBarcodeResultsListView = (ListView) findViewById(R.id.barcodeListView);
mBarcodeResultsListView.setAdapter(mBarcodeResultsArrayAdapter);
mBarcodeResultsListView.setFastScrollEnabled(true);
// Hook up the button actions
Button sButton = (Button)findViewById(R.id.扫描开始);
sButton.setOnClickListener(mScanButtonListener);
Button cButton = (Button)findViewById(R.id.清除按钮);
cButton.setOnClickListener(mClearButtonListener);
// The SeekBar provides an integer value for the antenna power
mPowerLevelTextView = (TextView)findViewById(R.id.powerTextView);
mPowerSeekBar = (SeekBar)findViewById(R.id.powerSeekBar);
mPowerSeekBar.setOnSeekBarChangeListener(mPowerSeekBarListener);
// Set the seek bar current value to maximum and to cover the range of the power settings
setPowerBarLimits();
mSessionArrayAdapter = new SessionArrayAdapter(this, android.R.layout.simple_spinner_item, mSessions);
// Find and set up the sessions spinner
Spinner spinner = (Spinner) findViewById(R.id.sessionSpinner);
mSessionArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(mSessionArrayAdapter);
spinner.setOnItemSelectedListener(mActionSelectedListener);
spinner.setSelection(0);
// Set up Fast Id check box listener
CheckBox cb = (CheckBox)findViewById(R.id.fastIdCheckBox);
cb.setOnClickListener(mFastIdCheckBoxListener);
//
// An AsciiCommander has been created by the base class
//
AsciiCommander commander = getCommander();
// Add the LoggerResponder - this simply echoes all lines received from the reader to the log
// and passes the line onto the next responder
// This is added first so that no other responder can consume received lines before they are logged.
commander.addResponder(new LoggerResponder());
// Add a synchronous responder to handle synchronous commands
commander.addSynchronousResponder();
//Create a (custom) model and configure its commander and handler
mModel = new InventoryModel();
mModel.setCommander(getCommander());
mModel.setHandler(mGenericModelHandler);
}
#Override
public synchronized void onPause() {
super.onPause();
mModel.setEnabled(false);
// Unregister to receive notifications from the AsciiCommander
LocalBroadcastManager.getInstance(this).unregisterReceiver(mCommanderMessageReceiver);
}
#Override
public synchronized void onResume() {
super.onResume();
mModel.setEnabled(true);
// Register to receive notifications from the AsciiCommander
LocalBroadcastManager.getInstance(this).registerReceiver(mCommanderMessageReceiver,
new IntentFilter(AsciiCommander.STATE_CHANGED_NOTIFICATION));
displayReaderState();
UpdateUI();
}
//----------------------------------------------------------------------------------------------
// Menu
//----------------------------------------------------------------------------------------------
private MenuItem mReconnectMenuItem;
private MenuItem mConnectMenuItem;
private MenuItem mDisconnectMenuItem;
private MenuItem mResetMenuItem;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.reader_menu, menu);
mResetMenuItem = menu.findItem(R.id.reset_reader_menu_item);
mReconnectMenuItem = menu.findItem(R.id.reconnect_reader_menu_item);
mConnectMenuItem = menu.findItem(R.id.insecure_connect_reader_menu_item);
mDisconnectMenuItem= menu.findItem(R.id.disconnect_reader_menu_item);
return true;
}
/**
* Prepare the menu options
*/
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean isConnecting = getCommander().getConnectionState() == AsciiCommander.ConnectionState.CONNECTING;
boolean isConnected = getCommander().isConnected();
mResetMenuItem.setEnabled(isConnected);
mDisconnectMenuItem.setEnabled(isConnected);
mReconnectMenuItem.setEnabled(!(isConnecting || isConnected));
mConnectMenuItem.setEnabled(!(isConnecting || isConnected));
return super.onPrepareOptionsMenu(menu);
}
/**
* Respond to menu item selections
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.reconnect_reader_menu_item:
Toast.makeText(this.getApplicationContext(), "重新连接中...", Toast.LENGTH_LONG).show();
reconnectDevice();
UpdateUI();
return true;
case R.id.insecure_connect_reader_menu_item:
// Choose a device and connect to it
selectDevice();
return true;
case R.id.disconnect_reader_menu_item:
Toast.makeText(this.getApplicationContext(), "断开连接中...", Toast.LENGTH_SHORT).show();
disconnectDevice();
displayReaderState();
return true;
case R.id.reset_reader_menu_item:
resetReader();
UpdateUI();
return true;
}
return super.onOptionsItemSelected(item);
}
//
private void UpdateUI() {
//boolean isConnected = getCommander().isConnected();
//TODO: configure UI control state
}
private void scrollResultsListViewToBottom() {
mResultsListView.post(new Runnable() {
#Override
public void run() {
// Select the last row so it will scroll into view...
mResultsListView.setSelection(mResultsArrayAdapter.getCount() - 1);
}
});
}
private void scrollBarcodeListViewToBottom() {
mBarcodeResultsListView.post(new Runnable() {
#Override
public void run() {
// Select the last row so it will scroll into view...
mBarcodeResultsListView.setSelection(mBarcodeResultsArrayAdapter.getCount() - 1);
}
});
}
//----------------------------------------------------------------------------------------------
// AsciiCommander message handling
//----------------------------------------------------------------------------------------------
//
// Handle the messages broadcast from the AsciiCommander
//
private BroadcastReceiver mCommanderMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (D) { Log.d(getClass().getName(), "AsciiCommander state changed - isConnected: " + getCommander().isConnected()); }
String connectionStateMsg = intent.getStringExtra(AsciiCommander.REASON_KEY);
Toast.makeText(context, connectionStateMsg, Toast.LENGTH_SHORT).show();
displayReaderState();
if( getCommander().isConnected() )
{
// Update for any change in power limits
setPowerBarLimits();
// This may have changed the current power level setting if the new range is smaller than the old range
// so update the model's inventory command for the new power value
mModel.getCommand().setOutputPower(mPowerLevel);
mModel.resetDevice();
mModel.updateConfiguration();
}
UpdateUI();
}
};
//----------------------------------------------------------------------------------------------
// Reader reset
//----------------------------------------------------------------------------------------------
//
// Handle reset controls
//
private void resetReader() {
try {
// Reset the reader
FactoryDefaultsCommand fdCommand = FactoryDefaultsCommand.synchronousCommand();
getCommander().executeCommand(fdCommand);
String msg = "Reset " + (fdCommand.isSuccessful() ? "succeeded" : "failed");
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
UpdateUI();
} catch (Exception e) {
e.printStackTrace();
}
}
//----------------------------------------------------------------------------------------------
// Power seek bar
//----------------------------------------------------------------------------------------------
//
// Set the seek bar to cover the range of the currently connected device
// The power level is set to the new maximum power
//
private void setPowerBarLimits()
{
DeviceProperties deviceProperties = getCommander().getDeviceProperties();
mPowerSeekBar.setMax(deviceProperties.getMaximumCarrierPower() - deviceProperties.getMinimumCarrierPower());
mPowerLevel = deviceProperties.getMaximumCarrierPower();
mPowerSeekBar.setProgress(mPowerLevel - deviceProperties.getMinimumCarrierPower());
}
//
// Handle events from the power level seek bar. Update the mPowerLevel member variable for use in other actions
//
private OnSeekBarChangeListener mPowerSeekBarListener = new OnSeekBarChangeListener() {
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Nothing to do here
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// Update the reader's setting only after the user has finished changing the value
updatePowerSetting(getCommander().getDeviceProperties().getMinimumCarrierPower() + seekBar.getProgress());
mModel.getCommand().setOutputPower(mPowerLevel);
mModel.updateConfiguration();
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
updatePowerSetting(getCommander().getDeviceProperties().getMinimumCarrierPower() + progress);
}
};
private void updatePowerSetting(int level) {
mPowerLevel = level;
mPowerLevelTextView.setText( mPowerLevel + " dBm");
}
//----------------------------------------------------------------------------------------------
// Button event handlers
//----------------------------------------------------------------------------------------------
// Scan action
private OnClickListener mScanButtonListener = new OnClickListener() {
public void onClick(View v) {
try {
mResultTextView.setText("");
// Perform a transponder scan
IMakeHttpCall();
UpdateUI();
} catch (Exception e) {
e.printStackTrace();
}
}
};
// Clear action
private OnClickListener mClearButtonListener = new OnClickListener() {
public void onClick(View v) {
try {
// Clear the list
mResultsArrayAdapter.clear();
mBarcodeResultsArrayAdapter.clear();
UpdateUI();
} catch (Exception e) {
e.printStackTrace();
}
}
};
//----------------------------------------------------------------------------------------------
// Handler for changes in session
//----------------------------------------------------------------------------------------------
private AdapterView.OnItemSelectedListener mActionSelectedListener = new AdapterView.OnItemSelectedListener()
{
#Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if( mModel.getCommand() != null ) {
QuerySession targetSession = (QuerySession)parent.getItemAtPosition(pos);
mModel.getCommand().setQuerySession(targetSession);
mModel.updateConfiguration();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
};
//----------------------------------------------------------------------------------------------
// Handler for changes in FastId
//----------------------------------------------------------------------------------------------
private OnClickListener mFastIdCheckBoxListener = new OnClickListener() {
public void onClick(View v) {
try {
CheckBox fastIdCheckBox = (CheckBox)v;
mModel.getCommand().setUsefastId(fastIdCheckBox.isChecked() ? TriState.YES : TriState.NO);
mModel.updateConfiguration();
UpdateUI();
} catch (Exception e) {
e.printStackTrace();
}
}
};
public void IMakeHttpCall(){
String url = "http://192.168.1.76:4300/lottem/query/toolsstockInventory/";
Uri.Builder builder = Uri.parse(url).buildUpon();
builder.appendQueryParameter("barCode", "1121212121");
JsonArrayRequest jsonObjRequest = new JsonArrayRequest(builder.toString(),
new Response.Listener<JSONArray>(){
#Override
public void onResponse(JSONArray response) {
try {
JSONObject person;
for (int i = 0; i < response.length(); i++) {
person = response.getJSONObject(i);
String name = person.getString(("toolsName"));
mResultTextView.setText(name);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// Handle your error types accordingly.For Timeout & No connection error, you can show 'retry' button.
// For AuthFailure, you can re login with user credentials.
// For ClientError, 400 & 401, Errors happening on client side when sending api request.
// In this case you can check how client is forming the api and debug accordingly.
// For ServerError 5xx, you can do retry or handle accordingly.
if (error instanceof NetworkError) {
} else if (error instanceof ServerError) {
} else if (error instanceof AuthFailureError) {
} else if (error instanceof ParseError) {
} else if (error instanceof NoConnectionError) {
} else if (error instanceof TimeoutError) {
}
}
});
//Set a retry policy in case of SocketTimeout & ConnectionTimeout Exceptions. Volley does retry for you if you have specified the policy.
mVolleyQueue.add(jsonObjRequest);
}
}
InventoryModel
//----------------------------------------------------------------------------------------------
// Copyright (c) 2013 Technology Solutions UK Ltd. All rights reserved.
//----------------------------------------------------------------------------------------------
package com.uk.tsl.rfid.samples.inventory;
import android.util.Log;
import com.uk.tsl.rfid.ModelBase;
import com.uk.tsl.rfid.asciiprotocol.commands.BarcodeCommand;
import com.uk.tsl.rfid.asciiprotocol.commands.FactoryDefaultsCommand;
import com.uk.tsl.rfid.asciiprotocol.commands.InventoryCommand;
import com.uk.tsl.rfid.asciiprotocol.enumerations.TriState;
import com.uk.tsl.rfid.asciiprotocol.responders.IBarcodeReceivedDelegate;
import com.uk.tsl.rfid.asciiprotocol.responders.ICommandResponseLifecycleDelegate;
import com.uk.tsl.rfid.asciiprotocol.responders.ITransponderReceivedDelegate;
import com.uk.tsl.rfid.asciiprotocol.responders.TransponderData;
import com.uk.tsl.utils.HexEncoding;
import java.util.Locale;
public class InventoryModel extends ModelBase
{
// Control
private boolean mAnyTagSeen;
private boolean mEnabled;
public boolean enabled() { return mEnabled; }
private InventoryActivity IAcitivity;
public void setEnabled(boolean state)
{
boolean oldState = mEnabled;
mEnabled = state;
// Update the commander for state changes
if(oldState != state) {
if( mEnabled ) {
// Listen for transponders
getCommander().addResponder(mInventoryResponder);
// Listen for barcodes
getCommander().addResponder(mBarcodeResponder);
} else {
// Stop listening for transponders
getCommander().removeResponder(mInventoryResponder);
// Stop listening for barcodes
getCommander().removeResponder(mBarcodeResponder);
}
}
}
// The command to use as a responder to capture incoming inventory responses
private InventoryCommand mInventoryResponder;
// The command used to issue commands
private InventoryCommand mInventoryCommand;
// The command to use as a responder to capture incoming barcode responses
private BarcodeCommand mBarcodeResponder;
// The inventory command configuration
public InventoryCommand getCommand() { return mInventoryCommand; }
public InventoryModel()
{
// This is the command that will be used to perform configuration changes and inventories
mInventoryCommand = new InventoryCommand();
mInventoryCommand.setResetParameters(TriState.YES);
// Configure the type of inventory
mInventoryCommand.setIncludeTransponderRssi(TriState.YES);
mInventoryCommand.setIncludeChecksum(TriState.YES);
mInventoryCommand.setIncludePC(TriState.YES);
mInventoryCommand.setIncludeDateTime(TriState.YES);
// Use an InventoryCommand as a responder to capture all incoming inventory responses
mInventoryResponder = new InventoryCommand();
// Also capture the responses that were not from App commands
mInventoryResponder.setCaptureNonLibraryResponses(true);
// Notify when each transponder is seen
mInventoryResponder.setTransponderReceivedDelegate(new ITransponderReceivedDelegate() {
int mTagsSeen = 0;
#Override
public void transponderReceived(final TransponderData transponder, boolean moreAvailable) {
mAnyTagSeen = true;
final String tidMessage = transponder.getTidData() == null ? "" : HexEncoding.bytesToString(transponder.getTidData());
final String infoMsg = String.format(Locale.US, "\nRSSI: %d PC: %04X CRC: %04X", transponder.getRssi(), transponder.getPc(), transponder.getCrc());
IAcitivity.IMakeHttpCall();
sendMessageNotification("条形码:"+transponder.getEpc());
mTagsSeen++;
if( !moreAvailable) {
sendMessageNotification("");
Log.d("扫描次数",String.format("扫描到的次数: %s", mTagsSeen));
}
}
});
}

leaked ServiceConnection android.speech.tts.TextToSpeech$Connection

I have a text to speech fragment. The fragment consists of button and if the button is pressed, tts speaks given text.
When I tried to run this fragment with real device, HTC HTL21 (API 16), I get "leaked ServiceConnection android.speech.tts.TextToSpeech$Connection"
Before this leak error, I can see tts stop/shutdown waring. But I can' t figure out what is wrong with my code.
Strange thing is I don't get this waring/error with AVD (Android Virtual Device) or Genymotion.
I'm very appreciated for any kind help...
Here's part of log cat:
10-15 18:23:45.524 14811-14811/jp.miyamura.hds_r I/TextToSpeech: Sucessfully bound to com.google.android.tts
10-15 18:23:45.574 14811-14811/jp.miyamura.hds_r W/TextToSpeech: stop failed: not bound to TTS engine
10-15 18:23:45.574 14811-14811/jp.miyamura.hds_r W/TextToSpeech: shutdown failed: not bound to TTS engine
10-15 18:23:45.654 14811-14811/jp.miyamura.hds_r I/TextToSpeech: Sucessfully bound to com.google.android.tts
10-15 18:23:45.654 14811-14811/jp.miyamura.hds_r W/TextToSpeech: shutdown failed: not bound to TTS engine
10-15 18:23:45.664 14811-14811/jp.miyamura.hds_r I/TextToSpeech: Sucessfully bound to com.google.android.tts
10-15 18:23:45.794 14811-14811/jp.miyamura.hds_r E/ActivityThread: Activity jp.miyamura.hds_r.MainActivity has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection#40fd08e8 that was originally bound here
10-15 18:23:45.794 14811-14811/jp.miyamura.hds_r E/ActivityThread: android.app.ServiceConnectionLeaked: Activity jp.miyamura.hds_r.MainActivity has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection#40fd08e8 that was originally bound here
And here's my speech fragment:
package jp.miyamura.hds_r;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Fragment;
import android.os.Build;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import java.util.HashMap;
import java.util.Locale;
public class SpeechFragment extends Fragment implements TextToSpeech.OnInitListener, View.OnClickListener {
private int buttonID = 0;
private TextToSpeech tts;
private String queuedText;
private String splitChar;
// Instantiate Speech fragment itself
public static SpeechFragment newInstance(String speechString, String buttonString, int buttonID, String splitChar) {
// Store data to be passed to the fragment in bundle format
Bundle args = new Bundle();
args.putString("Speech", speechString);
args.putString("ButtonTitle", buttonString);
args.putInt("ButtonID", buttonID);
args.putString("splitChar", splitChar);
// Instantiate Speech fragment and set arguments
SpeechFragment newFragment = new SpeechFragment();
newFragment.setArguments(args);
return newFragment;
}
// Declare interface to communicate with it's container Activity
public interface onSpeechFragmentListener {
void onUtteranceStatus(boolean status);
void onClickStatus(int buttonID);
}
private onSpeechFragmentListener myCallback;
#Override
#SuppressWarnings("deprecation")
public void onAttach(Activity activity) {
super.onAttach(activity);
// Check container Activity implements interface listener or not
try {
myCallback = (onSpeechFragmentListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement onSpeechFragmentListener");
}
}
#Override
public void onDetach() {
super.onDetach();
myCallback = null;
}
// Required empty public constructor
public SpeechFragment() {}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
#SuppressWarnings("deprecation")
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Instantiate TTS
if (tts == null) tts = new TextToSpeech(getActivity(), this);
// Retrieve root view of fragment
View rootView = inflater.inflate(R.layout.fragment_speech, container, false);
String button_title = "";
splitChar = "";
// Receive arguments from it's container Activity
if (getArguments() != null) {
queuedText = getArguments().getString("Speech");
button_title = getArguments().getString("ButtonTitle");
buttonID = getArguments().getInt("ButtonID");
splitChar = getArguments().getString("splitChar");
}
// Retrieve button and set onClick listener
Button button = (Button) rootView.findViewById(R.id.speech_Button);
button.setText(button_title);
button.setId(buttonID);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // ToDo: Should be Build.VERSION_CODES.MARSHMALLOW
button.setTextAppearance(android.R.style.TextAppearance_Medium);
} else {
button.setTextAppearance(getActivity(), android.R.style.TextAppearance_Medium);
}
button.setOnClickListener(this);
return rootView;
}
#Override
public void onStop() {
if(tts !=null) {
tts.stop();
}
super.onStop();
}
#Override
public void onDestroyView() {
if(tts !=null) {
tts.shutdown();
}
super.onDestroyView();
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS && tts != null) {
// Set TTS Locale
tts.setLanguage(Locale.getDefault());
}
}
#Override
public void onClick(View v) {
// tts must not be null
if (tts != null) {
String utteranceId = this.hashCode() + "";
// TTS Branch with Android OS Version
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ttsGreater21(queuedText, utteranceId);
} else {
ttsUnder20(queuedText, utteranceId);
}
}
myCallback.onClickStatus(buttonID);
}
// Speech method before LOLLIPOP
#SuppressWarnings("deprecation")
private void ttsUnder20(String text, String utteranceId) {
HashMap<String, String> map = new HashMap<>();
map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId);
// Split speech text with new line character and speak silence for each separation
String[] splitSpeech = text.split(splitChar);
for (int i = 0; i < splitSpeech.length; i++) {
if (i == 0) { // Use for the first split text to flush on audio stream
tts.speak(splitSpeech[i].trim(),TextToSpeech.QUEUE_FLUSH, map);
} else { // add the new text on previous then play the TTS
tts.speak(splitSpeech[i].trim(), TextToSpeech.QUEUE_ADD,map);
}
// Play Silence between text split
tts.playSilence(750, TextToSpeech.QUEUE_ADD, null); // ToDo Length of silence should be optimized
}
setTTSListener();
}
// Speech method for LOLLIPOP and after
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void ttsGreater21(String text, String utteranceId) {
// Split speech text with new line character and speak with silence
String[] splitSpeech = text.split(splitChar);
for (int i = 0; i < splitSpeech.length; i++) {
if (i == 0) { // Use for the first split text to flush on audio stream
tts.speak(splitSpeech[i].trim(), TextToSpeech.QUEUE_FLUSH, null, utteranceId);
} else { // add the new text on previous then play the TTS
tts.speak(splitSpeech[i].trim(), TextToSpeech.QUEUE_ADD, null, utteranceId);
}
// Play Silence between text split
tts.playSilentUtterance(750, TextToSpeech.QUEUE_ADD, null); // ToDo Length of silence should be optimized
}
setTTSListener();
}
// TTS Status listener method
private void setTTSListener(){
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
}
#Override
public void onDone(String utteranceId) {
myCallback.onUtteranceStatus(true);
}
#Override
public void onError(String utteranceId) {
myCallback.onUtteranceStatus(false);
}
});
}
}
tts stop/shutdown fail was caused by screen rotation. Once failed to stop/shutdown, a leak error was triggered. So nothing related with above fragment codes...
In my activity code, I re-created fragment on screen rotation. The reason why I re-create is that I forget to inherit onSavedinstancestate. And then fragment was lost by screen rotation.
So the countermeasure are:
#Override
public void onSaveInstanceState(#NonNull Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState); // Add this line
savedInstanceState.putString("inputWords", tv_input_word.getText().toString());
}
And
if (savedInstanceState == null) { // Add this line
// Setup Fragment
transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container1,
SpeechFragment.newInstance(speechString, buttonString, buttonID1, splitChar));
transaction.commit();
} // Add this line

Go to another activity after click on button - Android

I have this onClickListener method on my login activity in my android app:
btnLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(txtEmail.getWindowToken(), 0);
imm.hideSoftInputFromWindow(txtPassword.getWindowToken(), 0);
String password = txtPassword.getText().toString();
String email = txtEmail.getText().toString();
if ((txtEmail.length() == 0) || (txtPassword.length() == 0)) {
Toast.makeText(LoginMember.this, "You need to provide values for Email and Password", Toast.LENGTH_SHORT).show();
return;
}
//Go ahead and perform the transaction
String[] params = {email,password};
new EndpointsAsyncTaskInsert(LoginMember.this).execute(params);
}
});
It sends data to Google App Engine without any problems, already my EndpointsAsyncTask class defined, etc etc
Now, my problem is, I need to also go to another activity after this, I'm not realyl sure, but If I remember well I could do this automatically when logged in by using SQLite, don't know how to accomplish it here.
Already have the activities I need declared on manifest.
It should be something like this:
#Override
public void onClick(View v){
Intent intent = new Intent(LoginMember.this, WelcomeScreen.class);
startActivity(intent);
}
My problem is that I don't know how to "add" or "append" this activity transaction into this logic, I'm fairly new to android and google app engine, Any ideas???
Thanks in advance!
EDIT
This is my EndpointsAsyncTaskInsert code:
package com.kkoci.shairlook;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import com.appspot.shairlook1.userEndpoint.UserEndpoint;
import com.appspot.shairlook1.userEndpoint.model.User;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.extensions.android.json.AndroidJsonFactory;
import com.google.api.client.googleapis.services.AbstractGoogleClientRequest;
import com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/**
* Created by kristian on 04/07/2015.
*/
public class EndpointsAsyncTaskInsert extends AsyncTask<String, Void, User> implements GoogleClientRequestInitializer {
private static UserEndpoint myApiService = null;
private Context context;
EndpointsAsyncTaskInsert(Context context) {
this.context = context;
}
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
// put it here no in MyClass
abstractGoogleClientRequest.setDisableGZipContent(true);
}
// class MyClass{} // you don't need it
#Override
protected User doInBackground(String... params) {
User response = null;
if (myApiService == null) { // Only do this once
UserEndpoint.Builder builder = new UserEndpoint.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("https://shairlook1.appspot.com/_ah/api/")
.setGoogleClientRequestInitializer(this);
// end options for devappserver
myApiService = builder.build();
}
try {
User users = new User();
users.setEmail(params[0]);
users.setPassword(params[1]);
users.setName(params[2]);
response = myApiService.insertUser(users).execute();
} catch (Exception e) {
Log.d("Could not Add User", e.getMessage(), e);
}
return response;
}
}
SECOND EDIT
This is how it looks right now, it's giving me 'java.lang.NoClassDefFoundError' on this line: new EndpointsAsyncTaskInsert(LoginMember.this) {
:
public class LoginMember extends Activity {
private static
//DbAdapter dbAdapter = null;
//EditText txtUserName;
EditText txtPassword;
EditText txtEmail;
Button btnLogin;
TextView Forgot_text;
Button twitter;
Button facebook;
//Button btnRegister;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
txtPassword = (EditText) findViewById(R.id.et_pw);
txtEmail = (EditText) findViewById(R.id.et_email);
btnLogin = (Button) findViewById(R.id.btn_login);
twitter = (Button) findViewById(R.id.twitter);
facebook = (Button) findViewById(R.id.facebook);
Forgot_text = (TextView) findViewById(R.id.Forgot_text);
btnLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(txtEmail.getWindowToken(), 0);
imm.hideSoftInputFromWindow(txtPassword.getWindowToken(), 0);
String password = txtPassword.getText().toString();
String email = txtEmail.getText().toString();
if ((txtEmail.length() == 0) || (txtPassword.length() == 0)) {
Toast.makeText(LoginMember.this, "You need to provide values for Email and Password", Toast.LENGTH_SHORT).show();
return;
}
//Go ahead and perform the transaction
String[] params = {email,password};
//new EndpointsAsyncTaskInsert(LoginMember.this).execute(params);
/**try{ Intent k = new Intent(LoginMember.this, WelcomeScreen.class);
startActivity(k);
}catch(Exception e){
}**/
new EndpointsAsyncTaskInsert(LoginMember.this) {
protected void onPostExecute(User result) {
super.onPostExecute(result);
// Do something with result
Intent intent = new Intent(LoginMember.this, WelcomeScreen.class);
startActivity(intent);
}
}.execute(params);
}
});
}
public void getUser(View v) {
new EndpointsAsyncTask(this).execute();
}
public void insertUser(View v) {
new EndpointsAsyncTaskInsert(this).execute();
}
}
ok, i see, maybe you should do this, i haven't tryied this yet, but could help you:
Before onCreate method, declare a var of this way:
Activity currentActivity;
then inside onCreate method do this:
currentActivity=this;
so then, when you make you Asyctask, make this:
new EndpointsAsyncTaskInsert(currentActivity.getApplicationContext()).execute(params);
Hope that helps, let's me know if was helpFull, if not i try to help you in another way.
Regards.
In EndpointsAsyncTask class there should be method named onPostExecute() which is executed when your async task is completed. This is the place where you should notify your activity to go to another activity.
There are numerous way to do that.
You can create an Interface class for instance
public interface OnTaskFinishListener{
void onFinish();
}
and then implement this interface in your caller class:
public class YourActivity extends Activity implements OnTaskFinishListener {
void onFinish(){
Intent intent = new Intent(LoginMember.this, WelcomeScreen.class);
startActivity(intent);
}
}
When you create asynctask you should pass this reference as a parameter in its constructor and keep it in task fields and when the task is done call the onFinish method.
public EndpointsAsyncTaskInsert extends AsyncTask...{
private OnTaskFinishListener listener;
public EndpointsAsyncTaskInsert(OnTaskFinishListener listener){
this.listener = listener;
}
protected void onPostExecute(..){
//notify the listener
listener.onFinish();
}
}
Second and more loosely coupled way is to use an event bus library, for example, greenrobots EventBus https://github.com/greenrobot/EventBus, then you can post an event when your task is finished, and then you can receive that event in your activity without setting up any listeners.
You can make an anonymous version of your AsyncTask class and override the onPostExecute to start the new activity after it is done.
new EndpointsAsyncTaskInsert(LoginMember.this) {
protected void onPostExecute(User result) {
super.onPostExecute(result);
// Do something with result
Intent intent = new Intent(LoginMember.this, WelcomeScreen.class);
startActivity(intent);
}
}.execute(params);
Maybe this might work
if ((txtEmail.length() == 0) || (txtPassword.length() == 0)) {
Toast.makeText(LoginMember.this, "You need to provide values for Email and Password", Toast.LENGTH_SHORT).show();
return;
}
else{
String[] params = {email,password};
new EndpointsAsyncTaskInsert(LoginMember.this).execute(params);
}
In the class which extends the Asynctask override the onpostexecute method and add the following code
Intent in=new Intent(Login.this,Welcome.class);
in.putExtra("email",email);
in.putExtra("password",password);
startActivity(in);
You can use bundle to send data from one activity to another and retrieve from the bundle in the Welcome activity
Intent in=getIntent();
String email=in.getStringExtra("email");

Android button triggers by itself

I have a problem with an android application. I want to make a login screen, and I have an async task that should run when the login button is pressed. However, it appears that the runs automatically when the application starts, and I can't figure out why.
This is my code:
package com.connect.application;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import com.connect.utils.*;
/**
* Activity which displays a login screen to the user, offering registration as
* well.
*/
public class LoginActivity extends Activity {
/**
* A dummy authentication store containing known user names and passwords.
* TODO: remove after connecting to a real authentication system.
*/
// private static final String[] DUMMY_CREDENTIALS = new String[]{
// "foo#example.com:hello",
// "bar#example.com:world"
// };
/**
* The default email to populate the email field with.
*/
public static final String EXTRA_EMAIL = "com.example.android.authenticatordemo.extra.EMAIL";
/**
* Keep track of the login task to ensure we can cancel it if requested.
*/
private UserLoginTask mAuthTask = null;
// Values for email and password at the time of the login attempt.
private String mEmail;
private String mPassword;
// UI references.
private EditText mEmailView;
private EditText mPasswordView;
private View mLoginFormView;
private View mLoginStatusView;
private TextView mLoginStatusMessageView;
private Boolean mLoggedIn = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// Set up the login form.
mEmail = getIntent().getStringExtra(EXTRA_EMAIL);
mEmailView = (EditText) findViewById(R.id.email);
mEmailView.setText(mEmail);
mPasswordView = (EditText) findViewById(R.id.password);
// mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
// #Override
// public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
// if (id == R.id.login || id == EditorInfo.IME_NULL) {
// attemptLogin();
// return true;
// }
// return false;
// }
// });
mLoginFormView = findViewById(R.id.login_form);
mLoginStatusView = findViewById(R.id.login_status);
mLoginStatusMessageView = (TextView) findViewById(R.id.login_status_message);
findViewById(R.id.sign_in_button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.v("TAG/Login clicked", "Login button clicked");
attemptLogin();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.login, menu);
return true;
}
/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid email, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/
public void attemptLogin() {
if (mAuthTask != null) {
return;
}
// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);
// Store values at the time of the login attempt.
mEmail = mEmailView.getText().toString();
mPassword = mPasswordView.getText().toString();
boolean cancel = false;
View focusView = null;
// Check for a valid password.
if (TextUtils.isEmpty(mPassword)) {
mPasswordView.setError(getString(R.string.error_field_required));
focusView = mPasswordView;
cancel = true;
} else if (mPassword.length() < 4) {
mPasswordView.setError(getString(R.string.error_invalid_password));
focusView = mPasswordView;
cancel = true;
}
// Check for a valid email address.
if (TextUtils.isEmpty(mEmail)) {
mEmailView.setError(getString(R.string.error_field_required));
focusView = mEmailView;
cancel = true;
} else if (!mEmail.contains("#")) {
mEmailView.setError(getString(R.string.error_invalid_email));
focusView = mEmailView;
cancel = true;
}
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
mLoginStatusMessageView.setText(R.string.login_progress_signing_in);
showProgress(true);
mAuthTask = new UserLoginTask();
String[] credentials = new String[2];
credentials[0] = mEmail;
credentials[1] = mPassword;
Log.v("TAG/Auth task started", "Auth task started");
mAuthTask.execute(credentials);
}
}
/**
* Shows the progress UI and hides the login form.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
private void showProgress(final boolean show) {
// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
// for very easy animations. If available, use these APIs to fade-in
// the progress spinner.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
mLoginStatusView.setVisibility(View.VISIBLE);
mLoginStatusView.animate()
.setDuration(shortAnimTime)
.alpha(show ? 1 : 0)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
}
});
mLoginFormView.setVisibility(View.VISIBLE);
mLoginFormView.animate()
.setDuration(shortAnimTime)
.alpha(show ? 0 : 1)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
});
} else {
// The ViewPropertyAnimator APIs are not available, so simply show
// and hide the relevant UI components.
mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
}
/**
* Represents an asynchronous login/registration task used to authenticate
* the user.
*/
public class UserLoginTask extends AsyncTask<String, Void, Boolean> {
#Override
protected Boolean doInBackground(String... params) {
// TODO: attempt authentication against a network service.
// try {
// // Simulate network access.
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// return false;
// }
//
// for (String credential : DUMMY_CREDENTIALS) {
// String[] pieces = credential.split(":");
// if (pieces[0].equals(mEmail)) {
// // Account exists, return true if the password matches.
// return pieces[1].equals(mPassword);
// }
// }
Log.v("TAG/LoginAsync", "Task triggerred");
LoggedUser.getInstance().setId(1);
LoggedUser.getInstance().setmUsername("dummy#mail.com");
// TODO: register the new account here.
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
mAuthTask = null;
showProgress(false);
if (success) {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
mLoggedIn = true;
finish();
} else {
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
#Override
protected void onCancelled() {
mAuthTask = null;
showProgress(false);
}
}
}
And this is what appears in my log when I run the application:
01-16 14:36:01.593 13706-13706/com.connect.application V/TAG/Login clicked﹕ Login button clicked
01-16 14:36:01.597 13706-13706/com.connect.application V/TAG/Auth task started﹕ Auth task started
01-16 14:36:01.601 13706-13742/com.connect.application V/TAG/LoginAsync﹕ Task triggerred
, without pressing any buttons. Any help would be appreciated.

Categories

Resources