Improved SAF missing reporting

pull/214/head
M66B 1 year ago
parent b8f57ea098
commit ac92c2a013

@ -1677,9 +1677,10 @@ This might be because your custom ROM does not include it or because it was acti
FairEmail does not request storage permissions, so this framework is required to select files and folders. FairEmail does not request storage permissions, so this framework is required to select files and folders.
No app, except maybe file managers, targeting Android 4.4 KitKat or later should ask for storage permissions because it would allow access to *all* files. No app, except maybe file managers, targeting Android 4.4 KitKat or later should ask for storage permissions because it would allow access to *all* files.
Moreover, recent Android versions disallow access to all files for apps, except, under specific conditions, for file managers.
The storage access framework is provided by the package *com.android.documentsui*, The storage access framework is provided by the package *com.android.documentsui*,
which is visible as *Files* app on some Android versions (notable OxygenOS). which is visible as *Files* app on some Android versions (notably OxygenOS).
You can enable the storage access framework (again) with this adb command: You can enable the storage access framework (again) with this adb command:

@ -394,7 +394,8 @@ public class ActivityEML extends ActivityBase {
create.putExtra(Intent.EXTRA_TITLE, apart.attachment.name); create.putExtra(Intent.EXTRA_TITLE, apart.attachment.name);
Helper.openAdvanced(ActivityEML.this, create); Helper.openAdvanced(ActivityEML.this, create);
if (create.resolveActivity(getPackageManager()) == null) // system whitelisted if (create.resolveActivity(getPackageManager()) == null) // system whitelisted
ToastEx.makeText(ActivityEML.this, R.string.title_no_saf, Toast.LENGTH_LONG).show(); Log.unexpectedError(getSupportFragmentManager(),
new IllegalArgumentException(getString(R.string.title_no_saf)), 25);
else else
startActivityForResult(Helper.getChooser(ActivityEML.this, create), REQUEST_ATTACHMENT); startActivityForResult(Helper.getChooser(ActivityEML.this, create), REQUEST_ATTACHMENT);
} }

@ -755,7 +755,8 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
open.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); open.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
open.setType("*/*"); open.setType("*/*");
if (open.resolveActivity(getPackageManager()) == null) // system whitelisted if (open.resolveActivity(getPackageManager()) == null) // system whitelisted
ToastEx.makeText(this, R.string.title_no_saf, Toast.LENGTH_LONG).show(); Log.unexpectedError(getSupportFragmentManager(),
new IllegalArgumentException(getString(R.string.title_no_saf)), 25);
else else
startActivityForResult(Helper.getChooser(this, open), REQUEST_IMPORT_CERTIFICATE); startActivityForResult(Helper.getChooser(this, open), REQUEST_IMPORT_CERTIFICATE);
} }

@ -1263,7 +1263,8 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
Helper.openAdvanced(context, intent); Helper.openAdvanced(context, intent);
if (intent.resolveActivity(context.getPackageManager()) == null) { // // system/GET_CONTENT whitelisted if (intent.resolveActivity(context.getPackageManager()) == null) { // // system/GET_CONTENT whitelisted
ToastEx.makeText(context, R.string.title_no_saf, Toast.LENGTH_LONG).show(); Log.unexpectedError(parentFragment.getParentFragmentManager(),
new IllegalArgumentException(context.getString(R.string.title_no_saf)), 25);
return; return;
} }

@ -540,10 +540,10 @@ public class FragmentBase extends Fragment {
create.putExtra(Intent.EXTRA_TITLE, attachment.name); create.putExtra(Intent.EXTRA_TITLE, attachment.name);
Helper.openAdvanced(context, create); Helper.openAdvanced(context, create);
PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager();
if (create.resolveActivity(pm) == null) { // system whitelisted if (create.resolveActivity(pm) == null) // system whitelisted
Log.w("SAF missing"); Log.unexpectedError(getParentFragmentManager(),
ToastEx.makeText(context, R.string.title_no_saf, Toast.LENGTH_LONG).show(); new IllegalArgumentException(context.getString(R.string.title_no_saf)), 25);
} else else
startActivityForResult(Helper.getChooser(context, create), REQUEST_ATTACHMENT); startActivityForResult(Helper.getChooser(context, create), REQUEST_ATTACHMENT);
} }
@ -556,10 +556,10 @@ public class FragmentBase extends Fragment {
Intent tree = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); Intent tree = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
Helper.openAdvanced(context, tree); Helper.openAdvanced(context, tree);
PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager();
if (tree.resolveActivity(pm) == null) { // system whitelisted if (tree.resolveActivity(pm) == null) // system whitelisted
Log.w("SAF missing"); Log.unexpectedError(getParentFragmentManager(),
ToastEx.makeText(context, R.string.title_no_saf, Toast.LENGTH_LONG).show(); new IllegalArgumentException(context.getString(R.string.title_no_saf)), 25);
} else else
startActivityForResult(Helper.getChooser(context, tree), REQUEST_ATTACHMENTS); startActivityForResult(Helper.getChooser(context, tree), REQUEST_ATTACHMENTS);
} }

