diff --git a/app/src/main/java/org/kontalk/ui/prefs/AccountFragment.java b/app/src/main/java/org/kontalk/ui/prefs/AccountFragment.java index 3c3b94fa9..72a75ac1a 100644 --- a/app/src/main/java/org/kontalk/ui/prefs/AccountFragment.java +++ b/app/src/main/java/org/kontalk/ui/prefs/AccountFragment.java @@ -207,6 +207,7 @@ public void onPassphraseChanged(String passphrase) { Context ctx = getContext(); if (ctx != null) { new FolderChooserDialog.Builder(ctx) + .tag(AccountFragment.this.getClass().getName()) .initialPath(PersonalKeyPack.DEFAULT_KEYPACK.getParent()) .show(getParentFragmentManager()); } diff --git a/app/src/main/java/org/kontalk/ui/prefs/CopyDatabasePreference.java b/app/src/main/java/org/kontalk/ui/prefs/CopyDatabasePreference.java index bdbc71a05..c420bd3e5 100644 --- a/app/src/main/java/org/kontalk/ui/prefs/CopyDatabasePreference.java +++ b/app/src/main/java/org/kontalk/ui/prefs/CopyDatabasePreference.java @@ -19,16 +19,25 @@ package org.kontalk.ui.prefs; import android.annotation.TargetApi; +import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.net.Uri; import android.os.Build; import android.os.Environment; + +import androidx.fragment.app.Fragment; import androidx.preference.Preference; import android.util.AttributeSet; import android.widget.Toast; +import org.kontalk.Kontalk; +import org.kontalk.Log; import org.kontalk.R; import org.kontalk.provider.MessagesProvider; +import org.kontalk.reporting.ReportingManager; import org.kontalk.util.DataUtils; +import org.kontalk.util.MediaStorage; import java.io.File; import java.io.FileInputStream; @@ -37,12 +46,22 @@ import java.io.InputStream; import java.io.OutputStream; +import com.afollestad.materialdialogs.folderselector.FolderChooserDialog; + /** * Preference for copying the messages database to the external storage. * @author Daniele Ricci */ public class CopyDatabasePreference extends Preference { + private static final String TAG = Kontalk.TAG; + + public static final int REQUEST_COPY_DATABASE = Activity.RESULT_FIRST_USER + 4; + + private static final String DBFILE_MIME = "application/x-sqlite3"; + public static final String DBFILE_NAME = "kontalk-messages.db"; + + private Fragment mFragment; public CopyDatabasePreference(Context context) { super(context); @@ -69,25 +88,80 @@ private void init() { setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - copyDatabase(getContext()); + requestFile(getContext()); return true; } }); } - void copyDatabase(Context context) { - MessagesProvider.lockForImport(context); + public void setParentFragment(Fragment fragment) { + mFragment = fragment; + } - InputStream dbIn = null; + void requestFile(Context context) { + try { + if (MediaStorage.isStorageAccessFrameworkAvailable()) { + MediaStorage.createFile(mFragment, DBFILE_MIME, DBFILE_NAME, + REQUEST_COPY_DATABASE); + return; + } + } + catch (ActivityNotFoundException e) { + Log.w(TAG, "Storage Access Framework not working properly"); + ReportingManager.logException(e); + } + + // also used as a fallback if SAF is not working properly + Context ctx = getContext(); + if (ctx != null) { + new FolderChooserDialog.Builder(ctx) + .tag(getClass().getName()) + .initialPath(Environment.getExternalStorageDirectory().toString()) + .show(mFragment.getParentFragmentManager()); + } + } + + public static void copyDatabase(Context context, File dbOutFile) { OutputStream dbOut = null; try { - File dbOutFile = new File(Environment.getExternalStorageDirectory(), "kontalk-messages.db"); + dbOut = new FileOutputStream(dbOutFile); + copyDatabase(context, dbOut, dbOut.toString()); + } + catch (IOException e) { + Toast.makeText(context, context + .getString(R.string.msg_copy_database_failed, e.toString()), Toast.LENGTH_LONG) + .show(); + } + finally { + DataUtils.close(dbOut); + } + } + + public static void copyDatabase(Context context, Uri dbOutFile) { + OutputStream dbOut = null; + try { + dbOut = context.getContentResolver().openOutputStream(dbOutFile); + copyDatabase(context, dbOut, dbOutFile.toString()); + } + catch (IOException e) { + Toast.makeText(context, context + .getString(R.string.msg_copy_database_failed, e.toString()), Toast.LENGTH_LONG) + .show(); + } + finally { + DataUtils.close(dbOut); + } + } + private static void copyDatabase(Context context, OutputStream dbOut, String filename) { + MessagesProvider.lockForImport(context); + + InputStream dbIn = null; + try { dbIn = new FileInputStream(MessagesProvider.getDatabaseUri(context)); - dbOut = new FileOutputStream(dbOutFile); DataUtils.copy(dbIn, dbOut); Toast.makeText(context, context - .getString(R.string.msg_copy_database_success, dbOutFile.toString()), Toast.LENGTH_LONG) + .getString(R.string.msg_copy_database_success, filename), Toast.LENGTH_LONG) .show(); } catch (IOException e) { diff --git a/app/src/main/java/org/kontalk/ui/prefs/MaintenanceFragment.java b/app/src/main/java/org/kontalk/ui/prefs/MaintenanceFragment.java index a905a6404..9a7925c90 100644 --- a/app/src/main/java/org/kontalk/ui/prefs/MaintenanceFragment.java +++ b/app/src/main/java/org/kontalk/ui/prefs/MaintenanceFragment.java @@ -18,9 +18,13 @@ package org.kontalk.ui.prefs; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.widget.Toast; +import androidx.annotation.Nullable; import androidx.preference.CheckBoxPreference; import androidx.preference.Preference; @@ -53,6 +57,10 @@ public boolean onPreferenceClick(Preference preference) { } }); + // send our stuff to the copy database preference + final CopyDatabasePreference copyDatabase = findPreference("pref_copy_database"); + copyDatabase.setParentFragment(this); + if (Kontalk.get().getDefaultAccount() == null) { // no account, hide/disable some stuff restartMsgCenter.setEnabled(false); @@ -74,4 +82,18 @@ public void onResume() { .setTitle(R.string.pref_maintenance); } + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + if (requestCode == CopyDatabasePreference.REQUEST_COPY_DATABASE) { + if (resultCode == Activity.RESULT_OK) { + Context ctx = getActivity(); + if (ctx != null && data != null && data.getData() != null) { + CopyDatabasePreference.copyDatabase(ctx, data.getData()); + } + } + } + else { + super.onActivityResult(requestCode, resultCode, data); + } + } } diff --git a/app/src/main/java/org/kontalk/ui/prefs/PreferencesActivity.java b/app/src/main/java/org/kontalk/ui/prefs/PreferencesActivity.java index ce90e5afe..4cbc9c8e2 100644 --- a/app/src/main/java/org/kontalk/ui/prefs/PreferencesActivity.java +++ b/app/src/main/java/org/kontalk/ui/prefs/PreferencesActivity.java @@ -134,17 +134,24 @@ public void onNestedPreferenceSelected(int key) { @Override public void onFolderSelection(@NonNull FolderChooserDialog folderChooserDialog, @NonNull File folder) { - try { - AccountFragment f = (AccountFragment) getSupportFragmentManager() - .findFragmentById(R.id.container); - f.exportPersonalKey(this, - new FileOutputStream(new File(folder, PersonalKeyPack.KEYPACK_FILENAME))); + final String tag = folderChooserDialog.getTag(); + if (tag.equals(CopyDatabasePreference.class.getName())) { + CopyDatabasePreference.copyDatabase(this, + new File(folder, CopyDatabasePreference.DBFILE_NAME)); } - catch (FileNotFoundException e) { - Log.e(PreferencesFragment.TAG, "error exporting keys", e); - Toast.makeText(this, - R.string.err_keypair_export_write, - Toast.LENGTH_LONG).show(); + else if (tag.equals(AccountFragment.class.getName())) { + try { + AccountFragment f = (AccountFragment) getSupportFragmentManager() + .findFragmentById(R.id.container); + f.exportPersonalKey(this, + new FileOutputStream(new File(folder, PersonalKeyPack.KEYPACK_FILENAME))); + } + catch (FileNotFoundException e) { + Log.e(PreferencesFragment.TAG, "error exporting keys", e); + Toast.makeText(this, + R.string.err_keypair_export_write, + Toast.LENGTH_LONG).show(); + } } }