account: import/export

Add an option to export an account
Allow the user to import an account from his storage.
Ask permission if necessary.
Update translations.

Tuleap: #335
Change-Id: I6afefefb252871319930a0eb55bcf1a028a3da10
diff --git a/ring-android/app/src/main/AndroidManifest.xml b/ring-android/app/src/main/AndroidManifest.xml
index c466991..45c3e23 100644
--- a/ring-android/app/src/main/AndroidManifest.xml
+++ b/ring-android/app/src/main/AndroidManifest.xml
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?><!--
 Copyright (C) 2004-2016 Savoir-faire Linux Inc.
-                                                                    
+
 Author: 	Alexandre Lision <alexandre.lision@savoirfairelinux.com>
 			Adrien Beraud <adrien.beraud@savoirfairelinux.com>
-                                                                    
+
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3 of the License, or   
-(at your option) any later version.                                 
-                                                                    
-This program is distributed in the hope that it will be useful,     
-but WITHOUT ANY WARRANTY; without even the implied warranty of      
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       
-GNU General Public License for more details.                        
-                                                                    
-You should have received a copy of the GNU General Public License   
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/ring-android/app/src/main/java/cx/ring/client/AccountEditionActivity.java b/ring-android/app/src/main/java/cx/ring/client/AccountEditionActivity.java
index bf173da..ffbcdfc 100644
--- a/ring-android/app/src/main/java/cx/ring/client/AccountEditionActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/AccountEditionActivity.java
@@ -21,46 +21,69 @@
 
 package cx.ring.client;
 
+import android.Manifest;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Fragment;
 import android.app.FragmentManager;
-import android.content.*;
+import android.app.ProgressDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
 import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
 import android.util.Log;
 import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
+import android.widget.TextView;
+import android.widget.Toast;
 
 import com.astuetz.PagerSlidingTabStrip;
 
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Observable;
+import java.util.Observer;
+
 import cx.ring.R;
 import cx.ring.fragments.AdvancedAccountFragment;
+import cx.ring.fragments.GeneralAccountFragment;
 import cx.ring.fragments.MediaPreferenceFragment;
 import cx.ring.fragments.SecurityAccountFragment;
 import cx.ring.model.account.Account;
 import cx.ring.service.IDRingService;
 import cx.ring.service.LocalService;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.Observable;
-import java.util.Observer;
-
-import cx.ring.fragments.GeneralAccountFragment;
 
 public class AccountEditionActivity extends AppCompatActivity implements LocalService.Callbacks, GeneralAccountFragment.Callbacks, MediaPreferenceFragment.Callbacks,
         AdvancedAccountFragment.Callbacks, SecurityAccountFragment.Callbacks {
     private static final String TAG = AccountEditionActivity.class.getSimpleName();
 
     public static final Uri CONTENT_URI = Uri.withAppendedPath(LocalService.AUTHORITY_URI, "accounts");
+    private static final int REQUEST_WRITE_STORAGE = 112;
 
     private boolean mBound = false;
     private LocalService service;
@@ -87,6 +110,9 @@
             Log.i(TAG, "Service connected " + className.getClassName() + " " + getIntent().getData().toString());
 
             acc_selected = service.getAccount(account_id);
+            if (acc_selected == null)
+                finish();
+
             acc_selected.addObserver(mAccountObserver);
             getSupportActionBar().setTitle(acc_selected.getAlias());
 
@@ -97,8 +123,7 @@
                 fragments.add(new Pair<String, Fragment>(getString(R.string.account_preferences_basic_tab), new GeneralAccountFragment()));
                 fragments.add(new Pair<String, Fragment>(getString(R.string.account_preferences_media_tab), new MediaPreferenceFragment()));
                 fragments.add(new Pair<String, Fragment>(getString(R.string.account_preferences_advanced_tab), new AdvancedAccountFragment()));
-                if(acc_selected.isSip())
-                {
+                if (acc_selected.isSip()) {
                     fragments.add(new Pair<String, Fragment>(getString(R.string.account_preferences_security_tab), new SecurityAccountFragment()));
                 }
             }
@@ -154,15 +179,18 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-
+        AlertDialog dialog;
         switch (item.getItemId()) {
-        case android.R.id.home:
-            finish();
-            return true;
-        case R.id.menuitem_delete:
-            AlertDialog dialog = createDeleteDialog();
-            dialog.show();
-            break;
+            case android.R.id.home:
+                finish();
+                return true;
+            case R.id.menuitem_delete:
+                dialog = createDeleteDialog();
+                dialog.show();
+                break;
+            case R.id.menuitem_export:
+                startExport();
+                break;
         }
 
         return true;
@@ -176,7 +204,7 @@
     private void processAccount() {
         final Account acc = acc_selected;
         final IDRingService remote = getRemoteService();
-        getSupportActionBar().setTitle(acc.getAlias());;
+        getSupportActionBar().setTitle(acc.getAlias());
         service.getThreadPool().submit(new Runnable() {
             @Override
             public void run() {
@@ -193,8 +221,8 @@
     private AlertDialog createDeleteDialog() {
         Activity ownerActivity = this;
         AlertDialog.Builder builder = new AlertDialog.Builder(ownerActivity);
-        builder.setMessage("Do you really want to delete this account").setTitle("Delete Account")
-                .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
+        builder.setMessage(R.string.account_delete_confirmation).setTitle(R.string.account_delete_dialog_title)
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int whichButton) {
                         Bundle bundle = new Bundle();
@@ -207,12 +235,12 @@
                         }
                         finish();
                     }
-                }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int whichButton) {
+                }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int whichButton) {
                         /* Terminate with no action */
-                    }
-                });
+            }
+        });
 
         AlertDialog alertDialog = builder.create();
         alertDialog.setOwnerActivity(ownerActivity);
