diff --git a/.gitignore b/.gitignore
index ca6836599226eb0b8c3fc91b81320c143443829f..6b91a094ddc598f42be58e047b484adc972f9a56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
 env.sh
 obj/
 android-toolchain-*/
+.idea/
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 18f688351fb4801527da0df2ad50d9f8fa05fc70..99ea1786b16c64ab4e9908ba086f46948273aa49 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
@@ -311,12 +311,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
 
         switch (item.getItemId()) {
             case R.id.conv_action_download: {
-                File downloadDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "Ring");
-                downloadDir.mkdirs();
-                File newFile = new File(downloadDir, ((DataTransfer) conversationElement).getDisplayName());
-                if (newFile.exists())
-                    newFile.delete();
-                presenter.downloadFile((DataTransfer) conversationElement, newFile);
+                presenter.saveFile((DataTransfer) conversationElement);
                 break;
             }
             case R.id.conv_action_share: {
diff --git a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
index 0c0187a4df45a29ec9a0b2b0b0e12288f782da78..cec3c01262d27678403db8d4bbe26f31cbd7272a 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
@@ -23,13 +23,13 @@ package cx.ring.client;
 import android.content.Intent;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.Menu;
 
 import androidx.annotation.ColorInt;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
-import android.view.KeyEvent;
-import android.view.Menu;
 
 import butterknife.BindView;
 import butterknife.ButterKnife;
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 3f8cc908b100ebf93e732962d6f70a8fcec13097..ae76b6f0e7727487d4847d6b15341ccdc1bb3752 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
@@ -31,15 +31,6 @@ import android.content.pm.PackageManager;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Bundle;
 import android.provider.MediaStore;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import androidx.core.content.FileProvider;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.recyclerview.widget.DefaultItemAnimator;
-
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -56,6 +47,13 @@ import android.widget.Toast;
 
 import com.google.android.material.snackbar.Snackbar;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.FileProvider;
+import androidx.recyclerview.widget.DefaultItemAnimator;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
@@ -110,6 +108,7 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
     private static final int REQUEST_CODE_FILE_PICKER = 1000;
     private static final int REQUEST_PERMISSION_CAMERA = 1001;
     private static final int REQUEST_CODE_TAKE_PICTURE = 1002;
+    private static final int REQUEST_CODE_SAVE_FILE = 1003;
 
     private FragConversationBinding binding;
     private MenuItem mAudioCallBtn = null;
@@ -123,6 +122,7 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
     private SharedPreferences mPreferences;
 
     private File mCurrentPhoto = null;
+    private String mCurrentFileAbsolutePath = null;
     private Disposable actionbarTarget = null;
     private static int position;
 
@@ -379,6 +379,27 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
             mCurrentPhoto = null;
             startFileSend(sendFile(file));
         }
+        // File download trough SAF
+        else if(requestCode == ConversationFragment.REQUEST_CODE_SAVE_FILE
+                && resultCode == RESULT_OK){
+            if(resultData != null && resultData.getData() != null ) {
+                //Get the Uri of the file that was created by the app that received our intent
+                android.net.Uri createdUri = resultData.getData();
+
+                //Try to copy the data of the current file into the newly created one
+                File input = new File(mCurrentFileAbsolutePath);
+                if(requireContext().getContentResolver() != null)
+                    AndroidFileUtils.copyFileToUri(
+                            requireContext().getContentResolver(),input,createdUri).
+                            observeOn(AndroidSchedulers.mainThread()).
+                            subscribe(()->Toast.makeText(getContext(), R.string.file_saved_successfully,
+                                    Toast.LENGTH_SHORT).show(),
+                                    error->Toast.makeText(getContext(), R.string.generic_error,
+                                            Toast.LENGTH_SHORT).show());
+
+            }
+        }
+
     }
 
     @Override
@@ -725,20 +746,6 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
         }
     }
 
