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