I've written an app that sits in the 'Share via' menu (for quickly emailing myself links to things I find on the web or look at in RSS readers) For this I'm using an intent.action.SEND intent-filter:
<activity
android:name="uk.co.baroquedub.checkit.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Here's the MainActivity package, it grabs the page title and url from the Intent and uses a separate GMailSender class to directly email me this info:
package uk.co.baroquedub.checkit;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
private static Dialog dialog;
String title;
String url;
String message;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
intent.setFlags( Intent.FLAG_ACTIVITY_CLEAR_TOP );
String action = intent.getAction();
// if this is from the share menu
if (Intent.ACTION_SEND.equals(action)) {
title = intent.getStringExtra(Intent.EXTRA_SUBJECT);
url = intent.getStringExtra(Intent.EXTRA_TEXT);
// Flipboard fix (remove title in URL)
url = url.replace(title, "");
if (url != null){
url = title+"\n"+url;
} else {
url = "error getting URL";
}
// Asynch Task
doSendTask task = new doSendTask();
task.execute(new String[] { url });
}
}
protected void showDialog (String response){
dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog);
dialog.setTitle(response);
Button button = (Button) dialog.findViewById(R.id.Button01);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dialog.dismiss();
finish();
}
});
dialog.show();
}
private class doSendTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
String senderPassword = getResources().getString(R.string.senderPassword);
String senderEmail = getResources().getString(R.string.senderEmail);
String recipientEmail = getResources().getString(R.string.recipientEmail);
String subjectText = getResources().getString(R.string.subjectText);
GMailSender sender = new GMailSender(senderEmail, senderPassword);
try {
sender.sendMail(subjectText,
url,
senderEmail,
recipientEmail);
response = "Email sent";
} catch (Exception e) {
//Log.e("SendMail", e.getMessage(), e);
response = "Error sending email";
}
return response;
}
#Override
protected void onPostExecute(String result) {
showDialog(result);
}
}
#Override
public void onDestroy() {
super.onDestroy();
/*
* Kill application when the root activity is killed.
*/
UIHelper.killApp(true);
}
}
Version 1 worked fine but I was sending the email from within 'onCreate' which meant that until the "Email sent" notification appeared, the phone's browser would be unresponsive (I wasn't able to scroll or navigate to a new page). I then changed the code (as per above) to place the email sending code inside an AsyncTask class - but although the app still works the browser remains unresponsive until the dialog appears. AsyncTask seems to have made no difference.
Can anyone explain why, and hopefully suggest a solution?
ANSWER?... I've massively simplified the above code to try to work out what might be wrong. I created a bog standard App and used the following:
package uk.co.baroquedub.testcheck;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
doSendTask task = new doSendTask();
task.execute(new String[] { "urlString" });
}
protected void showDialog (String response){
Toast.makeText(this, response, Toast.LENGTH_SHORT).show();
finish();
}
private class doSendTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
try {
Thread.sleep(5000);
response = "Waited";
}
catch (InterruptedException ex) { }
return response;
}
#Override
protected void onPostExecute(String result) {
showDialog(result);
}
}
}
This has allowed me to see what's going wrong: my app is opening on top of the browser (a white screen appears with a title bar showing the name of the app) I wasn't aware of this with my proper app (above) because I was using a Theme that used a transparent background.
See: screencast for demo of problem
So although the email is being sent as an AsyncTask, while this is happening the app itself is appearing on top of the browser - which is what is stopping it from being accessible. (I'll post a request for help on this as a separate question)
No, AsyncTask is not bond to your UI thread and you will not be blocking it as you did while doing some lengthy operation in your onCreate().
I think you have a mistake in your code:
doSendTask task = new doSendTask();
task.execute(url); // if you want to put more urls task.execute(url,url1,url2);
try {
sender.sendMail(subjectText,
url[0], // get the first url
senderEmail,
recipientEmail);
response = "Email sent";
} catch (Exception e) {
//Log.e("SendMail", e.getMessage(), e);
response = "Error sending email";
}
ASyncTasks are, indeed, executed on their own thread, as written on their reference page.
When you create the intent for this activity, you pass the flag 'FLAG_ACTIVITY_CLEAR_TOP', which reads something like '...all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.'. Doesn't this mean that the old activity is locked until this new one terminates?
Related
So I've got a simple Android App that can upload a file to an AWS S3 bucket, I'm looking to see how I can go about storing the filename in the object URL using metadata support from AWS.
How would I got about doing so?
Below is my MainActivity.java file:
package com.example.s3imagetest;
import java.net.URL;
import java.util.Date;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.OpenableColumns;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.ResponseHeaderOverrides;
public class MainActivity extends Activity {
private AmazonS3Client s3Client = new AmazonS3Client(
new BasicAWSCredentials(Constants.ACCESS_KEY_ID,
Constants.SECRET_KEY));
private Button selectPhoto = null;
private Button showInBrowser = null;
private static final int PHOTO_SELECTED = 1;
/** Called when the activity is first created. */
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("OnCreate", "OnCreate Was Called");
s3Client.setRegion(Region.getRegion(Regions.EU_WEST_1));
setContentView(R.layout.activity_main);
selectPhoto = (Button) findViewById(R.id.button1);
selectPhoto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Start the image picker.
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, PHOTO_SELECTED);
}
});
showInBrowser = (Button) findViewById(R.id.button2);
showInBrowser.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new S3GeneratePresignedUrlTask().execute();
}
});
}
// This method is automatically called by the image picker when an image is
// selected.
protected void onActivityResult(int requestCode, int resultCode,
Intent imageReturnedIntent) {
super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
switch (requestCode) {
case PHOTO_SELECTED:
if(resultCode == RESULT_OK) {
Uri selectedImage = imageReturnedIntent.getData();
new S3PutObjectTask().execute(selectedImage);
}
}
}
// Display an Alert message for error or failure
protected void displayAlert(String title, String message) {
AlertDialog.Builder confirm = new AlertDialog.Builder(this);
confirm.setTitle(title);
confirm.setMessage(message);
confirm.setNegativeButton(MainActivity.this.getString(R.string.ok),
new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
confirm.show().show();
}
protected void displayErrorAlert(String title, String message) {
AlertDialog.Builder confirm = new AlertDialog.Builder(this);
confirm.setTitle(title);
confirm.setMessage(message);
confirm.setNegativeButton(MainActivity.this.getString(R.string.ok),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
MainActivity.this.finish();
}
});
confirm.show().show();
}
private class S3PutObjectTask extends AsyncTask<Uri, Void, S3TaskResult> {
ProgressDialog dialog;
protected void onPreExecute() {
Log.v("S3PutObjectTask", "Its Beginning ");
dialog = new ProgressDialog(MainActivity.this);
dialog.setMessage(MainActivity.this.getString(R.string.uploading));
dialog.setCancelable(false);
dialog.show();
Log.v("S3PutObjectTask", "onPreExecute Done");
}
protected S3TaskResult doInBackground(Uri... uris) {
Log.v("S3TaskResult", "Uri" + uris);
if (uris == null || uris.length != 1) {
return null;
}
// The file location of the image selected.
Uri selectedImage = uris[0];
ContentResolver resolver = getContentResolver();
String fileSizeColumn[] = { OpenableColumns.SIZE };
Cursor cursor = resolver.query(selectedImage, fileSizeColumn, null,
null, null);
cursor.moveToFirst();
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
String size = null;
if (!cursor.isNull(sizeIndex)) {
size = cursor.getString(sizeIndex);
}
cursor.close();
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(resolver.getType(selectedImage));
if (size != null) {
metadata.setContentLength(Long.parseLong(size));
}
S3TaskResult result = new S3TaskResult();
// Put the image data into S3.
try {
Log.v("PutData into S3", "Data has bee sent");
//s3Client.createBucket(Constants.getPictureBucket());
PutObjectRequest por = new PutObjectRequest(
Constants.PICTURE_BUCKET, Constants.PICTURE_NAME,
resolver.openInputStream(selectedImage), metadata);
Log.v("PubtObjectRequest", "Selected Image");
s3Client.putObject(por);
Log.v("S3Client.putOb", "por");
Toast.makeText(getApplicationContext(), "Photo Upload Success", Toast.LENGTH_LONG).show();
} catch (Exception exception) {
result.setErrorMessage(exception.getMessage());
}
return result;
}
protected void onPostExecute(S3TaskResult result) {
dialog.dismiss();
if (result.getErrorMessage() != null) {
displayErrorAlert(
MainActivity.this
.getString(R.string.upload_failure_title),
result.getErrorMessage());
}
}
}
private class S3GeneratePresignedUrlTask extends
AsyncTask<Void, Void, S3TaskResult> {
protected S3TaskResult doInBackground(Void... voids) {
S3TaskResult result = new S3TaskResult();
try {
// Ensure that the image will be treated as such.
Log.v("S3GeneratePresignedUrlTask", "S3TaskResult");
ResponseHeaderOverrides override = new ResponseHeaderOverrides();
override.setContentType("image/jpeg");
// Generate the pre-signed URL.
// Added an hour's worth of milliseconds to the current time.
Date expirationDate = new Date(
System.currentTimeMillis() + 3600000);
GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(
Constants.PICTURE_BUCKET, Constants.PICTURE_NAME);
urlRequest.setExpiration(expirationDate);
urlRequest.setResponseHeaders(override);
URL url = s3Client.generatePresignedUrl(urlRequest);
result.setUri(Uri.parse(url.toURI().toString()));
} catch (Exception exception) {
result.setErrorMessage(exception.getMessage());
}
return result;
}
protected void onPostExecute(S3TaskResult result) {
if (result.getErrorMessage() != null) {
displayErrorAlert(
MainActivity.this
.getString(R.string.browser_failure_title),
result.getErrorMessage());
} else if (result.getUri() != null) {
// Display in Browser.
startActivity(new Intent(Intent.ACTION_VIEW, result.getUri()));
}
}
}
private class S3TaskResult {
String errorMessage = null;
Uri uri = null;
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public Uri getUri() {
return uri;
}
public void setUri(Uri uri) {
this.uri = uri;
}
}
}
From
http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/
setUserMetadata(Map) seems like it might be what you are looking for, although I don't think this is the best way to approach this problem.
Sets the
custom user-metadata for the associated object.
Amazon S3 can store additional metadata on objects by internally
representing it as HTTP headers prefixed with "x-amz-meta-". Use
user-metadata to store arbitrary metadata alongside their data in
Amazon S3. When setting user metadata, callers should not include the
internal "x-amz-meta-" prefix; this library will handle that for them.
Likewise, when callers retrieve custom user-metadata, they will not
see the "x-amz-meta-" header prefix.
User-metadata keys are case insensitive and will be returned as
lowercase strings, even if they were originally specified with
uppercase strings.
Note that user-metadata for an object is limited by the HTTP request
header limit. All HTTP headers included in a request (including user
metadata headers and other standard HTTP headers) must be less than
8KB.
From your description, it sounds like this is an app where users can upload photos and view them from a browser as well as the phone. However, you want it so that when the user deletes a photo from the browser, the corresponding local copy will also be deleted on the phone.
I think you could probably accomplish this with the setUserMetadata method, by passing up metadata that describes the path. However, this would be very cumbersome because the metadata is only lower case, and unfortunately the path and file names are not guaranteed to be(I think?). So, you'd need your own way of mapping lower case strings to upper and lower case strings, which adds a lot of unnecessary complexity.
I feel like it would be better if you set up a database to hold the mappings from S3 file names to their respective local paths. You could do this either on the Android device, which would be much easier but could exhibit weird behavior if the user tries the data. You could also do this on your own back end, which probably makes more sense if you are going to be sending out push notifications to your users to handle deletion anyway, since you could just push the path to them.
An even bigger potential issue is that you have to differentiate between devices, although you could solve that by just keeping a unique id associated with each device in your table. There's also the problem of what to do when the user decides to move the picture.
In any case, those things aren't really relevant to the question at hand. To answer your question, you COULD do this with the metadata but it would be ill advised since it adds a lot of complexity with seeming little upside. If you are looking for the simplest approach(handling all this client side) then I would probably suggest you try to use Cognito and DynamoDB to directly add a row to some DynamoDB table from your app. Make sure you specify the correct permissions to both allow you to do this and also to not allow any extra power to the user if you do take this approach.
Hope some of that helped!
I am creating simple system that has a windows service running, complete with a table of users, and I want to validate someone's login credentials by sending something to the service. I am trying to send their username and password, but I keep getting an error with my Async Task. I know that you're not supposed to mess with UI stuff in there and I am not. Originally I had a call to another activity in there but I commented it out. Now the only thing in doInBackground is setting a boolean value to true if the validation was good. From there I read the value after the async has executed, and then put together a bundle to move to the next place. I just don't know what's wrong here. It has been awhile since I've programmed in Android so maybe I'm missing something stupid. If anyone could help me out I would greatly appreciate it! Thank you. This could also be an issue with sending the information to the service?
UPDATE: After adding in the internet usage in the manifest, if I have the log.d in the program in my doInBackground, it prints out. If I don't have it, the result value stays false. It looks like there is some issue with the connection between my service and android app...
import java.util.ArrayList;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class LoginActivity extends Activity {
private static final int DATA_FROM_MAIN_ACTIVITY = 1;
private static final String SERVICEURL = "http://localhost:56638/ClientService.svc";
private EditText userTextBox, passTextBox;
private Button loginButton;
private String uName, pass;
private Boolean result = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
userTextBox = (EditText) findViewById(R.id.userTextBox);
passTextBox = (EditText) findViewById(R.id.passTextBox);
loginButton = (Button) findViewById(R.id.loginButton);
loginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
uName = userTextBox.getText().toString();
pass = passTextBox.getText().toString();
SendLoginMessage task = new SendLoginMessage();
task.execute(uName, pass);
if (result.equals(true)) {
Intent goToMainScreen = new Intent(getApplicationContext(),
MainActivity.class);
goToMainScreen.putExtra("username", uName);
goToMainScreen.putExtra("password", pass);
startActivityForResult(goToMainScreen,
DATA_FROM_MAIN_ACTIVITY);
} else {
Toast.makeText(
getApplicationContext(),
"There was an issue with your username or password.",
Toast.LENGTH_LONG).show();
}
}
});
}
#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;
}
private class SendLoginMessage extends AsyncTask<String, String, Void> {
#Override
protected void onPreExecute() {
// Log.d("message almost at server, " + textFromArea, selected,
// null);
}
#Override
protected Void doInBackground(String... names) {
ArrayList<NameValuePair> postParams = new ArrayList<NameValuePair>();
postParams.add(new BasicNameValuePair("username", names[0]));
postParams.add(new BasicNameValuePair("password", names[1]));
String response = null;
try {
response = HttpClient.executeHttpPost(SERVICEURL, postParams);
String newResponse = response.toString();
newResponse = newResponse.replaceAll("\\s+", "");
// if user was authenticated...
if (newResponse.equals(true)) {
result = true;
// creating an intent to take user to next page.
// load their DB objects on the
// on create in other activity
// pass the username/password to next activity
// then make a request to the server for their database
// objects.
// Intent goToMainScreen = new
// Intent(getApplicationContext(), MainActivity.class);
// goToMainScreen.putExtra("username", names[0]);
// goToMainScreen.putExtra("password", names[1]);
// startActivityForResult(goToMainScreen,
// DATA_FROM_MAIN_ACTIVITY);
}
} catch (Exception e) {
Log.d("ERROR", "exception in background");
}
return null;
}
#Override
protected void onPostExecute(Void result) {
// Toast.makeText(getApplicationContext(),
// .show();
}
}
}
Do it like this:
private class SendLoginMessage extends AsyncTask<String, String, Boolean> {
#Override
protected Boolean doInBackground(String... names) {
ArrayList<NameValuePair> postParams = new ArrayList<NameValuePair>();
postParams.add(new BasicNameValuePair("username", names[0]));
postParams.add(new BasicNameValuePair("password", names[1]));
String response = null;
try {
response = HttpClient.executeHttpPost(SERVICEURL, postParams);
String newResponse = response.toString();
newResponse = newResponse.replaceAll("\\s+", "");
// if user was authenticated...
if (newResponse.equals("true")) {
return true;
}
} catch (Exception e) {
Log.d("ERROR", "exception in background");
}
return false;
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
Intent goToMainScreen = new Intent(getApplicationContext(),
MainActivity.class);
goToMainScreen.putExtra("username", uName);
goToMainScreen.putExtra("password", pass);
startActivityForResult(goToMainScreen,
DATA_FROM_MAIN_ACTIVITY);
} else {
Toast.makeText(
getApplicationContext(),
"There was an issue with your username or password.",
Toast.LENGTH_LONG).show();
}
}
}
and in your onClick() just call execute()
uName = userTextBox.getText().toString();
pass = passTextBox.getText().toString();
SendLoginMessage task = new SendLoginMessage();
task.execute(uName, pass);
After execution is finished onPostExecute() will be called and your Activity will start depending on result variable.
Don't check your result variable after you invoke execute() because execute() is invoked asynchronously. By the time you check your global result variable, your doInBackground() might not have been finished. Using my approach you don't need a global variable. Please read doc carefully before using any component.
I am trying to establish a chatting application between a server and a client, but the application could not run because of this line of code : message = (String) input.readObject();
because at first, inputStream is null ! any one can help please ? here is my code
package com.example.test;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.net.Socket;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.StrictMode;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
private Button send;
private Button connect;
private EditText userText;
private TextView chatWindow;
private String serverIP;
private ObjectOutputStream output;
private ObjectInputStream input;
private String message = "";
private Socket connection;
#SuppressLint({ "NewApi", "NewApi", "NewApi" })
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
serverIP = "192.168.1.4";
//userText.setEnabled(false);
send = (Button)findViewById(R.id.button1);
connect = (Button)findViewById(R.id.button2);
chatWindow =(TextView)findViewById(R.id.textView1);
userText = (EditText)findViewById(R.id.editText1);
userText.setHint("Enter your message here");
connect.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//connect to the server
try{
connectToServer();
setupStreams();
}catch(EOFException eofException){
showMessage("\n client terminated the connection");
}catch(IOException ioException){
ioException.printStackTrace();
}
}
});
send.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String message = userText.getText().toString();
sendMessage(message);
userText.setText("");
}
});
while(true){
try{
message = (String) input.readObject();
showMessage("\n" + message + " NULL ");
chatWindow.refreshDrawableState();
}catch(ClassNotFoundException classNotFoundException){
showMessage("\n I don't know that object type");
}
catch(NullPointerException e){
e.printStackTrace();
}
catch (OptionalDataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} // end of onCreate
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
//connect to the server
private void connectToServer() throws IOException {
showMessage("Attempting connection... \n");
connection = new Socket( "192.168.1.4", 6789);
showMessage("Connected to " + connection.getInetAddress().getHostName() );
}
//setup streams to send and receive messages
private void setupStreams() throws IOException{
output = new ObjectOutputStream(connection.getOutputStream());
output.flush();
input = new ObjectInputStream(connection.getInputStream());
showMessage("\n Your streams are now good to go! \n ");
}
//whileChatting with server
private void whileChatting() throws IOException{
try{
message = (String) input.readObject();
showMessage("\n" + message + " NULL ");
chatWindow.refreshDrawableState();
}catch(ClassNotFoundException classNotFoundException){
showMessage("\n I don't know that object type");
}
}
//close the streams and sockets
private void closeCrap(){
showMessage("\n closing crap down");
ableToType(false);
try{
output.close();
input.close();
connection.close();
}catch(IOException ioException){
ioException.printStackTrace();
}
}
// gives user permission to type into the text box
private void ableToType(final boolean tof){
userText.setEnabled(tof);
}
// send messages to server
private void sendMessage(String message){
try{
output.writeObject("CLIENT - " + message);
output.flush();
showMessage("\nCLIENT - " + message);
}catch(IOException ioException){
chatWindow.append("\n somethine messed up sending message!");
}
}
//change/update chatWindow
private void showMessage(final String m){
chatWindow.append(m);
chatWindow.refreshDrawableState();
}
} // end of class MainActivity
Your code is definitely not following any good UI guidelines: You are doing network operations on the Main (UI) Thread, and you have an infinite loop in onCreate(). Android should actually offer to force close your app if nothing crashes. Now, a likely cause for the null problem you are facing:
setupStreams() is only called upon a button click. However, your while (true) loop is in the root of onCreate(). This means that as soon as the click listeners are made and set, the loop runs, attempts to read in from input and fails since setupStreams() hasn't been called.
So please don't do away with StrictMode - it's there to help, and thing about your code from an event driven standpoint ("Once X happens, then do Y"). And also get rid of loops in the Main (UI) Thread. Loops are fine for Console windows, but with UIs (which have their own complex lifecycle), you can't do this without freezing a lot of things.
Do all of your network tasks in doInBackground() ofAsyncTask then update your UI variables in onPostExecute(). Something like
public class TalkToServer extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected String doInBackground(String... params) {
//do your work here
return something;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// do something with data here-display it or send to mainactivity
}
Here is the documentation on AsyncTask
Another thing to consider is you are using the variable message in different places which may cause you problems. It looks like you have it defined as a member variable then as a local variable to other methods. You don't want to re-use the same variable name this way. Don't define String message multiple times in the same Activity
In below code i download json from internet and want to show in list.
if list is empty go to another activity but other activity not start.
no error but no start activity.
thanks for your help
package ir.mohammadi.android.nightly;
import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
public class AllNotes extends ListActivity {
ProgressDialog pDialog;
ArrayList<HashMap<String, String>> noteList;
JSONArray notes = null;
private static String KEY_SUCCESS = "success";
private static String KEY_ERROR_MSG = "error_message";
private static String KEY_NOTE_ID = "note_id";
private static String KEY_NOTE_SUBJECT = "note_subject";
private static String KEY_NOTE_DATE = "note_date";
private static String EXECUTE_RESULT = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_list);
noteList = new ArrayList<HashMap<String, String>>();
new LoadAllNotes().execute();
ListView lv = getListView();
}
public class LoadAllNotes extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(AllNotes.this);
pDialog.setMessage("لطفا صبر کنید...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
protected String doInBackground(String... args) {
UserFunctions userFunctions = new UserFunctions();
JSONObject jSon = userFunctions.getAllNotes("1");
Log.i("AllNotes >> jSon >>", jSon.toString());
try {
String success = jSon.getString(KEY_SUCCESS);
if (success == "1") {
notes = jSon.getJSONArray("notes");
for (int i = 0; i < notes.length(); i++) {
JSONObject c = notes.getJSONObject(i);
String id = c.getString(KEY_NOTE_ID);
String subject = c.getString(KEY_NOTE_SUBJECT);
String date = c.getString(KEY_NOTE_DATE);
HashMap<String, String> map = new HashMap<String, String>();
map.put(KEY_NOTE_ID, id);
map.put(KEY_NOTE_SUBJECT, subject);
map.put(KEY_NOTE_DATE, date);
noteList.add(map);
}
} else {
finish();
Toast.makeText(getApplicationContext(),
jSon.getString(KEY_ERROR_MSG), Toast.LENGTH_SHORT).show();
Log.i("AllNotes >> No nightly >>", "...");
Intent i = new Intent(getApplicationContext(), login.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String file_url) {
pDialog.dismiss();
runOnUiThread(new Runnable() {
public void run() {
ListAdapter adapter = new SimpleAdapter(AllNotes.this,
noteList, R.layout.list_item, new String[] {
KEY_NOTE_ID, KEY_NOTE_SUBJECT,
KEY_NOTE_DATE }, new int[] {
R.id.list_lbl_id, R.id.list_lbl_subject,
R.id.list_lbl_date });
setListAdapter(adapter);
}
});
}
}
}
Logcat json :
11-08 22:17:44.467: I/getAllNotes params before getting from net >>(599): [tag=getNotesList, user_id=1]
11-08 22:17:47.647: I/Input stream >>(599): org.apache.http.conn.EofSensorInputStream#44ededd8
11-08 22:17:47.767: I/JSON string builder >>(599): a{"error":"1","error_message":"\u0634\u0645\u0627 \u0647\u0646\u0648\u0632 \u0634\u0628\u0627\u0646\u0647 \u0627\u06cc \u0646\u0646\u0648\u0634\u062a\u0647 \u0627\u06cc\u062f."}
11-08 22:17:47.797: I/getAllNotes params after getting from net >>(599): {"error":"1","error_message":"dont have any note"}
11-08 22:17:47.797: I/AllNotes >> jSon >>(599): {"error":"1","error_message":"dont have any note"}
The most likely cause of your problem is this line:
String success = jSon.getString(KEY_SUCCESS);
You didn't post it, but I'll bet there is a JSONException stack trace below that last line. getString() will throw an exception if the key is not found, and in the JSON data you logged out, there is no "success" key. That exception is causing all the code you wrote after the fact to not get called at all, which is why you see nothing happening.
A couple other things to note:
success == "1" is not the proper way to do String equality. The == operator checks object equality (same object), if you want to check if two Strings are the same, us success.equals("1") instead.
onPostExecute() is always called on the main thread for you. You do not have to call runOnUiThread() from the method...it is redundant.
It is technically okay to start an Activity from a background thread like you have done, but you cannot show a Toast this way. When you fix your exception, the Toast.makeText() line will end up failing because you cannot show a Toast from a thread other than the main thread.
In general, it's best to do all UI operations on the main thread and as a point of design you should move any code that manipulates or changes the screen into onPostExecute() so it can be called at the right time. That's what it is there for.
This gets tricky. Some things to watch out for:
Intent i = new Intent(getApplicationContext(), login.class);
Make sure that the login class's type is in the manifest. Usually you reference the class by the class's name, not the instantiated variable. Honestly, it shouldn't make a difference but it's worth trying.
Sometimes you get weird side-effects using different contexts. I think startActivity is one that's pretty sensitive to this. You can try AllNotes.this to get the running activity's context. There's a slim chance that this needs to be run on the UI thread, so you can try runOnUiThread() if that doesn't work.
how can I pass object stream (exmple: BufferedReader or DataOutputStream n etc) to other activity (from Client_layoutActivity.class to chat_wall.class ) if i use intent method like this:
Intent i = new Intent(Client_layoutActivity.this, chat_wall.class);
startActivity(i);
I made a chat application that consists of several pages. The first page is the login page. in the first page, client use socket to communicated with server. After login process is success than server will send "login success". After that clien side will change layout from first page (login page) to second page (chat wall page). I mean to use socket, BufferedReader and DataOuputstream method from first page (i assume socket for login process is still connected so i can still used this socket to communicate in second page - chatting process). So i want to pass object Socket, BufferedReader and DataOuputstream to second page to used this. i write my code bellow:
PAGE 1 : for login purpose
public void login(){
try {
String name = usrname.getText().toString(); // usrname is android object edit
text
String pass = password.getText().toString();// password is android
object edit text
String sending = "login."+name + "." + pass + "." +"\n";
System.out.println("Sending Message: "+sending);
Socket clientsock = new Socket("192.168.136.6", 28000);
System.out.println("connect to the server...");
BufferedReader in = new BufferedReader(new
InputStreamReader(clientsock.getInputStream()));
DataOutputStream out = new DataOutputStream(clientsock.getOutputStream());
out.writeBytes(sending);
String line = in.readLine();
if (line.contentEquals("login success")){
Toast.makeText(this, "login success", Toast.LENGTH_SHORT).show();
Toast.makeText(this, "receive data from
server:"+clientsock.getInetAddress(), Toast.LENGTH_SHORT).show();
Intent i = new Intent(Client_layoutActivity.this, chat_wall.class);
startActivity(i);
} else {
Toast.makeText(this, "Error ", Toast.LENGTH_SHORT).show();
}
}
catch (Exception e)
{
System.out.println(e);
System.out.println("Connection Failed");
}
PAGE 2 : for Chat purpose
package com.willis.layout;
import java.io.*;
import java.net.*;
import android.widget.*;
import android.os.Bundle;
import android.view.*;
import android.view.View.OnClickListener;
import android.app.Activity;
import android.R.integer;
public class chat_wall extends Activity {
Client_layoutActivity ob;
public EditText chatroom;
public Button sendbtn;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.chatwall);
sendbtn = (Button)findViewById(R.id.sendmsg);
sendbtn.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
sendingmsg();
}
});
chatroom = (EditText)findViewById(R.id.chatroom);
}
public void sendingmsg (){
try
{
BufferedReader in = new BufferedReader(new
InputStreamReader(clientsock.getInputStream()));
DataOutputStream out = new DataOutputStream(clientsock.getOutputStream());
String name = chatroom.getText().toString();
String send = "chat."+name+". \n";
System.out.println("Sending message: "+send);
out.writeBytes(send);
String msgin = in.readLine();
Toast.makeText(this, msgin, Toast.LENGTH_SHORT).show();
}
catch (Exception e)
{
System.out.println(e);
System.out.println("Connection Failed");
}
}
}
As I understood what you want is to use the same socket for login and chat purposes. Instead of sending a Socket object with Intent between activities consider another options:
Define a class to manage opened sockets and create it's static instance or make it a Singleton.
Use a local Service as described in documentation. Define your sendingmsg and login methods in it and bind with Activity on it's onResume:
private ChatService mService;
...
#Override
protected void onResume() {
doBindService();
sendbtn.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
mService.sendingmsg();
}
});
}
#Override
protected void onPause() {
doUnbindService();
}