-    @Override
-    public void displayCompletedDownload(DataTransfer transfer, File destination) {
-        DownloadManager downloadManager = (DownloadManager) requireContext().getSystemService(Context.DOWNLOAD_SERVICE);
-        if (downloadManager != null) {
-            downloadManager.addCompletedDownload(transfer.getDisplayName(),
-                    transfer.getDisplayName(),
-                    true,
-                    AndroidFileUtils.getMimeType(destination.getAbsolutePath()),
-                    destination.getAbsolutePath(),
-                    destination.length(),
-                    true);
-        }
-    }
-
     public void handleShareIntent(Intent intent) {
         String type = intent.getType();
         if (type == null) {
@@ -757,4 +764,26 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
             startFileSend(AndroidFileUtils.getCacheFile(requireContext(), uri).flatMapCompletable(this::sendFile));
         }
     }
+
+    /**
+     * Creates an intent using Android Storage Access Framework
+     * This intent is then received by applications that can handle it like
+     * Downloads or Google drive
+     * @param file DataTransfer of the file that is going to be stored
+     * @param currentFileAbsolutePath absolute path of the file we want to save
+     */
+    public void startSaveFile(DataTransfer file, String currentFileAbsolutePath){
+        //Get the current file absolute path and store it
+        mCurrentFileAbsolutePath = currentFileAbsolutePath;
+
+        //Use Android Storage File Access to download the file
+        Intent downloadFileIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+        downloadFileIntent.setType(AndroidFileUtils.getMimeTypeFromExtension(file.getExtension()));
+
+        downloadFileIntent.addCategory(Intent.CATEGORY_OPENABLE);
+        downloadFileIntent.putExtra(Intent.EXTRA_TITLE,file.getDisplayName());
+
+        startActivityForResult(downloadFileIntent, ConversationFragment.REQUEST_CODE_SAVE_FILE);
+    }
+
 }
diff --git a/ring-android/app/src/main/java/cx/ring/utils/AndroidFileUtils.java b/ring-android/app/src/main/java/cx/ring/utils/AndroidFileUtils.java
index e8b9c576022b8da3ddaf9b0e7caa52ec4a565987..473eb101ff4688c2bacaafa8ccff4a4abc096554 100644
--- a/ring-android/app/src/main/java/cx/ring/utils/AndroidFileUtils.java
+++ b/ring-android/app/src/main/java/cx/ring/utils/AndroidFileUtils.java
@@ -182,15 +182,20 @@ public class AndroidFileUtils {
 
     public static String getMimeType(String filename) {
         int pos = filename.lastIndexOf(".");
+        String fileExtension = null;
         if (pos >= 0) {
-            String fileExtension = MimeTypeMap.getFileExtensionFromUrl(filename.substring(pos));
-            String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.toLowerCase());
+            fileExtension = MimeTypeMap.getFileExtensionFromUrl(filename.substring(pos));
+        }
+        return getMimeTypeFromExtension(fileExtension);
+    }
+
+    public static String getMimeTypeFromExtension(String ext) {
+            String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.toLowerCase());
             if (!TextUtils.isEmpty(mimeType))
                 return mimeType;
-            if (fileExtension.contentEquals("gz")) {
+            if (ext.contentEquals("gz")) {
                 return "application/gzip";
             }
-        }
         return "application/octet-stream";
     }
 
@@ -242,6 +247,31 @@ public class AndroidFileUtils {
         }).subscribeOn(Schedulers.io());
     }
 
