Before Android Nougat ( 7.0 ) it was easy to create a KML file in external storage and launch an intent with the file uri and Google Earth mime type for KML. Google Earth app magically opened and flew down to my KML.
On Android Nougat ( 7.0 ) you must use a FileProvider with the intent. You must add a provider to AndroidManifest.xml plus a /res/xml/provider_paths.xml.
Using the FileProvider intent, the installed Google Earth app will launch, then display message "Error loading file" in Google Earth.
Logcat displays "FileNotFoundException Permission denied".
File: AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
File: /res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
// File: MainActivity.java
// I used Android Device Monitor to push mooCow.kml to
// /storage/emulated/0/Download/mooCow.kml
public void onClick(View arg0) {
File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File kmlFile = new File(folder, "mooCow.kml");
String pathToLocalKmlFile = kmlFile.getAbsolutePath();
openKmlInGoogleEarth(pathToLocalKmlFile);
}
// This works with KML and file extensions supported by getMimeTypeFromExtension
public void openKmlInGoogleEarth(String pathToFileInExternalStorage) {
try {
String[] splits = pathToFileInExternalStorage.split("\\.");
String ext = "";
if(splits.length >= 2) {
ext = splits[splits.length-1];
}
MimeTypeMap mime = MimeTypeMap.getSingleton();
String mimeType = mime.getMimeTypeFromExtension(ext);
File file = new File(pathToFileInExternalStorage);
Uri uri;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
// Use file Uri before Android Nougat ( 7.0 )
uri = Uri.fromFile(file);
} else {
// Android Nougat ( 7.0 ) and later,
// use a FileProvider, AndroidManifest provider, and /res/xml/provider_paths.xml
uri = FileProvider.getUriForFile(MainActivity.this,
BuildConfig.APPLICATION_ID + ".provider",
file);
}
final String type = "application/vnd.google-earth.kml+xml";
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
}
catch(IllegalArgumentException e) {
Log.e(TAG, "Unexpected exception openKmlInGoogleEarth", e);
}
}
adb logcat point to com.google.android.apps.earth permission problem.
How can Asus File manager app open the same exact file in Google Earth but my app cannot? My non-FileProvider works fine on Android 5.2 but on Android 6 it only launches Google Earth?
E Class : Error reading file from URI: content://com.subsite.googleearthbutton.provider/external_files/Documents/mooCow.kml
E Class : java.io.FileNotFoundException: Permission denied
E Class : at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:144)
E Class : at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
E Class : at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1104)
E Class : at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:942)
E Class : at android.content.ContentResolver.openInputStream(ContentResolver.java:662)
E Class : at com.google.android.apps.earth.p.m.a(FileUtil.java:6)
What could be causing a file access "Permission denied" ?