From 872ec4021444a536b484ffc78ee2d8d5a19b0588 Mon Sep 17 00:00:00 2001 From: arrase Date: Thu, 24 Nov 2016 02:22:41 +0100 Subject: [PATCH] restore backup dialog --- .../android/backup/BackupUtils.java | 35 +++++--- .../android/storage/ExternalStorage.java | 30 +++---- .../android/storage/PermissionManager.java | 50 ++++++++++++ .../android/ui/hs/HiddenServicesActivity.java | 11 ++- .../android/ui/hs/adapters/BackupAdapter.java | 50 ++++++++++++ .../ui/hs/dialogs/HSActionsDialog.java | 38 ++------- .../ui/hs/dialogs/SelectBackupDialog.java | 81 +++++++++++++++++++ .../res/layout/layout_hs_backups_list.xml | 10 +++ .../layout/layout_hs_backups_list_item.xml | 24 ++++++ app/src/main/res/values/strings.xml | 4 +- 10 files changed, 270 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/org/torproject/android/storage/PermissionManager.java create mode 100644 app/src/main/java/org/torproject/android/ui/hs/adapters/BackupAdapter.java create mode 100644 app/src/main/java/org/torproject/android/ui/hs/dialogs/SelectBackupDialog.java create mode 100644 app/src/main/res/layout/layout_hs_backups_list.xml create mode 100644 app/src/main/res/layout/layout_hs_backups_list_item.xml diff --git a/app/src/main/java/org/torproject/android/backup/BackupUtils.java b/app/src/main/java/org/torproject/android/backup/BackupUtils.java index e05532f4..57d8c8d3 100644 --- a/app/src/main/java/org/torproject/android/backup/BackupUtils.java +++ b/app/src/main/java/org/torproject/android/backup/BackupUtils.java @@ -2,17 +2,22 @@ package org.torproject.android.backup; import android.app.Application; import android.content.Context; +import android.widget.Toast; +import org.torproject.android.service.R; import org.torproject.android.service.TorServiceConstants; import org.torproject.android.storage.ExternalStorage; import java.io.File; +import java.io.IOException; public class BackupUtils { private File mHSBasePath; + private Context mContext; public BackupUtils(Context context) { - mHSBasePath = context.getDir( + mContext = context; + mHSBasePath = mContext.getDir( TorServiceConstants.DIRECTORY_TOR_DATA, Application.MODE_PRIVATE ); @@ -20,13 +25,12 @@ public class BackupUtils { public String createZipBackup(Integer port) { - ExternalStorage storage = new ExternalStorage(); - String storage_path = storage.createBackupDir(); - if (storage_path == null) { - return null; - } + File storage_path = ExternalStorage.getOrCreateBackupDir(); - String zip_path = storage_path + "/hs" + port + ".zip"; + if (storage_path == null) + return null; + + String zip_path = storage_path.getAbsolutePath() + "/hs" + port + ".zip"; String files[] = { mHSBasePath + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR + "/hs" + port + "/hostname", mHSBasePath + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR + "/hs" + port + "/private_key" @@ -42,7 +46,20 @@ public class BackupUtils { } public void restoreZipBackup(Integer port, String path) { - ZipIt zip = new ZipIt(null, path); - zip.unzip(mHSBasePath + "/hs" + port); + String hsBasePath; + + try { + hsBasePath = mHSBasePath.getCanonicalPath() + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR; + File hsPath = new File(hsBasePath, "/hs" + port); + if (hsPath.mkdirs()) { + ZipIt zip = new ZipIt(null, path); + zip.unzip(hsPath.getCanonicalPath()); + return; + } + } catch (IOException e) { + e.printStackTrace(); + } + + Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show(); } } diff --git a/app/src/main/java/org/torproject/android/storage/ExternalStorage.java b/app/src/main/java/org/torproject/android/storage/ExternalStorage.java index 6da650da..bea196e0 100644 --- a/app/src/main/java/org/torproject/android/storage/ExternalStorage.java +++ b/app/src/main/java/org/torproject/android/storage/ExternalStorage.java @@ -5,38 +5,30 @@ import android.os.Environment; import java.io.File; public class ExternalStorage { - private final String BACKUPS_DIR = "Orbot-HiddenServices"; + private static final String BACKUPS_DIR = "Orbot-HiddenServices"; - public String createBackupDir() { - if (!isExternalStorageWritable()) { + public static File getOrCreateBackupDir() { + if (!isExternalStorageWritable()) return null; - } - File path = Environment.getExternalStoragePublicDirectory(BACKUPS_DIR); + File dir = new File(Environment.getExternalStorageDirectory(), BACKUPS_DIR); - if (!path.mkdirs()) { + if (!dir.isDirectory() && !dir.mkdirs()) return null; - } - return path.getAbsolutePath(); + return dir; } /* Checks if external storage is available for read and write */ - public boolean isExternalStorageWritable() { + public static boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); - if (Environment.MEDIA_MOUNTED.equals(state)) { - return true; - } - return false; + return Environment.MEDIA_MOUNTED.equals(state); } /* Checks if external storage is available to at least read */ - public boolean isExternalStorageReadable() { + public static boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); - if (Environment.MEDIA_MOUNTED.equals(state) || - Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { - return true; - } - return false; + return Environment.MEDIA_MOUNTED.equals(state) || + Environment.MEDIA_MOUNTED_READ_ONLY.equals(state); } } diff --git a/app/src/main/java/org/torproject/android/storage/PermissionManager.java b/app/src/main/java/org/torproject/android/storage/PermissionManager.java new file mode 100644 index 00000000..128db8ef --- /dev/null +++ b/app/src/main/java/org/torproject/android/storage/PermissionManager.java @@ -0,0 +1,50 @@ +package org.torproject.android.storage; + + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.support.design.widget.Snackbar; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.FragmentActivity; +import android.view.View; + +import org.torproject.android.R; + +public class PermissionManager { + private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1; + + public static boolean usesRuntimePermissions() { + return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1); + } + + @SuppressLint("NewApi") + public static boolean hasExternalWritePermission(Context context) { + return (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED); + } + + public static void requestPermissions(FragmentActivity activity) { + final FragmentActivity mActivity = activity; + + if (ActivityCompat.shouldShowRequestPermissionRationale + (mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + Snackbar.make(mActivity.findViewById(android.R.id.content), + R.string.please_grant_permissions_for_external_storage, + Snackbar.LENGTH_INDEFINITE).setAction("ENABLE", + new View.OnClickListener() { + @Override + public void onClick(View v) { + ActivityCompat.requestPermissions(mActivity, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); + } + }).show(); + } else { + ActivityCompat.requestPermissions(mActivity, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); + } + } +} diff --git a/app/src/main/java/org/torproject/android/ui/hs/HiddenServicesActivity.java b/app/src/main/java/org/torproject/android/ui/hs/HiddenServicesActivity.java index 52103dcc..4474668c 100644 --- a/app/src/main/java/org/torproject/android/ui/hs/HiddenServicesActivity.java +++ b/app/src/main/java/org/torproject/android/ui/hs/HiddenServicesActivity.java @@ -16,9 +16,11 @@ import android.widget.ListView; import android.widget.TextView; import org.torproject.android.R; +import org.torproject.android.storage.PermissionManager; import org.torproject.android.ui.hs.adapters.OnionListAdapter; import org.torproject.android.ui.hs.dialogs.HSActionsDialog; import org.torproject.android.ui.hs.dialogs.HSDataDialog; +import org.torproject.android.ui.hs.dialogs.SelectBackupDialog; import org.torproject.android.ui.hs.providers.HSContentProvider; public class HiddenServicesActivity extends AppCompatActivity { @@ -98,7 +100,14 @@ public class HiddenServicesActivity extends AppCompatActivity { int id = item.getItemId(); if (id == R.id.menu_restore_backup) { - // TODO: Restore backup + if (PermissionManager.usesRuntimePermissions() + && !PermissionManager.hasExternalWritePermission(this)) { + PermissionManager.requestPermissions(this); + return true; + } + + SelectBackupDialog dialog = new SelectBackupDialog(); + dialog.show(getSupportFragmentManager(), "SelectBackupDialog"); return true; } diff --git a/app/src/main/java/org/torproject/android/ui/hs/adapters/BackupAdapter.java b/app/src/main/java/org/torproject/android/ui/hs/adapters/BackupAdapter.java new file mode 100644 index 00000000..bcc7a5a4 --- /dev/null +++ b/app/src/main/java/org/torproject/android/ui/hs/adapters/BackupAdapter.java @@ -0,0 +1,50 @@ +package org.torproject.android.ui.hs.adapters; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import org.torproject.android.R; + +import java.io.File; +import java.util.List; + +public class BackupAdapter extends ArrayAdapter { + private int mResource; + + public BackupAdapter(Context context, int resource) { + super(context, resource); + mResource = resource; + } + + public BackupAdapter(Context context, int resource, List zips) { + super(context, resource, zips); + mResource = resource; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + View v = convertView; + + if (v == null) { + LayoutInflater vi; + vi = LayoutInflater.from(getContext()); + v = vi.inflate(mResource, null); + } + + File p = getItem(position); + + if (p != null) { + TextView name = (TextView) v.findViewById(R.id.backup_name); + + if (name != null) + name.setText(p.getName()); + } + + return v; + } +} diff --git a/app/src/main/java/org/torproject/android/ui/hs/dialogs/HSActionsDialog.java b/app/src/main/java/org/torproject/android/ui/hs/dialogs/HSActionsDialog.java index 46315433..d5603b71 100644 --- a/app/src/main/java/org/torproject/android/ui/hs/dialogs/HSActionsDialog.java +++ b/app/src/main/java/org/torproject/android/ui/hs/dialogs/HSActionsDialog.java @@ -23,6 +23,7 @@ import android.widget.Toast; import org.torproject.android.R; import org.torproject.android.backup.BackupUtils; +import org.torproject.android.storage.PermissionManager; import org.torproject.android.ui.hs.providers.HSContentProvider; public class HSActionsDialog extends DialogFragment { @@ -44,8 +45,9 @@ public class HSActionsDialog extends DialogFragment { public void onClick(View v) { Context mContext = v.getContext(); - if (usesRuntimePermissions() && !hasExternalWritePermission(mContext)) { - requestPermissions(); + if (PermissionManager.usesRuntimePermissions() + && !PermissionManager.hasExternalWritePermission(mContext)) { + PermissionManager.requestPermissions(getActivity()); return; } @@ -58,7 +60,7 @@ public class HSActionsDialog extends DialogFragment { return; } - Toast.makeText(mContext, R.string.done, Toast.LENGTH_LONG).show(); + Toast.makeText(mContext, R.string.backup_saved_at_external_storage, Toast.LENGTH_LONG).show(); Uri selectedUri = Uri.parse(backupPath.substring(0, backupPath.lastIndexOf("/"))); Intent intent = new Intent(Intent.ACTION_VIEW); @@ -104,34 +106,4 @@ public class HSActionsDialog extends DialogFragment { return actionDialog; } - - private boolean usesRuntimePermissions() { - return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1); - } - - @SuppressLint("NewApi") - private boolean hasExternalWritePermission(Context context) { - return (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED); - } - - private void requestPermissions() { - if (ActivityCompat.shouldShowRequestPermissionRationale - (getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - Snackbar.make(getActivity().findViewById(android.R.id.content), - R.string.please_grant_permissions_for_external_storage, - Snackbar.LENGTH_INDEFINITE).setAction("ENABLE", - new View.OnClickListener() { - @Override - public void onClick(View v) { - ActivityCompat.requestPermissions(getActivity(), - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); - } - }).show(); - } else { - ActivityCompat.requestPermissions(getActivity(), - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); - } - } } diff --git a/app/src/main/java/org/torproject/android/ui/hs/dialogs/SelectBackupDialog.java b/app/src/main/java/org/torproject/android/ui/hs/dialogs/SelectBackupDialog.java new file mode 100644 index 00000000..37c722bf --- /dev/null +++ b/app/src/main/java/org/torproject/android/ui/hs/dialogs/SelectBackupDialog.java @@ -0,0 +1,81 @@ +package org.torproject.android.ui.hs.dialogs; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; + +import org.torproject.android.R; +import org.torproject.android.storage.ExternalStorage; +import org.torproject.android.ui.hs.adapters.BackupAdapter; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SelectBackupDialog extends DialogFragment { + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder backupsDialog = new AlertDialog.Builder(getActivity()); + + backupsDialog.setTitle(R.string.restore_backup); + + File backupDir = ExternalStorage.getOrCreateBackupDir(); + File[] files = null; + + try { + files = backupDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".zip"); + } + }); + } catch (NullPointerException e) { + // Silent block + } + + if (files == null || files.length < 1) { + backupsDialog.setMessage(R.string.create_a_backup_first); + backupsDialog.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + + return backupsDialog.create(); + } + + final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_backups_list, null); + + backupsDialog.setView(dialog_view); + backupsDialog.setPositiveButton(R.string.btn_okay, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + + ListView backups = (ListView) dialog_view.findViewById(R.id.listview_hs_backups); + + List zips = new ArrayList<>(); + Collections.addAll(zips, files); + + backups.setAdapter(new BackupAdapter(getContext(), R.layout.layout_hs_backups_list_item, zips)); + backups.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + // TODO + } + }); + + return backupsDialog.create(); + } +} diff --git a/app/src/main/res/layout/layout_hs_backups_list.xml b/app/src/main/res/layout/layout_hs_backups_list.xml new file mode 100644 index 00000000..6c123ffc --- /dev/null +++ b/app/src/main/res/layout/layout_hs_backups_list.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_hs_backups_list_item.xml b/app/src/main/res/layout/layout_hs_backups_list_item.xml new file mode 100644 index 00000000..fe76d471 --- /dev/null +++ b/app/src/main/res/layout/layout_hs_backups_list_item.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index adeba6f1..3e37d3d0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -337,14 +337,16 @@ Local Port Onion Port Name + Done! Invalid Port Copy address to clipboard Backup Service Delete Service - Done! + Backup saved at external storage Filemanager not available Please grant permissions for external storage Permission granted Permission denied Restore Backup + Create a backup first