How to modify style of android "force close" window? - android

Does anybody know how to modify the style of the "force close" window (FC dialog)?
I found a custom ROM with a nice picture at the dialog. At what place can I find the popup?

Try to override uncaughtException,
#Override
public void uncaughtException(Thread thread, Throwable e) {
e.printStackTrace();
try {
// create your custom dialog
displayErrorMessageToast();
Thread.sleep(3500);
} catch (Exception e1) {
Log.e(TAG, "Error: ", e1);
}
finally
{
killApplicationProcess(e);
}
}
for more info:
https://groups.google.com/forum/?fromgroups=#!topic/android-developers/2iUH1Knz8gw

Try the approach given in this blog
Copying here for quick reference:
Android UncaughtExceptionHandler
Implemented by objects that want to handle cases where a thread is being terminated by an uncaught exception. Upon such termination, the handler is notified of the terminating thread and causal exception. If there is no explicit handler set then the thread's group is the default handler.
Below i wrote the code user can send some bug report to Developer when application crashed.
Activity Code
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.ViewFlipper;
/**
*
* #author vijayakumar
*
*/
public class AndroidMADQAActivity extends Activity {
ViewFlipper flipper;
TextView textView = null;
Throwable throwable;
UnCaughtException un = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(new UnCaughtException(AndroidMADQAActivity.this));
Integer[] items = { R.drawable.a, R.drawable.e,R.drawable.d,R.drawable.c};
setContentView(R.layout.main);
textView.setText("Helloo Error Welcome");
}
}
UnCaughtException.java
package com.madqa;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Date;
import java.util.Locale;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.os.Looper;
import android.os.StatFs;
import android.util.Log;
/**
* {#link UncaughtExceptionHandler} send an e-mail with
* some debug information to the developer.
*
* #author VIJAYAKUMAR
*/
public class UnCaughtException implements UncaughtExceptionHandler {
private static final String RECIPIENT = "iamvijayakumar#gmail.com";
private Thread.UncaughtExceptionHandler previousHandler;
private Context context;
private static Context context1;
public UnCaughtException(Context ctx) {
context = ctx;
context1 = ctx;
}
private StatFs getStatFs() {
File path = Environment.getDataDirectory();
return new StatFs(path.getPath());
}
private long getAvailableInternalMemorySize(StatFs stat) {
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
return availableBlocks * blockSize;
}
private long getTotalInternalMemorySize(StatFs stat) {
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
return totalBlocks * blockSize;
}
private void addInformation(StringBuilder message) {
message.append("Locale: ").append(Locale.getDefault()).append('\n');
try {
PackageManager pm = context.getPackageManager();
PackageInfo pi;
pi = pm.getPackageInfo(context.getPackageName(), 0);
message.append("Version: ").append(pi.versionName).append('\n');
message.append("Package: ").append(pi.packageName).append('\n');
} catch (Exception e) {
Log.e("CustomExceptionHandler", "Error", e);
message.append("Could not get Version information for ").append(
context.getPackageName());
}
message.append("Phone Model: ").append(android.os.Build.MODEL).append(
'\n');
message.append("Android Version: ").append(
android.os.Build.VERSION.RELEASE).append('\n');
message.append("Board: ").append(android.os.Build.BOARD).append('\n');
message.append("Brand: ").append(android.os.Build.BRAND).append('\n');
message.append("Device: ").append(android.os.Build.DEVICE).append('\n');
message.append("Host: ").append(android.os.Build.HOST).append('\n');
message.append("ID: ").append(android.os.Build.ID).append('\n');
message.append("Model: ").append(android.os.Build.MODEL).append('\n');
message.append("Product: ").append(android.os.Build.PRODUCT).append(
'\n');
message.append("Type: ").append(android.os.Build.TYPE).append('\n');
StatFs stat = getStatFs();
message.append("Total Internal memory: ").append(
getTotalInternalMemorySize(stat)).append('\n');
message.append("Available Internal memory: ").append(
getAvailableInternalMemorySize(stat)).append('\n');
}
public void uncaughtException(Thread t, Throwable e) {
try {
StringBuilder report = new StringBuilder();
Date curDate = new Date();
report.append("Error Report collected on : ").append(curDate.toString()).append('\n').append('\n');
report.append("Informations :").append('\n');
addInformation(report);
report.append('\n').append('\n');
report.append("Stack:\n");
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
e.printStackTrace(printWriter);
report.append(result.toString());
printWriter.close();
report.append('\n');
report.append("**** End of current Report ***");
Log.e(UnCaughtException.class.getName(),
"Error while sendErrorMail"+report);
sendErrorMail(report);
} catch (Throwable ignore) {
Log.e(UnCaughtException.class.getName(),
"Error while sending error e-mail", ignore);
}
// previousHandler.uncaughtException(t, e);
}
/**
* This method for call alert dialog when application crashed!
* #author vijayakumar
*/
public void sendErrorMail(final StringBuilder errorContent) {
final AlertDialog.Builder builder= new AlertDialog.Builder(context);
new Thread(){
#Override
public void run() {
Looper.prepare();
builder.setTitle("Sorry...!");
builder.create();
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
System.exit(0);
}
});
builder.setPositiveButton("Report", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
String subject = "Your App crashed! Fix it!";
StringBuilder body = new StringBuilder("Yoddle");
body.append('\n').append('\n');
body.append(errorContent).append('\n').append('\n');
// sendIntent.setType("text/plain");
sendIntent.setType("message/rfc822");
sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { RECIPIENT });
sendIntent.putExtra(Intent.EXTRA_TEXT, body.toString());
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
sendIntent.setType("message/rfc822");
// context.startActivity(Intent.createChooser(sendIntent, "Error Report"));
context1.startActivity(sendIntent);
System.exit(0);
}
});
builder.setMessage("Unfortunately,This application has stopped");
builder.show();
Looper.loop();
}
}.start();
}
}