@@ -220,6 +248,180 @@
         return alertDialog;
     }
 
+    boolean checkPassword(@NonNull TextView pwd, TextView confirm) {
+        boolean error = false;
+        if (pwd.getText().length() < 6) {
+            pwd.setError(getString(R.string.error_password_char_count));
+            error = true;
+        } else {
+            pwd.setError(null);
+        }
+        if (confirm != null) {
+            if (!pwd.getText().toString().equals(confirm.getText().toString())) {
+                confirm.setError(getString(R.string.error_passwords_not_equals));
+                error = true;
+            } else {
+                confirm.setError(null);
+            }
+        }
+        return error;
+    }
+
+    private void startExport()
+    {
+        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
+        if (hasPermission) {
+            showExportDialog();
+        } else {
+            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        switch (requestCode) {
+            case REQUEST_WRITE_STORAGE: {
+                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    showExportDialog();
+                } else {
+                    Toast.makeText(this, R.string.permission_write_denied, Toast.LENGTH_LONG).show();
+                }
+            }
+        }
+    }
+
+    private AlertDialog showExportDialog() {
+        Activity ownerActivity = this;
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(ownerActivity);
+        LayoutInflater inflater = getLayoutInflater();
+        ViewGroup v = (ViewGroup) inflater.inflate(R.layout.dialog_account_export, null);
+        final TextView pwd = (TextView) v.findViewById(R.id.newpwd_txt);
+        final TextView pwd_confirm = (TextView) v.findViewById(R.id.newpwd_confirm_txt);
+        builder.setMessage(R.string.account_export_message)
+                .setTitle(R.string.account_export_title)
+                .setPositiveButton(R.string.account_export, null)
+                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int whichButton) {
+                /* Terminate with no action */
+            }
+        }).setView(v);
+
+
+        final AlertDialog alertDialog = builder.create();
+        pwd.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+            @Override
+            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+                Log.w(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+                if (actionId == EditorInfo.IME_ACTION_NEXT)
+                    return checkPassword(v, null);
+                return false;
+            }
+        });
+        pwd.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (!hasFocus) {
+                    checkPassword((TextView) v, null);
+                }
+                else {
+                    alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+                }
+            }
+        });
+        pwd_confirm.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+            @Override
+            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+                Log.w(TAG, "onEditorAction " + actionId + " " + (event == null ? null : event.toString()));
+                if (actionId == EditorInfo.IME_ACTION_DONE) {
+                    if (!checkPassword(pwd, v)) {
+                        alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).callOnClick();
+                        return true;
+                    }
+                }
+                return false;
+            }
+        });
+
+
+        alertDialog.setOwnerActivity(ownerActivity);
+        alertDialog.show();
+        alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (!checkPassword(pwd, pwd_confirm)) {
+                    final String pwd_txt = pwd.getText().toString();
+                    alertDialog.dismiss();
+                    new ExportAccountTask().execute(pwd_txt);
+                }
+            }
+        });
+
+        return alertDialog;
+    }
+
+    public File getExportStorageDir() {
+        // Get the directory for the user's public pictures directory.
+        String env = Environment.DIRECTORY_DOWNLOADS;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+            env = Environment.DIRECTORY_DOCUMENTS;
+
+        File path = Environment.getExternalStoragePublicDirectory(env);
+
+        if (!path.mkdirs() && !path.isDirectory()) {
+            Log.w(TAG, "Directory " + path.getAbsolutePath() + " not created, using fallback");
+            // Fallback case, use Downloads
+            env = Environment.DIRECTORY_DOWNLOADS;
+            path = Environment.getExternalStoragePublicDirectory(env);
+
+            if (!path.mkdirs() && !path.isDirectory())
+                Log.e(TAG, "Fallback on " + path.getAbsolutePath() + " failed!");
+        }
+
+        return new File(path, getAccount().getAlias() + ".ring");
+    }
+
+    private class ExportAccountTask extends AsyncTask<String, Void, Integer> {
+        private ProgressDialog loading_dialog = null;
+        private String path;
+
+        @Override
+        protected void onPreExecute() {
+            loading_dialog = ProgressDialog.show(AccountEditionActivity.this,
+                    getString(R.string.export_dialog_title),
+                    getString(R.string.import_export_wait), true);
+            loading_dialog.setCancelable(false);
+        }
+
+        protected Integer doInBackground(String... args) {
+            int ret = 1;
+            ArrayList<String> ids = new ArrayList<>(1);
+            ids.add(acc_selected.getAccountID());
+            File fpath = getExportStorageDir();
+            path = fpath.getAbsolutePath();
+            try {
+                ret = getRemoteService().exportAccounts(ids, path, args[0]);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+            return ret;
+        }
+
+        protected void onPostExecute(Integer ret) {
+            if (loading_dialog != null)
+                loading_dialog.dismiss();
+            Log.d(TAG, "Account export to " + path + " returned " + ret);
+            if (ret == 0) {
+                Snackbar.make(findViewById(android.R.id.content), getString(R.string.account_export_result, path), Snackbar.LENGTH_INDEFINITE).show();
+            } else
+                new AlertDialog.Builder(AccountEditionActivity.this).setTitle(R.string.export_failed_dialog_title)
+                        .setMessage(R.string.export_failed_dialog_msg)
+                        .setPositiveButton(android.R.string.ok, null).show();
+        }
+    }
+
     @Override
     public IDRingService getRemoteService() {
         return service.getRemoteService();
@@ -230,6 +432,16 @@
         return service;
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+    }
+
     private static class PreferencesPagerAdapter extends FragmentPagerAdapter {
         private final ArrayList<Pair<String, Fragment>> fragments;
 
diff --git a/ring-android/app/src/main/java/cx/ring/client/AccountWizard.java b/ring-android/app/src/main/java/cx/ring/client/AccountWizard.java
index 58ed1ef..91a624f 100644
--- a/ring-android/app/src/main/java/cx/ring/client/AccountWizard.java
+++ b/ring-android/app/src/main/java/cx/ring/client/AccountWizard.java
@@ -21,15 +21,15 @@
 
 package cx.ring.client;
 
-import android.app.Fragment;
-import android.app.FragmentManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.support.v13.app.FragmentStatePagerAdapter;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.app.Fragment;
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.AppCompatActivity;
 import android.util.Log;
@@ -71,7 +71,7 @@
 
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
         getSupportActionBar().setHomeButtonEnabled(true);
-        SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(AccountWizard.this, getFragmentManager());
+        SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(AccountWizard.this, getSupportFragmentManager());
         mViewPager.setAdapter(mSectionsPagerAdapter);
 
         if (!mBound) {
@@ -111,14 +111,13 @@
         public SectionsPagerAdapter(Context c, FragmentManager fm) {
             super(fm);
             mContext = c;
-            fragments = new ArrayList<Fragment>();
+            fragments = new ArrayList<>();
             fragments.add(new AccountCreationFragment());
 
         }
 
         @Override
         public Fragment getItem(int i) {
-
             return fragments.get(i);
         }
 
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AccountCreationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AccountCreationFragment.java
index 4633b20..fc964be 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AccountCreationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AccountCreationFragment.java
@@ -20,15 +20,31 @@
 
 package cx.ring.fragments;
 
+import android.Manifest;
+import android.annotation.SuppressLint;
 import android.app.Activity;
-import android.app.Fragment;
+import android.app.AlertDialog;
 import android.app.ProgressDialog;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.RemoteException;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -48,6 +64,9 @@
 public class AccountCreationFragment extends Fragment {
     static final String TAG = AccountCreationFragment.class.getSimpleName();
 
+    static private final int FILE_SELECT_CODE = 2;
+    private static final int REQUEST_READ_STORAGE = 113;
+
     // Values for email and password at the time of the login attempt.
     private String mAlias;
     private String mHostname;
@@ -61,6 +80,8 @@
     private EditText mUsernameView;
     private EditText mPasswordView;
 
+    private String mDataPath;
+
     private LocalService.Callbacks mCallbacks = LocalService.DUMMY_CALLBACKS;
     private boolean creatingAccount = false;
 
@@ -108,10 +129,261 @@
                 attemptCreation();
             }
         });
+        inflatedView.findViewById(R.id.select_file_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                startImport();
+            }
+        });
 
         return inflatedView;
     }
 