@ -540,7 +540,8 @@ public class FragmentContacts extends FragmentBase {
open.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); open.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
open.setType("*/*"); open.setType("*/*");
if (open.resolveActivity(pm) == null) // system whitelisted if (open.resolveActivity(pm) == null) // system whitelisted
ToastEx.makeText(context, R.string.title_no_saf, Toast.LENGTH_LONG).show(); Log.unexpectedError(getParentFragmentManager(),
new IllegalArgumentException(context.getString(R.string.title_no_saf)), 25);
else else
startActivityForResult(Helper.getChooser(context, open), REQUEST_IMPORT); startActivityForResult(Helper.getChooser(context, open), REQUEST_IMPORT);
} }

@ -1701,7 +1701,8 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
Intent intent = (export ? getIntentExport(context) : getIntentImport(context)); Intent intent = (export ? getIntentExport(context) : getIntentImport(context));
PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager();
if (intent.resolveActivity(pm) == null) { // // system/GET_CONTENT whitelisted if (intent.resolveActivity(pm) == null) { // // system/GET_CONTENT whitelisted
ToastEx.makeText(context, R.string.title_no_saf, Toast.LENGTH_LONG).show(); Log.unexpectedError(getParentFragmentManager(),
new IllegalArgumentException(context.getString(R.string.title_no_saf)), 25);
return; return;
} }

@ -409,7 +409,8 @@ public class FragmentOptionsEncryption extends FragmentBase
open.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); open.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
open.setType("*/*"); open.setType("*/*");
if (open.resolveActivity(pm) == null) // system whitelisted if (open.resolveActivity(pm) == null) // system whitelisted
ToastEx.makeText(context, R.string.title_no_saf, Toast.LENGTH_LONG).show(); Log.unexpectedError(getParentFragmentManager(),
new IllegalArgumentException(context.getString(R.string.title_no_saf)), 25);
else else
startActivityForResult(Helper.getChooser(context, open), REQUEST_IMPORT_CERTIFICATE); startActivityForResult(Helper.getChooser(context, open), REQUEST_IMPORT_CERTIFICATE);
} }

@ -87,6 +87,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -1932,6 +1933,14 @@ public class Log {
} }
static void unexpectedError(FragmentManager manager, Throwable ex, boolean report) { static void unexpectedError(FragmentManager manager, Throwable ex, boolean report) {
unexpectedError(manager, ex, report, null);
}
static void unexpectedError(FragmentManager manager, Throwable ex, int faq) {
unexpectedError(manager, ex, false, faq);
}
static void unexpectedError(FragmentManager manager, Throwable ex, boolean report, Integer faq) {
Log.e(ex); Log.e(ex);
if (ex instanceof OutOfMemoryError) if (ex instanceof OutOfMemoryError)
@ -1940,6 +1949,7 @@ public class Log {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putSerializable("ex", ex); args.putSerializable("ex", ex);
args.putBoolean("report", report); args.putBoolean("report", report);
args.putInt("faq", faq == null ? 0 : faq);
FragmentDialogUnexpected fragment = new FragmentDialogUnexpected(); FragmentDialogUnexpected fragment = new FragmentDialogUnexpected();
fragment.setArguments(args); fragment.setArguments(args);
@ -1950,17 +1960,28 @@ public class Log {
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final Throwable ex = (Throwable) getArguments().getSerializable("ex"); Bundle args = getArguments();
boolean report = getArguments().getBoolean("report", true); final Throwable ex = (Throwable) args.getSerializable("ex");
final boolean report = args.getBoolean("report", true);
final int faq = args.getInt("faq");
final Context context = getContext(); final Context context = getContext();
LayoutInflater inflater = LayoutInflater.from(context); LayoutInflater inflater = LayoutInflater.from(context);
View dview = inflater.inflate(R.layout.dialog_unexpected, null); View dview = inflater.inflate(R.layout.dialog_unexpected, null);
TextView tvError = dview.findViewById(R.id.tvError); TextView tvError = dview.findViewById(R.id.tvError);
Button btnHelp = dview.findViewById(R.id.btnHelp);
String message = Log.formatThrowable(ex, false); String message = Log.formatThrowable(ex, false);
tvError.setText(message); tvError.setText(message);
btnHelp.setVisibility(faq > 0 ? View.VISIBLE : View.GONE);
btnHelp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.viewFAQ(v.getContext(), faq);
}
});
AlertDialog.Builder builder = new AlertDialog.Builder(context) AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setView(dview) .setView(dview)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)

