Skip to content

Commit b957728

Browse files
committed
Added authentication
Added options for password generation Added settings
1 parent 4e29a8a commit b957728

File tree

12 files changed

+333
-43
lines changed

12 files changed

+333
-43
lines changed

app/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ android {
3131
dependencies {
3232

3333
implementation 'androidx.appcompat:appcompat:1.6.1'
34-
implementation 'com.google.android.material:material:1.8.0'
34+
implementation 'androidx.biometric:biometric:1.1.0'
35+
implementation 'com.google.android.material:material:1.9.0'
3536
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
37+
implementation 'androidx.preference:preference:1.2.1'
3638
testImplementation 'junit:junit:4.13.2'
3739
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
3840
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
4+
45
<uses-permission android:name="android.permission.INTERNET" />
6+
57
<application
68
android:allowBackup="true"
79
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -13,14 +15,21 @@
1315
android:theme="@style/Theme.UniventionPasswordManager"
1416
tools:targetApi="31">
1517
<activity
16-
android:name=".MainActivity"
18+
android:name=".AuthActivity"
1719
android:exported="true">
1820
<intent-filter>
1921
<action android:name="android.intent.action.MAIN" />
2022

2123
<category android:name="android.intent.category.LAUNCHER" />
2224
</intent-filter>
2325
</activity>
26+
<activity
27+
android:name=".SettingsActivity"
28+
android:exported="false"
29+
android:label="@string/title_activity_settings" />
30+
<activity
31+
android:name=".MainActivity"
32+
android:exported="false" />
2433
</application>
2534

2635
</manifest>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.codespoof.univpassm;
2+
3+
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
4+
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK;
5+
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
6+
7+
import android.content.Intent;
8+
import android.os.Bundle;
9+
import android.widget.Toast;
10+
11+
import androidx.annotation.NonNull;
12+
import androidx.appcompat.app.AppCompatActivity;
13+
import androidx.biometric.BiometricPrompt;
14+
import androidx.core.content.ContextCompat;
15+
import androidx.preference.PreferenceManager;
16+
17+
import java.util.concurrent.Executor;
18+
19+
public class AuthActivity extends AppCompatActivity {
20+
21+
private BiometricPrompt biometricPrompt;
22+
private BiometricPrompt.PromptInfo promptInfo;
23+
@Override
24+
protected void onCreate(Bundle savedInstanceState) {
25+
super.onCreate(savedInstanceState);
26+
setContentView(R.layout.activity_auth);
27+
Executor executor = ContextCompat.getMainExecutor(this);
28+
promptInfo = new BiometricPrompt.PromptInfo.Builder()
29+
.setTitle(getResources().getString(R.string.auth_title))
30+
.setSubtitle(getResources().getString(R.string.auth_subtitle))
31+
.setAllowedAuthenticators(BIOMETRIC_STRONG | BIOMETRIC_WEAK | DEVICE_CREDENTIAL)
32+
.build();
33+
biometricPrompt = new BiometricPrompt(AuthActivity.this,
34+
executor, new BiometricPrompt.AuthenticationCallback() {
35+
@Override
36+
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
37+
super.onAuthenticationError(errorCode, errString);
38+
Toast.makeText(getApplicationContext(), getResources().getString(R.string.auth_error) + ": " + errString, Toast.LENGTH_LONG).show();
39+
}
40+
41+
@Override
42+
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
43+
super.onAuthenticationSucceeded(result);
44+
openMain();
45+
}
46+
47+
@Override
48+
public void onAuthenticationFailed() {
49+
super.onAuthenticationFailed();
50+
biometricPrompt.authenticate(promptInfo);
51+
}
52+
});
53+
if (PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean("pref_req_auth", false))
54+
biometricPrompt.authenticate(promptInfo);
55+
else {
56+
openMain();
57+
}
58+
}
59+
60+
private void openMain() {
61+
Intent intent = new Intent(AuthActivity.this, MainActivity.class);
62+
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
63+
startActivity(intent);
64+
}
65+
}

app/src/main/java/io/codespoof/univpassm/MainActivity.java

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.codespoof.univpassm;
22

3-
import androidx.appcompat.app.AppCompatActivity;
4-
3+
import android.content.Intent;
4+
import android.content.SharedPreferences;
55
import android.database.Cursor;
66
import android.os.Bundle;
77
import android.text.InputType;
@@ -11,8 +11,8 @@
1111
import android.widget.SimpleCursorAdapter;
1212
import android.widget.Toast;
1313

14-
import java.security.NoSuchAlgorithmException;
15-
import java.security.SecureRandom;
14+
import androidx.appcompat.app.AppCompatActivity;
15+
import androidx.preference.PreferenceManager;
1616