+
+    /**
+     * Get a file path from a Uri. This will get the the path for Storage Access
+     * Framework Documents, as well as the _data field for the MediaStore and
+     * other file-based ContentProviders.
+     *
+     * @param context The context.
+     * @param uri     The Uri to query.
+     * @author paulburke
+     */
+    @SuppressLint("NewApi")
+    public static String getPath(final Context context, final Uri uri) {
+
+        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+
+        // DocumentProvider
+        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
+            // ExternalStorageProvider
+            if (isExternalStorageDocument(uri)) {
+                final String docId = DocumentsContract.getDocumentId(uri);
+                final String[] split = docId.split(":");
+                final String type = split[0];
+
+                if ("primary".equalsIgnoreCase(type)) {
+                    return Environment.getExternalStorageDirectory() + "/" + split[1];
+                }
+
+                // TODO handle non-primary volumes
+            }
+            // DownloadsProvider
+            else if (isDownloadsDocument(uri)) {
+
+                final String id = DocumentsContract.getDocumentId(uri);
+                final Uri contentUri = ContentUris.withAppendedId(
+                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+                return getDataColumn(context, contentUri, null, null);
+            }
+            // MediaProvider
+            else if (isMediaDocument(uri)) {
+                final String docId = DocumentsContract.getDocumentId(uri);
+                final String[] split = docId.split(":");
+                final String type = split[0];
+
+                Uri contentUri = null;
+                if ("image".equals(type)) {
+                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+                } else if ("video".equals(type)) {
+                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+                } else if ("audio".equals(type)) {
+                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+                }
+
+                final String selection = "_id=?";
+                final String[] selectionArgs = new String[]{
+                        split[1]
+                };
+
+                return getDataColumn(context, contentUri, selection, selectionArgs);
+            }
+        }
+        // MediaStore (and general)
+        else if ("content".equalsIgnoreCase(uri.getScheme())) {
+            return getDataColumn(context, uri, null, null);
+        }
+        // File
+        else if ("file".equalsIgnoreCase(uri.getScheme())) {
+            return uri.getPath();
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the value of the data column for this Uri. This is useful for
+     * MediaStore Uris, and other file-based ContentProviders.
+     *
+     * @param context       The context.
+     * @param uri           The Uri to query.
+     * @param selection     (Optional) Filter used in the query.
+     * @param selectionArgs (Optional) Selection arguments used in the query.
+     * @return The value of the _data column, which is typically a file path.
+     */
+    public static String getDataColumn(Context context, Uri uri, String selection,
+                                       String[] selectionArgs) {
+
+        Cursor cursor = null;
+        final String column = "_data";
+        final String[] projection = {
+                column
+        };
+
+        try {
+            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+                    null);
+            if (cursor != null && cursor.moveToFirst()) {
+                final int column_index = cursor.getColumnIndexOrThrow(column);
+                return cursor.getString(column_index);
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Can't find data column", e);
+        } finally {
+            if (cursor != null)
+                cursor.close();
+        }
+        return null;
+    }
+
+
+    /**
+     * @param uri The Uri to check.
+     * @return Whether the Uri authority is ExternalStorageProvider.
+     */
+    public static boolean isExternalStorageDocument(Uri uri) {
+        return "com.android.externalstorage.documents".equals(uri.getAuthority());
+    }
+
+    /**
+     * @param uri The Uri to check.
+     * @return Whether the Uri authority is DownloadsProvider.
+     */
+    public static boolean isDownloadsDocument(Uri uri) {
+        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+    }
+
+    /**
+     * @param uri The Uri to check.
+     * @return Whether the Uri authority is MediaProvider.
+     */
+    public static boolean isMediaDocument(Uri uri) {
+        return "com.android.providers.media.documents".equals(uri.getAuthority());
+    }
+
+    private void presentFilePicker() {
+        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+        intent.setType("*/*");
+        startActivityForResult(intent, FILE_SELECT_CODE);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case FILE_SELECT_CODE:
+                if (resultCode == Activity.RESULT_OK) {
+                    Log.w(TAG, "onActivityResult " + data.getDataString());
+                    this.mDataPath = getPath(getActivity(), data.getData());
+                    if (TextUtils.isEmpty(this.mDataPath))
+                        Toast.makeText(getActivity(), "Can't read " + data.getData(), Toast.LENGTH_LONG).show();
+                    else
+                        showImportDialog();
+                }
+                break;
+        }
+    }
+
+    private AlertDialog showImportDialog() {
+        Activity ownerActivity = getActivity();
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(ownerActivity);
+        LayoutInflater inflater = ownerActivity.getLayoutInflater();
+        ViewGroup v = (ViewGroup) inflater.inflate(R.layout.dialog_account_import, null);
+        final TextView pwd = (TextView) v.findViewById(R.id.pwd_txt);
+        builder.setMessage(R.string.account_import_message)
+                .setTitle(R.string.account_import_account)
+                .setPositiveButton(R.string.account_import_account, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        if (!TextUtils.isEmpty(mDataPath)) {
+                            new ImportAccountTask().execute(mDataPath, pwd.getText().toString());
+                        }
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int whichButton) {
+                /* Terminate with no action */
+                    }
+                }).setView(v);
+
+        final AlertDialog alertDialog = builder.create();
+        alertDialog.show();
+        return alertDialog;
+    }
+
+    private void startImport() {
+        Activity activity = getActivity();
+        if (null != activity) {
+            boolean hasPermission = (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
+            if (hasPermission) {
+                presentFilePicker();
+            } else {
+                requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_READ_STORAGE);
+            }
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        switch (requestCode) {
+            case REQUEST_READ_STORAGE: {
+                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    presentFilePicker();
+                } else {
+                    Activity activity = getActivity();
+                    if (null != activity) {
+                        Toast.makeText(activity, R.string.permission_read_denied, Toast.LENGTH_LONG).show();
+                    }
+                }
+            }
+        }
+    }
+
+    private class ImportAccountTask extends AsyncTask<String, Void, Integer> {
+        private ProgressDialog loading_dialog = null;
+
+        @Override
+        protected void onPreExecute() {
+            loading_dialog = ProgressDialog.show(getActivity(),
+                    getActivity().getString(R.string.import_dialog_title),
+                    getActivity().getString(R.string.import_export_wait), true);
+        }
+
+        protected Integer doInBackground(String... args) {
+            int ret = 1;
+            try {
+                ret = mCallbacks.getRemoteService().importAccounts(args[0], args[1]);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+            return ret;
+        }
+
+        protected void onPostExecute(Integer ret) {
+            if (loading_dialog != null)
+                loading_dialog.dismiss();
+            if (ret == 0)
+                getActivity().finish();
+            else
+                new AlertDialog.Builder(getActivity()).setTitle(R.string.import_failed_dialog_title)
+                        .setMessage(R.string.import_failed_dialog_msg)
+                        .setPositiveButton(android.R.string.ok, null).show();
+        }
+    }
+
     @Override
     public void onResume() {
         super.onResume();
@@ -120,7 +392,7 @@
     @Override
     public void onStart() {
         super.onStart();
-        ((AppCompatActivity)getActivity()).getSupportActionBar().setTitle(R.string.ab_account_creation);
+        ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.ab_account_creation);
     }
 
     @Override
@@ -188,9 +460,7 @@
 
     @SuppressWarnings("unchecked")
     private void initCreation() {
-
         try {
-
             HashMap<String, String> accountDetails = (HashMap<String, String>) mCallbacks.getRemoteService().getAccountTemplate(mAccountType);
             accountDetails.put(AccountDetailBasic.CONFIG_ACCOUNT_TYPE, mAccountType);
             accountDetails.put(AccountDetailBasic.CONFIG_VIDEO_ENABLED, "true");
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
index ebbd68b..fde9527 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/AccountsManagementFragment.java
@@ -98,9 +98,7 @@
         mAccountsAdapter = new AccountsAdapter(getActivity());
         this.setHasOptionsMenu(true);
 
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(LocalService.ACTION_ACCOUNT_UPDATE);
-        getActivity().registerReceiver(mReceiver, intentFilter);
+        getActivity().registerReceiver(mReceiver, new IntentFilter(LocalService.ACTION_ACCOUNT_UPDATE));
     }
 
     @Override
diff --git a/ring-android/app/src/main/java/cx/ring/service/DRingService.java b/ring-android/app/src/main/java/cx/ring/service/DRingService.java
index f4486a3..fc3420c 100644
--- a/ring-android/app/src/main/java/cx/ring/service/DRingService.java
+++ b/ring-android/app/src/main/java/cx/ring/service/DRingService.java
@@ -1426,5 +1426,25 @@
             });
         }
 