@ -17,10 +17,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawableStart="@drawable/twotone_bug_report_24" android:drawableStart="@drawable/twotone_bug_report_24"
android:drawablePadding="6dp" android:drawablePadding="6dp"
app:drawableTint="@color/red"
android:labelFor="@+id/etName" android:labelFor="@+id/etName"
android:text="@string/title_unexpected_error" android:text="@string/title_unexpected_error"
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:drawableTint="@color/red"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -29,10 +29,23 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:fontFamily="monospace"
android:text="Error" android:text="Error"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textIsSelectable="true" android:textIsSelectable="true"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvCaption" /> app:layout_constraintTop_toBottomOf="@id/tvCaption" />
<Button
android:id="@+id/btnHelp"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:drawableEnd="@drawable/twotone_help_24"
android:drawablePadding="6dp"
android:text="@string/title_setup_help"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvError" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</eu.faircode.email.ScrollViewEx> </eu.faircode.email.ScrollViewEx>

@ -995,8 +995,8 @@ Visit https://aka.ms/smtp_auth_disabled for more information.</code></pre>
<p><a name="faq25"></a> <strong>(25) Why cant I select/open/save an image, attachment or a file?</strong></p> <p><a name="faq25"></a> <strong>(25) Why cant I select/open/save an image, attachment or a file?</strong></p>
<p>🌎 <a href="https://translate.google.com/translate?sl=en&amp;u=https%3A%2F%2Fm66b.github.io%2FFairEmail%2F%23faq25">Google Translate</a></p> <p>🌎 <a href="https://translate.google.com/translate?sl=en&amp;u=https%3A%2F%2Fm66b.github.io%2FFairEmail%2F%23faq25">Google Translate</a></p>
<p>When a menu item to select/open/save a file is disabled (dimmed) or when you get the message <em>Storage access framework not available</em>, the <a href="https://developer.android.com/guide/topics/providers/document-provider">storage access framework</a>, a standard Android component, is probably not present. This might be because your custom ROM does not include it or because it was actively removed (debloated).</p> <p>When a menu item to select/open/save a file is disabled (dimmed) or when you get the message <em>Storage access framework not available</em>, the <a href="https://developer.android.com/guide/topics/providers/document-provider">storage access framework</a>, a standard Android component, is probably not present. This might be because your custom ROM does not include it or because it was actively removed (debloated).</p>
<p>FairEmail does not request storage permissions, so this framework is required to select files and folders. No app, except maybe file managers, targeting Android 4.4 KitKat or later should ask for storage permissions because it would allow access to <em>all</em> files.</p> <p>FairEmail does not request storage permissions, so this framework is required to select files and folders. No app, except maybe file managers, targeting Android 4.4 KitKat or later should ask for storage permissions because it would allow access to <em>all</em> files. Moreover, recent Android versions disallow access to all files for apps, except, under specific conditions, for file managers.</p>
<p>The storage access framework is provided by the package <em>com.android.documentsui</em>, which is visible as <em>Files</em> app on some Android versions (notable OxygenOS).</p> <p>The storage access framework is provided by the package <em>com.android.documentsui</em>, which is visible as <em>Files</em> app on some Android versions (notably OxygenOS).</p>
<p>You can enable the storage access framework (again) with this adb command:</p> <p>You can enable the storage access framework (again) with this adb command:</p>
<pre><code>pm install -k --user 0 com.android.documentsui</code></pre> <pre><code>pm install -k --user 0 com.android.documentsui</code></pre>
<p>Alternatively, you might be able to enable the <em>Files</em> app again using the Android app settings.</p> <p>Alternatively, you might be able to enable the <em>Files</em> app again using the Android app settings.</p>

Loading…
Cancel
Save