My app records GNSS positions as a tool to produce Orienteering's map using a cell phone. The main activity does all the job and when the mapper needs to export the field job done, app calls a child activity (Save_File) responsible to save on Documents/Oribooklet directory a file, using gpx format. Because of getExternalStoragePublicDirectory obsolescence, SAF is the way to choose to go. I am not an IT guy, I did this for fun, that is why I am so late with this matter: users drop me messages saying they couldn't save the field job anymore.
Main: Oribooklet
public class Oribooklet extends AppCompatActivity {
...
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Mapper clicked on a menu option in the app bar overflow menu
Intent intent;
switch (item.getItemId()) {
// Respond to a click on the "Save" menu option
case R.id.action_saveFile:
// Call the Save_File Class
// Prepare to show Save UI
// Save to Shared Preference
SharedPreferences sharedPref = getSharedPreferences("user_preferences", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putBoolean("bShowSaveUI", true);
editor.apply();
intent = new Intent(Oribooklet.this, Save_File.class);
startActivity(intent);
return true;
// Respond to a click on the "Preference" menu option
case R.id.action_settings:
// Call the Class Activity_Preference for options menu
intent = new Intent(Oribooklet.this,Activity_Preference.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
Inside Save_File activity I implemented and it worked fine: when the mapper push Save, startActivityForResult is triggered, then an Android file picker starts, mapper choose where to save.
void createExternalStoragePublicDocument(String fileName) {
try {
// Call SAF
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(INTENT_TYPE);
intent.putExtra(Intent.EXTRA_TITLE, fileName);
// https://stackoverflow.com/questions/6147884/onactivityresult-is-not-being-called-in-fragment
startActivityForResult(intent, CREATE_REQUEST_CODE);
} catch (Exception excep) {
excep.printStackTrace();
}
}
Then onActivityResult gets the returned Uri to complete the job by filling the file with the data collected on the field. Couldn't be any better!
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
super.onActivityResult(requestCode, resultCode, resultData);
Uri currentUri = null;
if (resultCode == Activity.RESULT_OK) {
if (requestCode == CREATE_REQUEST_CODE) {
if (resultData != null) {
currentUri = resultData.getData();
// Write contents
writeFileContent(currentUri, fileData);
}
} else if (requestCode == OPEN_REQUEST_CODE) {
if (resultData != null) {
currentUri = resultData.getData();
sendByMail(stringEMailAddress, stringSubject, currentUri);
// Back to Oribooklet
finish();
}
}
}
}
The app has a Preference fragment to set up some parameters. One of them is the standard to be used. Two standards must not be mixed doing a map. When the mapper changes the map's type, Preference used to call Save_File to do the job so no mix of data happens. It used to work fine.
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
final String key) {
// Find the one that changed and address it
Preference connectionPref = findPreference(key);
switch (key){
case "list_iof":
// IOF´s element set
SharedPreferences preferences = getActivity().getSharedPreferences("user_preferences", MODE_PRIVATE);
// Update preference Summary
ListPreference listIOFPreference = (ListPreference) findPreference("list_iof");
CharSequence iof = listIOFPreference.getEntry();
connectionPref.setSummary(getResources().getString(R.string.pref_iof_summary_1) +
iof + "\" " +
getResources().getString(R.string.pref_iof_summary_2));
// Save the field job to avoid IOF set mixing
Intent intent = new Intent(getActivity(), Save_File.class);
startActivity(intent);
break;
After SAF, the onActivityResult of Save_File is never triggered, startActivityForResult triggers the file provider but when the mapper chooses the place to save, instead of onActivityResult of Save_File it returns to Preference so the file is created empty.
I did many tests, debugging with many breaking points, setting and removing super in quite a few places, checked all S.O. posts about the matter without success.
Any idea?
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_oribooklet_v0"
android:name=".MyAppContext"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--android:requestLegacyExternalStore="true" -->
<!-- android:roundIcon="@mipmap/ic_oribooklet_v2"-->
<activity android:name="com.hbcavalcanti.oribooklet.Oribooklet"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- A child of the main activity -->
<activity
android:name=".Save_File"
android:label="@string/action_saveFile"
android:parentActivityName=".Oribooklet" >
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".Oribooklet" />
</activity>
<activity
android:name=".Activity_Preference"
android:label="@string/action_settings"
android:parentActivityName=".Oribooklet" >
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".Oribooklet" />
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.hbcavalcanti.oribooklet.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
When called from Preference, the Save File method used not to make any UI call, then its onActivityResult was never called.
Just for test purpose, I introduced one onActivityResult on Preference fragment and it received a trigger. I removed after the test.
I fixed the issue by also calling an UI inside Save File when Preference invoque it. This trigger its onActivityResult making everything works fine like being called from its parent.