+        public int exportAccounts(final List accountIDs, final String toDir, final String password) {
+            return getExecutor().executeAndReturn(new SipRunnableWithReturn<Integer>() {
+                @Override
+                protected Integer doRun() throws SameThreadException, RemoteException {
+                    StringVect ids = new StringVect();
+                    for (Object s : accountIDs)
+                        ids.add((String)s);
+                    return Ringservice.exportAccounts(ids, toDir, password);
+                }
+            });
+        }
+
+        public int importAccounts(final String archivePath, final String password) {
+            return getExecutor().executeAndReturn(new SipRunnableWithReturn<Integer>() {
+                @Override
+                protected Integer doRun() throws SameThreadException, RemoteException {
+                    return Ringservice.importAccounts(archivePath, password);
+                }
+            });
+        }
     };
 }
diff --git a/ring-android/app/src/main/java/cx/ring/service/IDRingService.aidl b/ring-android/app/src/main/java/cx/ring/service/IDRingService.aidl
index 20bf9d3..1093d78 100644
--- a/ring-android/app/src/main/java/cx/ring/service/IDRingService.aidl
+++ b/ring-android/app/src/main/java/cx/ring/service/IDRingService.aidl
@@ -108,4 +108,6 @@
 
     Map getConference(in String id);
 
