Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autobackup prompt limiter #26

Open
wants to merge 3 commits into
base: fingerprint
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions Safe/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ android {
sourceSets {

// Move the tests to tests/java, tests/res, etc...
instrumentTest.setRoot('tests')
androidTest.setRoot('tests')

// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
Expand All @@ -49,8 +49,10 @@ android {

applicationVariants.all { variant ->
variant.outputs.each { output ->
def relativeRootDir = output.packageApplication.outputDirectory.toPath()
.relativize(rootDir.toPath()).toFile()
def file = output.outputFile
output.outputFileName = new File(file.parent, file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk"))
output.outputFileName = new File("$relativeRootDir/release", file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk"))
}
}
}
Expand Down Expand Up @@ -88,21 +90,21 @@ play {
}

dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
implementation fileTree(dir: 'libs', include: '*.jar')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile 'com.github.openintents:distribution:3.0.1'
compile 'com.jakewharton:butterknife:8.5.1'
implementation 'com.github.openintents:distribution:3.0.1'
implementation 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
compile 'com.android.support:support-annotations:27.1.0'
compile 'com.android.support:support-fragment:27.1.0'
compile 'com.android.support:preference-v7:27.1.0'
compile 'com.android.support:design:27.1.0'

androidTestCompile 'com.android.support:support-annotations:27.1.0'
androidTestCompile 'com.android.support.test:runner:1.0.1'
androidTestCompile 'com.android.support.test:rules:1.0.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1'
androidTestCompile "com.android.support.test.espresso:espresso-intents:3.0.1"
implementation 'com.android.support:support-annotations:28.0.0'
implementation 'com.android.support:support-fragment:28.0.0'
implementation 'com.android.support:preference-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'

androidTestImplementation 'com.android.support:support-annotations:28.0.0'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation "com.android.support.test.espresso:espresso-intents:3.0.2"
}
repositories {
mavenCentral()
Expand Down
4 changes: 4 additions & 0 deletions Safe/src/main/java/org/openintents/safe/AskPassword.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ public void onCreate(Bundle icicle) {
switch (dbHelper.fetchVersion()) {
case 2:
databaseVersionError();
break;
case 4:
dbHelper.updateDbVersion4to5();
break;
}
}
dbSalt = dbHelper.fetchSalt();
Expand Down
68 changes: 37 additions & 31 deletions Safe/src/main/java/org/openintents/safe/CategoryList.java
Original file line number Diff line number Diff line change
Expand Up @@ -302,39 +302,44 @@ private void checkForAutoBackup() {
if (BuildConfig.DEBUG) {
Log.d(TAG, "in need of backup");
}
// used a custom layout in order to get the checkbox
AlertDialog.Builder builder;

LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.auto_backup, null);

builder = new AlertDialog.Builder(this);
builder.setView(layout);
builder.setTitle(getString(R.string.autobackup));
builder.setIcon(android.R.drawable.ic_menu_save);
builder.setNegativeButton(
R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
checkAutobackupTurnoff();

boolean hasUnsavedChange = new DBHelper(this).getDbHasChanged();

if (hasUnsavedChange) {
// used a custom layout in order to get the checkbox
AlertDialog.Builder builder;

LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.auto_backup, null);

builder = new AlertDialog.Builder(this);
builder.setView(layout);
builder.setTitle(getString(R.string.autobackup));
builder.setIcon(android.R.drawable.ic_menu_save);
builder.setNegativeButton(
R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
checkAutobackupTurnoff();
}
}
}
);
builder.setPositiveButton(
R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
checkAutobackupTurnoff();
backupThreadStart();
);
builder.setPositiveButton(
R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
checkAutobackupTurnoff();
backupThreadStart();
}
}
}
);
if (daysSinceLastBackup == julianDay) {
builder.setMessage(R.string.backup_never);
} else {
String backupInDays = getString(R.string.backup_in_days, daysSinceLastBackup);
builder.setMessage(backupInDays);
);
if (daysSinceLastBackup == julianDay) {
builder.setMessage(R.string.backup_never);
} else {
String backupInDays = getString(R.string.backup_in_days, daysSinceLastBackup);
builder.setMessage(backupInDays);
}
autobackupDialog = builder.create();
autobackupDialog.show();
}
autobackupDialog = builder.create();
autobackupDialog.show();
}
}