Related

App crashing due to two differnet Async Tasks?

So I have been working on this code for a while now trying to implement Google Visions into my prior app that displays an image from pixabay then tells me the tags of the photo.I had both the google vision app and pixabay app work just fine on their own. In this new version it should give me tags and the labels found by Google Visions but, whenever I activate the UP command on the sensors it crashes.
Here is my code:
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.speech.tts.TextToSpeech;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.vision.v1.Vision;
import com.google.api.services.vision.v1.VisionRequest;
import com.google.api.services.vision.v1.VisionRequestInitializer;
import com.google.api.services.vision.v1.model.AnnotateImageRequest;
import com.google.api.services.vision.v1.model.BatchAnnotateImagesRequest;
import com.google.api.services.vision.v1.model.BatchAnnotateImagesResponse;
import com.google.api.services.vision.v1.model.EntityAnnotation;
import com.google.api.services.vision.v1.model.Feature;
import com.google.api.services.vision.v1.model.Image;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static edu.ggc.lutz.recipe.pixabaysamplerwalkthrough.R.id.tvLabels;
import static edu.ggc.lutz.recipe.pixabaysamplerwalkthrough.R.id.tvTags;
public class MainActivity extends AppCompatActivity implements SensorEventListener {
public static final String PIXABAY = "Pixabay";
private ImageView imageView;
private static PixabayQueryResult result;
private String tags;
long numberOfHits;
long selected;
float[] gravity = new float[3];
float[] accel = new float[3];
private static final float ALPHA = 0.80f; // weighing factor used by the low pass filter
private static final String TAG = "OMNI";
private static final float VERTICAL_TOL = 0.3f;
private SensorManager manager;
private long lastUpdate;
private MediaPlayer popPlayer;
private MediaPlayer backgroundPlayer;
private TextToSpeech tts;
private TextView[] tvGravity;
private TextView[] tvAcceleration;
private boolean isDown = false;
private boolean isUp = false;
private static final String CLOUD_VISION_API_KEY = "AIzaSyCt35MZjvD_3ynTbYmeUuBFyMbYrjXUmzs";
private static final String ANDROID_CERT_HEADER = "X-Android-Cert";
private static final String ANDROID_PACKAGE_HEADER = "X-Android-Package";
private static final String TAGgoogle = MainActivity.class.getSimpleName();
private TextView pixtags;
private TextView googlelab;
private String urlString;
private Bitmap bitmapT;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
pixtags= (TextView) findViewById(tvTags);
googlelab= (TextView) findViewById(tvLabels);
/* FloatingActionButton fab = (FloatingActionButton) findViewById(fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
PixabayFetchTask task = new PixabayFetchTask();
String service = "https://pixabay.com/api/";
String key = "5535853-23bc4a5e307cd5d1a5e16ebcc";
String query_params = "&editor_choice=true&safesearch=true&image_type=photo";
String urlString = service + "?key=" + key + query_params;
task.execute(urlString);
}
});*/
imageView= (ImageView) findViewById(R.id.imageView);
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
int result1=0;
if(status == TextToSpeech.SUCCESS) {
result1 = tts.setLanguage(Locale.US);
}
if( result1 == TextToSpeech.LANG_MISSING_DATA || result1== TextToSpeech.LANG_NOT_SUPPORTED){
Log.e("TTS", "This Language is not supported");
}
else
{
Log.e("TTS", "Inizalization Failed");
}
}
});
//////////////////////////
manager = (SensorManager) getSystemService(SENSOR_SERVICE);
lastUpdate = System.currentTimeMillis();
backgroundPlayer = MediaPlayer.create(this, R.raw.mistsoftime4tmono);
//////////////////////////
//callCloudVision("https://pixabay.com/get/eb36b90f2df1053ed95c4518b7494395e67fe7d604b0154892f2c67da7eabc_640.jpg");
}
///////////////////////////////
#Override
protected void onResume() {
super.onResume();
manager.registerListener(this, manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_UI);
backgroundPlayer.start();
}
//////////////////////////////
#Override
protected void onPause() {
super.onPause();
manager.unregisterListener(this);
backgroundPlayer.pause();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
Intent intent = new Intent(this, About.class);
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
public static long getRandomLong(long minimum, long maximum)
{
return (long) (Math.random()* (maximum- minimum))+ minimum;
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
public void onSensorChanged(SensorEvent event) {
gravity[0] = lowPass(event.values[0], gravity[0]);
gravity[1] = lowPass(event.values[1], gravity[1]);
gravity[2] = lowPass(event.values[2], gravity[2]);
accel[0] = highPass(event.values[0], accel[0]);
accel[1] = highPass(event.values[1], accel[1]);
accel[2] = highPass(event.values[2], accel[2]);
long actualTime = System.currentTimeMillis();
if (actualTime - lastUpdate > 100) {
if (inRange(gravity[2], -9.81f, VERTICAL_TOL)) {
Log.i(TAG, "Down");
if (!isDown) {
Vibrator v = (Vibrator) this.getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(500);
PixabayFetchTask task = new PixabayFetchTask();
String service = "https://pixabay.com/api/";
String key = "5535853-23bc4a5e307cd5d1a5e16ebcc";
String query_params = "&editor_choice=true&safesearch=true&image_type=photo";
urlString = service + "?key=" + key + query_params;
task.execute(urlString);
backgroundPlayer.setVolume(0.1f, 0.1f);
tts.speak("The device is pointing down", TextToSpeech.QUEUE_FLUSH, null);
backgroundPlayer.setVolume(1.0f, 1.0f);
isDown = true;
isUp = false;
}
} else if (inRange(gravity[2], 9.81f, VERTICAL_TOL)) {
if (!isUp) {
try {
callCloudVision(urlString);
} catch (IOException e) {
e.printStackTrace();
}
backgroundPlayer.setVolume(0.1f, 0.1f);
Log.i(TAG, "Up");
tags= (String) result.getTags((int)selected);
pixtags.setText("Tags: "+tags, null);
/* Snackbar.make(imageView, tags, Snackbar.LENGTH_LONG)
.setAction("Action", null).show();*/
tts.speak(tags.toString(), TextToSpeech.QUEUE_ADD, null);
//tts.speak("up", TextToSpeech.QUEUE_FLUSH, null);
backgroundPlayer.setVolume(1.0f, 1.0f);
isUp = true;
isDown = false;
}
} else {
Log.i(TAG, "In between");
//isDown = false; // Rubbish!
//isUp = false;
}
lastUpdate = actualTime;
}
}
private boolean inRange(float value, float target, float tol) {
return value >= target-tol && value <= target+tol;
}
// de-emphasize transient forces
private float lowPass(float current, float gravity) {
return current * (1-ALPHA) + gravity * ALPHA; // ALPHA indicates the influence of past observations
}
// de-emphasize constant forces
private float highPass(float current, float gravity) {
return current - gravity;
}
class PixabayFetchTask extends AsyncTask<String, Void, PixabayQueryResult> {
/**
* Override this method to perform a computation on a background thread. The
* specified parameters are the parameters passed to {#link #execute}
* by the caller of this task.
* <p>
* This method can call {#link #publishProgress} to publish updates
* on the UI thread.
*
* #param params The parameters of the task.
* #return A result, defined by the subclass of this task.
* #see #onPreExecute()
* #see #onPostExecute
* #see #publishProgress
*/
#Override
protected PixabayQueryResult doInBackground(String... params) {
Log.v(PIXABAY,"String[0] =" + params[0]);
if(result==null || result.isExpired()) {
try {
String line;
URL u = new URL(params[0]);
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
InputStream in = new BufferedInputStream(conn.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder json = new StringBuilder();
while ((line = reader.readLine()) != null) json.append(line);
result = new PixabayQueryResult(json.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* <p>Runs on the UI thread after {#link #doInBackground}. The
* specified result is the value returned by {#link #doInBackground}.</p>
* <p>
* <p>This method won't be invoked if the task was cancelled.</p>
*
* #param bitmap The result of the operation computed by {#link #doInBackground}.
* #see #onPreExecute
* #see #doInBackground
* #see #onCancelled(Object)
*/
#Override
protected void onPostExecute(PixabayQueryResult result) {
super.onPostExecute(result);
numberOfHits= result.size();
selected = getRandomLong(0, numberOfHits);
Bitmap bitmap= result.getBitmap((int)selected);
imageView.setImageBitmap(bitmap);
/* try {
callCloudVision(urlString);
} catch (IOException e) {
e.printStackTrace();
}*/
}
}
private void callCloudVision(final String loc) throws IOException {
// Switch text to loading
googlelab.setText(R.string.loading_message);
// Do the real work in an async task, because we need to use the network anyway
new AsyncTask<Object, Bitmap, String>() {
#Override
protected String doInBackground(Object... params) {
try {
HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
VisionRequestInitializer requestInitializer =
new VisionRequestInitializer(CLOUD_VISION_API_KEY) {
/**
* We override this so we can inject important identifying fields into the HTTP
* headers. This enables use of a restricted cloud platform API key.
*/
#Override
protected void initializeVisionRequest(VisionRequest<?> visionRequest)
throws IOException {
super.initializeVisionRequest(visionRequest);
String packageName = getPackageName();
visionRequest.getRequestHeaders().set(ANDROID_PACKAGE_HEADER, packageName);
String sig = PackageManagerUtils.getSignature(getPackageManager(), packageName);
visionRequest.getRequestHeaders().set(ANDROID_CERT_HEADER, sig);
}
};
Vision.Builder builder = new Vision.Builder(httpTransport, jsonFactory, null);
builder.setVisionRequestInitializer(requestInitializer);
Vision vision = builder.build();
BatchAnnotateImagesRequest batchAnnotateImagesRequest =
new BatchAnnotateImagesRequest();
batchAnnotateImagesRequest.setRequests(new ArrayList<AnnotateImageRequest>() {{
AnnotateImageRequest annotateImageRequest = new AnnotateImageRequest();
Bitmap bitmap = null;
try {
InputStream stream = new URL(loc).openStream();
bitmap = BitmapFactory.decodeStream(stream);
publishProgress(bitmap);
} catch (IOException e) {
e.printStackTrace();
}
// Add the image
Image base64EncodedImage = new Image();
// Convert the bitmap to a JPEG
// Just in case it's a format that Android understands but Cloud Vision
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, byteArrayOutputStream);
byte[] imageBytes = byteArrayOutputStream.toByteArray();
// Base64 encode the JPEG
base64EncodedImage.encodeContent(imageBytes);
annotateImageRequest.setImage(base64EncodedImage);
// add the features we want
annotateImageRequest.setFeatures(new ArrayList<Feature>() {{
Feature labelDetection = new Feature();
labelDetection.setType("LABEL_DETECTION");
labelDetection.setMaxResults(10);
add(labelDetection);
}});
// Add the list of one thing to the request
add(annotateImageRequest);
}});
Vision.Images.Annotate annotateRequest =
vision.images().annotate(batchAnnotateImagesRequest);
// Due to a bug: requests to Vision API containing large images fail when GZipped.
annotateRequest.setDisableGZipContent(true);
Log.d(TAGgoogle, "created Cloud Vision request object, sending request");
BatchAnnotateImagesResponse response = annotateRequest.execute();
return convertResponseToString(response);
} catch (GoogleJsonResponseException e) {
Log.d(TAGgoogle, "failed to make API request because " + e.getContent());
} catch (IOException e) {
Log.d(TAGgoogle, "failed to make API request because of other IOException " +
e.getMessage());
}
return "Cloud Vision API request failed. Check logs for details.";
}
/**
* Runs on the UI thread after {#link #publishProgress} is invoked.
* The specified values are the values passed to {#link #publishProgress}.
*
* #param bitmaps The values indicating progress.
* #see #publishProgress
* #see #doInBackground
*/
#Override
protected void onProgressUpdate(Bitmap... bitmaps) {
super.onProgressUpdate(bitmaps);
imageView.setImageBitmap(bitmaps[0]);
}
protected void onPostExecute(String result) {
googlelab.setText(result);
}
}.execute();
}
private String convertResponseToString(BatchAnnotateImagesResponse response) {
String message = "Labels:\n\n";
List<EntityAnnotation> labels = response.getResponses().get(0).getLabelAnnotations();
if (labels != null) {
for (EntityAnnotation label : labels) {
message += String.format(Locale.US, "%.3f: %s", label.getScore(), label.getDescription());
message += "\n";
}
} else {
message += "nothing";
}
return message;
}
}
Here is the it gives me error:
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #3
Process: edu.ggc.lutz.recipe.pixabaysamplerwalkthrough, PID: 21223 java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:309)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat, int, java.io.OutputStream)' on a null object reference
at edu.ggc.lutz.recipe.pixabaysamplerwalkthrough.MainActivity$2$2.<init>(MainActivity.java:419)
at edu.ggc.lutz.recipe.pixabaysamplerwalkthrough.MainActivity$2.doInBackground(MainActivity.java:400)
at edu.ggc.lutz.recipe.pixabaysamplerwalkthrough.MainActivity$2.doInBackground(MainActivity.java:366)
at android.os.AsyncTask$2.call(AsyncTask.java:295) at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
at java.lang.Thread.run(Thread.java:818)
There is another error that says something about the text to speech but I think that is the result of this error.
I believe it has something to do with running two different Async tasks at the same time overloading it or that fact a null value it getting passed in causing the error.

Prompt user to send app crash info to Airbrake

I am using airbrake to catch crash reports
As soon as the app crashes I can see the report on the dashboard, but there is no alert in my app to ask the user to "Send Error Report". How can I enable airbrake alerts to ask the user to send the report for the crash. Could not find anything related to this on airbrake documentation. Thanks.
Reading the AirbrakeNotifier code you linked to reveals that it automatically sends crashes as soon as possible.
You would have to modify this class to ask the user whether to send crashes, e.g. the next time you initialise the Airbrake class.
For example, HockeyApp has an option to do this in its CrashManager class.
Though I prefer to always automatically send crash reports to the server; there's usually no reason to bother users with this request.
If you want to send crash report to be send you can make class to handle UnCaughtException that will handle all crash exception. You can write code like this:
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Date;
import java.util.Locale;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.Looper;
import android.os.StatFs;
import android.util.Log;
public class UnCaughtException implements UncaughtExceptionHandler {
private Context context;
private static Context context1;
public UnCaughtException(Context ctx) {
context = ctx;
context1 = ctx;
}
private StatFs getStatFs() {
File path = Environment.getDataDirectory();
return new StatFs(path.getPath());
}
private long getAvailableInternalMemorySize(StatFs stat) {
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
return availableBlocks * blockSize;
}
private long getTotalInternalMemorySize(StatFs stat) {
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
return totalBlocks * blockSize;
}
private void addInformation(StringBuilder message) {
message.append("Locale: ").append(Locale.getDefault()).append('\n');
try {
PackageManager pm = context.getPackageManager();
PackageInfo pi;
pi = pm.getPackageInfo(context.getPackageName(), 0);
message.append("Version: ").append(pi.versionName).append('\n');
message.append("Package: ").append(pi.packageName).append('\n');
} catch (Exception e) {
Log.e("CustomExceptionHandler", "Error", e);
message.append("Could not get Version information for ").append(
context.getPackageName());
}
message.append("Phone Model: ").append(android.os.Build.MODEL)
.append('\n');
message.append("Android Version: ")
.append(android.os.Build.VERSION.RELEASE).append('\n');
message.append("Board: ").append(android.os.Build.BOARD).append('\n');
message.append("Brand: ").append(android.os.Build.BRAND).append('\n');
message.append("Device: ").append(android.os.Build.DEVICE).append('\n');
message.append("Host: ").append(android.os.Build.HOST).append('\n');
message.append("ID: ").append(android.os.Build.ID).append('\n');
message.append("Model: ").append(android.os.Build.MODEL).append('\n');
message.append("Product: ").append(android.os.Build.PRODUCT)
.append('\n');
message.append("Type: ").append(android.os.Build.TYPE).append('\n');
StatFs stat = getStatFs();
message.append("Total Internal memory: ")
.append(getTotalInternalMemorySize(stat)).append('\n');
message.append("Available Internal memory: ")
.append(getAvailableInternalMemorySize(stat)).append('\n');
}
public void uncaughtException(Thread t, Throwable e) {
try {
StringBuilder report = new StringBuilder();
Date curDate = new Date();
report.append("Error Report collected on : ")
.append(curDate.toString()).append('\n').append('\n');
report.append("Informations :").append('\n');
addInformation(report);
report.append('\n').append('\n');
report.append("Stack:\n");
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
e.printStackTrace(printWriter);
report.append(result.toString());
printWriter.close();
report.append('\n');
report.append("**** End of current Report ***");
Log.e(UnCaughtException.class.getName(),
"Error while sendErrorMail" + report);
sendErrorMail(report);
} catch (Throwable ignore) {
Log.e(UnCaughtException.class.getName(),
"Error while sending error e-mail", ignore);
}
}
/**
* This method for call alert dialog when application crashed!
*/
public void sendErrorMail(final StringBuilder errorContent) {
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
new Thread() {
#Override
public void run() {
Looper.prepare();
builder.setTitle("Sorry...!");
builder.create();
builder.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
System.exit(0);
}
});
builder.setPositiveButton("Report",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
Intent sendIntent = new Intent(
Intent.ACTION_SEND);
String subject = "Your App crashed! Fix it!";
StringBuilder body = new StringBuilder("Yoddle");
body.append('\n').append('\n');
body.append(errorContent).append('\n')
.append('\n');
// sendIntent.setType("text/plain");
sendIntent.setType("message/rfc822");
sendIntent
.putExtra(
Intent.EXTRA_EMAIL,
new String[] { "your_email#domain.com" });
sendIntent.putExtra(Intent.EXTRA_TEXT,
body.toString());
sendIntent.putExtra(Intent.EXTRA_SUBJECT,
subject);
sendIntent.setType("message/rfc822");
context1.startActivity(sendIntent);
System.exit(0);
}
});
builder.setMessage("Oops,Your application has crashed.");
builder.show();
Looper.loop();
}
}.start();
}
}
And declare this exception class in every Class file to get log of each crash. Following code you can use to add in Activity or any other class:
Thread.setDefaultUncaughtExceptionHandler(new UnCaughtException(
YourActivity.this));