+    int exportAccounts(in List accountIDs, in String toDir, in String password);
+    int importAccounts(in String archivePath, in String password);
 }
diff --git a/ring-android/app/src/main/jni/configurationmanager.i b/ring-android/app/src/main/jni/configurationmanager.i
index e6322b0..173c6cc 100644
--- a/ring-android/app/src/main/jni/configurationmanager.i
+++ b/ring-android/app/src/main/jni/configurationmanager.i
@@ -164,6 +164,9 @@
 
 void sendTrustRequest(const std::string& accountId, const std::string& to, const std::vector<uint8_t>& payload);
 
+int exportAccounts(std::vector<std::string> accountIDs, std::string toDir, std::string password);
+int importAccounts(std::string archivePath, std::string password);
+
 }
 
 class ConfigurationCallback {
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_archive_black_48dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_archive_black_48dp.png
new file mode 100644
index 0000000..d6d60f6
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_archive_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_archive_white_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_archive_white_24dp.png
new file mode 100644
index 0000000..bb72e89
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png
new file mode 100644
index 0000000..18730f1
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_unarchive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_archive_black_48dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_archive_black_48dp.png
new file mode 100644
index 0000000..9b88218
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_archive_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_archive_white_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_archive_white_24dp.png
new file mode 100644
index 0000000..f6aa3f9
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png
new file mode 100644
index 0000000..8ec62cd
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_unarchive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png
new file mode 100644
index 0000000..b8c0376
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png
new file mode 100644
index 0000000..3513bd9
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png
new file mode 100644
index 0000000..a0a1509
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_unarchive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png
new file mode 100644
index 0000000..6c956e6
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png
new file mode 100644
index 0000000..00e04e4
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png
new file mode 100644
index 0000000..20d0157
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_unarchive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png
new file mode 100644
index 0000000..abdadd0
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png
new file mode 100644
index 0000000..34cd3fd
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png
new file mode 100644
index 0000000..a789520
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_unarchive_white_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/layout/dialog_account_export.xml b/ring-android/app/src/main/res/layout/dialog_account_export.xml
new file mode 100644
index 0000000..e5eb17b
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/dialog_account_export.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <EditText
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textPassword"
+        android:ems="10"
+        android:id="@+id/newpwd_txt"
+        android:layout_gravity="center_horizontal"
+        android:hint="@string/account_export_new_password"
+        android:layout_marginLeft="20dp"
+        android:layout_marginRight="20dp"
+        android:layout_marginTop="8dp" />
+
+    <EditText
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textPassword"
+        android:ems="10"
+        android:id="@+id/newpwd_confirm_txt"
+        android:hint="@string/account_export_confirm_password"
+        android:layout_marginLeft="20dp"
+        android:layout_marginRight="20dp"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginBottom="8dp" />
+</LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/dialog_account_import.xml b/ring-android/app/src/main/res/layout/dialog_account_import.xml
new file mode 100644
index 0000000..eb93f08
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/dialog_account_import.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <EditText
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textPassword"
+        android:ems="10"
+        android:id="@+id/pwd_txt"
+        android:layout_gravity="center_horizontal"
+        android:hint="@string/account_import_decryption_password"
+        android:layout_marginLeft="20dp"
+        android:layout_marginRight="20dp"
+        android:layout_marginTop="8dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/frag_account_creation.xml b/ring-android/app/src/main/res/layout/frag_account_creation.xml
index 96f3f13..9e74f05 100644
--- a/ring-android/app/src/main/res/layout/frag_account_creation.xml
+++ b/ring-android/app/src/main/res/layout/frag_account_creation.xml
@@ -53,12 +53,14 @@
                     android:layout_alignParentRight="true"
                     android:layout_alignParentTop="true"
                     android:layout_margin="16dp"
+                    android:contentDescription="@string/app_name"
                     android:src="@drawable/ring_logo_48dp" />
 
                 <TextView
                     android:id="@+id/ring_acc_title_txt"
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
+                    android:layout_alignParentLeft="true"
                     android:layout_alignParentStart="true"
                     android:layout_alignParentTop="true"
                     android:layout_toLeftOf="@+id/imageView6"
@@ -76,6 +78,7 @@
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
                     android:layout_alignParentStart="true"
+                    android:layout_alignParentLeft="true"
                     android:layout_below="@+id/ring_acc_title_txt"
                     android:padding="16dp"
                     android:singleLine="false"
@@ -90,10 +93,13 @@
                     android:layout_height="wrap_content"
                     android:layout_alignParentEnd="false"
                     android:layout_alignParentStart="false"
+                    android:layout_alignParentRight="false"
+                    android:layout_alignParentLeft="false"
                     android:layout_below="@+id/textView"
                     android:layout_margin="8dp"
                     android:padding="8dp"
-                    android:text="@string/create_ring_account" />
+                    android:text="@string/create_ring_account"
+                    android:gravity="center" />
             </RelativeLayout>
 
         </android.support.v7.widget.CardView>
@@ -134,24 +140,27 @@
                     android:id="@+id/textView4"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
+                    android:layout_alignParentLeft="true"
+                    android:layout_alignParentRight="true"
                     android:layout_alignParentEnd="true"
                     android:layout_alignParentStart="true"
                     android:layout_below="@+id/textView3"
-                    android:padding="16dp"
                     android:singleLine="false"
                     android:text="@string/help_sip"
                     android:textColor="@color/text_color_primary"
-                    android:textSize="14sp" />
+                    android:textSize="14sp"
+                    android:padding="16dp" />
 
                 <EditText
                     android:id="@+id/alias"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:layout_alignParentStart="true"
                     android:layout_alignParentLeft="true"
                     android:layout_below="@+id/textView4"
                     android:layout_marginBottom="8dp"
-                    android:layout_marginLeft="16dp"
-                    android:layout_marginRight="16dp"
+                    android:layout_marginLeft="12dp"
+                    android:layout_marginRight="12dp"
                     android:hint="@string/prompt_alias"
                     android:singleLine="true" />
 
@@ -159,11 +168,12 @@
                     android:id="@+id/hostname"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:layout_alignParentStart="true"
                     android:layout_alignParentLeft="true"
                     android:layout_below="@+id/alias"
                     android:layout_marginBottom="8dp"
-                    android:layout_marginLeft="16dp"
-                    android:layout_marginRight="16dp"
+                    android:layout_marginLeft="12dp"
+                    android:layout_marginRight="12dp"
                     android:hint="@string/prompt_hostname"
                     android:singleLine="true"
                     android:typeface="monospace" />
@@ -176,8 +186,8 @@
                     android:layout_alignParentStart="true"
                     android:layout_below="@+id/hostname"
                     android:layout_marginBottom="8dp"
-                    android:layout_marginLeft="16dp"
-                    android:layout_marginRight="16dp"
+                    android:layout_marginLeft="12dp"
+                    android:layout_marginRight="12dp"
                     android:hint="@string/prompt_username"
                     android:singleLine="true"
                     android:typeface="monospace" />
@@ -190,8 +200,8 @@
                     android:layout_alignParentStart="true"
                     android:layout_below="@+id/username"
                     android:layout_marginBottom="8dp"
-                    android:layout_marginLeft="16dp"
-                    android:layout_marginRight="16dp"
+                    android:layout_marginLeft="12dp"
+                    android:layout_marginRight="12dp"
                     android:hint="@string/prompt_password"
                     android:imeActionLabel="@string/action_create_short"
                     android:inputType="textPassword"
@@ -209,7 +219,8 @@
                     android:layout_below="@+id/password"
                     android:layout_margin="8dp"
                     android:padding="8dp"
-                    android:text="@string/create_sip_account" />
+                    android:text="@string/create_sip_account"
+                    android:gravity="center" />
 
                 <ImageView
                     android:id="@+id/imageView5"
@@ -219,11 +230,65 @@
                     android:layout_alignParentRight="true"
                     android:layout_alignParentTop="true"
                     android:layout_margin="16dp"
+                    android:contentDescription="@string/help_sip_title"
                     android:src="@drawable/ic_dialer_sip_black_48dp" />
             </RelativeLayout>
 
         </android.support.v7.widget.CardView>
 
+        <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
+            android:id="@+id/card_import"
+            android:layout_width="280dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_margin="8dp"
+            card_view:cardCornerRadius="2dp">
+            <RelativeLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:nestedScrollingEnabled="false"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_alignParentLeft="true"
+                    android:layout_alignParentStart="false"
+                    android:layout_alignParentTop="true"
+                    android:paddingLeft="16dp"
+                    android:paddingRight="16dp"
+                    android:paddingTop="24dp"
+                    android:singleLine="false"
+                    android:text="@string/account_import_title"
+                    android:textColor="@color/text_color_primary"
+                    android:textSize="24sp"
+                    android:id="@+id/import_acc_title_txt" />
+
+                <ImageView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_alignParentEnd="true"
+                    android:layout_alignParentRight="true"
+                    android:layout_alignParentTop="true"
+                    android:layout_margin="16dp"
+                    android:src="@drawable/ic_archive_black_48dp"
+                    android:contentDescription="@string/account_import_title"
+                    android:id="@+id/imageView4" />
+
+                <Button
+                    android:id="@+id/select_file_button"
+                    style="@style/Widget.AppCompat.Button.Borderless.Colored"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_below="@+id/import_acc_title_txt"
+                    android:layout_margin="8dp"
+                    android:padding="8dp"
+                    android:text="@string/account_import"
+                    android:gravity="center" />
+
+            </RelativeLayout>
+
+        </android.support.v7.widget.CardView>
 
     </LinearLayout>
 
diff --git a/ring-android/app/src/main/res/layout/frag_accounts_list.xml b/ring-android/app/src/main/res/layout/frag_accounts_list.xml
index 3de8015..6cd6910 100644
--- a/ring-android/app/src/main/res/layout/frag_accounts_list.xml
+++ b/ring-android/app/src/main/res/layout/frag_accounts_list.xml
@@ -10,23 +10,23 @@
         android:layout_width="match_parent"
         android:layout_height="48dp"
         android:gravity="center_vertical"
-        android:paddingStart="72dp"
         android:paddingEnd="72dp"
+        android:paddingStart="72dp"
         android:text="@string/normal_accounts_titles" />
 
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:background="@color/white"
+        android:elevation="2dp"
         android:minHeight="72dp">
 
         <cx.ring.views.dragsortlv.DragSortListView
             android:id="@+id/accounts_list"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:background="@color/white"
             android:choiceMode="multipleChoice"
             android:dividerHeight="1px"
-            android:elevation="2dp"
             android:visibility="visible"
             dslv:collapsed_height="1px"
             dslv:drag_enabled="true"
@@ -52,11 +52,4 @@
 
     </RelativeLayout>
 
-    <ListView
-        android:id="@+id/ip2ip"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@color/white"
-        android:elevation="2dp" />
-
 </LinearLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/menu/account_edition.xml b/ring-android/app/src/main/res/menu/account_edition.xml
index 0454d01..8d7965a 100644
--- a/ring-android/app/src/main/res/menu/account_edition.xml
+++ b/ring-android/app/src/main/res/menu/account_edition.xml
@@ -8,4 +8,10 @@
         android:title="@string/ab_account_edition_1"
         app:showAsAction="ifRoom"/>
 
+    <item
+        android:id="@+id/menuitem_export"
+        android:icon="@drawable/ic_archive_white_24dp"
+        android:title="@string/account_export_title"
+        app:showAsAction="ifRoom"/>
+
 </menu>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml
index ba55dbc..3a88484 100644
--- a/ring-android/app/src/main/res/values/strings.xml
+++ b/ring-android/app/src/main/res/values/strings.xml
@@ -134,4 +134,7 @@
     <string name="scan_qr">Scan QR Code</string>
     <string name="ab_action_flipcamera">Flip camera</string>
 
+    <string name="permission_read_denied">The app was not allowed to read to your storage. Hence, it cannot function properly. Please consider granting it this permission</string>
+    <string name="permission_write_denied">The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission</string>
+
 </resources>
diff --git a/ring-android/app/src/main/res/values/strings_account.xml b/ring-android/app/src/main/res/values/strings_account.xml
index 5d22f41..46f0af3 100644
--- a/ring-android/app/src/main/res/values/strings_account.xml
+++ b/ring-android/app/src/main/res/values/strings_account.xml
@@ -20,7 +20,7 @@
 
 <resources>
 
-    <!-- Strings related to login -->
+    <!-- Strings related to account creation -->
     <string name="prompt_alias">Alias</string>
     <string name="prompt_hostname">Hostname</string>
     <string name="prompt_username">Username</string>
@@ -31,7 +31,6 @@
     <string name="dialog_wait_create">Adding account</string>
     <string name="dialog_wait_create_details">Please wait while your new account is added…</string>
 
-    <!-- Strings related to account creation -->
     <string-array name="accountType">
         <item>RING</item>
         <item>SIP</item>
@@ -47,6 +46,10 @@
     <string name="configure_sip_account">Configure</string>
     <string name="share_number">Share number</string>
 
+    <!-- Strings related to account deletion -->
+    <string name="account_delete_confirmation">Do you really want to delete this account</string>
+    <string name="account_delete_dialog_title">Delete Account</string>
+
     <!-- AccountManagementFragment -->
     <string name="preference_section1">Accounts</string>
     <string name="empty_account_list">No account registered</string>
@@ -142,4 +145,26 @@
     <string name="account_rtp_min_port">Minimum</string>
     <string name="account_rtp_port_range">Audio RTP Port Range</string>
 
+    <!-- Import/export -->
+    <string name="account_export">Export</string>
+    <string name="account_import">Import</string>
+    <string name="account_import_account">Import account</string>
+    <string name="account_export_title">Export account</string>
+    <string name="account_import_title">Import accounts</string>
+    <string name="account_export_message">Save account settings and credentials, password encrypted.</string>
+    <string name="account_export_new_password">New password</string>
+    <string name="account_export_confirm_password">Confirm password</string>
+    <string name="account_import_message">Enter password to decrypt the file.</string>
+    <string name="account_import_decryption_password">Decryption password</string>
+    <string name="import_dialog_title">Importing account</string>
+    <string name="import_export_wait">Please wait…</string>
+    <string name="export_dialog_title">Exporting account</string>
+    <string name="account_export_result">"Account exported to %1$s"</string>
+    <string name="export_failed_dialog_title">Export failed</string>
+    <string name="export_failed_dialog_msg">An error occurred</string>
+    <string name="error_password_char_count">6 characters minimum</string>
+    <string name="error_passwords_not_equals">Passwords do not match</string>
+    <string name="import_failed_dialog_title">Import failed</string>
+    <string name="import_failed_dialog_msg">An error occured</string>
+
 </resources>