mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-22 14:59:14 +00:00
Merge pull request #1357 from michaelschattgen/feature/audit-log
Add audit log
This commit is contained in:
commit
ee1dd322b8
31 changed files with 834 additions and 7 deletions
|
@ -139,10 +139,13 @@ dependencies {
|
|||
def hiltVersion = '2.50'
|
||||
def junitVersion = '4.13.2'
|
||||
def libsuVersion = '5.2.2'
|
||||
def roomVersion = "2.6.1"
|
||||
|
||||
|
||||
annotationProcessor 'androidx.annotation:annotation:1.7.1'
|
||||
annotationProcessor "com.google.dagger:hilt-compiler:$hiltVersion"
|
||||
annotationProcessor "com.github.bumptech.glide:compiler:${glideVersion}"
|
||||
annotationProcessor "androidx.room:room-compiler:$roomVersion"
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.activity:activity:1.8.2'
|
||||
|
@ -157,6 +160,7 @@ dependencies {
|
|||
implementation "androidx.lifecycle:lifecycle-process:2.6.2"
|
||||
implementation "androidx.preference:preference:1.2.1"
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
implementation "androidx.room:room-runtime:$roomVersion"
|
||||
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||
implementation 'com.caverock:androidsvg-aar:1.4'
|
||||
|
|
|
@ -17,14 +17,23 @@ import androidx.lifecycle.Lifecycle;
|
|||
import androidx.lifecycle.LifecycleEventObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
import androidx.room.Room;
|
||||
|
||||
import com.beemdevelopment.aegis.database.AppDatabase;
|
||||
import com.beemdevelopment.aegis.database.AuditLogEntry;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.receivers.VaultLockReceiver;
|
||||
import com.beemdevelopment.aegis.ui.MainActivity;
|
||||
import com.beemdevelopment.aegis.util.IOUtils;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import dagger.hilt.InstallIn;
|
||||
import dagger.hilt.android.EarlyEntryPoint;
|
||||
|
|
|
@ -8,6 +8,8 @@ import android.os.Build;
|
|||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import com.beemdevelopment.aegis.database.AppDatabase;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.util.IOUtils;
|
||||
import com.beemdevelopment.aegis.vault.VaultFile;
|
||||
import com.beemdevelopment.aegis.vault.VaultRepository;
|
||||
|
@ -25,12 +27,16 @@ public class AegisBackupAgent extends BackupAgent {
|
|||
|
||||
private Preferences _prefs;
|
||||
|
||||
private AuditLogRepository _auditLogRepository;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// Cannot use injection with Dagger Hilt here, because the app is launched in a restricted mode on restore
|
||||
_prefs = new Preferences(this);
|
||||
AppDatabase appDatabase = AegisModule.provideAppDatabase(this);
|
||||
_auditLogRepository = AegisModule.provideAuditLogRepository(appDatabase);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,6 +59,7 @@ public class AegisBackupAgent extends BackupAgent {
|
|||
// report any runtime exceptions, in addition to the expected IOExceptions.
|
||||
try {
|
||||
fullBackup(data);
|
||||
_auditLogRepository.addAndroidBackupCreatedEvent();
|
||||
_prefs.setAndroidBackupResult(new Preferences.BackupResult(null));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, String.format("onFullBackup() failed: %s", e));
|
||||
|
|
|
@ -2,6 +2,11 @@ package com.beemdevelopment.aegis;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.room.Room;
|
||||
|
||||
import com.beemdevelopment.aegis.database.AppDatabase;
|
||||
import com.beemdevelopment.aegis.database.AuditLogDao;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.icons.IconPackManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
|
||||
|
@ -24,12 +29,27 @@ public class AegisModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static VaultManager provideVaultManager(@ApplicationContext Context context) {
|
||||
return new VaultManager(context);
|
||||
public static AuditLogRepository provideAuditLogRepository(AppDatabase appDatabase) {
|
||||
AuditLogDao auditLogDao = appDatabase.auditLogDao();
|
||||
return new AuditLogRepository(auditLogDao);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static VaultManager provideVaultManager(@ApplicationContext Context context, AuditLogRepository auditLogRepository) {
|
||||
return new VaultManager(context, auditLogRepository);
|
||||
}
|
||||
|
||||
@Provides
|
||||
public static Preferences providePreferences(@ApplicationContext Context context) {
|
||||
return new Preferences(context);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static AppDatabase provideAppDatabase(@ApplicationContext Context context) {
|
||||
return Room.databaseBuilder(context.getApplicationContext(),
|
||||
AppDatabase.class, "aegis-db")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
42
app/src/main/java/com/beemdevelopment/aegis/EventType.java
Normal file
42
app/src/main/java/com/beemdevelopment/aegis/EventType.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
package com.beemdevelopment.aegis;
|
||||
|
||||
public enum EventType {
|
||||
|
||||
VAULT_UNLOCKED,
|
||||
VAULT_BACKUP_CREATED,
|
||||
VAULT_ANDROID_BACKUP_CREATED,
|
||||
VAULT_EXPORTED,
|
||||
ENTRY_SHARED,
|
||||
VAULT_UNLOCK_FAILED_PASSWORD,
|
||||
VAULT_UNLOCK_FAILED_BIOMETRICS;
|
||||
private static EventType[] _values;
|
||||
|
||||
static {
|
||||
_values = values();
|
||||
}
|
||||
|
||||
public static EventType fromInteger(int x) {
|
||||
return _values[x];
|
||||
}
|
||||
|
||||
public static int getEventTitleRes(EventType eventType) {
|
||||
switch (eventType) {
|
||||
case VAULT_UNLOCKED:
|
||||
return R.string.event_title_vault_unlocked;
|
||||
case VAULT_BACKUP_CREATED:
|
||||
return R.string.event_title_backup_created;
|
||||
case VAULT_ANDROID_BACKUP_CREATED:
|
||||
return R.string.event_title_android_backup_created;
|
||||
case VAULT_EXPORTED:
|
||||
return R.string.event_title_vault_exported;
|
||||
case ENTRY_SHARED:
|
||||
return R.string.event_title_entry_shared;
|
||||
case VAULT_UNLOCK_FAILED_PASSWORD:
|
||||
return R.string.event_title_vault_unlock_failed_password;
|
||||
case VAULT_UNLOCK_FAILED_BIOMETRICS:
|
||||
return R.string.event_title_vault_unlock_failed_biometrics;
|
||||
default:
|
||||
return R.string.event_unknown;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.beemdevelopment.aegis.database;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Database(entities = {AuditLogEntry.class}, version = 1)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
public abstract AuditLogDao auditLogDao();
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.beemdevelopment.aegis.database;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface AuditLogDao {
|
||||
@Insert
|
||||
void insert(AuditLogEntry log);
|
||||
|
||||
@Query("SELECT * FROM audit_logs WHERE timestamp >= strftime('%s', 'now', '-30 days') ORDER BY timestamp DESC")
|
||||
LiveData<List<AuditLogEntry>> getAll();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.beemdevelopment.aegis.database;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import com.beemdevelopment.aegis.EventType;
|
||||
|
||||
@Entity(tableName = "audit_logs")
|
||||
public class AuditLogEntry {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
protected long id;
|
||||
|
||||
@NonNull
|
||||
@ColumnInfo(name = "event_type")
|
||||
private final EventType _eventType;
|
||||
|
||||
@ColumnInfo(name = "reference")
|
||||
private final String _reference;
|
||||
|
||||
@ColumnInfo(name = "timestamp")
|
||||
private final long _timestamp;
|
||||
|
||||
@Ignore
|
||||
public AuditLogEntry(@NonNull EventType eventType) {
|
||||
this(eventType, null);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public AuditLogEntry(@NonNull EventType eventType, @Nullable String reference) {
|
||||
_eventType = eventType;
|
||||
_reference = reference;
|
||||
_timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
AuditLogEntry(long id, @NonNull EventType eventType, @Nullable String reference, long timestamp) {
|
||||
this.id = id;
|
||||
_eventType = eventType;
|
||||
_reference = reference;
|
||||
_timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public EventType getEventType() {
|
||||
return _eventType;
|
||||
}
|
||||
|
||||
public String getReference() {
|
||||
return _reference;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return _timestamp;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package com.beemdevelopment.aegis.database;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.beemdevelopment.aegis.EventType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class AuditLogRepository {
|
||||
private final AuditLogDao _auditLogDao;
|
||||
private final Executor _executor;
|
||||
|
||||
public AuditLogRepository(AuditLogDao auditLogDao) {
|
||||
_auditLogDao = auditLogDao;
|
||||
_executor = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
public LiveData<List<AuditLogEntry>> getAllAuditLogEntries() {
|
||||
return _auditLogDao.getAll();
|
||||
}
|
||||
|
||||
public void addVaultUnlockedEvent() {
|
||||
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_UNLOCKED);
|
||||
insert(auditLogEntry);
|
||||
}
|
||||
|
||||
public void addBackupCreatedEvent() {
|
||||
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_BACKUP_CREATED);
|
||||
insert(auditLogEntry);
|
||||
}
|
||||
|
||||
public void addAndroidBackupCreatedEvent() {
|
||||
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_ANDROID_BACKUP_CREATED);
|
||||
insert(auditLogEntry);
|
||||
}
|
||||
|
||||
public void addVaultExportedEvent() {
|
||||
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_EXPORTED);
|
||||
insert(auditLogEntry);
|
||||
}
|
||||
|
||||
public void addEntrySharedEvent(String reference) {
|
||||
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.ENTRY_SHARED, reference);
|
||||
insert(auditLogEntry);
|
||||
}
|
||||
|
||||
public void addVaultUnlockFailedPasswordEvent() {
|
||||
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_UNLOCK_FAILED_PASSWORD);
|
||||
insert(auditLogEntry);
|
||||
|
||||
}
|
||||
|
||||
public void addVaultUnlockFailedBiometricsEvent() {
|
||||
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_UNLOCK_FAILED_BIOMETRICS);
|
||||
insert(auditLogEntry);
|
||||
}
|
||||
|
||||
public void insert(AuditLogEntry auditLogEntry) {
|
||||
_executor.execute(() -> {
|
||||
_auditLogDao.insert(auditLogEntry);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@ import androidx.core.view.ViewPropertyAnimatorCompat;
|
|||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.ThemeMap;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.helpers.ThemeHelper;
|
||||
import com.beemdevelopment.aegis.icons.IconPackManager;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
|
@ -48,6 +49,9 @@ public abstract class AegisActivity extends AppCompatActivity implements VaultMa
|
|||
@Inject
|
||||
protected VaultManager _vaultManager;
|
||||
|
||||
@Inject
|
||||
protected AuditLogRepository _auditLogRepository;
|
||||
|
||||
@Inject
|
||||
protected IconPackManager _iconPackManager;
|
||||
|
||||
|
|
|
@ -344,6 +344,8 @@ public class AuthActivity extends AegisActivity {
|
|||
finish(result.getKey(), result.isSlotRepaired());
|
||||
} else {
|
||||
_decryptButton.setEnabled(true);
|
||||
|
||||
_auditLogRepository.addVaultUnlockFailedPasswordEvent();
|
||||
onInvalidPassword();
|
||||
}
|
||||
}
|
||||
|
@ -356,6 +358,7 @@ public class AuthActivity extends AegisActivity {
|
|||
_bioPrompt = null;
|
||||
|
||||
if (!BiometricsHelper.isCanceled(errorCode)) {
|
||||
_auditLogRepository.addVaultUnlockFailedBiometricsEvent();
|
||||
Toast.makeText(AuthActivity.this, errString, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -522,6 +522,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
}
|
||||
|
||||
private void onDecryptResult() {
|
||||
_auditLogRepository.addVaultUnlockedEvent();
|
||||
|
||||
loadEntries();
|
||||
checkTimeSyncSetting();
|
||||
}
|
||||
|
@ -1176,6 +1178,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
for (VaultEntry entry : _selectedEntries) {
|
||||
GoogleAuthInfo authInfo = new GoogleAuthInfo(entry.getInfo(), entry.getName(), entry.getIssuer());
|
||||
authInfos.add(authInfo);
|
||||
|
||||
_auditLogRepository.addEntrySharedEvent(entry.getUUID().toString());
|
||||
}
|
||||
|
||||
intent.putExtra("authInfos", authInfos);
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package com.beemdevelopment.aegis.ui.fragments.preferences;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.database.AuditLogEntry;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.helpers.MetricsHelper;
|
||||
import com.beemdevelopment.aegis.ui.models.AuditLogEntryModel;
|
||||
import com.beemdevelopment.aegis.ui.views.AuditLogAdapter;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class AuditLogPreferencesFragment extends Fragment {
|
||||
|
||||
@Inject
|
||||
AuditLogRepository _auditLogRepository;
|
||||
|
||||
private AuditLogAdapter _adapter;
|
||||
|
||||
private RecyclerView _auditLogRecyclerView;
|
||||
private LinearLayout _noAuditLogsView;
|
||||
|
||||
@Inject
|
||||
VaultManager _vaultManager;
|
||||
|
||||
public AuditLogPreferencesFragment() {
|
||||
super(R.layout.fragment_audit_log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||
LiveData<List<AuditLogEntry>> entries = _auditLogRepository.getAllAuditLogEntries();
|
||||
|
||||
_adapter = new AuditLogAdapter();
|
||||
_noAuditLogsView = view.findViewById(R.id.vEmptyList);
|
||||
_auditLogRecyclerView = view.findViewById(R.id.list_audit_log);
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(requireContext());
|
||||
_auditLogRecyclerView.addItemDecoration(new SpacesItemDecoration(8));
|
||||
_auditLogRecyclerView.setLayoutManager(layoutManager);
|
||||
_auditLogRecyclerView.setAdapter(_adapter);
|
||||
_auditLogRecyclerView.setNestedScrollingEnabled(false);
|
||||
|
||||
entries.observe(getViewLifecycleOwner(), entries1 -> {
|
||||
_noAuditLogsView.setVisibility(entries1.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
|
||||
for (AuditLogEntry entry : entries1) {
|
||||
|
||||
VaultEntry referencedEntry = null;
|
||||
if (entry.getReference() != null) {
|
||||
referencedEntry = _vaultManager.getVault().getEntryByUUID(UUID.fromString(entry.getReference()));
|
||||
}
|
||||
|
||||
AuditLogEntryModel auditLogEntryModel = new AuditLogEntryModel(entry, referencedEntry);
|
||||
_adapter.addAuditLogEntryModel(auditLogEntryModel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class SpacesItemDecoration extends RecyclerView.ItemDecoration {
|
||||
private final int _space;
|
||||
|
||||
public SpacesItemDecoration(int dpSpace) {
|
||||
_space = MetricsHelper.convertDpToPixels(getContext(), dpSpace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
outRect.left = _space;
|
||||
outRect.right = _space;
|
||||
outRect.bottom = _space;
|
||||
|
||||
if (parent.getChildLayoutPosition(view) == 0) {
|
||||
outRect.top = _space;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import androidx.preference.Preference;
|
|||
|
||||
import com.beemdevelopment.aegis.BuildConfig;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.helpers.DropdownHelper;
|
||||
import com.beemdevelopment.aegis.importers.DatabaseImporter;
|
||||
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
|
||||
|
@ -63,6 +64,7 @@ import java.util.UUID;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class ImportExportPreferencesFragment extends PreferencesFragment {
|
||||
// keep a reference to the type of database converter that was selected
|
||||
|
@ -277,6 +279,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
|
|||
.setType(getExportMimeType(getExportRequestCode(pos, encrypt)))
|
||||
.putExtra(Intent.EXTRA_TITLE, fileInfo.toString());
|
||||
|
||||
_auditLogRepository.addVaultExportedEvent();
|
||||
ActivityResultLauncher<Intent> resultLauncher = getExportRequestLauncher(pos, encrypt);
|
||||
_vaultManager.fireIntentLauncher(this, intent, resultLauncher);
|
||||
});
|
||||
|
@ -320,7 +323,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
|
|||
|
||||
// if the user creates an export, hide the backup reminder
|
||||
_prefs.setLatestExportTimeNow();
|
||||
|
||||
_auditLogRepository.addVaultExportedEvent();
|
||||
Uri uri = FileProvider.getUriForFile(requireContext(), BuildConfig.FILE_PROVIDER_AUTHORITY, file);
|
||||
Intent intent = new Intent(Intent.ACTION_SEND)
|
||||
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
@ -559,7 +562,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
|
|||
} else {
|
||||
// if the user creates an export, hide the backup reminder
|
||||
_prefs.setLatestExportTimeNow();
|
||||
|
||||
_auditLogRepository.addVaultExportedEvent();
|
||||
Toast.makeText(requireContext(), getString(R.string.exported_vault), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.preference.PreferenceFragmentCompat;
|
|||
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
import com.beemdevelopment.aegis.vault.VaultManager;
|
||||
|
@ -38,6 +39,9 @@ public abstract class PreferencesFragment extends PreferenceFragmentCompat {
|
|||
@Inject
|
||||
VaultManager _vaultManager;
|
||||
|
||||
@Inject
|
||||
protected AuditLogRepository _auditLogRepository;
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package com.beemdevelopment.aegis.ui.models;
|
||||
|
||||
import com.beemdevelopment.aegis.database.AuditLogEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AuditLogEntryModel {
|
||||
private AuditLogEntry _auditLogEntry;
|
||||
private VaultEntry _referencedVaultEntry;
|
||||
|
||||
public AuditLogEntryModel(AuditLogEntry auditLogEntry, @Nullable VaultEntry referencedVaultEntry) {
|
||||
_auditLogEntry = auditLogEntry;
|
||||
_referencedVaultEntry = referencedVaultEntry;
|
||||
}
|
||||
|
||||
public AuditLogEntry getAuditLogEntry() {
|
||||
return _auditLogEntry;
|
||||
}
|
||||
|
||||
public VaultEntry getReferencedVaultEntry() {
|
||||
return _referencedVaultEntry;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.beemdevelopment.aegis.ui.views;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.database.AuditLogEntry;
|
||||
import com.beemdevelopment.aegis.ui.models.AuditLogEntryModel;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AuditLogAdapter extends RecyclerView.Adapter<AuditLogHolder> {
|
||||
private List<AuditLogEntryModel> _auditLogEntryModels;
|
||||
private List<VaultEntry> _referencedEntries;
|
||||
|
||||
public AuditLogAdapter() {
|
||||
_auditLogEntryModels = new ArrayList<>();
|
||||
_referencedEntries = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void addAuditLogEntryModel(AuditLogEntryModel auditLogEntryModel) {
|
||||
_auditLogEntryModels.add(auditLogEntryModel);
|
||||
|
||||
int position = getItemCount() - 1;
|
||||
if (position == 0) {
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
notifyItemInserted(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void addReferencedEntry(VaultEntry vaultEntry) {
|
||||
_referencedEntries.add(vaultEntry);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AuditLogHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_audit_log, parent, false);
|
||||
return new AuditLogHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AuditLogHolder holder, int position) {
|
||||
AuditLogEntryModel auditLogEntryModel = _auditLogEntryModels.get(position);
|
||||
|
||||
VaultEntry referencedEntry = null;
|
||||
holder.setData(auditLogEntryModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return _auditLogEntryModels.size();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package com.beemdevelopment.aegis.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.beemdevelopment.aegis.EventType;
|
||||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.database.AuditLogEntry;
|
||||
import com.beemdevelopment.aegis.ui.models.AuditLogEntryModel;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
public class AuditLogHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final CardView _cardView;
|
||||
private final ImageView _auditLogEntryIcon;
|
||||
private final TextView _auditLogEntryTitle;
|
||||
private final TextView _auditLogEntryDescription;
|
||||
private final TextView _auditLogEntryReference;
|
||||
private final TextView _auditLogEntryTimestamp;
|
||||
private final int _errorBackgroundColor;
|
||||
private final ColorStateList _initialBackgroundColor;
|
||||
private final int _initialIconColor;
|
||||
|
||||
public AuditLogHolder(final View view) {
|
||||
super(view);
|
||||
|
||||
_cardView = (CardView)view;
|
||||
_auditLogEntryIcon = view.findViewById(R.id.iv_icon_view);
|
||||
_auditLogEntryTitle = view.findViewById(R.id.text_audit_log_title);
|
||||
_auditLogEntryDescription = view.findViewById(R.id.text_audit_log_description);
|
||||
_auditLogEntryReference = view.findViewById(R.id.text_audit_log_reference);
|
||||
_auditLogEntryTimestamp = view.findViewById(R.id.text_audit_log_timestamp);
|
||||
|
||||
_initialBackgroundColor = _cardView.getCardBackgroundColor();
|
||||
_initialIconColor = MaterialColors.getColor(view, com.google.android.material.R.attr.colorTertiaryContainer);
|
||||
_errorBackgroundColor = MaterialColors.getColor(view, com.google.android.material.R.attr.colorErrorContainer);
|
||||
}
|
||||
|
||||
public void setData(AuditLogEntryModel auditLogEntryModel) {
|
||||
AuditLogEntry auditLogEntry = auditLogEntryModel.getAuditLogEntry();
|
||||
_auditLogEntryIcon.setImageResource(getIconResource(auditLogEntry.getEventType()));
|
||||
_auditLogEntryTitle.setText(EventType.getEventTitleRes(auditLogEntry.getEventType()));
|
||||
_auditLogEntryDescription.setText(getEventTypeDescriptionRes(auditLogEntry.getEventType()));
|
||||
|
||||
_auditLogEntryTimestamp.setText(formatTimestamp(_cardView.getContext(), auditLogEntry.getTimestamp()).toLowerCase());
|
||||
|
||||
if (auditLogEntryModel.getReferencedVaultEntry() != null) {
|
||||
VaultEntry referencedVaultEntry = auditLogEntryModel.getReferencedVaultEntry();
|
||||
_auditLogEntryReference.setText(String.format("%s (%s)", referencedVaultEntry.getIssuer(), referencedVaultEntry.getName()));
|
||||
} else {
|
||||
_auditLogEntryReference.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
setCardBackgroundColor(auditLogEntry.getEventType());
|
||||
}
|
||||
|
||||
private void setCardBackgroundColor(EventType eventType) {
|
||||
if (eventType == EventType.VAULT_UNLOCK_FAILED_PASSWORD || eventType == EventType.VAULT_UNLOCK_FAILED_BIOMETRICS) {
|
||||
_cardView.setCardBackgroundColor(_errorBackgroundColor);
|
||||
_auditLogEntryIcon.setBackgroundColor(_errorBackgroundColor);
|
||||
} else {
|
||||
_cardView.setCardBackgroundColor(_initialBackgroundColor);
|
||||
_auditLogEntryIcon.setBackgroundColor(_initialIconColor);
|
||||
}
|
||||
}
|
||||
|
||||
private int getEventTypeDescriptionRes(EventType eventType) {
|
||||
switch (eventType) {
|
||||
case VAULT_UNLOCKED:
|
||||
return R.string.event_description_vault_unlocked;
|
||||
case VAULT_BACKUP_CREATED:
|
||||
return R.string.event_description_backup_created;
|
||||
case VAULT_ANDROID_BACKUP_CREATED:
|
||||
return R.string.event_description_android_backup_created;
|
||||
case VAULT_EXPORTED:
|
||||
return R.string.event_description_vault_exported;
|
||||
case ENTRY_SHARED:
|
||||
return R.string.event_description_entry_shared;
|
||||
case VAULT_UNLOCK_FAILED_PASSWORD:
|
||||
return R.string.event_description_vault_unlock_failed_password;
|
||||
case VAULT_UNLOCK_FAILED_BIOMETRICS:
|
||||
return R.string.event_description_vault_unlock_failed_biometrics;
|
||||
default:
|
||||
return R.string.event_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
private int getIconResource(EventType eventType) {
|
||||
switch(eventType) {
|
||||
case VAULT_UNLOCKED:
|
||||
return R.drawable.ic_lock_open;
|
||||
case VAULT_BACKUP_CREATED:
|
||||
case VAULT_ANDROID_BACKUP_CREATED:
|
||||
return R.drawable.ic_folder_zip;
|
||||
case VAULT_EXPORTED:
|
||||
return R.drawable.ic_export_notes;
|
||||
case ENTRY_SHARED:
|
||||
return R.drawable.ic_share;
|
||||
case VAULT_UNLOCK_FAILED_PASSWORD:
|
||||
case VAULT_UNLOCK_FAILED_BIOMETRICS:
|
||||
return R.drawable.ic_lock;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static String formatTimestamp(Context context, long epochMilli) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime timestamp = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault());
|
||||
|
||||
long daysBetween = ChronoUnit.DAYS.between(timestamp.toLocalDate(), now.toLocalDate());
|
||||
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("h:mm a");
|
||||
|
||||
if (daysBetween < 1) {
|
||||
String formattedTime = timestamp.format(timeFormatter);
|
||||
return context.getString(R.string.today_at_time, formattedTime);
|
||||
} else if (daysBetween < 7) {
|
||||
DateTimeFormatter dayOfWeekFormatter = DateTimeFormatter.ofPattern("EEEE");
|
||||
String dayOfWeek = timestamp.format(dayOfWeekFormatter);
|
||||
String formattedTime = timestamp.format(timeFormatter);
|
||||
return context.getString(R.string.day_of_week_at_time, dayOfWeek, formattedTime);
|
||||
} else {
|
||||
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(context.getString(R.string.date_format));
|
||||
return timestamp.format(dateFormatter);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import com.beemdevelopment.aegis.Preferences;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.util.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -41,17 +42,20 @@ public class VaultBackupManager {
|
|||
private final Context _context;
|
||||
private final Preferences _prefs;
|
||||
private final ExecutorService _executor;
|
||||
private final AuditLogRepository _auditLogRepository;
|
||||
|
||||
public VaultBackupManager(Context context) {
|
||||
public VaultBackupManager(Context context, AuditLogRepository auditLogRepository) {
|
||||
_context = context;
|
||||
_prefs = new Preferences(context);
|
||||
_executor = Executors.newSingleThreadExecutor();
|
||||
_auditLogRepository = auditLogRepository;
|
||||
}
|
||||
|
||||
public void scheduleBackup(File tempFile, Uri dirUri, int versionsToKeep) {
|
||||
_executor.execute(() -> {
|
||||
try {
|
||||
createBackup(tempFile, dirUri, versionsToKeep);
|
||||
_auditLogRepository.addBackupCreatedEvent();
|
||||
_prefs.setBuiltInBackupResult(new Preferences.BackupResult(null));
|
||||
} catch (VaultRepositoryException | VaultBackupPermissionException e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.beemdevelopment.aegis.Preferences;
|
|||
import com.beemdevelopment.aegis.R;
|
||||
import com.beemdevelopment.aegis.crypto.KeyStoreHandle;
|
||||
import com.beemdevelopment.aegis.crypto.KeyStoreHandleException;
|
||||
import com.beemdevelopment.aegis.database.AuditLogRepository;
|
||||
import com.beemdevelopment.aegis.services.NotificationService;
|
||||
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
|
||||
|
||||
|
@ -37,12 +38,15 @@ public class VaultManager {
|
|||
private final List<LockListener> _lockListeners;
|
||||
private boolean _blockAutoLock;
|
||||
|
||||
public VaultManager(@NonNull Context context) {
|
||||
private final AuditLogRepository _auditLogRepository;
|
||||
|
||||
public VaultManager(@NonNull Context context, AuditLogRepository auditLogRepository) {
|
||||
_context = context;
|
||||
_prefs = new Preferences(_context);
|
||||
_backups = new VaultBackupManager(_context);
|
||||
_backups = new VaultBackupManager(_context, auditLogRepository);
|
||||
_androidBackups = new BackupManager(context);
|
||||
_lockListeners = new ArrayList<>();
|
||||
_auditLogRepository = auditLogRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
5
app/src/main/res/drawable/ic_export_notes.xml
Normal file
5
app/src/main/res/drawable/ic_export_notes.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:alpha="0.5" android:height="24dp"
|
||||
android:viewportHeight="960" android:viewportWidth="960"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="m648,820 l112,-112v92h40v-160L640,640v40h92L620,792l28,28ZM200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h560q33,0 56.5,23.5T840,200v268q-19,-9 -39,-15.5t-41,-9.5v-243L200,200v560h242q3,22 9.5,42t15.5,38L200,840ZM200,720v40,-560 243,-3 280ZM280,680h163q3,-21 9.5,-41t14.5,-39L280,600v80ZM280,520h244q32,-30 71.5,-50t84.5,-27v-3L280,440v80ZM280,360h400v-80L280,280v80ZM720,920q-83,0 -141.5,-58.5T520,720q0,-83 58.5,-141.5T720,520q83,0 141.5,58.5T920,720q0,83 -58.5,141.5T720,920Z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_folder_zip.xml
Normal file
5
app/src/main/res/drawable/ic_folder_zip.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:alpha="0.5" android:height="24dp"
|
||||
android:viewportHeight="960" android:viewportWidth="960"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M640,480v-80h80v80h-80ZM640,560h-80v-80h80v80ZM640,640v-80h80v80h-80ZM447,320l-80,-80L160,240v480h400v-80h80v80h160v-400L640,320v80h-80v-80L447,320ZM160,800q-33,0 -56.5,-23.5T80,720v-480q0,-33 23.5,-56.5T160,160h240l80,80h320q33,0 56.5,23.5T880,320v400q0,33 -23.5,56.5T800,800L160,800ZM160,720v-480,480Z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_lock.xml
Normal file
5
app/src/main/res/drawable/ic_lock.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:alpha="0.5" android:height="24dp"
|
||||
android:viewportHeight="960" android:viewportWidth="960"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M240,880q-33,0 -56.5,-23.5T160,800v-400q0,-33 23.5,-56.5T240,320h40v-80q0,-83 58.5,-141.5T480,40q83,0 141.5,58.5T680,240v80h40q33,0 56.5,23.5T800,400v400q0,33 -23.5,56.5T720,880L240,880ZM240,800h480v-400L240,400v400ZM480,680q33,0 56.5,-23.5T560,600q0,-33 -23.5,-56.5T480,520q-33,0 -56.5,23.5T400,600q0,33 23.5,56.5T480,680ZM360,320h240v-80q0,-50 -35,-85t-85,-35q-50,0 -85,35t-35,85v80ZM240,800v-400,400Z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_lock_open.xml
Normal file
5
app/src/main/res/drawable/ic_lock_open.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:alpha="0.5" android:height="24dp"
|
||||
android:viewportHeight="960" android:viewportWidth="960"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M240,320h360v-80q0,-50 -35,-85t-85,-35q-50,0 -85,35t-35,85h-80q0,-83 58.5,-141.5T480,40q83,0 141.5,58.5T680,240v80h40q33,0 56.5,23.5T800,400v400q0,33 -23.5,56.5T720,880L240,880q-33,0 -56.5,-23.5T160,800v-400q0,-33 23.5,-56.5T240,320ZM240,800h480v-400L240,400v400ZM480,680q33,0 56.5,-23.5T560,600q0,-33 -23.5,-56.5T480,520q-33,0 -56.5,23.5T400,600q0,33 23.5,56.5T480,680ZM240,800v-400,400Z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_share.xml
Normal file
5
app/src/main/res/drawable/ic_share.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:alpha="0.5" android:height="24dp"
|
||||
android:viewportHeight="960" android:viewportWidth="960"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M720,880q-50,0 -85,-35t-35,-85q0,-7 1,-14.5t3,-13.5L322,568q-17,15 -38,23.5t-44,8.5q-50,0 -85,-35t-35,-85q0,-50 35,-85t85,-35q23,0 44,8.5t38,23.5l282,-164q-2,-6 -3,-13.5t-1,-14.5q0,-50 35,-85t85,-35q50,0 85,35t35,85q0,50 -35,85t-85,35q-23,0 -44,-8.5T638,288L356,452q2,6 3,13.5t1,14.5q0,7 -1,14.5t-3,13.5l282,164q17,-15 38,-23.5t44,-8.5q50,0 85,35t35,85q0,50 -35,85t-85,35ZM720,240q17,0 28.5,-11.5T760,200q0,-17 -11.5,-28.5T720,160q-17,0 -28.5,11.5T680,200q0,17 11.5,28.5T720,240ZM240,520q17,0 28.5,-11.5T280,480q0,-17 -11.5,-28.5T240,440q-17,0 -28.5,11.5T200,480q0,17 11.5,28.5T240,520ZM720,800q17,0 28.5,-11.5T760,760q0,-17 -11.5,-28.5T720,720q-17,0 -28.5,11.5T680,760q0,17 11.5,28.5T720,800ZM720,200ZM240,480ZM720,760Z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_timeline_24.xml
Normal file
10
app/src/main/res/drawable/ic_timeline_24.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M120,720Q87,720 63.5,696.5Q40,673 40,640Q40,607 63.5,583.5Q87,560 120,560Q126,560 130.5,560Q135,560 140,562L322,380Q320,375 320,370.5Q320,366 320,360Q320,327 343.5,303.5Q367,280 400,280Q433,280 456.5,303.5Q480,327 480,360Q480,362 478,380L580,482Q585,480 589.5,480Q594,480 600,480Q606,480 610.5,480Q615,480 620,482L762,340Q760,335 760,330.5Q760,326 760,320Q760,287 783.5,263.5Q807,240 840,240Q873,240 896.5,263.5Q920,287 920,320Q920,353 896.5,376.5Q873,400 840,400Q834,400 829.5,400Q825,400 820,398L678,540Q680,545 680,549.5Q680,554 680,560Q680,593 656.5,616.5Q633,640 600,640Q567,640 543.5,616.5Q520,593 520,560Q520,554 520,549.5Q520,545 522,540L420,438Q415,440 410.5,440Q406,440 400,440Q398,440 380,438L198,620Q200,625 200,629.5Q200,634 200,640Q200,673 176.5,696.5Q153,720 120,720Z"/>
|
||||
</vector>
|
111
app/src/main/res/layout/card_audit_log.xml
Normal file
111
app/src/main/res/layout/card_audit_log.xml
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal"
|
||||
app:cardElevation="0dp"
|
||||
style="@style/Widget.Aegis.EntryCardView">
|
||||
<LinearLayout
|
||||
android:layout_weight="1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center_vertical">
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingStart="14dp">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/iv_icon_view"
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="45dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:background="?attr/colorTertiaryContainer"
|
||||
android:cropToPadding="true"
|
||||
android:padding="2dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_lock_open"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Aegis.ImageView.Rounded" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="bottom"
|
||||
android:id="@+id/relativeLayout"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingStart="12dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/description">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/text_audit_log_title"
|
||||
android:text="@string/issuer"
|
||||
android:textStyle="bold"
|
||||
android:includeFontPadding="false"
|
||||
android:textSize="16sp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"/>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/text_audit_log_description"
|
||||
android:layoutDirection="ltr"
|
||||
android:layout_below="@id/description"
|
||||
android:includeFontPadding="false"
|
||||
tools:text="The vault has been succesfully unlocked"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textStyle="normal"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/text_audit_log_reference"
|
||||
android:layoutDirection="ltr"
|
||||
android:layout_below="@id/text_audit_log_description"
|
||||
android:includeFontPadding="false"
|
||||
tools:text="Aegis (authenticator)"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textStyle="italic"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/text_audit_log_timestamp"
|
||||
android:layoutDirection="ltr"
|
||||
android:layout_below="@id/text_audit_log_reference"
|
||||
android:includeFontPadding="false"
|
||||
tools:text="Today at 5:59 PM"
|
||||
android:textSize="12sp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:textColor="?attr/colorOutline"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textStyle="normal"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
56
app/src/main/res/layout/fragment_audit_log.xml
Normal file
56
app/src/main/res/layout/fragment_audit_log.xml
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center|fill_vertical"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/vEmptyList"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="150dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="75dp"
|
||||
android:layout_height="75dp"
|
||||
android:src="@drawable/ic_timeline_24" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_events_title"
|
||||
android:paddingTop="17dp"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_no_audit_logs"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:lineSpacingExtra="5dp"
|
||||
android:paddingTop="7dp"
|
||||
android:text="@string/no_events_description"
|
||||
android:textAlignment="center" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_audit_log"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -31,6 +31,8 @@
|
|||
<string name="pref_section_security_summary">Configure encryption, biometric unlock, auto lock and other security settings.</string>
|
||||
<string name="pref_section_import_export_title">Import & Export</string>
|
||||
<string name="pref_section_import_export_summary">Import backups of Aegis or other authenticator apps. Create manual exports of your Aegis vault.</string>
|
||||
<string name="pref_section_audit_log_title">Audit log</string>
|
||||
<string name="pref_section_audit_log_summary">Find a list of all reported important events that happened within the app.</string>
|
||||
<string name="pref_section_backups_title">Backups</string>
|
||||
<string name="pref_section_backups_summary">Set up automatic backups to a location of your choosing or enable participation in Android\'s cloud backup system.</string>
|
||||
<string name="pref_section_icon_packs">Icon packs</string>
|
||||
|
@ -106,6 +108,28 @@
|
|||
<string name="pref_set_password_title">Change password</string>
|
||||
<string name="pref_set_password_summary">Set a new password which you will need to unlock your vault</string>
|
||||
|
||||
<string name="no_events_title">No reported events</string>
|
||||
<string name="no_events_description">No important events have been reported within the app</string>
|
||||
<string name="event_title_vault_unlocked">Vault unlocked</string>
|
||||
<string name="event_description_vault_unlocked">The vault has been successfully unlocked</string>
|
||||
<string name="event_title_backup_created">Backup created</string>
|
||||
<string name="event_description_backup_created">A backup of the vault has been successfully created</string>
|
||||
<string name="event_title_android_backup_created">Backup created by Android</string>
|
||||
<string name="event_description_android_backup_created">A backup of the vault has been successfully created by Android</string>
|
||||
<string name="event_title_vault_exported">Vault exported</string>
|
||||
<string name="event_description_vault_exported">A copy of the vault has been exported</string>
|
||||
<string name="event_title_entry_shared">Entry shared</string>
|
||||
<string name="event_description_entry_shared">An entry was shared</string>
|
||||
<string name="event_title_vault_unlock_failed_password">Vault unlock failed (password)</string>
|
||||
<string name="event_description_vault_unlock_failed_password">An attempt to unlock the vault with a password failed</string>
|
||||
<string name="event_title_vault_unlock_failed_biometrics">Vault unlock failed (biometrics)</string>
|
||||
<string name="event_description_vault_unlock_failed_biometrics">An attempt to unlock the vault with biometrics failed</string>
|
||||
<string name="event_unknown">Unknown event type</string>
|
||||
|
||||
<string name="today_at_time">Today at %1$s</string>
|
||||
<string name="day_of_week_at_time">%1$s at %2$s</string>
|
||||
<string name="date_format">dd/MM/yyyy</string>
|
||||
|
||||
<string name="export_encrypted">Encrypt the vault</string>
|
||||
<string name="export_help">This action will export the vault out of Aegis\' internal storage. Select the format you\'d like your export to be in:</string>
|
||||
<string name="export_warning_unencrypted">You are about to export an unencrypted copy of your Aegis vault. <b>This is not recommended</b>.</string>
|
||||
|
|
|
@ -258,4 +258,9 @@
|
|||
<style name="Widget.Aegis.AlertDialog.Icon.Warning" parent="@style/MaterialAlertDialog.Material3.Title.Icon.CenterStacked">
|
||||
<item name="android:tint">?attr/colorError</item>
|
||||
</style>
|
||||
|
||||
<style name="ShapeAppearanceOverlay.Aegis.ImageView.Rounded" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">8dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -40,4 +40,10 @@
|
|||
app:title="@string/pref_section_import_export_title"
|
||||
app:summary="@string/pref_section_import_export_summary" />
|
||||
|
||||
<Preference
|
||||
android:fragment="com.beemdevelopment.aegis.ui.fragments.preferences.AuditLogPreferencesFragment"
|
||||
app:icon="@drawable/ic_timeline_24"
|
||||
app:title="@string/pref_section_audit_log_title"
|
||||
app:summary="@string/pref_section_audit_log_summary" />
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
|
Loading…
Add table
Reference in a new issue