Android - Crash report notification

How can I show notification that shows when the application crashed (or service crashed), and if the user click on it, it send the StackTrace by email?
I saw some applications that do that.
Make your own class by extending the interface UncaughtExceptionHandler
public class UnCaughtException implements UncaughtExceptionHandler
{
private Context context;
private static Context context1;
public UnCaughtException(Context ctx)
{
context = ctx;
context1 = ctx;
}
private StatFs getStatFs()
{
File path = Environment.getDataDirectory();
return new StatFs(path.getPath());
}
private long getAvailableInternalMemorySize(StatFs stat)
{
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
return availableBlocks * blockSize;
}
private long getTotalInternalMemorySize(StatFs stat)
{
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
return totalBlocks * blockSize;
}
private void addInformation(StringBuilder message)
{
message.append("Locale: ").append(Locale.getDefault()).append('\n');
try
{
PackageManager pm = context.getPackageManager();
PackageInfo pi;
pi = pm.getPackageInfo(context.getPackageName(), 0);
message.append("Version: ").append(pi.versionName).append('\n');
message.append("Package: ").append(pi.packageName).append('\n');
}
catch ( Exception e )
{
Log.e("CustomExceptionHandler", "Error", e);
message.append("Could not get Version information for ").append(context.getPackageName());
}
message.append("Phone Model: ").append(android.os.Build.MODEL).append('\n');
message.append("Android Version: ").append(android.os.Build.VERSION.RELEASE).append('\n');
message.append("Board: ").append(android.os.Build.BOARD).append('\n');
message.append("Brand: ").append(android.os.Build.BRAND).append('\n');
message.append("Device: ").append(android.os.Build.DEVICE).append('\n');
message.append("Host: ").append(android.os.Build.HOST).append('\n');
message.append("ID: ").append(android.os.Build.ID).append('\n');
message.append("Model: ").append(android.os.Build.MODEL).append('\n');
message.append("Product: ").append(android.os.Build.PRODUCT).append('\n');
message.append("Type: ").append(android.os.Build.TYPE).append('\n');
StatFs stat = getStatFs();
message.append("Total Internal memory: ").append(getTotalInternalMemorySize(stat)).append('\n');
message.append("Available Internal memory: ").append(getAvailableInternalMemorySize(stat)).append('\n');
}
#Override
public void uncaughtException(Thread t, Throwable e)
{
try
{
StringBuilder report = new StringBuilder();
Date curDate = new Date();
report.append("Error Report collected on : ").append(curDate.toString()).append('\n').append('\n');
report.append("Informations :").append('\n');
addInformation(report);
report.append('\n').append('\n');
report.append("Stack:\n");
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
e.printStackTrace(printWriter);
report.append(result.toString());
printWriter.close();
report.append('\n');
report.append("**** End of current Report ***");
Log.e(UnCaughtException.class.getName(), "Error while sendErrorMail" + report);
sendErrorMail(report);
}
catch ( Throwable ignore )
{
Log.e(UnCaughtException.class.getName(), "Error while sending error e-mail", ignore);
}
}
/**
* This method for call alert dialog when application crashed!
*/
public void sendErrorMail(final StringBuilder errorContent)
{
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
new Thread()
{
#Override
public void run()
{
Looper.prepare();
builder.setTitle("Sorry...!");
builder.create();
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
System.exit(0);
}
});
builder.setPositiveButton("Report", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
Intent sendIntent = new Intent(Intent.ACTION_SEND);
String subject = "Your App crashed! Fix it!";
StringBuilder body = new StringBuilder("Yoddle");
body.append('\n').append('\n');
body.append(errorContent).append('\n').append('\n');
// sendIntent.setType("text/plain");
sendIntent.setType("message/rfc822");
sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { "yourmail#domain.com" });
sendIntent.putExtra(Intent.EXTRA_TEXT, body.toString());
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
sendIntent.setType("message/rfc822");
context1.startActivity(sendIntent);
System.exit(0);
}
});
builder.setMessage("Oops,Your application has crashed");
builder.show();
Looper.loop();
}
}.start();
}
}
Set the CustomExceptionHandler as the DefaultExceptionHandler in your MainActivity
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Thread.setDefaultUncaughtExceptionHandler(new UnCaughtException(MainActivity.this));
int y = 5 / 0;
}
}
Note :
The above example code sends a email with the crash data. You can modify it to meet your need.
you can use ACRA library. From the doc
Acra catches exceptions, retrieves lots of context data and send them
to a Google Spreadsheet... or whatever backend you prefer.
or Crittercism. It supports
Error Monitoring
App Monitoring