+    /**
+     * Copies a file to a predefined Uri destination
+     * Uses the underlying copyFile(InputStream,OutputStream)
+     * @param cr content resolver
+     * @param input the file we want to copy
+     * @param outUri the uri destination
+     * @return success value
+     */
+    public static Completable copyFileToUri(ContentResolver cr, File input, Uri outUri){
+        return Completable.fromAction(() -> {
+            InputStream inputStream = null;
+            OutputStream outputStream = null;
+            try {
+                inputStream = new FileInputStream(input);
+                outputStream = cr.openOutputStream(outUri);
+                FileUtils.copyFile(inputStream, outputStream);
+            } finally {
+                if (outputStream != null)
+                    outputStream.close();
+                if (inputStream != null)
+                    inputStream.close();
+            }
+        }).subscribeOn(Schedulers.io());
+    }
+
     public static File getConversationFile(Context context, Uri uri, String conversationId, String name) throws IOException {
         File file = getConversationPath(context, conversationId, name);
         FileOutputStream output = new FileOutputStream(file);
diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml
index 38ff8327e0d68f981f3746eb86dadc04425d3621..157733aba31867e7ff4d47795701f6ff84ace9ad 100644
--- a/ring-android/app/src/main/res/values/strings.xml
+++ b/ring-android/app/src/main/res/values/strings.xml
@@ -275,10 +275,11 @@ along with this program; if not, write to the Free Software
     <string name="file_transfer_status_invalid_pathname">invalid pathname</string>
     <string name="file_transfer_status_unjoinable_peer">unjoinable peer</string>
     <string name="file_saved_in">File saved in %s</string>
+    <string name="file_saved_successfully">File saved successfully</string>
     <string name="no_space_left_on_device">No space left on device</string>
     <string name="title_media_viewer">Media viewer</string>
     <string name="menu_file_open">Open file</string>
-    <string name="menu_file_download">Download file</string>
+    <string name="menu_file_download">Save file</string>
     <string name="menu_file_delete">Delete file</string>
     <string name="menu_file_share">Share file</string>
     <string name="menu_message_copy">Copy</string>
diff --git a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
index 9b66e985755786ab79bcb36a24c3c801a165a84e..ead9ea8091115345e98c58f21c4ac3bc98265faa 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
@@ -232,25 +232,16 @@ public class ConversationPresenter extends RootPresenter<ConversationView> {
         mConversationFacade.sendFile(mAccountId, mContactRingId, file).subscribe();
     }
 
-    public void downloadFile(final DataTransfer transfer, final File dest) {
-        mCompositeDisposable.add(
-                Single.fromCallable(() -> {
-                    if (!transfer.isComplete())
-                        throw new IllegalStateException();
-                    File file = getDeviceRuntimeService().getConversationPath(transfer.getPeerId(), transfer.getStoragePath());
-                    if (FileUtils.copyFile(file, dest)) {
-                        Log.w(TAG, "Copied file to " + dest.getAbsolutePath() + " (" + FileUtils.readableFileSize(file.length()) + ")");
-                        return dest;
-                    }
-                    throw new IOException();
-                })
-                .subscribeOn(Schedulers.io())
-                .observeOn(mUiScheduler)
-                .subscribe(file -> {
-                    getView().displayCompletedDownload(transfer, file);
-                }, error -> {
-                    Log.e(TAG, "Can't download file " + dest, error);
-                }));
+    /**
+     * Gets the absolute path of the file dataTransfer and sends both the DataTransfer and the
+     * found path to the ConversationView in order to start saving the file
+     * @param transfer DataTransfer of the file
+     */
+    public void saveFile(DataTransfer transfer) {
+       String fileAbsolutePath =  getDeviceRuntimeService().
+                getConversationPath(transfer.getPeerId(), transfer.getStoragePath())
+               .getAbsolutePath();
+        getView().startSaveFile(transfer,fileAbsolutePath);
     }
 
     public void shareFile(DataTransfer file) {
diff --git a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationView.java b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationView.java
index 458f54bcdf7a2b90523b6eef083d60ba307936a5..801ed5fd996de2c710149985b55899c292f27737 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationView.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationView.java
@@ -43,8 +43,6 @@ public interface ConversationView extends BaseView {
 
     void displayErrorToast(int error);
 
-    void displayCompletedDownload(DataTransfer transfer, File destination);
-
     void hideNumberSpinner();
 
     void clearMsgEdit();
@@ -78,4 +76,6 @@ public interface ConversationView extends BaseView {
     void removeElement(ConversationElement e);
 
     void setConversationColor(int integer);
+
+    void startSaveFile(DataTransfer currentFile, String fileAbsolutePath);
 }