1717
public class MainActivity extends AppCompatActivity {
1818

@@ -22,38 +22,7 @@ public class MainActivity extends AppCompatActivity {
2222

2323
final int[] to = new int[] {R.id.datetime, R.id.content};
2424

25-
private String generatePassword() {
26-
StringBuilder pool = new StringBuilder();
27-
String chars = "abcdefghijklmnopqrstuvwxyz";
28-
String signs = ".,-_:#*+=!";
29-
String numbers = "0123456789";
30-
SecureRandom secureRandomGenerator;
31-
try {
32-
secureRandomGenerator = SecureRandom.getInstance("SHA1PRNG");
33-
} catch (NoSuchAlgorithmException e) {
34-
throw new RuntimeException(e);
35-
}
36-
int upper = 0;
37-
for (int i = 0; i < 5; i++) {
38-
String c = chars.substring(secureRandomGenerator.nextInt(chars.length())).substring(0, 1);
39-
if (upper < 4 && secureRandomGenerator.nextBoolean()) {
40-
c = c.toUpperCase();
41-
upper++;
42-
}
43-
pool.append(c);
44-
}
45-
pool.append(signs.charAt(secureRandomGenerator.nextInt(signs.length())));
46-
pool.append(numbers.charAt(secureRandomGenerator.nextInt(numbers.length())));
47-
pool.append(numbers.charAt(secureRandomGenerator.nextInt(numbers.length())));
48-
49-
StringBuilder ret = new StringBuilder();
50-
for (int i = 0; i < 8; i++) {
51-
int ind = secureRandomGenerator.nextInt(pool.length());
52-
ret.append(pool.charAt(ind));
53-
pool.deleteCharAt(ind);
54-
}
55-
return ret.toString();
56-
}
25+
PasswordGenerator generator;
5726

5827
@Override
5928
protected void onCreate(Bundle savedInstanceState) {
@@ -75,16 +44,21 @@ protected void onCreate(Bundle savedInstanceState) {
7544

7645
listView.setAdapter(adapter);
7746

47+
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
48+
49+
generator = new PasswordGenerator(preferences.getBoolean("pref_url_compat", false), Integer.parseInt(preferences.getString("pref_level", "10")));
50+
7851
EditText editPassword = findViewById(R.id.editPassword);
7952
Button resetButton = findViewById(R.id.buttonReset);
8053
Button repairButton = findViewById(R.id.buttonRepair);
54+
Button settingsButton = findViewById(R.id.buttonSettings);
8155
resetButton.setOnClickListener(v -> {
8256
Cursor c = dbManager.fetch();
8357
c.moveToFirst();
8458
if (c.getCount() > 0) {
8559
String oldPass = c.getString(2);
86-
String newPass = generatePassword();
87-
ChangePasswordTask task = new ChangePasswordTask("chrsal2", oldPass, newPass, "login.schulen-wetteraukreis.de", getApplicationContext(), dbManager, adapter, editPassword, repairButton, resetButton);
60+
String newPass = generator.generatePassword();
61+
ChangePasswordTask task = new ChangePasswordTask(preferences.getString("pref_username", ""), oldPass, newPass, preferences.getString("pref_server_address", ""), getApplicationContext(), dbManager, adapter, editPassword, repairButton, resetButton);
8862
task.execute();
8963
} else {
9064
Toast.makeText(getApplicationContext(), R.string.history_empty, Toast.LENGTH_LONG).show();
@@ -108,6 +82,9 @@ protected void onCreate(Bundle savedInstanceState) {
10882
editPassword.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
10983
}
11084
});
85+
settingsButton.setOnClickListener(v -> startActivity(new Intent(this, SettingsActivity.class)));
86+
if (preferences.getString("pref_username", "").equals(""))
87+
startActivity(new Intent(this, SettingsActivity.class));
11188
}
11289

11390
@Override
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.codespoof.univpassm;
2+
3+
import java.security.NoSuchAlgorithmException;
4+
import java.security.SecureRandom;
5+
6+
public class PasswordGenerator {
7+
final String chars = "abcdefghijklmnopqrstuvwxyz";
8+
final String signs;
9+
final String numbers = "0123456789";
10+
final String urlCompatibles = ".-_+";
11+
final String otherSigns = ",:#*=!";
12+
SecureRandom secureRandomGenerator;
13+
final int length;
14+
15+
public PasswordGenerator(boolean urlCompatible, int length) {
16+
try {
17+
secureRandomGenerator = SecureRandom.getInstance("SHA1PRNG");
18+
} catch (NoSuchAlgorithmException e) {
19+
throw new RuntimeException(e);
20+
}
21+
this.length = length;
22+
this.signs = this.urlCompatibles + (urlCompatible ? "" : this.otherSigns);
23+
}
24+
25+
public String generatePassword() {
26+
StringBuilder pool = new StringBuilder();
27+
int upper = 0;
28+
for (int i = 0; i < (length - 3); i++) {
29+
String c = chars.substring(secureRandomGenerator.nextInt(chars.length())).substring(0, 1);
30+
if (upper < (length - 4) && secureRandomGenerator.nextBoolean()) {
31+
c = c.toUpperCase();
32+
upper++;
33+
}
34+
pool.append(c);
35+
}
36+
pool.append(signs.charAt(secureRandomGenerator.nextInt(signs.length())));
37+
pool.append(numbers.charAt(secureRandomGenerator.nextInt(numbers.length())));
38+
pool.append(numbers.charAt(secureRandomGenerator.nextInt(numbers.length())));
39+
40+
StringBuilder ret = new StringBuilder();
41+
for (int i = 0; i < length; i++) {
42+
int ind = secureRandomGenerator.nextInt(pool.length());
43+
ret.append(pool.charAt(ind));
44+
pool.deleteCharAt(ind);
45+
}
46+
return ret.toString();
47+
}
48+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.codespoof.univpassm;
2+
3+
import android.content.SharedPreferences;
4+
import android.os.Bundle;
5+
import android.widget.Toast;
6+
7+
import androidx.appcompat.app.ActionBar;
8+
import androidx.appcompat.app.AppCompatActivity;
9+
import androidx.preference.PreferenceFragmentCompat;
10+
import androidx.preference.PreferenceManager;
11+
12+
import java.util.Objects;
13+
14+
public class SettingsActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
15+
@Override
16+
protected void onCreate(Bundle savedInstanceState) {
17+
super.onCreate(savedInstanceState);
18+
setContentView(R.layout.settings_activity);
19+
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
20+
if (savedInstanceState == null) {
21+
getSupportFragmentManager()
22+
.beginTransaction()
23+
.replace(R.id.settings, new SettingsFragment())
24+
.commit();
25+
}
26+
ActionBar actionBar = getSupportActionBar();
27+
if (actionBar != null) {
28+
actionBar.setDisplayHomeAsUpEnabled(true);
29+
}
30+
}
31+
32+
@Override
33+
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
34+
if (Objects.equals(key, "pref_level") || Objects.equals(key, "pref_url_compat") || Objects.equals(key, "pref_req_auth")) {
35+
Toast.makeText(getApplicationContext(), R.string.settings_restart, Toast.LENGTH_LONG).show();
36+
}
37+
}
38+
39+
public static class SettingsFragment extends PreferenceFragmentCompat {
40+
@Override
41+
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
42+
setPreferencesFromResource(R.xml.root_preferences, rootKey);
43+
}
44+
}
45+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent"
7+
tools:context=".AuthActivity">
8+
9+
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/layout/activity_main.xml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,20 @@
1717
android:enabled="false"
1818
android:fontFamily="sans-serif"
1919
android:hint="@string/hint_password"
20+
android:importantForAutofill="no"
2021
android:inputType="text|textVisiblePassword"
2122
android:maxWidth="10dp"
2223
android:singleLine="true"
2324
android:textAlignment="center"
2425
android:textAllCaps="false"
25-
android:textSize="30sp"
26+
android:textSize="24sp"
2627
android:textStyle="bold"
2728
android:typeface="normal"
2829
app:layout_constraintBottom_toTopOf="@id/cardButtons"
2930
app:layout_constraintEnd_toEndOf="parent"
3031
app:layout_constraintStart_toStartOf="parent"
3132
app:layout_constraintTop_toTopOf="parent"
32-
app:layout_constraintVertical_chainStyle="packed"
33-
android:importantForAutofill="no" />
33+
app:layout_constraintVertical_chainStyle="packed" />
3434

3535
<ListView
3636
android:id="@+id/listHistory"
@@ -98,4 +98,14 @@
9898
app:layout_constraintStart_toStartOf="@id/listHistory"
9999
app:layout_constraintTop_toTopOf="parent" />
100100

101+
<Button
102+
android:id="@+id/buttonSettings"
103+
android:layout_width="wrap_content"
104+
android:layout_height="wrap_content"
105+
android:layout_margin="10dp"
106+
android:text="@string/button_settings"
107+
app:layout_constraintEnd_toEndOf="parent"
108+
app:layout_constraintStart_toStartOf="parent"
109+
app:layout_constraintTop_toBottomOf="@+id/cardButtons" />
110+
101111
</androidx.constraintlayout.widget.ConstraintLayout>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:app="http://schemas.android.com/apk/res-auto"
3+
android:id="@+id/linearLayout"
4+
android:layout_width="match_parent"
5+
android:layout_height="match_parent">
6+
7+
<FrameLayout
8+
android:id="@+id/settings"
9+
android:layout_width="0dp"
10+
android:layout_height="0dp"
11+
app:layout_constraintBottom_toBottomOf="parent"
12+
app:layout_constraintEnd_toEndOf="parent"
13+
app:layout_constraintStart_toStartOf="parent"
14+
app:layout_constraintTop_toTopOf="parent" >
15+
16+
</FrameLayout>
17+
18+
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values/arrays.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<resources>
2+
<string-array name="pref_strength">
3+
<item>@string/pref_strength_minimum</item>
4+
<item>@string/pref_strength_normal</item>
5+
<item>@string/pref_strength_advanced</item>
6+
<item>@string/pref_strength_strong</item>
7+
<item>@string/pref_strength_extremely_strong</item>
8+
</string-array>
9+
<string-array name="pref_strength_values">
10+
<item>8</item>
11+
<item>10</item>
12+
<item>14</item>
13+
<item>17</item>
14+
<item>20</item>
15+
</string-array>
16+
</resources>

0 commit comments

Comments
 (0)