Android FTP Server

I am using the following code to make the android device a ftp server (Android Internal storage). I am getting the exception of os.android.NetworkOnMainThread. I have tried to put the onStart code in the AsyncTask but app never executes and crashes on launch. Any help regarding the ftp server on Android will be great as i have no idea how to get it working.
Here is the MainActivity Code
package com.googlecode.simpleftp;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
public class FTPServer extends Activity {
private static int COMMAND_PORT = 2121;
static final int DIALOG_ALERT_ID = 0;
private static ExecutorService executor = Executors.newCachedThreadPool();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.my_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.new_game:
System.out.println("New game button is pressed!");
//newGame();
return true;
case R.id.quit:
System.out.println("Quit button is pressed!");
showDialog(DIALOG_ALERT_ID);
return true;
default:
return super.onOptionsItemSelected(item); }
}
#Override
protected Dialog onCreateDialog(int id){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are you sure you want to exit?")
.setCancelable(false).setPositiveButton("yes", new DialogInterface.OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int id){
FTPServer.this.finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
return alert;
}
HEre is the ServerPI Code
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class ServerPI implements Runnable{
private Socket clientSocket;
private BufferedReader in;
private PrintWriter out;
private String baseDir;
private String relativeDir;
private String absoluteDir;
private String fileName;
private String filePath;
public ServerPI(Socket incoming) throws IOException{
this.clientSocket = incoming;
in = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
out = new PrintWriter(this.clientSocket.getOutputStream(), true);
baseDir = new File("").getAbsolutePath();
relativeDir = "/";
absoluteDir = baseDir + relativeDir;
fileName = "";
filePath = absoluteDir + "/" + fileName;
}
private void readCommandLoop() throws IOException {
String line = null;
reply(220, "Welcome to the SimpleFTP server!");
while((line = in.readLine()) != null){
int replyCode = executeCommand(line.trim());
if(replyCode == 221){
return;
}
}
}
private int executeCommand(String trim) {
// TODO Auto-generated method stub
return 0;
}
public int reply(int statusCode, String statusMessage){
out.println(statusCode + " " + statusMessage);
return statusCode;
}
#Override
public void run(){
try{
this.readCommandLoop();
} catch (IOException e){
e.printStackTrace();
}
finally {
try {
if(in != null){
in.close();
in = null;
}
if(out != null){
out.close();
out = null;
}
if (clientSocket != null){
clientSocket.close();
clientSocket = null;
}
}
catch (IOException e){
e.printStackTrace();
}
}
}
}
I have put the code in the AsyncTask, here it is
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
ServerSocket s = null;
Socket incoming = null;
try{
s = new ServerSocket(COMMAND_PORT);
String ip = (s.getInetAddress()).getHostAddress();
Context context = this.getApplicationContext();
CharSequence text = ip;
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);
Thread.sleep(1000);
toast.show();
while(true){
incoming = s.accept();
executor.execute(new ServerPI(incoming));
}
}
catch(Exception e){
System.out.println(e.toString());
e.printStackTrace();
}
finally{
try
{
if(incoming != null)incoming.close();
}
catch(IOException ignore)
{
//ignore
}
try
{
if (s!= null)
{
s.close();
}
}
catch(IOException ignore)
{
//ignore
}
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
Iam calling the longOpertation in onCreate method. What is the problem that the app crashes on launch.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen);
new LongOperation().execute();
}
Maybe because you didn't set up the permissions in the manifest? You've to set permission for internet usage.
If this doesn't work, please tell us which line is it throwing the exception.
while(true){ incoming = s.accept(); ...} You cannot put that in OnStart(). That should be done in a thread. So ServerSocket s = null; should be a variable of you activity.
So I went with Swiftp application (open source) as a service in my application which helped me to achieve my task. Thanks everyone who stepped forward to help. Here is the link if someone wants to follow
Please post your code here.
NetworkOnMainthreadException occurs because you maybe running Network related operation on the Main UI Thread. You should use asynctask for this purpose
This is only thrown for applications targeting the Honeycomb SDK or higher. Applications targeting earlier SDK versions are allowed to do networking on their main event loop threads, but it's heavily discouraged.
http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
class TheTask extends AsyncTask<Void,Void,Void>
{
protected void onPreExecute()
{ super.onPreExecute();
//display progressdialog.
}
protected void doInBackground(Void ...params)//return result here
{
//http request. do not update ui here
//call webservice
//return result here
return null;
}
protected void onPostExecute(Void result)//result of doInBackground is passed a parameter
{
super.onPostExecute(result);
//dismiss progressdialog.
//update ui using the result returned form doInbackground()
}
}
http://developer.android.com/reference/android/os/AsyncTask.html. Check the topic under the heading The 4 Steps.
A working example of asynctask # To use the tutorial in android 4.0.3 if had to work with AsynxTasc but i still dont work?.
The above makes a webserive call in doInBakckground(). Returns result and updates the ui by setting the result in textview in onPostExecute().
You can not do network operation in main thread in android 3.0 higher. Use AsyncTask for this network operation. See this for further explanation