Expand Down Expand Up @@ -662,7 +667,8 @@ private void launchPassList(long id) {

private String backupDatabase(String filename, OutputStream str) {
Backup backup = new Backup(this);
backup.write(filename, str);
boolean changesSaved = backup.write(filename, str);
if (changesSaved) new DBHelper(this).setChangesSaved();
return backup.getResult();
}

Expand Down
106 changes: 103 additions & 3 deletions Safe/src/main/java/org/openintents/safe/DBHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public class DBHelper {
private static final String TABLE_SALT = "salt";
private static final String TABLE_PACKAGE_ACCESS = "package_access";
private static final String TABLE_CIPHER_ACCESS = "cipher_access";
private static final int DATABASE_VERSION = 4;
private static final String TABLE_HAS_UNBACKED_CHANGE = "has_change_to_backup";
private static final int DATABASE_VERSION = 5;
private static String TAG = "DBHelper";
Context myCtx;

Expand All @@ -77,6 +78,10 @@ public class DBHelper {
private static final String PASSWORDS_DROP =
"drop table " + TABLE_PASSWORDS + ";";

private static final String HAS_UNBACKED_CHANGE_CREATE =
"create table if not exists " + TABLE_HAS_UNBACKED_CHANGE + " ("
+ "has_change boolean default 1);";

private static final String PACKAGE_ACCESS_CREATE =
"create table " + TABLE_PACKAGE_ACCESS + " ("
+ "id integer not null, "
Expand Down Expand Up @@ -190,6 +195,9 @@ private void CreateDatabase(SQLiteDatabase db) {
db.execSQL(CIPHER_ACCESS_CREATE);
db.execSQL(MASTER_KEY_CREATE);
db.execSQL(SALT_CREATE);

createHasChangeTable();

} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
Expand All @@ -210,10 +218,43 @@ public void deleteDatabase() {
}
}

public boolean needsUpgrade() {
boolean needsUpgrade() {
return needsUpgrade;
}

/**
* Create {@link DBHelper#TABLE_HAS_UNBACKED_CHANGE} table of simple boolean, to facilitate
* quick determination as to whether an autobackup should be prompted.
*
* Default value is false so we don't prompt for a backup before any data has been stored in the db.
*
* @throws SQLException
*/
private void createHasChangeTable() throws SQLException {

db.execSQL(HAS_UNBACKED_CHANGE_CREATE);

ContentValues defaultValue = new ContentValues();
defaultValue.put("has_change", 0);
db.insert(TABLE_HAS_UNBACKED_CHANGE, null, defaultValue);
}

/**
* Apply to DB ver 4 to update to DB ver 5.
* Calls {@link DBHelper#createHasChangeTable()} and updates the DB version to 5.
*/
void updateDbVersion4to5() {
try {
createHasChangeTable();

ContentValues dbVersion = new ContentValues();
dbVersion.put("version", DATABASE_VERSION);
db.update(TABLE_DBVERSION, dbVersion, "version=" + fetchVersion(), null);
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
}

public boolean getPrePopulate() {
return needsPrePopulation;
}
Expand All @@ -236,7 +277,7 @@ public void close() {
}
}

public int fetchVersion() {
int fetchVersion() {
int version = 0;
try {
Cursor c = db.query(
Expand Down Expand Up @@ -362,6 +403,7 @@ public long addCategory(CategoryEntry entry) {

try {
rowID = db.insert(TABLE_CATEGORIES, null, initialValues);
setDbHasChanged();
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
Expand All @@ -376,6 +418,7 @@ public long addCategory(CategoryEntry entry) {
public void deleteCategory(long Id) {
try {
db.delete(TABLE_CATEGORIES, "id=" + Id, null);
setDbHasChanged();
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
Expand Down Expand Up @@ -466,6 +509,7 @@ public void updateCategory(long Id, CategoryEntry entry) {

try {
db.update(TABLE_CATEGORIES, args, "id=" + Id, null);
setDbHasChanged();
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
Expand Down Expand Up @@ -498,6 +542,57 @@ public int countPasswords(long categoryId) {
return count;
}

/**
* To be called only after backup is successful.
*/
void setChangesSaved() {
setDbHasChangedFlag(false);
}

/**
* Set the flag to indicate that an autobackup should be prompted at the next opportunity.
*/
private void setDbHasChanged() {
setDbHasChangedFlag(true);
}

/**
* Sets has_change bit.
*/
private void setDbHasChangedFlag(boolean hasChanged) {
ContentValues value = new ContentValues();
value.put("has_change", hasChanged);
try {
db.update(TABLE_HAS_UNBACKED_CHANGE, value, null, null);
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
}

/**
*
* @return true if a change has been registered in the db.
*/
boolean getDbHasChanged() {
boolean hasChanged = true;
if (db != null) {
try {
Cursor c = db.query(
true, TABLE_HAS_UNBACKED_CHANGE, new String[]{"has_change"},
null, null, null, null, null, null);
if (c.getCount() > 0) {
c.moveToFirst();
hasChanged = c.getShort(0) == 1;
} else setDbHasChanged(); // Bit was never set? Set it now.
c.close();
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
setDbHasChanged(); // Bit was never set? Set it now.
}
}
return hasChanged;
}

/**
* @return A list of all password entries filtered by the CategoryId.
* If CategoryId is 0, then return all entries in the database.
Expand Down Expand Up @@ -724,6 +819,7 @@ public long updatePassword(long Id, PassEntry entry) {
args.put("lastdatetimeedit", dateOut);
try {
db.update(TABLE_PASSWORDS, args, "id=" + Id, null);
setDbHasChanged();
} catch (SQLException e) {
Log.d(TAG, "updatePassword: SQLite exception: " + e.getLocalizedMessage());
return -1;
Expand All @@ -748,6 +844,7 @@ public void updatePasswordCategory(long Id, long newCategoryId) {

try {
db.update(TABLE_PASSWORDS, args, "id=" + Id, null);
setDbHasChanged();
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
Expand Down Expand Up @@ -784,6 +881,7 @@ public long addPassword(PassEntry entry) {

try {
id = db.insertOrThrow(TABLE_PASSWORDS, null, initialValues);
setDbHasChanged();
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
id = -1;
Expand All @@ -798,6 +896,7 @@ public void deletePassword(long Id) {
try {
db.delete(TABLE_PASSWORDS, "id=" + Id, null);
db.delete(TABLE_PACKAGE_ACCESS, "id=" + Id, null);
setDbHasChanged();
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
Expand Down Expand Up @@ -825,6 +924,7 @@ public void addCipherAccess(String packageToAdd, long expiration) {
initialValues.put("dateadded", dateOut);
try {
db.insert(TABLE_CIPHER_ACCESS, null, initialValues);
setDbHasChanged();
} catch (SQLException e) {
Log.d(TAG, "SQLite exception: " + e.getLocalizedMessage());
}
Expand Down
1 change: 1 addition & 0 deletions Safe/src/main/res/layout/pass_edit_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:minHeight="100dp"
android:gravity="top"
android:hint="@string/notes"
android:inputType="textMultiLine"
Expand Down
6 changes: 4 additions & 2 deletions SafeDemo/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ android {

applicationVariants.all { variant ->
variant.outputs.each { output ->
def relativeRootDir = output.packageApplication.outputDirectory.toPath()
.relativize(rootDir.toPath()).toFile()
def file = output.outputFile
output.outputFileName = new File(file.parent, file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk"))
output.outputFileName = new File("$relativeRootDir/release", file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk"))
}
}
}
Expand Down Expand Up @@ -72,5 +74,5 @@ repositories {
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
Loading