The app I am working on has a login/register which connects to a mysql database, At first I was running everything on the UI Thread I later found out it was not working because of the against running long code on Android's UI Thread. I attempted to edit my code to run the long task on a new Thread that i added.
. Now my app registers the I see the result in mysql but my app keeps closing because of this error
android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
the error is understandable but I don't know how to run the View or Views back to the UI Thread.
I've done some research about the runOnuithread but I dont know where to place it in my code, or weather I placed the new Thread I added before in the wrong place to begin with please
can anyone help my fix this
here is a snippet of the code
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.register);
// Importing all assets like buttons, text fields
inputFullName = (EditText) findViewById(R.id.registerName);
inputEmail = (EditText) findViewById(R.id.registerEmail);
inputPassword = (EditText) findViewById(R.id.registerPassword);
btnRegister = (Button) findViewById(R.id.btnRegister);
btnLinkToLogin = (Button) findViewById(R.id.btnLinkToLoginScreen);
registerErrorMsg = (TextView) findViewById(R.id.register_error);
// Register Button Click event
btnRegister.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
/** According with the new StrictGuard policy, running long tasks on the Main UI thread is not possible
So creating new thread to create and execute http operations */
new Thread (new Runnable() {
#Override
public void run() {
//
String name = inputFullName.getText().toString();
String email = inputEmail.getText().toString();
String password = inputPassword.getText().toString();
UserFunctions userFunction = new UserFunctions();
JSONObject json = userFunction.registerUser(name, email, password);
// check for login response
try {
//
//
if (json.getString(KEY_SUCCESS) != null) {
registerErrorMsg.setText("");
String res = json.getString(KEY_SUCCESS);
if(Integer.parseInt(res) == 1){
// user successfully registred
// Store user details in SQLite Database
DatabaseHandler db = new DatabaseHandler(getApplicationContext());
JSONObject json_user = json.getJSONObject("user");
// Clear all previous data in database
userFunction.logoutUser(getApplicationContext());
db.addUser(json_user.getString(KEY_NAME), json_user.getString(KEY_EMAIL), json.getString(KEY_UID), json_user.getString(KEY_CREATED_AT));
// Launch Dashboard Screen
Intent dashboard = new Intent(getApplicationContext(), MainActivity.class);
// Close all views before launching Dashboard
dashboard.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(dashboard);
// Close Registration Screen
finish();
}else{
// Error in registration
registerErrorMsg.setText("Error occured in registration");
}
}//
} catch (JSONException e) {
e.printStackTrace();
}
}
}).start();
}
});
// Link to Login Screen
btnLinkToLogin.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i = new Intent(getApplicationContext(),
LoginActivity.class);
startActivity(i);
// Close Registration View
finish();
}
});
}
}
Since you seem to have network code mixed in with UI code, I'm not going to try and write it for you. I can tell you how it should be and assume that you can rearrange the code yourself since you know what all of it does.
Ok, inside your run() you put your network code then when you need to update the UI you can have
runOnUiThread(new Runnable()
{
#Override
public void run()
{
// code to update UI
}
});
With that said, I recommend using AsyncTask for your network operations. This makes it much easier as it already has the functions for background stuff and updating the UI. You start the task and doInBackground() runs and that is where you do all of your network operations. Then you can update the UI in any of AsyncTasks other 3 methods.
See this answer for an example of using AsyncTask, along with the link to the docs above.
Related
I'm trying to develop a Beacon Android application using AltBeacon specification whereby the application will start the service, stop, and repeat the service again. As of now, I'm able to start and stop the services however, I'm unable to repeat to the very first 'If' statement. May I know what am I missing?
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
// Enter details
if (major.getText().toString().equals("") || minor.getText().toString().equals(""))
{
SuperActivityToast.create(getActivity(), new Style(), Style.TYPE_BUTTON)
.setText("Please fill Major and Minor details!")
.setDuration(Style.DURATION_LONG)
.setFrame(Style.FRAME_LOLLIPOP)
.setColor(PaletteUtils.getSolidColor(PaletteUtils.MATERIAL_PURPLE))
.setAnimations(Style.ANIMATIONS_POP).show();
}
// If user has entered
{
final Beacon beacon = new Beacon.Builder()
.setId1(uuid1)
.setId2(major.getText().toString())
.setId3(minor.getText().toString())
.setManufacturer(0x0118)
.setTxPower(-69)
.setRssi(-66)
.setBluetoothName("Hall 1")
.setDataFields(Arrays.asList(new Long[] {0l}))
.build();
BeaconParser beaconParser = new BeaconParser()
.setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25");
// Making the instance of BeaconTransmitter
final BeaconTransmitter beaconTransmitter = new BeaconTransmitter(getActivity(), beaconParser);
// Start Advertising the above Beacon Object
beaconTransmitter.startAdvertising(beacon);
// Toasting the message that beacon succesfully made
SuperActivityToast.create(getActivity(), new Style(), Style.TYPE_BUTTON)
.setText("Successfully Started!")
.setDuration(Style.DURATION_LONG)
.setFrame(Style.FRAME_LOLLIPOP)
.setColor(PaletteUtils.getSolidColor(PaletteUtils.MATERIAL_PURPLE))
.setAnimations(Style.ANIMATIONS_POP).show();
// Making some animation and changing the text of button
AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);
alphaAnimation.setDuration(1500);
alphaAnimation.setFillEnabled(true);
alphaAnimation.setInterpolator(new BounceInterpolator());
b1.startAnimation(alphaAnimation);
b1.setText("Stop Broadcasting");
b1.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
beaconTransmitter.stopAdvertising();
b1.setText("Broadcast Beacon");
}
});
}
}
});
I am trying to load progressBar as dialog while the client app send some data to server and dismiss the bar when data is received back from server.The below code has implemented this model.. with and interface being implemented inside an activity.
private void setup(){
ConnectionHandler connectionHandler = new ConnectionHandler() {
#Override
public void onSendData(){
runOnUiThread(new Runnable() {
#Override
public void run() {
progressDialog = new ProgressDialog(SignupActivity.this);
Log.d("progress bar", "getData: true" );
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setTitle("Loading..");
progressDialog.setMessage("Please wait");
progressDialog.setCancelable(false);
progressDialog.setIndeterminate(true);
progressDialog.show();
}
});
}
#Override
public void onReceiveData(List<Model> model) {
//do nothing for now
LoginMessage message = (LoginMessage)model.get(0);
if(message.getUserCode() != 0){
//registration successfull prompt to verify code;
Log.d("LoginMessage", "onListUpdate: " + message.toJson());
Intent intent = new Intent(getBaseContext(), VerificationActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra(getString(R.string.login_message),message.toJson());
intent.putExtra("email", credentials.getEmail());
progressDialog.dismiss();
startActivity(intent);
}else{
//something went wrong;
}
}
#Override
public Class<? extends Model> getType() {
return LoginMessage.class;
}
};
connectivity = new Connectivity(this, Properties.LOCALHOST + Properties.REGISTRATION_PHP);
connectivity.setConnectionHandler(connectionHandler);
}
The setup method is called when activity is created and connectivity is responsible for calling the onSendData() and onReceiveData().Everything was working fine untill i decided to have progressBar to make client wait for the data to load. Here the error is thrown saying android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application. I followed the solution shown in the other similar problem but didn't worked for me.
Some advised to use
progressBar = new ProgressBar(Activity.this) instead of
progressBar = new ProgressBar(getApplicationContext()) or new ProgressBar(getBaseContext)
but none of them worked for me.What is this error actually is saying to me.
Why is this error thrown?? please help
Below is the oncreate to my activity. My issue is that when the activity is started it is completely unresponsive until I press the back button. Once I press the back button it works perfectly.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_packs);
count = 0;
Bundle extras = getIntent().getExtras();
if(extras != null || !equals("")){
name = extras.getString("name");
}
else{name="PackActivity";}
//getActionBar().setTitle(name);
ActionBar actionBar = getActionBar();
//actionBar.setBackgroundDrawable(R.drawable.navigation_bar_colour_image);
//actionBar.setHomeButtonEnabled(true);
actionBar.setTitle(name);
actionBar.setDisplayHomeAsUpEnabled(false);
//actionBar.hide();
database = new DataObjectDataSource(this.getApplicationContext());
//load all the packs from the DB
packs = loadPacks();
//make the request for GetPacks
sortArrayById();
if(isOnline(this)) {
HTTPRequest.getHTTPRequest(HTTPRequest.getPacksURL, this);
}
else{
dialog("No internet connection available","their is limited functionality available in offline mode",1);
}
gridView = (GridView) findViewById(R.id.packGrid);
adapter = new PackGridAdapter(this, getApplicationContext(), packs);
gridView.setAdapter(adapter);
gridView.setOnItemClickListener(this);
System.out.println(" pack_ids ");
}
I have included the dialog function as the unresponsiveness comes after it have been dismissed.
public boolean dialog(String mes,String message,int type){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// Add the buttons
if(type==2){
builder.setMessage(message)
.setTitle(mes);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
}
// Create the AlertDialog
final AlertDialog dialog = builder.create();
dialog.show();
if(type==2){
final Timer t = new Timer();
t.schedule(new TimerTask() {
public void run() {
dialog.dismiss();
// when the task active then close the dialog
t.cancel(); // also just top the timer thread, otherwise, you may receive a crash report
}
}, 5000);
}
return true;
}
2 Things:
1st confirm that your ActionBar code is working (comment the actionbar part to make sure that is not culprit here) to see if the activity is responsive or not
If the 1st doesn't work, and even after commenting ActionBar activity is unresponsive .. then
2nd comment these lines:
if(isOnline(this)) {
HTTPRequest.getHTTPRequest(HTTPRequest.getPacksURL, this);
}
else{
dialog("No internet connection available","their is limited functionality available in offline mode",1);
}
I suspect you're doing some network operation on your UI Thread, that could be the cause of Activity not Responding or it must have something to do with the dialog method you're using. If you show that method code, it could lead further to diagnose.
In my activity a user is shown another user based around various criteria. I am trying to figure out a way to alert that the other user that this individual has selected him upon the current user button click. I am thinking that once the user has click on the confirm button, an alert dialog message would be displayed to the user so that he is aware that someone has click confirm on him.
I am using Parse to manage the users, and the below is the code that would display a user based around various criteria.
query1.findInBackground(new FindCallback<ParseUser>() {
#Override
public void done(List<ParseUser> objects,ParseException e) {
if (e == null) {
for(int i = 0; i < objects.size(); i++){
// Do whatever you need to extract object from "users"
ParseQuery<ParseObject> query1 = ParseQuery.getQuery("User");
query1.whereNotEqualTo("objectId", ParseUser.getCurrentUser().
getObjectId());
Button buttonconfirm = (Button) getView().
findViewById(R.id.btnMatchConfirm);
buttonconfirm.setText("Confirm");
mUserNameRetrieved = (TextView) getActivity().
findViewById(R.id.userlistname);
mUserNameRetrieved.setText(objects.get(i).get("Name").toString());
Button newPage = (Button)getView().
findViewById(R.id.btnMatchConfirm);
newPage.setVisibility(View.VISIBLE);
newPage.setText("Confirm");
newPage.setTextSize(TypedValue.COMPLEX_UNIT_SP, 30);
newPage.setTextColor(Color.parseColor("#ff0000"));
newPage.setBackgroundColor(Color.TRANSPARENT);
ViewGroup.LayoutParams params = newPage.getLayoutParams();
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
newPage.setLayoutParams(params);
newPage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// ParseUser currentUser = ParseUser.getCurrentUser();
// currentUser.put("UserMatchName", mUserRetrieved);
Intent intent = new Intent(getActivity(),
OptionActivity.class);
startActivity(intent);
}
});
}
} else if (e != null) {
}
}
});
I am not sure where to go from here, and any clarification would be greatly appreciated.
Thanks in advance.
If you already using Parse, you should use their Push notifications. Register receivers with a channel for each user.
When a user selects another, notify your parse back-end and then from the back-end send push filtered by the selected channel.
See more :
https://parse.com/docs/android_guide#push
Introduction
What I want to accomplish [sounds] simple. I want to prompt a user with a login dialog until the user successfully authenticates.
What I planned to do is use an AsyncTask for the data handling and web requests, but this has turned into a nightmare quickly; most likely due to my lack of experience in Android.
However, I know this can be done, as I've seen it before in other apps.
What I want to accomplish
The question is how? I know what I want to do:
1. Initially prompt a user to login.
2. Send authentication data.
3. If successful, continue the application.
4. If unsuccessful, reprompt the user until success.
What I have so far
What I have so far is my AsyncTask (LoginTask) which will handle the web requests and login data:
public class LoginTask extends AsyncTask<String, Void, App.STATUS>
{
private boolean m_proceed = false;
private String m_username, m_key;
#Override
protected void onPreExecute()
{
// Check if there is a dialog on screen. //
m_proceed = !App.DIALOG_ONSCREEN;
}
#Override
protected App.STATUS doInBackground(String ... p_args)
{
// Do not do this if a dialog is on screen. //
if(!m_proceed)
return App.STATUS.DENIED;
// Make a web request. //
try
{
URL t_url = new URL("https://mysite.com/api/login");
HttpsURLConnection t_con = (HttpsURLConnection)t_url.openConnection();
t_con.setRequestMethod("POST");
t_con.setRequestProperty("User-Agent", "Mozilla/5.0");
t_con.setDoOutput(true);
DataOutputStream t_wr = new DataOutputStream(t_con.getOutputStream());
t_wr.writeBytes("username="+p_args[0]+"&password="+p_args[1]);
t_wr.flush();
t_wr.close();
t_con.connect();
BufferedReader t_in = new BufferedReader(new InputStreamReader(t_con.getInputStream()));
String t_input_line;
StringBuffer t_response = new StringBuffer();
while((t_input_line = t_in.readLine()) != null)
{
t_response.append(t_input_line);
}
t_in.close();
// If denied, return failed. If accepted, set the username and key. //
if(t_response.toString().equals("DENIED"))
return App.STATUS.FAILED;
else
{
m_key = t_response.toString();
m_username = p_args[0];
}
return App.STATUS.ACCEPTED;
}
catch(Exception err)
{
System.err.println(err.getMessage());
}
return App.STATUS.FAILED;
}
#Override
protected void onPostExecute(App.STATUS p_status)
{
// Authenticate the user if the username and key are valid. //
if(p_status == App.STATUS.ACCEPTED)
App.acceptCredentials(m_username, m_key);
// The dialog is no longer on the screen. //
App.DIALOG_ONSCREEN = false;
}
}
And the main activity (HomeActivity) which will prompt the user if they are not authenticated, and will show content if they are:
public class HomeActivity extends Activity
{
#Override
public void onCreate(Bundle p_data)
{
// Basic crap... //
super.onCreate(p_data);
setContentView(R.layout.activity_home);
// Are we authenticated? //
if(!App.isAuthenticated())
{
// Create the dialog. //
LayoutInflater t_infl = LayoutInflater.from(this);
View t_login_view = t_infl.inflate(R.layout.login_dialog, null);
AlertDialog.Builder t_builder = new AlertDialog.Builder(this);
t_builder.setTitle("Login").setView(t_login_view).setPositiveButton("Login", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
// What should go here? //
}
});
t_builder.create();
}
// How do I keep checking if the user is not authenticated, and keep showing the dialog as such? //
}
}
What I need help with
My main question is how do I design my program in such a way that I can easily keep displaying the login dialog until the user has successfully authenticated? I thought of using a while loop, but then it would keep displaying dialogs and hamper performance. It's pretty tricky when I have asynchronous and synchronous tasks working in tandem.
I'm not looking for straight code, but general insight would be much appreciated.
Thank you for taking your time to read this and thank you for helping!
The solution
HomeActivity.java
private void promptLogin()
{
final Context t_main_context = this;
// Create the dialog. //
LayoutInflater t_infl = LayoutInflater.from(this);
final View t_login_view = t_infl.inflate(R.layout.login_dialog, null);
final AlertDialog t_dialog = new AlertDialog.Builder(this)
.setTitle("Login")
.setCancelable(false)
.setView(t_login_view)
.setPositiveButton("Login", null)
.create();
t_dialog.show();
t_dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View t_view)
{
String t_username = ((EditText)t_login_view.findViewById(R.id.in_username)).getText().toString(),
t_password = ((EditText)t_login_view.findViewById(R.id.in_password)).getText().toString();
try
{
new LoginTask(t_main_context, t_dialog).execute(t_username, t_password);
}
catch(Exception err)
{
err.printStackTrace();
}
}
});
}
#Override
public void onCreate(Bundle p_data)
{
// Basic crap... //
super.onCreate(p_data);
setContentView(R.layout.activity_home);
// Are we authenticated? //
if(!App.isAuthenticated())
promptLogin();
}
LoginTask.java
private String m_username, m_key;
private Context m_context;
private AlertDialog m_dialog;
private ProgressDialog m_loading;
public LoginTask(Context p_context, AlertDialog p_dialog)
{
m_context = p_context;
m_dialog = p_dialog;
m_loading = ProgressDialog.show(m_context, "", "Logging in...", true);
}
#Override
protected App.STATUS doInBackground(String ... p_args)
{
// Make a web request. //
try
{
URL t_url = new URL("https://mysite.com/api/login");
HttpsURLConnection t_con = (HttpsURLConnection)t_url.openConnection();
t_con.setRequestMethod("POST");
t_con.setRequestProperty("User-Agent", "Mozilla/5.0");
t_con.setDoOutput(true);
DataOutputStream t_wr = new DataOutputStream(t_con.getOutputStream());
t_wr.writeBytes("username="+p_args[0]+"&password="+p_args[1]);
t_wr.flush();
t_wr.close();
t_con.connect();
BufferedReader t_in = new BufferedReader(new InputStreamReader(t_con.getInputStream()));
String t_input_line;
StringBuffer t_response = new StringBuffer();
while((t_input_line = t_in.readLine()) != null)
{
t_response.append(t_input_line);
}
t_in.close();
// If denied, return failed. If accepted, set the username and key. //
if(t_response.toString().equals("DENIED"))
return App.STATUS.FAILED;
else
{
m_key = t_response.toString();
m_username = p_args[0];
}
return App.STATUS.ACCEPTED;
}
catch(Exception err)
{
System.err.println(err.getMessage());
}
return App.STATUS.FAILED;
}
#Override
protected void onPostExecute(App.STATUS p_status)
{
m_loading.dismiss();
// Authenticate the user if the username and key are valid. //
if(p_status == App.STATUS.ACCEPTED)
{
App.acceptCredentials(m_username, m_key);
m_dialog.dismiss();
}
else
Toast.makeText(m_context, "Login failed", Toast.LENGTH_SHORT).show();
}
So what I did in promptLogin() in HomeActivity.java was that I overrode the button onClickListener, so that the dialog would not close unless closed by t_dialog.dismiss(). I then sent the web request to LoginTask and passed the dialog as a parameter, so that the dialog would only close until I dismissed the dialog.
I only dismiss the dialog when the credentials are accepted, as you can see in onPostExecute().
In this way, the dialog stays on screen until the user successfully logs in, which is the behavior I was looking for.
Thanks everyone for helping!
1. Initially prompt a user to login.
keep prompting the dialog here, with setCancelable(false), so the user will not cancel the login process. Then create a View.OnclickListner on the button that the user have to click on in order to send data to your server. Let's say Login button.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Delete entry")
.setMessage("Are you sure you want to delete this entry?")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// send data here with AsyncTask
}
})
.setIcon(R.drawable.ic_my_icon);
builder.setCancelable(false);
builder.show();
2. Send authentication data.
Use your AsyncTask here, do the sending task in doInBackgroud() method, and return something onPostExecute() to know if authentication succeeded or not. if success, dismiss the dialog, if not, you keep the dialog and wait for the user to click again the Login button.
protected void onPostExecute(Boolean result) {
if(result) {
// he is now authenticated, dismiss dialog and continue in your app
dialog.dismiss();
} else {
// nothing to do, until he succeed
}
}
3. If successful, continue the application.
Dismiss the dialog here buy using the dismiss() method.
4. If unsuccessful, reprompt the user until success.
Don't do anything, let the dialog shown, until the authentication process succeeds. You can also show something to the user (a toast, an image etc) to tell him that he hasn't logged in yet.
Hope it's clear for you.
You have to create one activity which handles your login, and another which is your main activity. If the login fails, nothing happens, if it succeeds you start the second activity. No need for your complicated setup.
You can also have a look at the Volley library, makes http connections pretty easy.