I have used Retrofit2 for file download. I am not able to update ProgressBar with progress value. I got progress value. So there is not issue. When I set the progress value to progress bar not reflected in UI.
I am talking about Progress Bar which is present inside RecyclerView Adapter.
Below is my retrofit call,
And this method will be called when clicking a item inside RecyclerView.
private void downloadFileFromServer(String otpapi, String userName, String password, String code, String vmFileName, String filePath, String vmFileSize, int position, CircularProgressBar circularProgress) {
GetDataForApiCall getDataForApiCall= RetrofitInstance.getRetrofit(url,otpapi,context).create(GetDataForApiCall.class);
Call<ResponseBody> downloadVoicemail= getDataForApiCall.downloadVoiceMail(userName,password,code,vmFileName);
this.circularProgressBar=circularProgress;
this.circularProgressBar.setIndeterminate(false);
this.circularProgressBar.setProgress(0);
this.circularProgressBar.setMax(100);
this.circularProgressBar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
AndroidLogger.log(5,"onClick","circularProgressBar onClick executed!!");
Toast.makeText(context,"cancel clicked",Toast.LENGTH_LONG).show();
cancelDownload = true;
}
});
downloadVoicemail.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
boolean downloadResult = writeResponseBodyToDisk(response.body(),vmFileSize,filePath);
if(downloadResult) {
Toast.makeText(context, "File downloaded", Toast.LENGTH_SHORT).show();
updateVoiceMailFilePath(position, filePath);
updateViews(position);
}else {
deleteVoiceMailFileFromLocalSystem(filePath);
updateViews(position);
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
private boolean writeResponseBodyToDisk( ResponseBody body, String fileSize, String filePath) {
try {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[8192];
//long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
long lengthOfFile = Long.parseLong( String.format( "%.0f",Double.parseDouble(fileSize )) ) * 1024;
AndroidLogger.log(5,TAG,"filesize"+fileSize + "length of file"+lengthOfFile);
inputStream = body.byteStream();
outputStream = new FileOutputStream(filePath);
while (true) {
int read = inputStream.read(fileReader);
if(cancelDownload){
inputStream.close();
return false;
}
if (read == -1) {
AndroidLogger.log(5,TAG,"-1 value so break");
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
if(lengthOfFile >0) {
AndroidLogger.log(5,TAG,"FileSize downloaded"+ fileSizeDownloaded);
int progress = (int) (fileSizeDownloaded * 100 / lengthOfFile);
AndroidLogger.log(5,TAG,"Length of file"+ lengthOfFile);
AndroidLogger.log(5,TAG,"Progress"+ progress);
this.circularProgressBar.setProgress(progress);
update(progress);
}
AndroidLogger.log(5,TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
Also I have tried Listener to update value because retrofit call done on some other thread. So for update UI I have used listener which is not helped.
I am using Retrofit2 for making API calls. So for updating UI in all Activities I had used interface listeners. This works perfect for all Activities. But when I tried the same thing in RecyclerView Adapter class, not able to update progress bar. Before calling api I had set Progress bar to 0 and max to 100.
Below case Works fine,
Circular ProgressBar before API call set to Zero
Circular ProgressBar after download Completed will change to a tick mark
Below is not Working,
Circular ProgressBar with indication of loading
NOTE: I am facing this issue only when used Retrofit2 to make API call. If I used normal HTTPUrlConnection for making API call inside a Asynctask, then progress loading working fine.
I have checked whether the progress updation is occurs on main thread or not by below code,
if(Looper.myLooper() == Looper.getMainLooper()) {
circularProgressBar.setProgress(progress);
}
The above if condition is satisfied. Eventhough Progress bar not updated.
Also I have tried below,
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
AndroidLogger.log(5,TAG,"Running on UI thread");
circularProgressBar.setProgress(progress);
}
};
mainHandler.post(myRunnable);
}
I have placed this inside writeResponseBodyToDisk method, inside while loop,
But it was called only two times and progress bar not updated.
I commented the part, where the progress bar loading will change to a tick mark. After that when I tried download,once download completed able to see 100 percent download completed in progress bar. Before progress percent updation not reflected.
Please Anybody help me out to solve this issue.
Thanks in advance.
The UI updates are needed to be happened in the UI thread. Setting the color from a background thread (i.e. AsyncTask) will not actually update the UI as this is not happening in the UI thread. There are several ways to update the progress color in the UI. I would recommend having an interface along with a callback function so that you can invoke that callback function to update the UI from the activity of fragment that implemented it. Here's a clarification.
Let us declare an interface first.
public interface UIUpdater {
void updateUI(int progressValue);
}
Now implement this interface in the activity or fragment where you want to update the UI.
public class MainActivity extends Activity implements UIUpdater {
#Override
public void updateUI(int progressValue) {
// Do the UI update here.
circularProgress.setProgress(progressValue); // Or anything else that you want to do.
}
}
You want to modify the constructor of initializing your AsyncTask to have the UIUpdater class to be passed as parameter.
public class YourAsyncTask extends AsyncTask<String, Void, String> {
UIUpdater listener;
public YourAsyncTask(UIUpdater listener) {
this.listener = listener;
}
}
So that you can call the AsyncTask from the activity/fragment using something as following.
YourAsyncTask myTask = new YourAsyncTask(this); // When you are passing this from activity, you are implicitly passing the interface that it implemented.
myTask.execute();
Now from the async task, while you are publishing the progress, invoke the listener function in order to update the UI using the UI thread of your activity/fragment.
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
try {
listener.updateUI(values[0]);
}
}
Hope you get the idea.
Your problem is not that you are using a RecyclerView, your problem is that you're not using it correctly.
It's is neither the adapter, nor the view (RecyclerView) nor even the ViewHolder's responsibility to determine anything here.
I assume you have a ViewHolder type with a progress bar in it.
I assume you have a ListAdapter<T, K> with the corresponding DiffUtilCallback implementation.
I assume progress is handled/reported elsewhere (in your repository, through a viewModel or Presenter, via a useCase, Interactor, or even the plain ViewModel).
Your adapter now has nothing to do but wait.
When progress is updated, you prepare the List you are displaying so the item whose progress has changed is updated.
Afterwards you submit this new list (with the new progress) to your adapter.
Your DiffUtil calculates this (it can be async too!)
Your RecyclerView is magically updated, no need to hack anything.
If any of these is not true, perform the necessary adjustments so your code can be properly tested and the concerns of each piece of code is better separated.
Think about it this way, imagine you had all that, and there was a small bug in the "progress value" you update. Which would be easier, search in the little function in your ViewModel or UseCase/interactor that transforms the Retrofit value into your <Thing>, or all over the place in your spaghetti code of callbacks?
Thank you all for trying to help me!!
This answer helped me to update Circular ProgressBar
https://stackoverflow.com/a/42119419/11630822
public static Retrofit getDownloadRetrofit(String baseUrl, DownloadVoicemailListener listener) {
return new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(getOkHttpDownloadClientBuilder(listener).build())
.build();
}
private static OkHttpClient.Builder getOkHttpDownloadClientBuilder(DownloadVoicemailListener listener) {
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder().connectionSpecs(Collections.singletonList(getConnectionSpec()));
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
if (!releaseMode) {
logging.level(HttpLoggingInterceptor.Level.BODY);
httpClientBuilder.addInterceptor(logging);
}
httpClientBuilder.connectTimeout(20, TimeUnit.SECONDS);
httpClientBuilder.writeTimeout(0, TimeUnit.SECONDS);
httpClientBuilder.readTimeout(5, TimeUnit.MINUTES);
httpClientBuilder.addInterceptor(new Interceptor() {
#NotNull
#Override
public Response intercept(#NotNull Interceptor.Chain chain) throws IOException {
if (listener == null) return chain.proceed(chain.request());
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(), listener))
.build();
}
});
return httpClientBuilder;
}
In Progressbody class,
public class ProgressResponseBody extends ResponseBody {
private final String TAG=ProgressResponseBody.class.getSimpleName();
private ResponseBody responseBody;
private BufferedSource bufferedSource;
public ProgressResponseBody(ResponseBody responseBody, DownloadVoicemailListener progressListener) {
this.responseBody = responseBody;
progressListener.readFile(responseBody);
}
#Override public MediaType contentType() {
return responseBody.contentType();
}
#Override public long contentLength() {
return responseBody.contentLength();
}
#NotNull
#Override public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
#Override public long read(Buffer sink, long byteCount) throws IOException {
return byteCount;
}
};
}
}
#Override
public void readFile(ResponseBody responseBody) {
boolean result = writeResponseBodyToDisk(responseBody);
}
private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
long fileSizeDownloaded = 0;
AndroidLogger.log(5, TAG, "File path" + filePath);
AndroidLogger.log(5, TAG, "File size" + fileSize);
long lengthOfFile = Long.parseLong(String.format("%.0f", Double.parseDouble(this.fileSize))) * 1024;
AndroidLogger.log(5, TAG, "filesize" + fileSize + "length of file" + lengthOfFile);
inputStream = body.byteStream();
outputStream = new FileOutputStream(this.filePath);
byte[] data = new byte[4096];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
if (cancelDownload) {
AndroidLogger.log(5,TAG,"Cancel download clicked");
inputStream.close();
return false;
}
total += count;
outputStream.write(data, 0, count);
fileSizeDownloaded += count;
if (lengthOfFile > 0) {
AndroidLogger.log(5, TAG, "FileSize downloaded" + fileSizeDownloaded);
int progress = (int) (fileSizeDownloaded * 100 / lengthOfFile);
AndroidLogger.log(5, TAG, "Length of file" + lengthOfFile);
AndroidLogger.log(5, TAG, "Progress" + progress);
((Activity) context).runOnUiThread(new Runnable() {
#Override
public void run() {
circularProgressBar.setProgress(progress);
}
});
}
}
AndroidLogger.log(5, TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
outputStream.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
Related
I am working with my diploma thesis and I have problem with communication Arduino -> Android using bluetooth.
This is my app:
Activity where I want to display distance to obstruction
In the TextView, I want to put data from Arduino with the distance and I need idea, I can't find something, how sending data from different sensors to different Views (for example front , back bumper, left and right).
Here you have arduino code:
#include <SoftwareSerial.h>
// Mid-back sensor
#define trigPinLeft 11
#define echoPinLeft 10
// Right-back sensor (looking from back)
#define trigPinRight 7
#define echoPinRight 6
SoftwareSerial btSerial = SoftwareSerial(0,1);
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
btSerial.begin(115200);
// Mid-back sensor
pinMode(trigPinLeft, OUTPUT);
pinMode(echoPinLeft, INPUT);
// Right-back sensor
pinMode(trigPinRight, OUTPUT);
pinMode(echoPinRight, INPUT);
}
void loop() {
// put your main code here, to run repeatedly:
long durationLeft, distanceLeft;
digitalWrite(trigPinLeft, LOW);
delayMicroseconds(5);
digitalWrite(trigPinLeft, HIGH);
delayMicroseconds(5);
digitalWrite(trigPinLeft, LOW);
durationLeft = pulseIn(echoPinLeft, HIGH);
distanceLeft = (durationLeft *0.034 / 2);
if (distanceLeft>=400 || distanceLeft<=18){
Serial.println("Out of range");
btSerial.println("Out of range");
}
else{
Serial.print("BACK LEFT: ");
Serial.print(distanceLeft);
Serial.println(" cm");
btSerial.println(distanceLeft + "cm");
}
//delayMicroseconds(1);
long durationRight, distanceRight;
digitalWrite(trigPinRight, LOW);
delayMicroseconds(5);
digitalWrite(trigPinRight, HIGH);
delayMicroseconds(10);
digitalWrite(trigPinRight, LOW);
durationRight = pulseIn(echoPinRight, HIGH);
distanceRight = (durationRight *0.034 / 2);
if (distanceRight>=400 || distanceRight<=18){
Serial.println("Out of range");
btSerial.println("Out of range");
}
else{
Serial.print("BACK RIGHT: ");
Serial.print(distanceRight);
Serial.println(" cm");
btSerial.println(distanceRight + "cm");
}
delay(10000);
}
This is Android-Studio code where I want one time get data from Arduino to one textView (not working):
public class HomeActivity extends AppCompatActivity {
ImageView imageView;
TextView rearLeft,rearMid,rearRight,frontLeft,frontMid,frontRight;
public static final String PREFS_NAME = "ParkingPrefsFile";
public static final String FIRST_TIME = "firstTime";
public static final String IMAGE_VAL = "imageValue";
private BluetoothServerSocket mmServerSocket;
private BluetoothAdapter mAdapter;
private BluetoothDevice mDevice;
private static final UUID MY_UUID = UUID.fromString("5951c386-e2e7-485d-aebe-a32eec769f7b");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
imageView = (ImageView) findViewById(R.id.carView);
rearLeft = (TextView) findViewById(R.id.rearLeftText);
rearMid = (TextView) findViewById(R.id.rearMidText);
rearRight = (TextView) findViewById(R.id.rearRightText);
frontLeft = (TextView) findViewById(R.id.frontLeftText);
frontMid = (TextView) findViewById(R.id.frontMidText);
frontRight = (TextView) findViewById(R.id.frontRightText);
BluetoothSocket socket = null;
mAdapter = BluetoothAdapter.getDefaultAdapter();
SharedPreferences sharedPreferences = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
boolean firstTime = sharedPreferences.getBoolean(FIRST_TIME,false);
if(!firstTime){
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(FIRST_TIME,true);
int image = getIntent().getIntExtra("image", R.drawable.ic_default);
imageView.setImageResource(image);
editor.putString(IMAGE_VAL, String.valueOf(getIntent().getIntExtra("image",R.drawable.ic_default)));
editor.commit();
}
else {
SharedPreferences.Editor editor = sharedPreferences.edit();
int image = getIntent().getIntExtra("image", Integer.parseInt(sharedPreferences.getString(IMAGE_VAL,null )));
imageView.setImageResource(image);
editor.putString(IMAGE_VAL, String.valueOf(getIntent().getIntExtra("image",image)));
editor.commit();
}
/*try{
//mmServerSocket = mAdapter.listenUsingInsecureRfcommWithServiceRecord("My Adapter", MY_UUID);
mmServerSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}*/
/*byte[] buffer = new byte[256];
int bytes;
try{
mmServerSocket.close();
InputStream inputStream = null;
OutputStream outputStream = null;
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
DataInputStream mmInputStream = new DataInputStream(inputStream);
DataOutputStream mmOutputStream = new DataOutputStream(outputStream);
bytes = mmInputStream.read(buffer);
String readMessage = new String(buffer, 0 , bytes);
rearLeft.setText(readMessage);
} catch (IOException e) {
e.printStackTrace();
}*/
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.home_activity_menu,menu);
return true;
}
}
I compiled and runed app, but after I saw splashscreen, I had white screen and lag on the phone.
I hadn't errors in Android-Studio.
Thanks for help.
You clearly seem to have threading issue.
In your HomeActivity you have commented out the code that allows to open a Bluetooth Server on your Phone so that your Arduino device may connect to it, supplying the relevant UUID and other relevant parameters in RFCOM mode.
That code, however, is network-related and blocking and therefore should never be executed on the app UI Thread which is responsible to handle all UI tasks such as displaying views, monitoring user interactions (touch events) etc.
This is the reason why your phone displays a white screen with lag.
So you should definitely execute the Bluetooth logic on a separate thread.
I'd propose the following class to handle all bluetooth-related logic. It's very straightforward.
public class BluetoothHandler {
private final Handler handler;
private final BluetoothAdapter bluetoothAdapter;
#Nullable
private BluetoothServerSocket serverSocket;
private BluetoothSocket bluetoothSocket;
public BluetoothHandler(Context context) {
final HandlerThread ht = new HandlerThread("Bluetooth Handler Thread", Thread.NORM_PRIORITY);
ht.start(); // starting thread
this.handler = new Handler(ht.getLooper());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
this.bluetoothAdapter = ((BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
} else {
this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
}
public void startBluetoothServer() {
// execute code in our background worker thread
this.handler.post(new Runnable() {
#Override
public void run() {
try {
serverSocket = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("name", "your UUID");
bluetoothSocket = serverSocket.accept(); // will wait as long as possible (no timeout) so there is blocking
// do your logic to retrieve in and out put streams to read / write data from / to your Arduino device
} catch (IOException ioe) {
}
}
});
}
#AnyThread
public void writeData(byte[] data) {
// remember, all network operation are to be executed in a background thread
this.handler.post(new Runnable() {
#Override
public void run() {
// write data in output stream
}
});
}
#AnyThread
public void readData(OnDataReadCallback callback) {
// remember, all network operation are to be executed in a background thread
this.handler.post(new Runnable() {
#Override
public void run() {
// read data and notify via callback.
}
});
}
#AnyThread // should be call from your Activity onDestroy() to clear resources and avoid memory leaks.
public void termainte() {
try {
if (serverSocket != null) {
serverSocket.close();
}
if (bluetoothSocket != null) {
bluetoothSocket.close();
}
} catch (IOException ioe) {
}
this.handler.getLooper().quit(); // will no longer be usable. Basically, this class instance is now trash.
}
public interface OnDataReadCallback {
#WorkerThread // watch out if you need to update some view, user your Activity#runOnUiThread method !
void onDataRead(byte[] data);
}
}
I am reading a inputstream chunck by chunk and tryng to set read chunk to a textview from a Thread class but text is only getting printed after completion of while loop below is my code :
class SendFileThread extends Thread
{
Handler mHandler;
FileInputStream instream;
SendFileThread(Handler h, FileInputStream stream )
{
mHandler = h;
instream = stream;
this.setPriority(Thread.MAX_PRIORITY);
}
#Override
public void run()
{
final StringBuilder result = new StringBuilder();
Message msg;
byte [] usbdata = new byte[64];
int readcount = 0;
sendByteCount = 0;
int val = 0;
if(instream != null)
{
try
{
readcount = instream.read(usbdata,0,64);
}
catch (IOException e){e.printStackTrace();}
while(readcount > 0)
{
sendData(readcount, usbdata);
sendByteCount += readcount;
try
{
readcount = instream.read(usbdata,0,64);
if(readcount == -1){
pending = false;
//send_file = false;
setDefaultsBoo("pending",pending, J2xxHyperTerm.this);
}else{
result.append(new String(usbdata, 0, readcount));
}
runOnUiThread(new Runnable() {
#Override
public void run() {
readText.setMovementMethod(new ScrollingMovementMethod());
readText.setText(result.toString());
//scrollView.smoothScrollTo(0, readText.getHeight() + 30);
}
});
}
catch (IOException e){e.printStackTrace();}
}
}
}
}
text is getting set to textview only after all the work is done.
After work is done, try this -
Message msg = Message.obtain(handler, what, arg1, arg2, "text");
// what= some int identififer, lets say 1101
msg.sendToTarget();
in the listening activity implement Handler.Callback. You will have to implement handleMessage
#Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 1104: // the what identifier you set in message
String text = (String) msg.obj;
textView.setText(text)
break;
}
return false;
}
You could use an AsyncTask for this use-case, as the ability to update views on the UI thread is included right out of the box.
http://developer.android.com/reference/android/os/AsyncTask.html
This is from the usage example provided, with comments to show you where the main thread action happens.
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
// Done in the background, on a separate thread
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
// This part of the loop publishes the progress to onProgressUpdate(..)
publishProgress((int) ((i / (float) count) * 100));
if (isCancelled()) break;
}
return totalSize;
}
// Called on the main thread whenever publishProgress(..) is called
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
// Also called on the main thread, after the background task is finished
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Hi you can use this method in your thread :
runOnUiThread(new Runnable() {
#Override
public void run() {
//update your textview
}
});
You could use an EventBus, to communicate between your thread to your activity.
This is their github project. you can implement it and also enable communication between every Android app component. For example, like in your case, between thread and activity.
I have gone through Demo's but I tried with the QuickStart example in which a image is uploaded. but I am not getting how to upload a audio file in which i will give path to my files or Intent Picker to select the file.I am using createFile() method
how to upload a audio file to my drive?
I need to convert it to any streams ?
why google has made this so much complicated just to upload file?
How to Synch Drive files ?
How to stream (play audio file from drive)?
The Below code just upload file which contains nothing.
public class MainActivity extends Activity implements ConnectionCallbacks,
OnConnectionFailedListener {
private static final String TAG = "android-drive-quickstart";
//private static final int REQUEST_CODE_CAPTURE_IMAGE = 1;
private static final int REQUEST_CODE_CREATOR = 2;
private static final int REQUEST_CODE_RESOLUTION = 3;
private static final int PICKFILE_RESULT_CODE = 1;
private static Uri fileUri;
private ContentsResult result;
private GoogleApiClient mGoogleApiClient;
private Bitmap mBitmapToSave;
#Override
protected void onResume() {
super.onResume();
if (mGoogleApiClient == null) {
// Create the API client and bind it to an instance variable.
// We use this instance as the callback for connection and connection
// failures.
// Since no account name is passed, the user is prompted to choose.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
// Connect the client. Once connected, the camera is launched.
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Called whenever the API client fails to connect.
Log.i(TAG, "GoogleApiClient connection failed: " + result.toString());
if (!result.hasResolution()) {
// show the localized error dialog.
showToast("Error in on connection failed");
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show();
return;
}
// The failure has a resolution. Resolve it.
// Called typically when the app is not yet authorized, and an
// authorization
// dialog is displayed to the user.
try {
result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
} catch (SendIntentException e) {
showToast("error"+e.toString());
Log.e(TAG, "Exception while starting resolution activity", e);
}
}
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "API client connected.");
showToast("Inside Connected");
result = Drive.DriveApi.newContents(mGoogleApiClient).await();
showToast(""+result.getContents().toString());
OutputStream outputStream = result.getContents().getOutputStream();
ByteArrayOutputStream bitmapStream = new ByteArrayOutputStream();
//java.io.File fileContent = new java.io.File(fileUri.getPath());
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("New file")
.setMimeType("audio/MP3")
.setStarred(true).build();
showToast("meta data created");
DriveFileResult dfres= Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFile(getGoogleApiClient(), changeSet, result.getContents())
.await();
showToast("await() complete");
if (!result.getStatus().isSuccess()) {
showToast("Error while trying to create the file");
return;
}
showToast("Created a file: " + dfres.getDriveFile().getDriveId());
}
private void saveFileToDrive()
{
}
#Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == REQUEST_CODE_RESOLUTION && resultCode == RESULT_OK) {
mGoogleApiClient.connect();
showToast("Connected");
}
}
#Override
protected void onPause() {
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onPause();
}
public void showToast(final String toast) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), toast, Toast.LENGTH_SHORT).show();
}
});
}
public GoogleApiClient getGoogleApiClient() {
return mGoogleApiClient;
}
#Override
public void onConnectionSuspended(int cause) {
Log.i(TAG, "GoogleApiClient connection suspended");
}
}
Try this:
**
* An AsyncTask that maintains a connected client.
*/
public abstract class ApiClientAsyncTask<Params, Progress, Result>
extends AsyncTask<Params, Progress, Result> {
private GoogleApiClient mClient;
public ApiClientAsyncTask(Context context) {
GoogleApiClient.Builder builder = new GoogleApiClient.Builder(context)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE);
mClient = builder.build();
}
#Override
protected final Result doInBackground(Params... params) {
Log.d("TAG", "in background");
final CountDownLatch latch = new CountDownLatch(1);
mClient.registerConnectionCallbacks(new ConnectionCallbacks() {
#Override
public void onConnectionSuspended(int cause) {
}
#Override
public void onConnected(Bundle arg0) {
latch.countDown();
}
});
mClient.registerConnectionFailedListener(new OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult arg0) {
latch.countDown();
}
});
mClient.connect();
try {
latch.await();
} catch (InterruptedException e) {
return null;
}
if (!mClient.isConnected()) {
return null;
}
try {
return doInBackgroundConnected(params);
} finally {
mClient.disconnect();
}
}
/**
* Override this method to perform a computation on a background thread, while the client is
* connected.
*/
protected abstract Result doInBackgroundConnected(Params... params);
/**
* Gets the GoogleApliClient owned by this async task.
*/
protected GoogleApiClient getGoogleApiClient() {
return mClient;
}
}
Class to save file:
/**
* An async task that creates a new text file by creating new contents and
* metadata entities on user's root folder. A number of blocking tasks are
* performed serially in a thread. Each time, await() is called on the
* result which blocks until the request has been completed.
*/
public class CreateFileAsyncTask extends ApiClientAsyncTask<String, Void, Metadata>
{
public CreateFileAsyncTask(Context context)
{
super(context);
}
#Override
protected Metadata doInBackgroundConnected(String... arg0)
{
// First we start by creating a new contents, and blocking on the
// result by calling await().
DriveApi.ContentsResult contentsResult = Drive.DriveApi.newContents(getGoogleApiClient()).await();
if (!contentsResult.getStatus().isSuccess()) {
// We failed, stop the task and return.
return null;
}
//file to save in drive
String pathFile = arg0[0];
File file = new File(pathFile);
// Read the contents and open its output stream for writing, then
// write a short message.
Contents originalContents = contentsResult.getContents();
OutputStream os = originalContents.getOutputStream();
try
{
InputStream dbInputStream = new FileInputStream(file);
byte[] buffer = new byte[1024];
int length;
int counter = 0;
while((length = dbInputStream.read(buffer)) > 0)
{
++counter;
os.write(buffer, 0, length);
}
dbInputStream.close();
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
return null;
}
// Create the metadata for the new file including title and MIME
// type.
MetadataChangeSet originalMetadata = new MetadataChangeSet.Builder()
.setTitle(file.getName())
.setMimeType("application/x-sqlite3").build();
// Create the file in the root folder, again calling await() to
// block until the request finishes.
DriveFolder rootFolder = Drive.DriveApi.getRootFolder(getGoogleApiClient());
DriveFolder.DriveFileResult fileResult = rootFolder.createFile(
getGoogleApiClient(), originalMetadata, originalContents).await();
if (!fileResult.getStatus().isSuccess()) {
// We failed, stop the task and return.
return null;
}
// Finally, fetch the metadata for the newly created file, again
// calling await to block until the request finishes.
DriveResource.MetadataResult metadataResult = fileResult.getDriveFile()
.getMetadata(getGoogleApiClient())
.await();
if (!metadataResult.getStatus().isSuccess()) {
// We failed, stop the task and return.
return null;
}
// We succeeded, return the newly created metadata.
return metadataResult.getMetadata();
}
#Override
protected void onPostExecute(Metadata result)
{
super.onPostExecute(result);
if (result == null)
{
// The creation failed somehow, so show a message.
App.showAppMsg(getActivity(),"Error while creating the file.",Style.ALERT);
return;
}
// The creation succeeded, show a message.
App.showAppMsg(getActivity(),"File created: " + result.getDriveId(),Style.CONFIRM);
}
}
I haven't played with audio files, but in general, the Google Drive Android API (GDAA) does not deal with audio files per say. You just create a file, set metadata and stuff binary content in it. Look at the code here (plus some readme blah blah here). You'll find a code line
byte[] buff = ("written on: " + _dfn.getName()).getBytes();
if (null == _gc.creatFile(fldr, name, MIMETEXT, buff)) return;
there, that produces byte[] buffer and creates a file with text MIME type. So, try to use it, just replace the MIME type and stuff the 'buff' with your audio stream. I do it successfully with JPEG binaries.
There is also GooApiClnt wrapper class there that handles most of the basic GDAA functions. Don't try to code this way at work, though, it may get you fired :-).
Good luck.
In your onConnected method you create the new file, but you never put any new content in it. You create the new content in this line:
result = Drive.DriveApi.newContents(mGoogleApiClient).await();
Than you get a hold of it's output stream in this line:
OutputStream outputStream = result.getContents().getOutputStream();
And than you create an empty byte array output stream in this line:
ByteArrayOutputStream bitmapStream = new ByteArrayOutputStream();
But you never fill this 'bitmapStream' with any content, and worst: you never write it to your content's 'outputStream'.
What you should do next is write your audio file's contents to 'bitmapStream' something like this:
InputStream in = file.getInputStream(/*you need to get the file's path and put it here*/ "some_audio_file.mp3");
int singleByte;
while((singleByte = in.read()) != -1){
bitmapStream.write(b);
}
Now you'd have your file's content inside 'bitmapStrea' and you can write it to the new content's 'outputStream' like this:
outputStream.write(bitmapStream.toByteArray());
Than you do the 'MetadataChangeSet' stuff and you should be fine.
Some advices:
1. It is not a good practice to do I/O operations like file or network activities (or file AND network activities in your case) on the main thread. Better use an AsyncTask to do it in a background thread.
Don't call your ByteArrayOutputStream instance 'bitmapStream' if you use it to upload an audio file.
Here's an example of a class that uses an AsyncTask to upload an image (and guess what I called the ByteArrayOutputStream... right - 'bitmapStream'):
public class TakePhotoActivity extends Activity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
/**
* Request code for auto Google Play Services error resolution.
*/
protected static final int REQUEST_CODE_RESOLUTION = 1;
private static final String TAG = "TakePhotoActivity";
private static final String KEY_IN_RESOLUTION = "is_in_resolution";
private static final int REQUEST_CODE_CREATOR = 2;
/**
* Google API client.
*/
private GoogleApiClient mGoogleApiClient;
/**
* Receives the new file's contents and executes the editor AsyncTask
*/
private ResultCallback<DriveApi.ContentsResult> mSaveFileCallback = new ResultCallback<DriveApi.ContentsResult>() {
#Override
public void onResult(DriveApi.ContentsResult contentsResult) {
EditFileAsyncTask editFileAsyncTask = new EditFileAsyncTask();
editFileAsyncTask.execute(contentsResult);
}
};
/**
* Determines if the client is in a resolution state, and
* waiting for resolution intent to return.
*/
private boolean mIsInResolution;
private Bitmap mBitmapToSave;
/**
* Called when the activity is starting. Restores the activity state.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_take_menu_photo);
if (savedInstanceState != null) {
mIsInResolution = savedInstanceState.getBoolean(KEY_IN_RESOLUTION, false);
}
try {
InputStream inputStream = getAssets().open("some_image.jpg");
mBitmapToSave = BitmapFactory.decodeStream(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Called when the Activity is made visible.
* A connection to Play Services need to be initiated as
* soon as the activity is visible. Registers {#code ConnectionCallbacks}
* and {#code OnConnectionFailedListener} on the
* activities itself.
*/
#Override
protected void onStart() {
super.onStart();
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
// Optionally, add additional APIs and scopes if required.
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
Log.d("test", "connect()");
mGoogleApiClient.connect();
}
/**
* Called when activity gets invisible. Connection to Play Services needs to
* be disconnected as soon as an activity is invisible.
*/
#Override
protected void onStop() {
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onStop();
}
/**
* Saves the resolution state.
*/
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_IN_RESOLUTION, mIsInResolution);
}
/**
* Handles Google Play Services resolution callbacks.
*/
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_RESOLUTION:
retryConnecting();
break;
}
}
private void retryConnecting() {
mIsInResolution = false;
if (!mGoogleApiClient.isConnecting()) {
Log.d("test", "connect()");
mGoogleApiClient.connect();
}
}
/**
* Called when {#code mGoogleApiClient} is connected.
*/
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "GoogleApiClient connected");
// TODO: Start making API requests.
if (mBitmapToSave != null) {
saveFileToDrive();
}
}
/**
* Called when {#code mGoogleApiClient} connection is suspended.
*/
#Override
public void onConnectionSuspended(int cause) {
Log.i(TAG, "GoogleApiClient connection suspended");
retryConnecting();
}
/**
* Called when {#code mGoogleApiClient} is trying to connect but failed.
* Handle {#code result.getResolution()} if there is a resolution
* available.
*/
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "GoogleApiClient connection failed: " + result.toString());
if (!result.hasResolution()) {
// Show a localized error dialog.
GooglePlayServicesUtil.getErrorDialog(
result.getErrorCode(), this, 0, new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
retryConnecting();
}
}
).show();
return;
}
// If there is an existing resolution error being displayed or a resolution
// activity has started before, do nothing and wait for resolution
// progress to be completed.
if (mIsInResolution) {
return;
}
mIsInResolution = true;
try {
result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
} catch (SendIntentException e) {
Log.e(TAG, "Exception while starting resolution activity", e);
retryConnecting();
}
}
private void saveFileToDrive() {
Log.i(TAG, "Creating new contents.");
Drive.DriveApi.newContents(mGoogleApiClient).setResultCallback(mSaveFileCallback);
}
private void showMessage(String message) {
Log.i(TAG, message);
// Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
private class EditFileAsyncTask extends AsyncTask<DriveApi.ContentsResult, Void, Boolean> {
#Override
protected Boolean doInBackground(DriveApi.ContentsResult... params) {
DriveApi.ContentsResult contentsResult = params[0];
if (!contentsResult.getStatus().isSuccess()) {
showMessage("Failed to create new contents.");
return false;
}
showMessage("New contents created.");
OutputStream outputStream = contentsResult.getContents().getOutputStream();
ByteArrayOutputStream bitmapStream = new ByteArrayOutputStream();
mBitmapToSave.compress(Bitmap.CompressFormat.PNG, 100, bitmapStream);
try {
outputStream.write(bitmapStream.toByteArray());
} catch (IOException e) {
showMessage("Unable to write file contents.");
e.printStackTrace();
}
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setMimeType("image/jpeg")
.setTitle("some_image.jpg")
.build();
IntentSender intentSender = Drive.DriveApi
.newCreateFileActivityBuilder()
.setInitialMetadata(metadataChangeSet)
.setInitialContents(contentsResult.getContents())
.build(mGoogleApiClient);
try {
startIntentSenderForResult(intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0);
} catch (SendIntentException e) {
showMessage("Failed to launch file chooser.");
e.printStackTrace();
}
return true;
}
#Override
protected void onPostExecute(Boolean result) {
if (!result) {
showMessage("Error while editing contents");
return;
}
showMessage("Successfully edited contents");
}
}
}
By the way, most of the code in this class was auto-generated by Android Studio, because when I created the project I marked the initial class to be a google services class.
It,s simple. After I trying hard, I found the solution.
private String mFileName = null;
File folder = new File(Environment.getExternalStorageDirectory() +
"/FolderFile");
if (!folder.exists()) {
folder.mkdir();
}
mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
mFileName += "/FolderFile/a.mp3";
After the audio is recorded. You must
buildGoogleSignInClient()
createFileWithIntent(mFileName);
private void createFileWithIntent(String I) {
final String audio = I;
final Task<DriveFolder> rootFolderTask = getDriveResourceClient().getRootFolder();
final Task<DriveContents> createContentsTask = getDriveResourceClient().createContents();
Tasks.whenAll(rootFolderTask, createContentsTask)
.continueWithTask(new Continuation<Void, Task<DriveFile>>() {
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
public Task<DriveFile> then(#NonNull Task<Void> task) throws Exception {
DriveFolder PASTA = rootFolderTask.getResult();
DriveContents DADOS = createContentsTask.getResult();
File file = new File(audio);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
FileInputStream fis = new FileInputStream(file);
for (int readNum; (readNum = fis.read(buf)) != -1;) {
baos.write(buf, 0, readNum);
}
OutputStream outputStream = DADOS.getOutputStream();
outputStream.write(baos.toByteArray());
MetadataChangeSet TIPO = new MetadataChangeSet.Builder()
.setMimeType("audio/mp3")
.setTitle("audio.mp3")
.setStarred(true)
.build();
return getDriveResourceClient().createFile(PASTA, TIPO, DADOS);
}
});
}
SO currently i have an AsyncTask class that runs and POST's data to my server when I click a button(which works great).
What im trying to do now is handle what happens when the user is not connected to the internet. so i have set up these classes to notify the app when internet has connected so that the data can be sent automatically to the server.
AsyncTask class(inner class)
private class HttpAsyncTask extends AsyncTask<String, Void, String> {
ProgressDialog dialog = new ProgressDialog(getActivity());
final AlertDialog finishedDialog = new AlertDialog.Builder(getActivity())
.setCancelable(false)
.setPositiveButton(android.R.string.ok, null)
.create();
#Override
protected String doInBackground(String... urls) {
onProgressUpdate("Uploading Data...");
return POST(urls[0]);
}
#Override
protected void onPreExecute() {
this.dialog.show();
finishedDialog.setOnShowListener(new DialogInterface.OnShowListener(){
#Override
public void onShow(DialogInterface dialog) {
Button b = finishedDialog.getButton(AlertDialog.BUTTON_POSITIVE);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// navigate to match summary.....
finishedDialog.dismiss();
}
});
}
});
}
protected void onProgressUpdate(String msg) {
dialog.setMessage(msg);
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result) {
if (result != ""){
finishedDialog.setTitle("Upload Complete!");
finishedDialog.setMessage("Data Sent Successfully");
finishedDialog.show();
dialog.dismiss();
editor.clear();
editor.commit();
//Toast.makeText(getActivity().getBaseContext(), result, Toast.LENGTH_LONG).show();
}else
{
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
finishedDialog.setTitle("Upload Failed!");
finishedDialog.setMessage("Data Will Automatically Be Uploaded When Internet Connection Is Available");
finishedDialog.show();
dialog.dismiss();
}}, 1000);
setFlag(true);
}
}
}
public static boolean getFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String POST(String url){
InputStream inputStream = null;
String result = "";
try {
// 1. create HttpClient
HttpClient httpclient = new DefaultHttpClient();
// 2. make POST request to the given URL
HttpPost httpPost = new HttpPost(url);
if(adapter.updateNeeded()){
JSONObject main = new JSONObject(exmaplePrefs.getString("jsonString", "cant find json"));
JSONObject dbUpdates = new JSONObject(exmaplePrefs.getString("ChangesJSON", "cant find Changejson"));
main.put("Team_Updates", dbUpdates);
json = main.toString();
}else{
json = exmaplePrefs.getString("jsonString", "cant find json");
// String json = "{\"twitter\":\"test\",\"country\":\"test\",\"name\":\"test\"}";
}
// 5. set json to StringEntity
StringEntity se = new StringEntity(json);
se.setContentType("application/json;charset=UTF-8");
// 6. set httpPost Entity
httpPost.setEntity(se);
// 7. Set some headers to inform server about the type of the content
// httpPost.setHeader("Accept", "application/json");
// httpPost.setHeader("Content-type", "application/json");
// httpPost.setHeader("json", json);
// 8. Execute POST request to the given URL
HttpResponse httpResponse = httpclient.execute(httpPost);
// 9. receive response as inputStream
inputStream = httpResponse.getEntity().getContent();
String status = httpResponse.getStatusLine().toString();
// 10. convert inputstream to string
if (!status.equals("HTTP/1.1 500 Internal Server Error")){
if(inputStream != null){
result = convertInputStreamToString(inputStream);
}
else{
result = "Did not work!";
}
}else{
System.out.println("500 Error");
}
} catch (Exception e) {
Log.d("InputStream", e.getLocalizedMessage());
System.out.println("eerroorr "+e);
}
// 11. return result
System.out.println(result);
return result;
}
private static String convertInputStreamToString(InputStream inputStream) throws IOException{
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream));
String line = "";
String result = "";
while((line = bufferedReader.readLine()) != null)
result += line;
inputStream.close();
return result;
}
}
NetworkUtil class
public class NetworkUtil {
public static int TYPE_WIFI = 1;
public static int TYPE_MOBILE = 2;
public static int TYPE_NOT_CONNECTED = 0;
public static int getConnectivityStatus(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (null != activeNetwork) {
if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
return TYPE_WIFI;
if(activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
return TYPE_MOBILE;
}
return TYPE_NOT_CONNECTED;
}
public static String getConnectivityStatusString(Context context) {
int conn = NetworkUtil.getConnectivityStatus(context);
String status = null;
if (conn == NetworkUtil.TYPE_WIFI) {
status = "Wifi enabled";
} else if (conn == NetworkUtil.TYPE_MOBILE) {
status = "Mobile data enabled";
} else if (conn == NetworkUtil.TYPE_NOT_CONNECTED) {
status = "Not connected to Internet";
}
return status;
}
}
BroadcastReceiver class
public class NetworkChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
intent.getExtras();
String status = NetworkUtil.getConnectivityStatusString(context);
Toast.makeText(context, status, Toast.LENGTH_LONG).show();
if(MatchFragment.getFlag()){
//send data
}
}
}
So in the BroadcastReceiver class I check the flag that gets set to true when the app attempts to send data but there is not internet (onPostExecute in AsyncTask Class).
so what want to do is some how call the POST method. do i have to create a new Async task class? Im a bit stumped here .
Thanks
Using AsyncTask in BroadcastReceiver is a bad practice.
You should use Service because Android OS may kill your process or onReceive() may run to completion before asyncTask will return result, so there is no guarantee you will get the expected result.
You shouldn't use AsyncTask in Broadcast Receiver because the system can kill your process after returning from onReceive method (if there is no any active service or activity).
Proof link
Official documentation recommends IntentService for such cases (see paragraph about Broadcast Receivers).
The other answers are not correct according to Google's documentation. The Broadcast Receivers developer guide explicitly calls out that you can use AsyncTasks from BroadcastReceivers if you call goAsync() first and report the status to the pending result in the AsyncTask
For this reason, you should not start long running background threads from a broadcast receiver. After onReceive(), the system can kill the process at any time to reclaim memory, and in doing so, it terminates the spawned thread running in the process. To avoid this, you should either call goAsync() (if you want a little more time to process the broadcast in a background thread) or schedule a JobService from the receiver using the JobScheduler, so the system knows that the process continues to perform active work.
And later it clarifies how much time you actually get:
Calling goAsync() in your receiver's onReceive() method and passing
the BroadcastReceiver.PendingResult to a background thread. This keeps
the broadcast active after returning from onReceive(). However, even
with this approach the system expects you to finish with the broadcast
very quickly (under 10 seconds). It does allow you to move work to
another thread to avoid glitching the main thread.
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
#Override
public void onReceive(final Context context, final Intent intent) {
final PendingResult pendingResult = goAsync();
AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
#Override
protected String doInBackground(String... params) {
StringBuilder sb = new StringBuilder();
sb.append("Action: " + intent.getAction() + "\n");
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
Log.d(TAG, log);
// Must call finish() so the BroadcastReceiver can be recycled.
pendingResult.finish();
return data;
}
};
asyncTask.execute();
}
}
I've an Android app that has a login capabilities, and the login box has a TextView that displays messages to the user when trying to login(ie. wrong name, wrong pass, etc..).
I have two methods, the first one check if the fields is filled or not, and if filled it redirects the app to the second method that will check the user/pass from the local server.
the problem is when resetting the text in the second method, as when i set the text at the first method everything is OK, but when changing it in the second method it doesn't change, I can set it like million times in the first method and everything going well, another thing is when i set the text at the first time from the second method it works perfectly.
Hint1: this first method is the onClick method of an OnClickListener.
Hint2: the printed log is prented like million times in the logcat so the while condition verified
public class Login extends Activity {
public EditText user, pw;
public TextView errorMessage;
private static String response = null;
private static String data;
the first method :
public void onClick(View v) {
if (v == login) {
String userName = Login.this.user.getText().toString();
String Password = Login.this.pw.getText().toString();
if (userName.equals(null_string)
|| Password.equals(null_string)) {
errorMessage.setText(R.string.request);
} else {
protocol = protocol_login;
boolean status = false;
try {
status = checkLogin(userName, Password);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
if (status) {
Intent intent = new Intent(v.getContext(),
MainPage.class);
go(intent);
} else {
errorMessage.setText(R.string.login_error);
}
}
}
}
the second method:
private String connect(String data) throws UnknownHostException,
IOException, JSONException {
setData(data);
Thread connect = new Thread(new ConnectToServer(getData()));
connect.start();
while (response == null) {
System.out.println("waiting");
errorMessage.setText(R.string.waiting);
}
return response;
}
}
Your problem lies in the fact that in the second method you are trying to update the GUI while actually being a second thread.
U can use the runOnUIThread method
Activity.runOnUiThread(new Runnable() {
#Override
public void run() {
errorMessage.setText(R.string.waiting);
}
});
while (response == null) {
System.out.println("waiting");
}
U also shouldn't set the text in a while-loop if the text isn't changing so you don't use unnecessary resources.
I'm not sure what exactly is causing your problem (you are blocking the UI thread somewhere), but there are better ways of getting a response from the server. You are essentially synchronously checking for an asynchronous response (below), because you are continuously polling whether response is not null.
Android has a useful class called AsyncTask. You give an AsyncTask some work to do on a background thread (what ConnectToServer(..) does), and when it is done, another method on the AsyncTask (called onPostExecute(..)) is called. The benefit of this over your approach is that it handles all the threading for you, and doesn't poll. There is also a method onPreExecute() which you would set your waiting text in.
N.B. checking synchronously for an asynchronous response
What I mean by this is that the response can come back at any time (asynchronously), yet you are checking for it at any point you can (synchronously). This is going to waste valuable resources on the CPU - you should get the response to tell you when it is finished rather than continually ask whether it is.
First, these two string variables are declared globally:
String userName,Password
Try this easy Asyntask method:
private class SetDataOfWebService extends AsyncTask<Void, Void,Boolean> {
ProgressDialog pDialog;
boolean success = false;
ConnectivityManager connectivity;
#Override
protected void onPreExecute() {
pDialog = new ProgressDialog(MailSettings.this);
pDialog.setMessage("Please Wait..");
pDialog.show();
}
#Override
protected Boolean doInBackground(Void... params) {
if (isNetworkAvailable()) {
success = true;
if (userName.length()>0 || Password.length()>0) {
status = checkLogin(userName, Password);
}
else
{
errorMessage.setText(R.string.request);
}
} else {
success = false;
}
return success;
}
#Override
protected void onPostExecute(Boolean result) {
if (pDialog != null && pDialog.isShowing())
pDialog.dismiss();
if (result) {
if (status) {
Intent intent = new Intent(v.getContext(),
MainPage.class);
go(intent);
} else {
errorMessage.setText(R.string.login_error);
}
} else {
return;
}
}
public boolean isNetworkAvailable() {
connectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null) {
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
Log.i("Class", info[i].getState().toString());
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
}
}
return false;
}
}
////Calling This Function On Button CLick Like This
userName = Login.this.user.getText().toString();
Password = Login.this.pw.getText().toString();
new SetDataOfWebService().execute();