From bc5d23545487447c1b6f32833c6a67ab2f3306e1 Mon Sep 17 00:00:00 2001 From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> Date: Thu, 23 Jan 2020 14:27:59 -0500 Subject: [PATCH] contenturihandler: attempt to fix Huawei getUriForFile crashes a known issue supposedly fixed with: https://stackoverflow.com/a/41309223 Change-Id: I1372969f672514952baf1ea7ec932967ec751c8d --- .../ring/account/ProfileCreationFragment.java | 3 +- .../cx/ring/adapters/ConversationAdapter.java | 10 ++--- .../ring/fragments/ConversationFragment.java | 12 ++--- .../navigation/HomeNavigationFragment.java | 3 +- .../java/cx/ring/utils/ContentUriHandler.java | 45 +++++++++++++++++-- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/ring-android/app/src/main/java/cx/ring/account/ProfileCreationFragment.java b/ring-android/app/src/main/java/cx/ring/account/ProfileCreationFragment.java index 14925c2c6..a35751883 100644 --- a/ring-android/app/src/main/java/cx/ring/account/ProfileCreationFragment.java +++ b/ring-android/app/src/main/java/cx/ring/account/ProfileCreationFragment.java @@ -41,7 +41,6 @@ import android.widget.ImageView; import java.io.File; import java.io.IOException; -import androidx.core.content.FileProvider; import butterknife.BindView; import butterknife.OnClick; import butterknife.OnTextChanged; @@ -200,7 +199,7 @@ public class ProfileCreationFragment extends BaseSupportFragment<ProfileCreation try { Context context = requireContext(); File file = AndroidFileUtils.createImageFile(context); - Uri uri = FileProvider.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, file); + Uri uri = ContentUriHandler.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, file); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); tmpProfilePhotoUri = uri; diff --git a/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java index 5235cf0a4..3ec9e81f8 100644 --- a/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java +++ b/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java @@ -91,8 +91,6 @@ import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; -import static androidx.core.content.FileProvider.getUriForFile; - public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHolder> { private final static String TAG = ConversationAdapter.class.getSimpleName(); @@ -481,7 +479,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo .into(new DrawableImageViewTarget(viewHolder.mImage).waitForLayout()); viewHolder.mImage.setOnClickListener(v -> { - Uri contentUri = getUriForFile(v.getContext(), ContentUriHandler.AUTHORITY_FILES, path); + Uri contentUri = ContentUriHandler.getUriForFile(v.getContext(), ContentUriHandler.AUTHORITY_FILES, path); Intent i = new Intent(context, MediaViewerActivity.class); i.setAction(Intent.ACTION_VIEW).setDataAndType(contentUri, "image/*").setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); ActivityOptionsCompat options = ActivityOptionsCompat. @@ -494,7 +492,8 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo if (viewHolder.player != null) { viewHolder.player.release(); } - final MediaPlayer player = MediaPlayer.create(context, getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, path)); + final MediaPlayer player = MediaPlayer.create(context, + ContentUriHandler.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, path)); if (player == null) return; viewHolder.player = player; @@ -560,7 +559,8 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo Context context = viewHolder.itemView.getContext(); try { ((ImageView) viewHolder.btnAccept).setImageResource(R.drawable.baseline_play_arrow_24); - final MediaPlayer player = MediaPlayer.create(context, getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, path)); + final MediaPlayer player = MediaPlayer.create(context, + ContentUriHandler.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, path)); viewHolder.player = player; if (player != null) { player.setOnCompletionListener(mp -> { diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java index ad73e9614..993173f63 100644 --- a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java +++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java @@ -53,7 +53,6 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.view.menu.MenuBuilder; import androidx.appcompat.view.menu.MenuPopupHelper; import androidx.appcompat.widget.PopupMenu; -import androidx.core.content.FileProvider; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.DefaultItemAnimator; @@ -97,8 +96,6 @@ import io.reactivex.Completable; import io.reactivex.Single; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; import static android.app.Activity.RESULT_OK; @@ -357,7 +354,6 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen Log.e(TAG, "takePicture: error creating temporary file", ex); break; } - //intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, mCurrentPhoto)); startActivityForResult(intent, REQUEST_CODE_CAPTURE_AUDIO); } else { Toast.makeText(getActivity(), "Can't find audio recorder app", Toast.LENGTH_SHORT).show(); @@ -376,7 +372,7 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen Log.e(TAG, "takePicture: error creating temporary file", ex); break; } - intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, mCurrentPhoto)); + intent.putExtra(MediaStore.EXTRA_OUTPUT, ContentUriHandler.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, mCurrentPhoto)); startActivityForResult(intent, REQUEST_CODE_CAPTURE_VIDEO); } else { Toast.makeText(getActivity(), "Can't find video recorder app", Toast.LENGTH_SHORT).show(); @@ -421,7 +417,7 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen } Log.i(TAG, "takePicture: trying to save to " + photoFile); mCurrentPhoto = photoFile; - android.net.Uri photoURI = FileProvider.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, photoFile); + android.net.Uri photoURI = ContentUriHandler.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); takePictureIntent.putExtra("android.intent.extras.CAMERA_FACING", 1); takePictureIntent.putExtra("android.intent.extras.LENS_FACING_FRONT", 1); @@ -563,7 +559,7 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen return; android.net.Uri fileUri = null; try { - fileUri = FileProvider.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path); + fileUri = ContentUriHandler.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path); } catch (IllegalArgumentException e) { Log.e("File Selector", "The selected file can't be shared: " + path.getName()); } @@ -585,7 +581,7 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen return; android.net.Uri fileUri = null; try { - fileUri = FileProvider.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path); + fileUri = ContentUriHandler.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path); } catch (IllegalArgumentException e) { Log.e("File Selector", "The selected file can't be shared: " + path.getName()); } diff --git a/ring-android/app/src/main/java/cx/ring/navigation/HomeNavigationFragment.java b/ring-android/app/src/main/java/cx/ring/navigation/HomeNavigationFragment.java index 4de238c4a..6f6da39b5 100644 --- a/ring-android/app/src/main/java/cx/ring/navigation/HomeNavigationFragment.java +++ b/ring-android/app/src/main/java/cx/ring/navigation/HomeNavigationFragment.java @@ -46,7 +46,6 @@ import java.io.File; import java.util.ArrayList; import androidx.annotation.NonNull; -import androidx.core.content.FileProvider; import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -448,7 +447,7 @@ public class HomeNavigationFragment extends BaseSupportFragment<HomeNavigationPr try { Context context = requireContext(); File file = AndroidFileUtils.createImageFile(context); - Uri uri = FileProvider.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, file); + Uri uri = ContentUriHandler.getUriForFile(context, ContentUriHandler.AUTHORITY_FILES, file); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); tmpProfilePhotoUri = uri; diff --git a/ring-android/app/src/main/java/cx/ring/utils/ContentUriHandler.java b/ring-android/app/src/main/java/cx/ring/utils/ContentUriHandler.java index 24c0b1c02..8272aecd9 100644 --- a/ring-android/app/src/main/java/cx/ring/utils/ContentUriHandler.java +++ b/ring-android/app/src/main/java/cx/ring/utils/ContentUriHandler.java @@ -19,19 +19,22 @@ */ package cx.ring.utils; -import android.content.Intent; +import android.content.Context; import android.net.Uri; -import android.os.Bundle; +import android.os.Build; -import java.util.List; +import androidx.annotation.NonNull; +import androidx.core.content.FileProvider; + +import java.io.File; import cx.ring.BuildConfig; -import cx.ring.fragments.ConversationFragment; /** * This class distributes content uri used to pass along data in the app */ public class ContentUriHandler { + private final static String TAG = ContentUriHandler.class.getSimpleName(); public static final String AUTHORITY = BuildConfig.APPLICATION_ID; public static final String AUTHORITY_FILES = AUTHORITY + ".file_provider"; @@ -45,4 +48,38 @@ public class ContentUriHandler { private ContentUriHandler() { // hidden constructor } + + /** + * The following is a workaround used to mitigate getUriForFile exceptions + * on Huawei devices taken from stackoverflow + * https://stackoverflow.com/a/41309223 + */ + private static final String HUAWEI_MANUFACTURER = "Huawei"; + + public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file) { + if (HUAWEI_MANUFACTURER.equalsIgnoreCase(Build.MANUFACTURER)) { + Log.w(TAG, "Using a Huawei device Increased likelihood of failure..."); + try { + return FileProvider.getUriForFile(context, authority, file); + } catch (IllegalArgumentException e) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + Log.w(TAG, "Returning Uri.fromFile to avoid Huawei 'external-files-path' bug for pre-N devices", e); + return Uri.fromFile(file); + } else { + Log.w(TAG, "ANR Risk -- Copying the file the location cache to avoid Huawei 'external-files-path' bug for N+ devices", e); + // Note: Periodically clear this cache + final File cacheFolder = new File(context.getCacheDir(), HUAWEI_MANUFACTURER); + final File cacheLocation = new File(cacheFolder, file.getName()); + if (FileUtils.copyFile(file, cacheLocation)) { + Log.i(TAG, "Completed Android N+ Huawei file copy. Attempting to return the cached file"); + return FileProvider.getUriForFile(context, authority, cacheLocation); + } + Log.e(TAG, "Failed to copy the Huawei file. Re-throwing exception"); + throw new IllegalArgumentException("Huawei devices are unsupported for Android N"); + } + } + } else { + return FileProvider.getUriForFile(context, authority, file); + } + } } -- GitLab