|
@ -2,8 +2,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.stevesoltys.backup"
|
||||
android:versionCode="5"
|
||||
android:versionName="0.3.0">
|
||||
android:versionCode="6"
|
||||
android:versionName="1.0.0-alpha1">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.BACKUP"
|
||||
|
@ -29,6 +29,7 @@
|
|||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
@ -62,18 +63,6 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.stevesoltys.backup.activity.MainActivity"
|
||||
android:label="@string/app_name" />
|
||||
|
||||
<activity
|
||||
android:name="com.stevesoltys.backup.activity.backup.CreateBackupActivity"
|
||||
android:parentActivityName="com.stevesoltys.backup.activity.MainActivity" />
|
||||
|
||||
<activity
|
||||
android:name="com.stevesoltys.backup.activity.restore.RestoreBackupActivity"
|
||||
android:parentActivityName="com.stevesoltys.backup.activity.MainActivity" />
|
||||
|
||||
<service
|
||||
android:name=".transport.ConfigurableBackupTransportService"
|
||||
android:exported="false">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.stevesoltys.backup.session.backup
|
||||
package com.stevesoltys.backup
|
||||
|
||||
import android.app.backup.BackupManagerMonitor.*
|
||||
import android.app.backup.IBackupManagerMonitor
|
|
@ -1,103 +0,0 @@
|
|||
package com.stevesoltys.backup.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
public class MainActivity extends Activity implements View.OnClickListener {
|
||||
|
||||
public static final int OPEN_DOCUMENT_TREE_REQUEST_CODE = 1;
|
||||
|
||||
public static final int OPEN_DOCUMENT_TREE_BACKUP_REQUEST_CODE = 2;
|
||||
|
||||
public static final int LOAD_DOCUMENT_REQUEST_CODE = 3;
|
||||
|
||||
private MainActivityController controller;
|
||||
private Button automaticBackupsButton;
|
||||
private Button changeLocationButton;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
controller = new MainActivityController();
|
||||
|
||||
findViewById(R.id.create_backup_button).setOnClickListener(this);
|
||||
findViewById(R.id.restore_backup_button).setOnClickListener(this);
|
||||
|
||||
automaticBackupsButton = findViewById(R.id.automatic_backups_button);
|
||||
automaticBackupsButton.setOnClickListener(this);
|
||||
|
||||
changeLocationButton = findViewById(R.id.change_backup_location_button);
|
||||
changeLocationButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (controller.isChangeBackupLocationButtonVisible(this)) {
|
||||
changeLocationButton.setVisibility(VISIBLE);
|
||||
} else {
|
||||
changeLocationButton.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int viewId = view.getId();
|
||||
|
||||
switch (viewId) {
|
||||
|
||||
case R.id.create_backup_button:
|
||||
controller.onBackupButtonClicked(this);
|
||||
break;
|
||||
|
||||
case R.id.restore_backup_button:
|
||||
controller.showLoadDocumentActivity(this);
|
||||
break;
|
||||
|
||||
case R.id.automatic_backups_button:
|
||||
if (controller.onAutomaticBackupsButtonClicked(this)) {
|
||||
automaticBackupsButton.setVisibility(GONE);
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.change_backup_location_button:
|
||||
controller.onChangeBackupLocationButtonClicked(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent result) {
|
||||
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
Log.e(MainActivity.class.getName(), "Error in activity result: " + requestCode);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (requestCode) {
|
||||
|
||||
case OPEN_DOCUMENT_TREE_REQUEST_CODE:
|
||||
controller.handleChooseFolderResult(result, this, false);
|
||||
break;
|
||||
|
||||
case OPEN_DOCUMENT_TREE_BACKUP_REQUEST_CODE:
|
||||
controller.handleChooseFolderResult(result, this, true);
|
||||
break;
|
||||
|
||||
case LOAD_DOCUMENT_REQUEST_CODE:
|
||||
controller.handleLoadDocumentResult(result, this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package com.stevesoltys.backup.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stevesoltys.backup.activity.backup.CreateBackupActivity;
|
||||
import com.stevesoltys.backup.activity.restore.RestoreBackupActivity;
|
||||
import com.stevesoltys.backup.transport.ConfigurableBackupTransportService;
|
||||
|
||||
import static android.content.Intent.ACTION_OPEN_DOCUMENT;
|
||||
import static android.content.Intent.ACTION_OPEN_DOCUMENT_TREE;
|
||||
import static android.content.Intent.CATEGORY_OPENABLE;
|
||||
import static android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
|
||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||
import static com.stevesoltys.backup.activity.MainActivity.OPEN_DOCUMENT_TREE_BACKUP_REQUEST_CODE;
|
||||
import static com.stevesoltys.backup.activity.MainActivity.OPEN_DOCUMENT_TREE_REQUEST_CODE;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
* @author Torsten Grote
|
||||
*/
|
||||
public class MainActivityController {
|
||||
|
||||
public static final String DOCUMENT_MIME_TYPE = "application/octet-stream";
|
||||
|
||||
void onBackupButtonClicked(Activity parent) {
|
||||
Uri folderUri = null;
|
||||
if (folderUri == null) {
|
||||
showChooseFolderActivity(parent, true);
|
||||
} else {
|
||||
// ensure that backup service is started
|
||||
parent.startService(new Intent(parent, ConfigurableBackupTransportService.class));
|
||||
showCreateBackupActivity(parent);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isChangeBackupLocationButtonVisible(Activity parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void showChooseFolderActivity(Activity parent, boolean continueToBackup) {
|
||||
Intent openTreeIntent = new Intent(ACTION_OPEN_DOCUMENT_TREE);
|
||||
openTreeIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
|
||||
FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
try {
|
||||
Intent documentChooser = Intent.createChooser(openTreeIntent, "Select the backup location");
|
||||
int requestCode = continueToBackup ? OPEN_DOCUMENT_TREE_BACKUP_REQUEST_CODE : OPEN_DOCUMENT_TREE_REQUEST_CODE;
|
||||
parent.startActivityForResult(documentChooser, requestCode);
|
||||
|
||||
} catch (ActivityNotFoundException ex) {
|
||||
Toast.makeText(parent, "Please install a file manager.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
void showLoadDocumentActivity(Activity parent) {
|
||||
Intent loadDocumentIntent = new Intent(ACTION_OPEN_DOCUMENT);
|
||||
loadDocumentIntent.addCategory(CATEGORY_OPENABLE);
|
||||
loadDocumentIntent.setType(DOCUMENT_MIME_TYPE);
|
||||
|
||||
try {
|
||||
Intent documentChooser = Intent.createChooser(loadDocumentIntent, "Select the backup location");
|
||||
parent.startActivityForResult(documentChooser, MainActivity.LOAD_DOCUMENT_REQUEST_CODE);
|
||||
|
||||
} catch (ActivityNotFoundException ex) {
|
||||
Toast.makeText(parent, "Please install a file manager.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
boolean onAutomaticBackupsButtonClicked(Activity parent) {
|
||||
Toast.makeText(parent, "Please make at least one manual backup first.", Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
void onChangeBackupLocationButtonClicked(Activity parent) {
|
||||
showChooseFolderActivity(parent, false);
|
||||
}
|
||||
|
||||
void handleChooseFolderResult(Intent result, Activity parent, boolean continueToBackup) {
|
||||
|
||||
if (result == null || result.getData() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uri folderUri = result.getData();
|
||||
|
||||
// persist permission to access backup folder across reboots
|
||||
int takeFlags = result.getFlags() &
|
||||
(FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
parent.getContentResolver().takePersistableUriPermission(folderUri, takeFlags);
|
||||
|
||||
if (!continueToBackup) return;
|
||||
|
||||
showCreateBackupActivity(parent);
|
||||
}
|
||||
|
||||
private void showCreateBackupActivity(Activity parent) {
|
||||
Intent intent = new Intent(parent, CreateBackupActivity.class);
|
||||
parent.startActivity(intent);
|
||||
}
|
||||
|
||||
void handleLoadDocumentResult(Intent result, Activity parent) {
|
||||
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(parent, RestoreBackupActivity.class);
|
||||
intent.setData(result.getData());
|
||||
parent.startActivity(intent);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package com.stevesoltys.backup.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public abstract class PackageListActivity extends Activity implements AdapterView.OnItemClickListener {
|
||||
|
||||
protected ListView packageListView;
|
||||
|
||||
protected final Set<String> selectedPackageList = new HashSet<>();
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
String clickedPackage = (String) packageListView.getItemAtPosition(position);
|
||||
|
||||
if (!selectedPackageList.remove(clickedPackage)) {
|
||||
selectedPackageList.add(clickedPackage);
|
||||
packageListView.setItemChecked(position, true);
|
||||
|
||||
} else {
|
||||
packageListView.setItemChecked(position, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
|
||||
if (item.getItemId() == R.id.action_unselect_all) {
|
||||
|
||||
IntStream.range(0, packageListView.getCount())
|
||||
.forEach(position -> {
|
||||
selectedPackageList.remove((String) packageListView.getItemAtPosition(position));
|
||||
packageListView.setItemChecked(position, false);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void preSelectAllPackages() {
|
||||
IntStream.range(0, packageListView.getCount())
|
||||
.forEach(position -> {
|
||||
selectedPackageList.add((String) packageListView.getItemAtPosition(position));
|
||||
packageListView.setItemChecked(position, true);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package com.stevesoltys.backup.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.PopupWindow;
|
||||
import com.stevesoltys.backup.R;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public class PopupWindowUtil {
|
||||
|
||||
public static PopupWindow showLoadingPopupWindow(Activity parent) {
|
||||
LayoutInflater inflater = (LayoutInflater) parent.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
ViewGroup popupViewGroup = parent.findViewById(R.id.popup_layout);
|
||||
View popupView = inflater.inflate(R.layout.progress_popup_window, popupViewGroup);
|
||||
|
||||
PopupWindow popupWindow = new PopupWindow(popupView, 750, 350, true);
|
||||
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
|
||||
popupWindow.setElevation(10);
|
||||
popupWindow.setFocusable(false);
|
||||
popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0);
|
||||
popupWindow.setOutsideTouchable(false);
|
||||
return popupWindow;
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package com.stevesoltys.backup.activity.backup;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.session.backup.BackupResult;
|
||||
import com.stevesoltys.backup.session.backup.BackupSession;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public class BackupPopupWindowListener implements Button.OnClickListener {
|
||||
|
||||
private static final String TAG = BackupPopupWindowListener.class.getName();
|
||||
|
||||
private final BackupSession backupSession;
|
||||
|
||||
public BackupPopupWindowListener(BackupSession backupSession) {
|
||||
this.backupSession = backupSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int viewId = view.getId();
|
||||
|
||||
if (viewId == R.id.popup_cancel_button) {
|
||||
try {
|
||||
backupSession.stop(BackupResult.CANCELLED);
|
||||
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error cancelling backup session: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package com.stevesoltys.backup.activity.backup;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.activity.PackageListActivity;
|
||||
|
||||
public class CreateBackupActivity extends PackageListActivity implements View.OnClickListener {
|
||||
|
||||
private CreateBackupActivityController controller;
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int viewId = view.getId();
|
||||
|
||||
if (viewId == R.id.create_confirm_button) {
|
||||
controller.onCreateBackupButtonClicked(selectedPackageList, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_create_backup);
|
||||
findViewById(R.id.create_confirm_button).setOnClickListener(this);
|
||||
|
||||
packageListView = findViewById(R.id.create_package_list);
|
||||
selectedPackageList.clear();
|
||||
|
||||
controller = new CreateBackupActivityController();
|
||||
AsyncTask.execute(() -> controller.populatePackageList(packageListView, CreateBackupActivity.this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.backup_menu, menu);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
package com.stevesoltys.backup.activity.backup;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.os.RemoteException;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.activity.PopupWindowUtil;
|
||||
import com.stevesoltys.backup.service.PackageService;
|
||||
import com.stevesoltys.backup.service.backup.BackupService;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
class CreateBackupActivityController {
|
||||
|
||||
private static final String TAG = CreateBackupActivityController.class.getName();
|
||||
|
||||
private final BackupService backupService = new BackupService();
|
||||
|
||||
private final PackageService packageService = new PackageService();
|
||||
|
||||
void populatePackageList(ListView packageListView, CreateBackupActivity parent) {
|
||||
AtomicReference<PopupWindow> popupWindow = new AtomicReference<>();
|
||||
|
||||
parent.runOnUiThread(() -> {
|
||||
popupWindow.set(PopupWindowUtil.showLoadingPopupWindow(parent));
|
||||
TextView textView = popupWindow.get().getContentView().findViewById(R.id.popup_text_view);
|
||||
textView.setText(R.string.loading_packages);
|
||||
|
||||
View popupWindowButton = popupWindow.get().getContentView().findViewById(R.id.popup_cancel_button);
|
||||
popupWindowButton.setOnClickListener(view -> parent.finish());
|
||||
});
|
||||
|
||||
String[] eligiblePackageList;
|
||||
|
||||
try {
|
||||
eligiblePackageList = packageService.getEligiblePackages();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error while obtaining package list: ", e);
|
||||
Toast.makeText(parent, "Error obtaining package list", Toast.LENGTH_SHORT).show();
|
||||
parent.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
parent.runOnUiThread(() -> {
|
||||
if (popupWindow.get() != null) {
|
||||
popupWindow.get().dismiss();
|
||||
}
|
||||
|
||||
packageListView.setOnItemClickListener(parent);
|
||||
packageListView.setAdapter(new ArrayAdapter<>(parent, R.layout.checked_list_item, eligiblePackageList));
|
||||
packageListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
parent.preSelectAllPackages();
|
||||
});
|
||||
}
|
||||
|
||||
void onCreateBackupButtonClicked(Set<String> selectedPackages, Activity parent) {
|
||||
backupService.backupPackageData(selectedPackages, parent);
|
||||
}
|
||||
|
||||
private void showEnterPasswordAlert(Set<String> selectedPackages, Activity parent) {
|
||||
final EditText passwordTextView = new EditText(parent);
|
||||
passwordTextView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
|
||||
new AlertDialog.Builder(parent)
|
||||
.setTitle("Enter a password")
|
||||
.setMessage("You'll need this to restore your backup, so write it down!")
|
||||
.setView(passwordTextView)
|
||||
|
||||
.setPositiveButton("Set password", (dialog, button) -> {
|
||||
if (passwordTextView.getText().length() == 0) {
|
||||
Toast.makeText(parent, "Please enter a password", Toast.LENGTH_SHORT).show();
|
||||
dialog.cancel();
|
||||
showEnterPasswordAlert(selectedPackages, parent);
|
||||
} else {
|
||||
showConfirmPasswordAlert(selectedPackages, parent,
|
||||
passwordTextView.getText().toString());
|
||||
}
|
||||
})
|
||||
|
||||
.setNegativeButton("Cancel", (dialog, button) -> dialog.cancel())
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showConfirmPasswordAlert(Set<String> selectedPackages, Activity parent,
|
||||
String originalPassword) {
|
||||
final EditText passwordTextView = new EditText(parent);
|
||||
passwordTextView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
|
||||
new AlertDialog.Builder(parent)
|
||||
.setTitle("Confirm password")
|
||||
.setView(passwordTextView)
|
||||
|
||||
.setPositiveButton("Confirm", (dialog, button) -> {
|
||||
String password = passwordTextView.getText().toString();
|
||||
|
||||
if (originalPassword.equals(password)) {
|
||||
backupService.backupPackageData(selectedPackages, parent);
|
||||
|
||||
} else {
|
||||
new AlertDialog.Builder(parent)
|
||||
.setMessage("Passwords do not match, please try again.")
|
||||
.setPositiveButton("Ok", (dialog2, button2) -> dialog2.dismiss())
|
||||
.show();
|
||||
|
||||
dialog.cancel();
|
||||
}
|
||||
})
|
||||
|
||||
.setNegativeButton("Cancel", (dialog, button) -> dialog.cancel())
|
||||
.show();
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package com.stevesoltys.backup.activity.restore;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.activity.PackageListActivity;
|
||||
|
||||
public class RestoreBackupActivity extends PackageListActivity implements View.OnClickListener {
|
||||
|
||||
private RestoreBackupActivityController controller;
|
||||
|
||||
private Uri contentUri;
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int viewId = view.getId();
|
||||
|
||||
if (viewId == R.id.restore_confirm_button) {
|
||||
controller.showEnterPasswordAlert(selectedPackageList, contentUri, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_restore_backup);
|
||||
findViewById(R.id.restore_confirm_button).setOnClickListener(this);
|
||||
|
||||
packageListView = findViewById(R.id.restore_package_list);
|
||||
selectedPackageList.clear();
|
||||
|
||||
contentUri = getIntent().getData();
|
||||
|
||||
controller = new RestoreBackupActivityController();
|
||||
AsyncTask.execute(() -> controller.populatePackageList(packageListView, contentUri, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.backup_menu, menu);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package com.stevesoltys.backup.activity.restore;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.activity.PopupWindowUtil;
|
||||
import com.stevesoltys.backup.service.restore.RestoreService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import static com.stevesoltys.backup.transport.backup.plugins.DocumentsStorageKt.DIRECTORY_FULL_BACKUP;
|
||||
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
class RestoreBackupActivityController {
|
||||
|
||||
private static final String TAG = RestoreBackupActivityController.class.getName();
|
||||
|
||||
private final RestoreService restoreService = new RestoreService();
|
||||
|
||||
void populatePackageList(ListView packageListView, Uri contentUri, RestoreBackupActivity parent) {
|
||||
AtomicReference<PopupWindow> popupWindow = new AtomicReference<>();
|
||||
|
||||
parent.runOnUiThread(() -> {
|
||||
popupWindow.set(PopupWindowUtil.showLoadingPopupWindow(parent));
|
||||
TextView textView = popupWindow.get().getContentView().findViewById(R.id.popup_text_view);
|
||||
textView.setText(R.string.loading_backup);
|
||||
|
||||
View popupWindowButton = popupWindow.get().getContentView().findViewById(R.id.popup_cancel_button);
|
||||
popupWindowButton.setOnClickListener(view -> parent.finish());
|
||||
});
|
||||
|
||||
List<String> eligiblePackageList = new LinkedList<>();
|
||||
|
||||
try {
|
||||
eligiblePackageList.addAll(getEligiblePackages(contentUri, parent));
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error while obtaining package list: ", e);
|
||||
}
|
||||
|
||||
parent.runOnUiThread(() -> {
|
||||
if (popupWindow.get() != null) {
|
||||
popupWindow.get().dismiss();
|
||||
}
|
||||
|
||||
packageListView.setOnItemClickListener(parent);
|
||||
packageListView.setAdapter(new ArrayAdapter<>(parent, R.layout.checked_list_item, eligiblePackageList));
|
||||
packageListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
parent.preSelectAllPackages();
|
||||
});
|
||||
}
|
||||
|
||||
private List<String> getEligiblePackages(Uri contentUri, Activity context) throws IOException {
|
||||
List<String> results = new LinkedList<>();
|
||||
|
||||
ParcelFileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(contentUri, "r");
|
||||
FileInputStream fileInputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
|
||||
ZipInputStream inputStream = new ZipInputStream(fileInputStream);
|
||||
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = inputStream.getNextEntry()) != null) {
|
||||
String zipEntryPath = zipEntry.getName();
|
||||
|
||||
if (zipEntryPath.startsWith(DIRECTORY_FULL_BACKUP)) {
|
||||
String fileName = new File(zipEntryPath).getName();
|
||||
results.add(fileName);
|
||||
}
|
||||
|
||||
inputStream.closeEntry();
|
||||
}
|
||||
|
||||
IoUtils.closeQuietly(inputStream);
|
||||
IoUtils.closeQuietly(fileDescriptor.getFileDescriptor());
|
||||
return results;
|
||||
}
|
||||
|
||||
void showEnterPasswordAlert(Set<String> selectedPackages, Uri contentUri, Activity parent) {
|
||||
final EditText passwordTextView = new EditText(parent);
|
||||
passwordTextView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
|
||||
new AlertDialog.Builder(parent)
|
||||
.setTitle("Enter a password")
|
||||
.setMessage("If you didn't enter one while creating the backup, you can leave this blank.")
|
||||
.setView(passwordTextView)
|
||||
|
||||
.setPositiveButton("Confirm", (dialog, button) ->
|
||||
restoreService.restorePackages(selectedPackages, contentUri, parent,
|
||||
passwordTextView.getText().toString()))
|
||||
|
||||
.setNegativeButton("Cancel", (dialog, button) -> dialog.cancel())
|
||||
.show();
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package com.stevesoltys.backup.activity.restore;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.session.restore.RestoreResult;
|
||||
import com.stevesoltys.backup.session.restore.RestoreSession;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public class RestorePopupWindowListener implements Button.OnClickListener {
|
||||
|
||||
private static final String TAG = RestorePopupWindowListener.class.getName();
|
||||
|
||||
private final RestoreSession restoreSession;
|
||||
|
||||
public RestorePopupWindowListener(RestoreSession restoreSession) {
|
||||
this.restoreSession = restoreSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int viewId = view.getId();
|
||||
|
||||
switch (viewId) {
|
||||
|
||||
case R.id.popup_cancel_button:
|
||||
try {
|
||||
restoreSession.stop(RestoreResult.CANCELLED);
|
||||
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error cancelling restore session: ", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.stevesoltys.backup.restore
|
||||
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -64,7 +65,10 @@ class RestoreProgressFragment : Fragment() {
|
|||
activity?.window?.clearFlags(FLAG_KEEP_SCREEN_ON)
|
||||
})
|
||||
|
||||
button.setOnClickListener { requireActivity().finishAfterTransition() }
|
||||
button.setOnClickListener {
|
||||
requireActivity().setResult(RESULT_OK)
|
||||
requireActivity().finishAfterTransition()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import com.stevesoltys.backup.Backup
|
||||
import com.stevesoltys.backup.R
|
||||
import com.stevesoltys.backup.session.backup.BackupMonitor
|
||||
import com.stevesoltys.backup.BackupMonitor
|
||||
import com.stevesoltys.backup.transport.TRANSPORT_ID
|
||||
import com.stevesoltys.backup.ui.RequireProvisioningViewModel
|
||||
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
package com.stevesoltys.backup.service;
|
||||
|
||||
import android.app.backup.IBackupManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
import com.stevesoltys.backup.session.backup.BackupSession;
|
||||
import com.stevesoltys.backup.session.backup.BackupSessionObserver;
|
||||
import com.stevesoltys.backup.session.restore.RestoreSession;
|
||||
import com.stevesoltys.backup.session.restore.RestoreSessionObserver;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public class TransportService {
|
||||
|
||||
private static final String BACKUP_TRANSPORT = "com.stevesoltys.backup.transport.ConfigurableBackupTransport";
|
||||
|
||||
private final IBackupManager backupManager;
|
||||
|
||||
public TransportService() {
|
||||
backupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
|
||||
}
|
||||
|
||||
public BackupSession backup(BackupSessionObserver observer, Set<String> packages) throws RemoteException {
|
||||
|
||||
if (!BACKUP_TRANSPORT.equals(backupManager.getCurrentTransport())) {
|
||||
backupManager.selectBackupTransport(BACKUP_TRANSPORT);
|
||||
}
|
||||
|
||||
if (!backupManager.isBackupEnabled()) {
|
||||
backupManager.setBackupEnabled(true);
|
||||
}
|
||||
|
||||
BackupSession backupSession = new BackupSession(backupManager, observer, packages);
|
||||
backupSession.start();
|
||||
return backupSession;
|
||||
}
|
||||
|
||||
public RestoreSession restore(RestoreSessionObserver observer, Set<String> packages) throws RemoteException {
|
||||
|
||||
if (!BACKUP_TRANSPORT.equals(backupManager.getCurrentTransport())) {
|
||||
backupManager.selectBackupTransport(BACKUP_TRANSPORT);
|
||||
}
|
||||
|
||||
if (!backupManager.isBackupEnabled()) {
|
||||
backupManager.setBackupEnabled(true);
|
||||
}
|
||||
|
||||
RestoreSession restoreSession = new RestoreSession(backupManager, observer, packages);
|
||||
restoreSession.start();
|
||||
return restoreSession;
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package com.stevesoltys.backup.service.backup;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.backup.BackupProgress;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.session.backup.BackupResult;
|
||||
import com.stevesoltys.backup.session.backup.BackupSession;
|
||||
import com.stevesoltys.backup.session.backup.BackupSessionObserver;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
class BackupObserver implements BackupSessionObserver {
|
||||
|
||||
private final Activity context;
|
||||
|
||||
private final PopupWindow popupWindow;
|
||||
|
||||
BackupObserver(Activity context, PopupWindow popupWindow) {
|
||||
this.context = context;
|
||||
this.popupWindow = popupWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backupPackageStarted(BackupSession backupSession, String packageName, BackupProgress backupProgress) {
|
||||
context.runOnUiThread(() -> {
|
||||
|
||||
TextView textView = popupWindow.getContentView().findViewById(R.id.popup_text_view);
|
||||
|
||||
if (textView != null) {
|
||||
textView.setText(packageName);
|
||||
}
|
||||
|
||||
ProgressBar progressBar = popupWindow.getContentView().findViewById(R.id.popup_progress_bar);
|
||||
|
||||
if (progressBar != null) {
|
||||
progressBar.setMax((int) backupProgress.bytesExpected);
|
||||
progressBar.setProgress((int) backupProgress.bytesTransferred);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backupPackageCompleted(BackupSession backupSession, String packageName, BackupResult result) {
|
||||
context.runOnUiThread(() -> {
|
||||
|
||||
TextView textView = popupWindow.getContentView().findViewById(R.id.popup_text_view);
|
||||
|
||||
if (textView != null) {
|
||||
textView.setText(packageName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backupSessionCompleted(BackupSession backupSession, BackupResult backupResult) {
|
||||
context.runOnUiThread(() -> {
|
||||
if (backupResult == BackupResult.SUCCESS) {
|
||||
Toast.makeText(context, R.string.backup_success, Toast.LENGTH_LONG).show();
|
||||
|
||||
} else if (backupResult == BackupResult.CANCELLED) {
|
||||
Toast.makeText(context, R.string.backup_cancelled, Toast.LENGTH_LONG).show();
|
||||
|
||||
} else {
|
||||
Toast.makeText(context, R.string.backup_failure, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
popupWindow.dismiss();
|
||||
context.finish();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package com.stevesoltys.backup.service.backup;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.activity.PopupWindowUtil;
|
||||
import com.stevesoltys.backup.activity.backup.BackupPopupWindowListener;
|
||||
import com.stevesoltys.backup.service.TransportService;
|
||||
import com.stevesoltys.backup.session.backup.BackupSession;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public class BackupService {
|
||||
|
||||
private static final String TAG = BackupService.class.getName();
|
||||
|
||||
private final TransportService transportService = new TransportService();
|
||||
|
||||
public void backupPackageData(Set<String> selectedPackages, Activity parent) {
|
||||
try {
|
||||
selectedPackages.add("@pm@");
|
||||
|
||||
Log.i(TAG, "Backing up " + selectedPackages.size() + " packages...");
|
||||
|
||||
PopupWindow popupWindow = PopupWindowUtil.showLoadingPopupWindow(parent);
|
||||
BackupObserver backupObserver = new BackupObserver(parent, popupWindow);
|
||||
BackupSession backupSession = transportService.backup(backupObserver, selectedPackages);
|
||||
|
||||
View popupWindowButton = popupWindow.getContentView().findViewById(R.id.popup_cancel_button);
|
||||
popupWindowButton.setOnClickListener(new BackupPopupWindowListener(backupSession));
|
||||
|
||||
TextView textView = popupWindow.getContentView().findViewById(R.id.popup_text_view);
|
||||
textView.setText(R.string.initializing);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error while running backup: ", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package com.stevesoltys.backup.service.restore;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.session.restore.RestoreResult;
|
||||
import com.stevesoltys.backup.session.restore.RestoreSessionObserver;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
class RestoreObserver implements RestoreSessionObserver {
|
||||
|
||||
private final Activity context;
|
||||
|
||||
private final PopupWindow popupWindow;
|
||||
|
||||
private final int packageCount;
|
||||
|
||||
RestoreObserver(Activity context, PopupWindow popupWindow, int packageCount) {
|
||||
this.context = context;
|
||||
this.popupWindow = popupWindow;
|
||||
this.packageCount = packageCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreSessionStarted(int packageCount) {
|
||||
context.runOnUiThread(() -> {
|
||||
TextView textView = popupWindow.getContentView().findViewById(R.id.popup_text_view);
|
||||
textView.setText(R.string.initializing);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restorePackageStarted(int packageIndex, String packageName) {
|
||||
context.runOnUiThread(() -> {
|
||||
ProgressBar progressBar = popupWindow.getContentView().findViewById(R.id.popup_progress_bar);
|
||||
|
||||
if (progressBar != null) {
|
||||
progressBar.setMax(packageCount);
|
||||
progressBar.setProgress(packageIndex);
|
||||
}
|
||||
|
||||
TextView textView = popupWindow.getContentView().findViewById(R.id.popup_text_view);
|
||||
|
||||
if (textView != null) {
|
||||
textView.setText(packageName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreSessionCompleted(RestoreResult restoreResult) {
|
||||
context.runOnUiThread(() -> {
|
||||
if (restoreResult == RestoreResult.SUCCESS) {
|
||||
Toast.makeText(context, R.string.restore_success, Toast.LENGTH_LONG).show();
|
||||
|
||||
} else if (restoreResult == RestoreResult.CANCELLED) {
|
||||
Toast.makeText(context, R.string.restore_cancelled, Toast.LENGTH_LONG).show();
|
||||
|
||||
} else {
|
||||
Toast.makeText(context, R.string.restore_failure, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
popupWindow.dismiss();
|
||||
context.finish();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package com.stevesoltys.backup.service.restore;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import com.stevesoltys.backup.R;
|
||||
import com.stevesoltys.backup.activity.PopupWindowUtil;
|
||||
import com.stevesoltys.backup.activity.restore.RestorePopupWindowListener;
|
||||
import com.stevesoltys.backup.service.TransportService;
|
||||
import com.stevesoltys.backup.session.restore.RestoreSession;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public class RestoreService {
|
||||
|
||||
private static final String TAG = RestoreService.class.getName();
|
||||
|
||||
private final TransportService transportService = new TransportService();
|
||||
|
||||
public void restorePackages(Set<String> selectedPackages, Uri contentUri, Activity parent, String password) {
|
||||
try {
|
||||
PopupWindow popupWindow = PopupWindowUtil.showLoadingPopupWindow(parent);
|
||||
RestoreObserver restoreObserver = new RestoreObserver(parent, popupWindow, selectedPackages.size());
|
||||
RestoreSession restoreSession = transportService.restore(restoreObserver, selectedPackages);
|
||||
|
||||
View popupWindowButton = popupWindow.getContentView().findViewById(R.id.popup_cancel_button);
|
||||
|
||||
if (popupWindowButton != null) {
|
||||
popupWindowButton.setOnClickListener(new RestorePopupWindowListener(restoreSession));
|
||||
}
|
||||
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error while running restore: ", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package com.stevesoltys.backup.session.backup;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public enum BackupResult {
|
||||
SUCCESS, FAILURE, CANCELLED
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package com.stevesoltys.backup.session.backup;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.app.backup.BackupProgress;
|
||||
import android.app.backup.IBackupManager;
|
||||
import android.app.backup.IBackupObserver;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static android.app.backup.BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public class BackupSession extends IBackupObserver.Stub {
|
||||
|
||||
private final IBackupManager backupManager;
|
||||
|
||||
private final BackupSessionObserver backupSessionObserver;
|
||||
|
||||
private final Set<String> packages;
|
||||
|
||||
public BackupSession(IBackupManager backupManager, BackupSessionObserver backupSessionObserver,
|
||||
Set<String> packages) {
|
||||
this.backupManager = backupManager;
|
||||
this.backupSessionObserver = backupSessionObserver;
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
public void start() throws RemoteException {
|
||||
String [] selectedPackageArray = packages.toArray(new String[0]);
|
||||
backupManager.requestBackup(selectedPackageArray, this, new BackupMonitor(), FLAG_NON_INCREMENTAL_BACKUP);
|
||||
}
|
||||
|
||||
public void stop(BackupResult result) throws RemoteException {
|
||||
backupManager.cancelBackups();
|
||||
backupSessionObserver.backupSessionCompleted(this, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(String currentPackage, BackupProgress backupProgress) {
|
||||
backupSessionObserver.backupPackageStarted(this, currentPackage, backupProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(String currentPackage, int status) {
|
||||
backupSessionObserver.backupPackageCompleted(this, currentPackage, getBackupResult(status));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backupFinished(int status) {
|
||||
backupSessionObserver.backupSessionCompleted(this, getBackupResult(status));
|
||||
}
|
||||
|
||||
private BackupResult getBackupResult(int status) {
|
||||
if (status == BackupManager.SUCCESS) {
|
||||
return BackupResult.SUCCESS;
|
||||
|
||||
} else if (status == BackupManager.ERROR_BACKUP_CANCELLED) {
|
||||
return BackupResult.CANCELLED;
|
||||
|
||||
} else {
|
||||
return BackupResult.FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.stevesoltys.backup.session.backup;
|
||||
|
||||
import android.app.backup.BackupProgress;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public interface BackupSessionObserver {
|
||||
|
||||
void backupPackageStarted(BackupSession backupSession, String packageName, BackupProgress backupProgress);
|
||||
|
||||
void backupPackageCompleted(BackupSession backupSession, String packageName, BackupResult result);
|
||||
|
||||
void backupSessionCompleted(BackupSession backupSession, BackupResult result);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package com.stevesoltys.backup.session.restore;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public enum RestoreResult {
|
||||
SUCCESS, CANCELLED, FAILURE
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
package com.stevesoltys.backup.session.restore;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.app.backup.IBackupManager;
|
||||
import android.app.backup.IRestoreObserver;
|
||||
import android.app.backup.IRestoreSession;
|
||||
import android.app.backup.RestoreSet;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public class RestoreSession extends IRestoreObserver.Stub {
|
||||
|
||||
private final IBackupManager backupManager;
|
||||
|
||||
private final RestoreSessionObserver observer;
|
||||
|
||||
private final Set<String> packages;
|
||||
|
||||
private IRestoreSession restoreSession;
|
||||
|
||||
public RestoreSession(IBackupManager backupManager, RestoreSessionObserver observer, Set<String> packages) {
|
||||
this.backupManager = backupManager;
|
||||
this.observer = observer;
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
public void start() throws RemoteException {
|
||||
|
||||
if (restoreSession != null || packages.isEmpty()) {
|
||||
observer.restoreSessionCompleted(RestoreResult.FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
restoreSession = backupManager.beginRestoreSessionForUser(UserHandle.myUserId(), null, null);
|
||||
|
||||
if (restoreSession == null) {
|
||||
stop(RestoreResult.FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
int result = restoreSession.getAvailableRestoreSets(this, null);
|
||||
|
||||
if (result != BackupManager.SUCCESS) {
|
||||
stop(RestoreResult.FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop(RestoreResult restoreResult) throws RemoteException {
|
||||
clearSession();
|
||||
observer.restoreSessionCompleted(restoreResult);
|
||||
}
|
||||
|
||||
private void clearSession() throws RemoteException {
|
||||
if (restoreSession != null) {
|
||||
restoreSession.endRestoreSession();
|
||||
restoreSession = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreSetsAvailable(RestoreSet[] restoreSets) throws RemoteException {
|
||||
if (restoreSets.length > 0) {
|
||||
RestoreSet restoreSet = restoreSets[0];
|
||||
String[] packageArray = packages.toArray(new String[0]);
|
||||
int result = restoreSession.restorePackages(restoreSet.token, this, packageArray, null);
|
||||
|
||||
if (result != BackupManager.SUCCESS) {
|
||||
stop(RestoreResult.FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreStarting(int numPackages) {
|
||||
observer.restoreSessionStarted(numPackages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(int nowBeingRestored, String currentPackage) {
|
||||
observer.restorePackageStarted(nowBeingRestored, currentPackage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFinished(int result) throws RemoteException {
|
||||
if (result == BackupManager.SUCCESS) {
|
||||
stop(RestoreResult.SUCCESS);
|
||||
|
||||
} else if (result == BackupManager.ERROR_BACKUP_CANCELLED) {
|
||||
stop(RestoreResult.CANCELLED);
|
||||
|
||||
} else {
|
||||
stop(RestoreResult.FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.stevesoltys.backup.session.restore;
|
||||
|
||||
/**
|
||||
* @author Steve Soltys
|
||||
*/
|
||||
public interface RestoreSessionObserver {
|
||||
|
||||
void restoreSessionStarted(int packageCount);
|
||||
|
||||
void restorePackageStarted(int packageIndex, String packageName);
|
||||
|
||||
void restoreSessionCompleted(RestoreResult restoreResult);
|
||||
}
|
|
@ -2,7 +2,6 @@ package com.stevesoltys.backup.transport
|
|||
|
||||
import android.app.Service
|
||||
import android.app.backup.BackupManager
|
||||
import android.app.backup.BackupTransport
|
||||
import android.content.Context
|
||||
import android.content.Context.BACKUP_SERVICE
|
||||
import android.app.backup.BackupManager.FLAG_NON_INCREMENTAL_BACKUP
|
||||
|
@ -15,8 +14,7 @@ import androidx.annotation.WorkerThread
|
|||
import com.stevesoltys.backup.Backup
|
||||
import com.stevesoltys.backup.NotificationBackupObserver
|
||||
import com.stevesoltys.backup.R
|
||||
import com.stevesoltys.backup.service.PackageService
|
||||
import com.stevesoltys.backup.session.backup.BackupMonitor
|
||||
import com.stevesoltys.backup.BackupMonitor
|
||||
|
||||
private val TAG = ConfigurableBackupTransportService::class.java.simpleName
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.stevesoltys.backup.service
|
||||
package com.stevesoltys.backup.transport
|
||||
|
||||
import android.content.pm.IPackageManager
|
||||
import android.content.pm.PackageInfo
|
||||
|
@ -27,7 +27,7 @@ private val IGNORED_PACKAGES = newArraySet(
|
|||
* @author Steve Soltys
|
||||
* @author Torsten Grote
|
||||
*/
|
||||
class PackageService {
|
||||
internal class PackageService {
|
||||
|
||||
private val backupManager = Backup.backupManager
|
||||
private val packageManager: IPackageManager = IPackageManager.Stub.asInterface(getService("package"))
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_create_backup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:weightSum="1"
|
||||
tools:context="com.stevesoltys.backup.activity.restore.RestoreBackupActivity">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/create_package_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0.99" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/create_confirm_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/button_vertical_margin"
|
||||
android:layout_marginLeft="@dimen/button_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/button_horizontal_margin"
|
||||
android:text="@string/create_backup_button" />
|
||||
|
||||
</LinearLayout>
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="com.stevesoltys.backup.activity.MainActivity">
|
||||
|
||||
<Button
|
||||
android:id="@+id/create_backup_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/button_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/button_vertical_margin"
|
||||
android:layout_marginRight="@dimen/button_horizontal_margin"
|
||||
android:text="@string/create_backup_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/restore_backup_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/button_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/button_vertical_margin"
|
||||
android:layout_marginRight="@dimen/button_horizontal_margin"
|
||||
android:text="@string/restore_backup_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/automatic_backups_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginLeft="@dimen/button_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/button_horizontal_margin"
|
||||
android:text="Activate automatic backups" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/change_backup_location_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/button_horizontal_margin"
|
||||
android:layout_marginTop="72dp"
|
||||
android:layout_marginRight="@dimen/button_horizontal_margin"
|
||||
android:text="@string/change_backup_location_button" />
|
||||
|
||||
</LinearLayout>
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_restore_backup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:weightSum="1"
|
||||
tools:context="com.stevesoltys.backup.activity.restore.RestoreBackupActivity">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/restore_package_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0.99"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/restore_confirm_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/button_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/button_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/button_vertical_margin"
|
||||
android:text="@string/restore_button"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||
android:gravity="center_vertical"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/popup_layout"
|
||||
android:layout_width="@dimen/popup_width"
|
||||
android:layout_height="@dimen/popup_height"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/popup_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/popup_progress_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/progress_bar_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/progress_bar_horizontal_margin" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/popup_cancel_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/button_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/button_vertical_margin"
|
||||
android:layout_marginRight="@dimen/button_horizontal_margin"
|
||||
android:text="@string/popup_cancel" />
|
||||
|
||||
</LinearLayout>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@+id/action_unselect_all"
|
||||
android:title="@string/unselect_all"
|
||||
android:showAsAction="never" />
|
||||
|
||||
</menu>
|
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 11 KiB |
|
@ -1,6 +0,0 @@
|
|||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
|
@ -1,8 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
|
||||
<color name="divider">#8A000000</color>
|
||||
</resources>
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
|
||||
<dimen name="button_horizontal_margin">5dp</dimen>
|
||||
<dimen name="button_vertical_margin">7dp</dimen>
|
||||
|
||||
<dimen name="progress_bar_horizontal_margin">5dp</dimen>
|
||||
|
||||
<dimen name="popup_width">300dp</dimen>
|
||||
<dimen name="popup_height">300dp</dimen>
|
||||
</resources>
|
|
@ -1,27 +1,8 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<string name="app_name">Backup</string>
|
||||
|
||||
<string name="create_backup_button">Create backup</string>
|
||||
<string name="restore_backup_button">Restore backup</string>
|
||||
<string name="change_backup_location_button">Change backup location</string>
|
||||
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="backup_failure">Backup failure</string>
|
||||
<string name="backup_cancelled">Backup cancelled</string>
|
||||
<string name="backup_in_progress">Backup already in progress</string>
|
||||
|
||||
<string name="restore_button">Restore packages</string>
|
||||
<string name="restore_success">Restore success</string>
|
||||
<string name="restore_failure">Restore failure</string>
|
||||
<string name="restore_cancelled">Restore cancelled</string>
|
||||
<string name="restore_in_progress">Restore already in progress</string>
|
||||
|
||||
<string name="popup_cancel">Cancel</string>
|
||||
|
||||
<string name="unselect_all">Unselect all</string>
|
||||
<string name="loading_backup">Loading backup…</string>
|
||||
<string name="loading_packages">Loading packages…</string>
|
||||
<string name="initializing">Initializing…</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="settings_backup">Backup my data</string>
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
</resources>
|