I am pretty new to Android studio, but so far, so good!
I have an android app which is up and running together with an websocket that is running on my computer.
So when both the websocket and the app is up and running.
I could write a message, which then gets printed in the websocket terminal, and the websocket also return an message which get printed on the list on my phone. Also, if the websocket receive a special message from the app, the webscocket also return a special message which is shown on and added on the list! Perfect! Isnt it?
Now what I am trying to do is that, the app listens to a special message/event from the websocket which in this case is "SuperSpecialMessage". If the websocket sends this specific message, the application is
going to react and do something. In this case make an Invisible buttong to be visible.
SO WHAT IS THE PROBLEM? The problem here is that I get the error:
"android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
Could someone help me please?
Here is my code:
public class WebSocketActivity extends AppCompatActivity {
private Socket mSocket;
private TextView mSocketStatus;
EditText messageTextview;
ListView the_list_view_on_layout;
ArrayAdapter arrayAdapter;
String TheMessages;
List<String> list_with_messages;
Button theExtraButton;
Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_websocket);
this.mHandler = new Handler();
m_Runnable.run();
// Elements that are created on the layout
mSocketStatus = (TextView)findViewById(R.id.socketStatus);
theExtraButton = (Button)findViewById(R.id.extraBtn);
theExtraButton.setVisibility(View.INVISIBLE);
the_list_view_on_layout = findViewById(R.id.listViewID);
messageTextview = findViewById(R.id.messageTextView);
String[] TheMessages = new String[]{};
list_with_messages = new ArrayList<String>(Arrays.asList(TheMessages));
//ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list_with_messages);
arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list_with_messages);
the_list_view_on_layout.setAdapter(arrayAdapter);
TheSocketClass app = (TheSocketClass) getApplication();
mSocket = app.getSocket();
mSocket.connect();
checkIfConnectedToSocket();
Button sendMessageToSocketServer = (Button) findViewById(R.id.sendMessageToSocketBtn);
sendMessageToSocketServer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String theTextWeWrite = messageTextview.getText().toString();
mSocket.emit("sendTextToServer", theTextWeWrite);
list_with_messages.add(theTextWeWrite);
//arrayAdapter.notifyDataSetChanged();
}
});
runOnUiThread(new Runnable() {
#Override
public void run() {
mSocket.on("ServerMessage", new Emitter.Listener() {
#Override
public void call(Object... args) {
JSONObject data = (JSONObject) args[0];
list_with_messages.add(data.toString());
//arrayAdapter.notifyDataSetChanged(); // I GET ERROR HERE
}
});
}
});
runOnUiThread(new Runnable() {
#Override
public void run() {
mSocket.on("SuperSpecialMessage", new Emitter.Listener() {
#Override
public void call(Object... args) {
theExtraButton.setVisibility(View.VISIBLE);
}
});
}
});
}
private void checkIfConnectedToSocket(){
mSocket.connect();
if (mSocket.connected() == true) {
mSocketStatus.setText("Connected to socket!");
mSocketStatus.setTextColor(Color.GREEN);
}
else if (!mSocket.connected()){
mSocketStatus.setText("Error connecting to socket!");
mSocketStatus.setTextColor(Color.RED);
}
}
}
Thank you.
EDIT: Solved!
First i made an method like this:
private void makeTheButtonVisible(){
runOnUiThread(new Runnable() {
#Override
public void run() {
theExtraButton.setVisibility(View.VISIBLE);
}
});
}
and when the app recieve the message, the method is called like this:
runOnUiThread(new Runnable() {
#Override
public void run() {
mSocket.on("SuperSpecialMessage", new Emitter.Listener() {
#Override
public void call(Object... args) {
makeTheButtonVisible();
}
});
}
});
Related
I have a problem with my countdown and I want to play an individual sound at the end. For this I have created a list. When the corresponding picture is clicked, the selection should be saved and played in this method:
private void loadMusic() {
if (timerStatus == TimerStatus.PAUSED)
new Runnable() {
#Override
public void run() {
playSound(R.raw.sound1);
}
public void playSound(int sound1) {
MediaPlayer mp = MediaPlayer.create(getBaseContext(), (R.raw.sound2));
mp.start();
}
}.run()
}
I tried to solve this with an array in the onCreate method, but this is "void" and therefore I don't get a return:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layout.sound);
final int[] selection = new int[1];
imageButton1 = findViewById(id.iv_bowl1);
imageButton1.setOnClickListener(v -> {
selection[0] = 1;
Toast.makeText(Sound.this, "1-click", Toast.LENGTH_SHORT).show();
});
return selection[0];
}
Thanks for help.
I'm new to android studio and I'm trying to create a simple weather app using OpenWeatherMap API. I am using OkHttp library to perform a GET request. All it does is take an input throught EditText and update the TextView on button click using a Button.
But the problem is, the TextView updates after two clicks on the Button. I want to update it right after the first click. So, how do I go over this?
Here is my code:
public class MainActivity extends AppCompatActivity {
private EditText cityName;
private TextView weatherData;
private TextView hiddenText;
private Button getBtn;
public String s = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
weatherData = (TextView)findViewById(R.id.weatherText);
getBtn = (Button)findViewById(R.id.getData);
cityName = (EditText)findViewById(R.id.cityName);
getBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getWeatherData(cityName.getText().toString());
weatherData.setText(s);
}
});
}
public void getWeatherData(String cityText){
String url = "https://api.openweathermap.org/data/2.5/weather?q=" + cityText + "&appid=ba45ceb57328448f7wd666hdc6d57aaf";
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
s = "Something went wrong!";
}
});
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if(response.isSuccessful()){
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
try{
s = response.body().string();
}
catch (IOException ioe){
s = "Error while getting JSON.";
}
}
});
}
}
});
}
}
I know that I can update the TextView in onResponse itself but I wanna know if it is possible to update it through onClickListener. If it's not possible, which method should I use? Any help would be appreciated.
you have to update text value in server response call back
public class MainActivity extends AppCompatActivity {
private EditText cityName;
private TextView weatherData;
private TextView hiddenText;
private Button getBtn;
public String s = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
weatherData = (TextView)findViewById(R.id.weatherText);
getBtn = (Button)findViewById(R.id.getData);
cityName = (EditText)findViewById(R.id.cityName);
getBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getWeatherData(cityName.getText().toString());
}
});
}
public void getWeatherData(String cityText){
String url = "https://api.openweathermap.org/data/2.5/weather?q=" + cityText + "&appid=ba45ceb57328448f7wd666hdc6d57aaf";
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
s = "Something went wrong!";
}
});
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if(response.isSuccessful()){
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
try{
s = response.body().string();
weatherData.setText(s);
}
catch (IOException ioe){
s = "Error while getting JSON.";
}
}
});
}
}
});
}
}
You are setting data on edit text on click after calling GET Request.
Update the textview with the data , once you get the response.
public class MainActivity extends AppCompatActivity {
private EditText cityName;
private TextView weatherData;
private TextView hiddenText;
private Button getBtn;
public String s = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
weatherData = (TextView)findViewById(R.id.weatherText);
getBtn = (Button)findViewById(R.id.getData);
cityName = (EditText)findViewById(R.id.cityName);
getBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getWeatherData(cityName.getText().toString());
}
});
}
public void getWeatherData(String cityText){
String url = "https://api.openweathermap.org/data/2.5/weather?q=" + cityText + "&appid=ba45ceb57328448f7wd666hdc6d57aaf";
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
s = "Something went wrong!";
}
});
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if(response.isSuccessful()){
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
try{
s = response.body().string();
weatherData.setText(s);
}
catch (IOException ioe){
s = "Error while getting JSON.";
}
}
});
}
}
});
}
}
I am trying to use realm database to display my api data. I want to display the company name, however the data is saids it is inserted in the log but cant seem to display the data on the UI. Here is the code..
Any help would be greatly appreciated with this problem. The variables are at the top and the problem is when it hits on success, ive written the code "write to DB", but it doesnt display the data but tells me the data has been inserted.
// Variables for the search input field and results TextViews.
private EditText mCompanyInput;
private TextView mTitleText;
private TextView mDescriptionText;
private TextView mOfficerText;
private TextView mTitleText1;
private TextView mDescriptionText1;
private OkHttpClient okHttpClient;
private static final String TAG = "MainActivity";
private Request request;
private String url = "https://api.companieshouse.gov.uk/search/companies?q=";
Button save;
TextView log;
Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCompanyInput = findViewById(R.id.companyInput);
log = findViewById(R.id.log);
mDescriptionText = findViewById(R.id.descriptionText);
mOfficerText = findViewById(R.id.officerText);
mTitleText1 = findViewById(R.id.titleText1);
mTitleText = findViewById(R.id.titleText);
mDescriptionText1 = findViewById(R.id.descriptionText1);
save = findViewById(R.id.searchButton);
realm = Realm.getDefaultInstance();
save.setOnClickListener(this);
}
public void onClick(View view){
okHttpClient = new OkHttpClient();
request = new Request.Builder().url(url).header("Authorization", "k6DNRbTp-AnQWn51JBz5VuPiTl8jv4_etdzoMyhf") .method("GET", null).build();
Log.d(TAG, "onClick:"+url);
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.i(TAG, e.getMessage());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG,response.body().string());
Log.d(TAG, "onResponse:"+response.code());
}
});
writeToDB(mCompanyInput.getText().toString().trim(), (mDescriptionText.getText().toString().trim()));
showData();
}
public void showData(){
RealmResults<Company> guests = realm.where(Company.class).findAll();
// Use an iterator to invite all guests
String op="";
for (Company guest : guests) {
op+=guest.getName();
op+=guest.getAppointments();
}
log.setText(op);
}
public void writeToDB(final String mTitleText1, final String mDescriptionText1){
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
Company user = new Company(mTitleText1, mDescriptionText1);
bgRealm.insert(user);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
writeToDB(mCompanyInput.getText().toString().trim(), (mOfficerText.getText().toString().trim()));
showData();
// Transaction was a success.
Log.v("Database", "Data Inserted");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
Log.e("Database", error.getMessage());
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
Why are you calling writeToDB() from the onSuccess() method? This will cause recursion and keep writing the same data into the realm. It's correct to call showData() from onSuccess(), but there's not much point calling it directly from onClick().
I think your problem though is that you're trying to update the UI from a thread: it's called from an async transaction thread and not the main thread. See this answer (and there are others you can find easily once you know the problem: Updating UI / runOnUiThread / final variables: How to write lean code that does UI updating when called from another Thread.
I push a web service call in my activity to a thread (shown below). The first time I do this in the activity it works fine (gets the text from my edittext and loads the service to get lat/lng data)
But when I click the back button (emulator) and try to fire off this thread a second time it blows up after the .start(); in my click handler. What might I be doing wrong here? thanks
private Thread getLocationByZip = new Thread() {
public void run() {
try {
EditText filterText = (EditText) findViewById(R.id.zipcode);
Editable zip = filterText.getText();
LocationLookupService locationLookupService = new LocationLookupService();
selectedLocation = locationLookupService.getLocationByZip(zip.toString());
locationHandler.post(launchFindWithLocationInfo);
} catch (Exception e) {
}
}
};
private Runnable launchFindWithLocationInfo = new Runnable() {
#Override
public void run() {
try {
Intent abc = new Intent(LocationLookup.this, FindWithLocation.class);
startActivity(abc);
} catch (Exception e) {
}
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.location);
locationHandler = new Handler();
findViewById(R.id.findbyzip).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getLocationByZip.start();
}
});
}
Update
After the great advice I went with an AsyncTask so if anyone finds this going forward the above thread/handler model looks something like the below as an asynctask
private class LocationLookupTask extends AsyncTask<String, Void, Location> {
private ProgressDialog dialog;
#Override
protected void onPreExecute() {
this.dialog = ProgressDialog.show(LocationLookup.this, "", "Loading...");
}
#Override
protected Location doInBackground(String... zips) {
Location selectedLocation = null;
for (String zip : zips) {
LocationLookupService locationLookupService = new LocationLookupService();
selectedLocation = locationLookupService.getLocationByZip(zip);
}
return selectedLocation;
}
#Override
protected void onPostExecute(Location location) {
this.dialog.dismiss();
((AppDelegate) getApplicationContext()).setSelectedLocation(location);
Intent abc = new Intent(LocationLookup.this, FindWithLocation.class);
startActivity(abc);
}
}
Now to call this in the onclick you would do this
findViewById(R.id.findbyzip).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
EditText filterText = (EditText) findViewById(R.id.zipcode);
Editable zip = filterText.getText();
LocationLookupTask task = new LocationLookupTask();
task.execute(new String[]{zip.toString()});
}
});
You can't start a thread twice:
It is never legal to start a thread more than once.
Taken from Thread.start().
So, you need to create a new thread and start that one.
You can not call twice the start method of the Thread class, I suggest you also control the logic within the method onCreate since according to the life cycle of an Activity that method may be called by Android lifecycle Activity Manager.
Furthermore i suggest you to avoid this approach and consider to use the AsyncTask provided by the Android SDK.
http://developer.android.com/reference/android/os/AsyncTask.html
If you really want to do this without creating a new class or using AsyncTask, you could just make a method to get a new Thread on each call:
private Thread getLocationByZip;
private void getLocation() {
getLocationByZip = new Thread() {
public void run() {
try {
EditText filterText = (EditText) findViewById(R.id.zipcode);
Editable zip = filterText.getText();
LocationLookupService locationLookupService = new LocationLookupService();
selectedLocation = locationLookupService.getLocationByZip(zip.toString());
locationHandler.post(launchFindWithLocationInfo);
} catch (Exception e) {
}
}
};
getLocationByZip.start();
}
Then replace getLocationByZip.start() in your code with getLocation(). However, I agree that an AsyncTask would be a better way to go, though this would work for you.
I get this error "Can't create handler inside thread that has not called Looper.prepare()"
Can you tell me how to fix it?
public class PaymentActivity extends BaseActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.payment);
final Button buttonBank = (Button) findViewById(R.id.buttonBank);
buttonBank.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
progressDialog = ProgressDialog.show(PaymentActivity.this, "",
"Redirecting to payment gateway...", true, true);
new Thread() {
public void run() {
try {
startPayment("Bank");
} catch (Exception e) {
alertDialog.setMessage(e.getMessage());
handler.sendEmptyMessage(1);
progressDialog.cancel();
}
}
}.start();
}
});
StartPayment Method:
private void startPayment(String id) {
Bundle b = getIntent().getExtras();
final Sail sail = b.getParcelable(Constant.SAIL);
final Intent bankIntent = new Intent(this, BankActivity.class);
try {
Reservation reservation = RestService.createReservation(
sail.getId(),
getSharedPreferences(Constant.PREF_NAME_CONTACT, 0));
bankIntent.putExtra(Constant.RESERVATION, reservation);
// <workingWithDB> Storing Reservation info in Database
DBAdapter db = new DBAdapter(this);
db.open();
#SuppressWarnings("unused")
long rowid;
rowid = db.insertRow(sail.getId(), sail.getFrom(),
sail.getTo(), sail.getShip(), sail.getDateFrom().getTime(),
sail.getPrice().toString(), reservation.getId().floatValue());
db.close();
// </workingWithDB>
String html = PaymentService.getRedirectHTML(id, reservation);
bankIntent.putExtra(Constant.BANK, html);
} catch (Exception e) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog alertDialog = builder.create();
alertDialog.setMessage(e.getMessage());
alertDialog.show();
}
startActivity(bankIntent);
}
You should know that when you try to modify your UI , the only thread who can do that is the UiThread.
So if you want to modify your UI in another thread, try to use the method: Activity.runOnUiThread(new Runnable);
Your code should be like this :
new Thread() {
public void run() {
YourActivity.this.runOnUiThread(new Runnable(){
#Override
public void run(){
try {
startPayment("Bank");//Edit,integrate this on the runOnUiThread
} catch (Exception e) {
alertDialog.setMessage(e.getMessage());
handler.sendEmptyMessage(1);
progressDialog.cancel();
}
});
}
}
}.start();
I assume you create a Handler in startPayment() method. You can't do that, as handlers can be created on th UI thread only. Just create it in your activity class.
Instead of new Thread() line, try giving
this.runOnUiThread(new Runnable() {
you cant change any UI in thread you can use runOnUIThread or AsyncTask for more detail about this click here
I've found that most thread handling can be replaced by AsyncTasks like this:
public class TestStuff extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonBank = (Button) findViewById(R.id.button);
buttonBank.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new StartPaymentAsyncTask(TestStuff.this).execute((Void []) null);
}
});
}
private class StartPaymentAsyncTask extends AsyncTask<Void, Void, String> {
private ProgressDialog dialog;
private final Context context;
public StartPaymentAsyncTask(Context context) {
this.context = context;
}
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(context);
// setup your dialog here
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setMessage(context.getString(R.string.doing_db_work));
dialog.setCancelable(false);
dialog.show();
}
#Override
protected String doInBackground(Void... ignored) {
String returnMessage = null;
try {
startPayment("Bank");
} catch (Exception e) {
returnMessage = e.getMessage();
}
return returnMessage;
}
#Override
protected void onPostExecute(String message) {
dialog.dismiss();
if (message != null) {
// process the error (show alert etc)
Log.e("StartPaymentAsyncTask", String.format("I received an error: %s", message));
} else {
Log.i("StartPaymentAsyncTask", "No problems");
}
}
}
public void startPayment(String string) throws Exception {
SystemClock.sleep(2000); // pause for 2 seconds for dialog
Log.i("PaymentStuff", "I am pretending to do some work");
throw new Exception("Oh dear, database error");
}
}
I pass in the Application Context to the Async so it can create dialogs from it.
The advantage of doing it this way is you know exactly which methods are run in your UI and which are in a separate background thread. Your main UI thread isn't delayed, and the separation into small async tasks is quite nice.
The code assumes your startPayment() method does nothing with the UI, and if it does, move it into the onPostExecute of the AsyncTask so it's done in the UI thread.
Try
final Handler handlerTimer = new Handler(Looper.getMainLooper());
handlerTimer.postDelayed(new Runnable() {
public void run() {
......
}
}, time_interval});