How create a folder on SdCard and how to copy files from one to another

In this Project i am trying to copy files from sdcard (for eg images in DICM) to Recycle Folder whenever user click delete button. But, i am facing problem. I am able to delete files but but unable to copy thing.
C.Java - Using for assigning directories.
package com.haha.recyclebin;
public class C
{
public static String SDCARD = "/mnt/sdcard";
public static String RECYCLE_BIN_ROOT = SDCARD+"/.Recycle";
}
U.Java - Using for copying file from one folder on Sdcard to Recycle Folder.
package com.haha.recyclebin;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class U
{
public static void copyFile(File sourceLocation, File targetLocation)
throws FileNotFoundException, IOException
{
U.debug("copying from "+sourceLocation.getAbsolutePath()+" to "+targetLocation.getAbsolutePath());
String destDirPath = targetLocation.getParent();
File destDir = new File(destDirPath);
if(!destDir.exists()){
destDir.mkdirs();
}
InputStream in = new FileInputStream(sourceLocation);
OutputStream out = new FileOutputStream(targetLocation);
// Copy the bits from instream to outstream
byte[] buf = new byte[1024*512];
int len;
while ((len = in.read(buf)) > 0) {
System.out.println("papa");
out.write(buf, 0, len);
System.out.println(">");
}
System.out.println(".");
in.close();
out.close();
}
public static void debug(Object msg){
System.out.println(msg);
}
}
RecycleActivity - using U.java and C.java in this code :-
package com.haha.recyclebin;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Set;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Toast;
public class RecycleActivity extends Activity {
private OnClickListener exitListener = new OnClickListener()
{
#Override
public void onClick(DialogInterface paramDialogInterface, int paramInt)
{
RecycleActivity.this.finish();
}
};
/**
* need a standalone class to hold data (file name)
*/
private final class DeleteFileListener implements OnClickListener
{
String file = null;
/**
* #param file the file to set
*/
public void setFile(String file)
{
this.file = file;
}
#Override
public void onClick(DialogInterface paramDialogInterface, int paramInt)
{
RecycleActivity.this.prepareRecyclebin();
File src = new File(file);
String destPath = C.RECYCLE_BIN_ROOT+file;
File dest = new File(destPath);
try
{
U.copyFile(src, dest); /* using U.java here */
src.delete();
String msg = RecycleActivity.this.getResources().getString(R.string.deleted) + destPath;
Toast.makeText(RecycleActivity.this, msg, Toast.LENGTH_SHORT).show();
} catch (Exception e)
{
e.printStackTrace();
Toast.makeText(RecycleActivity.this, R.string.delete_failed, Toast.LENGTH_SHORT).show();
}
RecycleActivity.this.finish();
}
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Intent intent = getIntent();
debugIntent(intent);
Bundle extras = intent.getExtras();
/* For File Explorer */
Object obj = extras.get(Intent.EXTRA_INTENT);
if(null!=obj){
Intent it2 = (Intent) obj;
Bundle ex2 = it2.getExtras();
Object obj2 = ex2.get(Intent.EXTRA_STREAM);
if(null!=obj2){
Uri uri = (Uri) obj2;
String file = uri.getPath();
System.out.println("file: "+file);
toRecyclebin(file);
}
}
}
/**
* #param file
*/
private void toRecyclebin(String file)
{
if(!file.startsWith(C.SDCARD))
{
promptLimit();
return;
}
String conf = this.getResources().getString(R.string.confirm_delete);
conf+="\n\n"+file;
DeleteFileListener listener = new DeleteFileListener();
listener.setFile(file);
new AlertDialog.Builder(this)
.setMessage(conf)
.setPositiveButton(R.string.yes, listener)
.setNegativeButton(R.string.no, exitListener)
.show();
}
/**
*
*/
private void promptLimit()
{
new AlertDialog.Builder(this)
.setMessage(R.string.limit)
.setPositiveButton(R.string.ok, exitListener)
.show();
}
/**
* #param intent
*/
private void debugIntent(Intent intent)
{
System.out.println("intent: "+intent);
Bundle extras = intent.getExtras();
Set<String> keys = extras.keySet();
for(String key:keys){
Object value = extras.get(key);
System.out.println("-["+key+"]:["+value+"]");
if(value instanceof Intent){
Intent intent2 = (Intent) value;
Bundle ext2 = intent2.getExtras();
Set<String> ks2 = ext2.keySet();
for(String k:ks2){
Object v2 = ext2.get(k);
System.out.println("--["+k+"]:["+v2+"]");
if(v2 instanceof Intent){
Intent i3 = (Intent) v2;
Bundle e3 = i3.getExtras();
Set<String> ks3 = e3.keySet();
for(String kk:ks3){
Object v3 = e3.get(kk);
System.out.println("---["+kk+"]:["+v3+"]");
}
}
}
}
}
Uri data = intent.getData();
System.out.println("data: "+data);
}
void prepareRecyclebin(){
File root = new File(C.RECYCLE_BIN_ROOT);
if(!root.exists()){
root.mkdirs();
}
}
}
I have file explorer which is working fine, I can see images and music on sdcard and i am able to delete then also. But after deletion they should go to Recycle folder (as stated in C.java ). I have created Recycle Folder (/mnt/sdcard/Recycle) manually using file explorer in eclipse.
But i don't see files in recycle folder.
Is there any problem with the Code ?
Any kind of Help will be appreciated.
Thanks !!
Have you Debug and make sure the copyfile has been executed?
And this is My CopyFile function, and they are quite the same:
public static boolean copyFile(String from, String to) {
try {
int bytesum = 0;
int byteread = 0;
File oldfile = new File(from);
if (oldfile.exists()) {
InputStream inStream = new FileInputStream(from);
FileOutputStream fs = new FileOutputStream(to);
byte[] buffer = new byte[1444];
while ((byteread = inStream.read(buffer)) != -1) {
bytesum += byteread;
fs.write(buffer, 0, byteread);
}
inStream.close();
fs.close();
}
return true;
} catch (Exception e) {
return false;
}
}
Try following Code, it will Work.
public void Save_To_Phone(Bitmap bitmap){
try {
FileOutputStream os = new FileOutputStream(YourSDCardPath);
bitmap.compress(CompressFormat.JPEG, 80, os);
os.close();
} catch (Exception e) {
Log.w("ExternalStorage", "Error writing file", e);
}
}

Categories

Resources