My application is live on Play Store, I noticed that I was not asking permissions when calling:
File savedImagesDirectory = getBaseContext.getExternalFilesDir("SavedImages");
if(savedImagesDirectory != null) {
if (!savedImagesDirectory.exists()) {
if (savedImagesDirectory.mkdir()) ; //directory is created;
}
}
Strange thing is, I'm not getting any crashes when calling this without runtime permissions. I tested on my device running Nougat and the files are created and I experienced no crashes.
I only have permissions in my Manifest, but I don't ask runtime permissions.
I've seen A LOT of answers on SO that say that runtime permissions should be asked when creating a folder/file in external file directory.
My question:
Should I ask runtime permissions when calling the above? If yes, why am I not experiencing any crashes?
It would also be helpful if someone can provide a link where I can read up about this.
Although it returns path to external storage, you don't need permission to access directory returned by getExternalFilesDir().
It is same as getFilesDir() but it is public to other apps as well.
Also note that if you write anything in that directory and clear app data or uninstall the app, everything will be deleted.
For more information, read the documentation at https://developer.android.com/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, WRITE_REQUEST_CODE);
use this code
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[]
grantResults) {
switch (requestCode) {
case WRITE_REQUEST_CODE:
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
//Granted.
}
else{
//Denied.
}
break;
}
}
In Android, taking a picture can be done with this basic setup as seen here.
protected void dispatchTakePictureIntent(){
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
...
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
//do stuff with the output file
}
}
However, third-party camera apps are unpredictable and cannot be guaranteed to play nice.
What is the correct way to validate the output / catch any errors? Should I be wrapping startActivityForResult inside of a try/catch block? What are the various types of exceptions I should be trying to catch and what should I do with them?
Should I be wrapping startActivityForResult inside of a try/catch block?
Yes, though this has nothing to do with ACTION_IMAGE_CAPTURE specifically. The user may not have access to a camera app (e.g., the user is running under a restricted profile on a shared device). Any time you call startActivity() or startActivityForResult() with an implicit Intent, you need to deal with the possibility that there is no available app.
What are the various types of exceptions I should be trying to catch and what should I do with them?
In the answer that you linked to, I was pointing out crashes in the camera app. You cannot catch those, as that is not your app.
Beyond ActivityNotFoundException, you also need to think about Android 6.0 runtime permissions, as you need the CAMERA permission to start an ACTION_IMAGE_CAPTURE activity. Usually, though, this will not involve an exception handler.
The other validations would be not based on exceptions, but based on flawed results:
If you supplied EXTRA_OUTPUT, is there any content at the Uri you specified? In theory, if the user did not cancel out of the camera app, there will be, but that is not guaranteed, due to camera app bugs.
If there is is content, is it plausibly an image file? For example, you might use BitmapFactory with inJustDecodeBounds to see if the image really is an image.
If there is no content, did you happen to get a Uri back in the result Intent in onActivityResult()? The buggy camera app might have stored the image where it wanted to and supplied the Uri to it, even though this is way out of spec for ACTION_IMAGE_CAPTURE.
If there is no content, and there is no Uri, does getExtras("data") return a non-null value? In that case, you at least got a thumbnail and can save it yourself, even though the image quality may suck.
I am trying to take a photo in my Android application using my PC's built-in webcam. I am using the eclipse Android emulator and have set the AVD to use webcam0 as the rear-facing camera, but when I run my program it always crashes, saying "Unfortunately, Camera has stopped". I have added the following line to my Manifest xml:
<uses-permission android:name="android.permission.CAMERA"/>
though it still does not seem to work. I have read in a few places that there's supposed to be a "Hardware" section in the AVD manager edit/create screen, but mine does not have it.
Am I missing something? Here is the logcat that appears when I try to run the app:
Any thoughts about what might be happening? I've searched for solutions all over the place and can't seem to find any that solve this exact problem. Thanks for the help.
EDIT
Here's my image capture button/method code:
/* Create capture button */
Button capture = (Button) findViewById(R.id.btnCapture);
capture.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//create directory/picture file
count++;
file = dir + count + ".jpg";
File picFile = new File(file);
try {
picFile.createNewFile();
} catch (IOException e) {}
Uri outputFileUri = Uri.fromFile(picFile);
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(cameraIntent, TAKE_PHOTO_CODE);
}
});
/* Check if valid photo */
#Override
protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_PHOTO_CODE && resultCode == RESULT_OK) {
Log.d("CameraDemo", "Pic saved");
}
}
This code was mostly created based on a thread I found here on SO, I'm afraid I don't recall which one though.
Have you added:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
in your manifest?
If you feel that you have followed all the right procedures for activating your camera but fails, then as for my case, I had to try the in-built camera in the emulator to rule out any possibility that it is my codes. I realized that the same error was being produced by the default camera app - as you reported.
So, after several trials: (adding space, ram, more manifest files, clearing Google Play Services Data, updating Play Services, etc), I decided to use an Android Studio on Windows 10 x64 machine (the earlier one was Windows 7 x64). To my surprise, the error was gone and the camera did not crash. Now, I don't know if it has to do with OS (Win 7) or the Processor, or any other hardware or software issue. You can try this if you are not already using Windows 10.
I am working on an android application that uses the TextToSpeech functionality provided by google and have followed this example:
TTS Example from Google
I want to know with this line:
int result = mTts.setLanguage(Locale.US);
if (result == TextToSpeech.LANG_MISSING_DATA ||
result == TextToSpeech.LANG_NOT_SUPPORTED) {
// Lanuage data is missing or the language is not supported.
}
What can I do if the Language data is missing off the users device? The app will not continue if the data is not there? Is there a way to allow the user to get the language on their device? I have a test device that seems to not have any languages on it at all.
From http://developer.android.com/resources/articles/tts.html:
// missing data, install it
Intent installIntent = new Intent();
installIntent.setAction(
TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
No good way to know exactly what happens if the language they want simply doesn't exist at all, but....that is the recommended way of dealing with it.
Take accessing contacts in android
android.jar for versions 1.6 has People.CONTENT_URI for invoking contacts related info whereas in later versions we need to have api support for RawContacts.CONTENT_URI.
Same thing is true for accessing calendar for instance as its URI is changed in android 2.2.
Is there a best practice to manage all different changes without adding additional application or build separately for each version of changes?
For my money, a very good answer is at http://android-developers.blogspot.co.uk/2010/07/how-to-have-your-cupcake-and-eat-it-too.html. However, the example there is a little more complicated than need be, so based on that, here is an example of how to cope with it when building notifications. The underlying reason this works is a consequence of how java engines interpret classes: it only looks at them when needed, so if you wrap version specific code up in a class and only create it when you know you are using that version, it all works ...
There are, as far as I can tell, two generations of approaches to creating notification, and a naming change along the way in the second. So that gives three ways to do it. For each way, create a class with the notification generation in it:
The first approach (used through to Gingerbread):
public class MyNotificationBuilderToGingerBread {
Notification notification = null;
MyNotificationBuilderToGingerBread(Context myContext, int icon, String ticker, String title, String info, Long timeStamp, PendingIntent pendingIntent, int flags) {
notification = new Notification(R.drawable.ic_sb, ticker, timeStamp);
notification.setLatestEventInfo(myContext, title, info, pendingIntent);
notification.flags |= flags;
}
Notification get() {
return notification;
}
}
The second approach, Honeycomb to IceCreamSandwich:
public class MyNotificationBuilderHoneyCombToIceCreamSandwich {
Notification.Builder mb = null;
MyNotificationBuilderHoneyCombToIceCreamSandwich(Context myContext, int icon, String ticker, String title, String info, Long timeStamp, PendingIntent pendingIntent, boolean onGoing) {
mb = new Notification.Builder(myContext);
mb.setSmallIcon(icon);
mb.setContentIntent(pendingIntent);
mb.setContentTitle(title);
mb.setContentText(info);
mb.setWhen(timeStamp);
if (ticker != null) mb.setTicker(ticker);
mb.setOngoing(onGoing);
}
Notification get() {
return mb.getNotification();
}
}
The second generation, with the name change, Jellybean (onwards, so far ...):
public class MyNotificationBuilderJellyBean {
Notification.Builder mb = null;
MyNotificationBuilderJellyBean(Context myContext, int icon, String ticker, String title, String info, Long timeStamp, PendingIntent pendingIntent, boolean onGoing) {
mb = new Notification.Builder(myContext);
mb.setSmallIcon(icon);
mb.setContentIntent(pendingIntent);
mb.setContentTitle(title);
mb.setContentText(info);
mb.setWhen(timeStamp);
if (ticker != null) mb.setTicker(ticker);
mb.setOngoing(onGoing);
}
Notification get() {
return mb.build();
}
}
Then, you just need to pick which class to instantiate on the fly:
// System information
private final int sdkVersion = Build.VERSION.SDK_INT;
// If you want to go really old:
// (actually, there is a question about how this issue should be handled
// systematically. Suggestions welcome.)
// final int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
// This is for a permanent notification. Change the final argument (flags or boolean) if it isn't meant ot be
// For meaning of other variable, see notification documentation on the android website.
if (sdkVersion < Build.VERSION_CODES.HONEYCOMB) {
MyNotificationBuilderToGingerBread mnb = new MyNotificationBuilderToGingerBread(myContext, R.drawable.notification_icon, ticketText, title, infoText, timeStampMillis, pendingIntentForTapOnFullNotitifcation, Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR);
notification = mnb.get();
}
else if (sdkVersion < Build.VERSION_CODES.JELLY_BEAN) {
MyNotificationBuilderHoneyCombToIceCreamSandwich mnb = new MyNotificationBuilderHoneyCombToIceCreamSandwich(myContext, R.drawable.notification_icon, ticketText, title, infoText, timeStampMillis, pendingIntentForTapOnFullNotitifcation, true);
notification = mnb.get();
}
else {
MyNotificationBuilderJellyBean mnb = new MyNotificationBuilderJellyBean(myContext, R.drawable.notification_icon, ticketText, title, infoText, timeStampMillis, pendingIntentForTapOnFullNotitifcation, true);
notification = mnb.get();
}
// Send the notification.
notificationManager.notify(idForNotificationManager, notification);
Hope this helps!
There are many resources for you to utilize to help support multiple versions of android.
Read this blog post here and
then read this one here, they
will help you address API level
version support issues.
Read this blog post on multiple
screen support, especially how the
asset hierarchy in parsed in res
folder. This will help you
understand and design how to do
asset folder structure to support
different screen size/densities and
android versions.
Lastly write your own custom ant build
scripts so that you can compile with
all versions of android.
Quite Honestly, it's a pain.
I usually, just isolate parts of code that are different and access them using abstract classes. So technically creating different version for different OS.
But there are other ways. The best one i've seen involves using reflection.
If you don't really need the new functionnalities, and really have to support old Android versions, drop it. Build your app for the oldest version, and don't bother with this kind of thing.
In the other case, you can detect the version using Build, and use reflection to load the classes you need. An example of that can be found in the source code of the K9Mail app
There's a nice article on android.com about it:
http://developer.android.com/resources/articles/backward-compatibility.html
Personally I would suggest the wrapper class or wrapper library solution. But in small cases the reflection should be fine (and in case performance is not a problem for you).
If you need more info, ask in comments.
This is a great article for when you have to do reflection in Android (to support multiple API levels).
And when you have to have different resources for different API Levels, this is the reference to use (see the section on "Platform Version (API level)").
If on Eclipse, from ADT version 17 you can specify code to run with some version simply as described in Lint API Check.
The code word is #TargetAPI(XX)
Hope it helps
Best practice (though not for Android, but for J2ME) to my knowledge is to use preprocessing C/C++ styled statements, like:
//#if S40
...
//#else
...
//#endif
Some IDE's support these kind of preprocessing, e.g. Netbeans. To my knowledge Eclipse has some plugins to enable preprocessing also. I don't really know are they applicable to Android development. Try to google yourself.