diff --git a/.gitignore b/.gitignore index 8ccc4f86..d95437f9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ .externalNativeBuild .cxx local.properties +keystore.properties +*.jks diff --git a/app/build.gradle b/app/build.gradle index e10de03a..62a6b94b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,21 +11,50 @@ android { minSdk 26 targetSdk 33 versionCode 1 - versionName "1.0" + versionName "0.1.0-Alpha" + } + + viewBinding { + enabled = true } signingConfigs { release { - storeFile file("passwordmanager.jks") - storePassword 'beMbDcv94lv3' - keyAlias 'passwordmanager' - keyPassword 'beMbDcv94lv3' + def keystorePropertiesFile = rootProject.file("keystore.properties") + if (keystorePropertiesFile.exists()) { + def keystoreProperties = new Properties() + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile file(keystoreProperties['storeFile']) + storePassword keystoreProperties['storePassword'] + } + } + } + + splits { + abi { + enable true + reset() + include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + universalApk true } } + + lintOptions { + // You can enable lint checking and provide a baseline file if needed: + // baseline file("lint-baseline.xml") + + // To ensure that lint uses the lint.xml configuration + lintConfig rootProject.file("lint.xml") + } buildTypes { release { - signingConfig signingConfigs.release + if (rootProject.file("keystore.properties").exists()) { + signingConfig signingConfigs.release + } minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 11d3ca40..4d70ade2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:ignore="ScopedStorage" /> + @@ -25,7 +26,7 @@ android:supportsRtl="true" android:theme="@style/PasswordManagerTheme"> @@ -33,6 +34,10 @@ + + + + diff --git a/app/src/main/java/com/passwordmanager/MainActivity.java b/app/src/main/java/com/passwordmanager/MainActivity.java deleted file mode 100644 index a7b46bfb..00000000 --- a/app/src/main/java/com/passwordmanager/MainActivity.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.passwordmanager; - -import android.os.Bundle; -import android.widget.Toast; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.view.WindowCompat; -import com.passwordmanager.utils.Permissions; - -public class MainActivity extends AppCompatActivity { - - private Permissions permissionsHandle; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - // Make window fullscreen - WindowCompat.setDecorFitsSystemWindows(getWindow(), false); - - // Check and request permission when the app is first opened - permissionsHandle = new Permissions(this); - if (!permissionsHandle.checkPermission()) permissionsHandle.requestPermission(); - } - - @Override - public void onRequestPermissionsResult( - int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == Permissions.PERMISSION_REQUEST_CODE) { - if (permissionsHandle.isPermissionGranted(grantResults)) { - // Permission has been granted - Toast.makeText(this, getString(R.string.permission_granted), Toast.LENGTH_LONG).show(); - } else { - // Permission not granted - Toast.makeText(this, getString(R.string.permission_denied), Toast.LENGTH_LONG).show(); - } - } - } -} diff --git a/app/src/main/java/com/passwordmanager/database/MyDatabaseHelper.java b/app/src/main/java/com/passwordmanager/database/MyDatabaseHelper.java new file mode 100644 index 00000000..8d2f8022 --- /dev/null +++ b/app/src/main/java/com/passwordmanager/database/MyDatabaseHelper.java @@ -0,0 +1,39 @@ +package com.passwordmanager.database; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +public class MyDatabaseHelper extends SQLiteOpenHelper { + private static final String DATABASE_NAME = "master.db"; + private static final int DATABASE_VERSION = 1; + + public static final String PASSWORDS_TABLE = "passwords"; + + // SQL statement to create a passwords table. + private static final String CREATE_PASSWORDS_TABLE = "CREATE TABLE IF NOT EXISTS " + PASSWORDS_TABLE + " (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "domain VARCHAR(40) NOT NULL, " + + "username VARCHAR(60) NOT NULL, " + + "password VARCHAR(60) NOT NULL, " + + "notes VARCHAR(100), " + + "createdat DATE DEFAULT CURRENT_TIMESTAMP, " + + "updatedat DATE DEFAULT CURRENT_TIMESTAMP);"; + + + public MyDatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + // Create passwords table. + db.execSQL(CREATE_PASSWORDS_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // not Implemented a not needed. + return; + } +} diff --git a/app/src/main/java/com/passwordmanager/models/PasswordModel.java b/app/src/main/java/com/passwordmanager/models/PasswordModel.java new file mode 100644 index 00000000..e063c329 --- /dev/null +++ b/app/src/main/java/com/passwordmanager/models/PasswordModel.java @@ -0,0 +1,111 @@ +package com.passwordmanager.models; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class PasswordModel { + private int id; // Primary key of passwords table. + private String domain; // Domain associated with password entity. + private String username; // Username associated with password entity. + private String password; // Password of password entity. + private String notes; // Notes associated with the password entity. + private String createdAt; // Date of password entity when the entity was first saved. + private String updatedAt; // Date of password entity when the entity was last updated. + + // Constructor to initialize a new password model without an ID. + public PasswordModel(String domain, String username, String password, String notes) { + this.domain = domain; + this.username = username; + this.password = password; + this.notes = notes; + + // Retrieve the current date and time + LocalDateTime currentDate = LocalDateTime.now(); + + // Formatting the time so that it is better seen by users + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + this.createdAt = currentDate.format(formatter); + this.updatedAt = currentDate.format(formatter); + } + + // Constructor with ID (e.g., when fetching from the database) + public PasswordModel(int id, String domain, String username, String password, String notes, String createdAt, String updatedAt) { + this.id = id; + this.domain = domain; + this.username = username; + this.password = password; + this.notes = notes; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + + // Getters and Setters + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public String getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(String updatedAt) { + this.updatedAt = updatedAt; + } + + @Override + public String toString() { + return "PasswordModel{" + + "id=" + id + + ", domain='" + domain + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", notes='" + notes + '\'' + + ", createdAt='" + createdAt + '\'' + + ", updatedAt='" + updatedAt + '\'' + + '}'; + } +} diff --git a/app/src/main/java/com/passwordmanager/ui/LoadPasswordActivity.java b/app/src/main/java/com/passwordmanager/ui/LoadPasswordActivity.java new file mode 100644 index 00000000..c7e72049 --- /dev/null +++ b/app/src/main/java/com/passwordmanager/ui/LoadPasswordActivity.java @@ -0,0 +1,60 @@ +package com.passwordmanager.ui; + +import android.os.Bundle; +import android.content.Intent; +import android.widget.Toast; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; + +import com.passwordmanager.R; +import com.passwordmanager.utils.Controller; +import com.passwordmanager.models.PasswordModel; +import com.passwordmanager.databinding.ActivityLoadPasswordBinding; +import com.passwordmanager.ui.adapter.PasswordAdapter; + +import java.util.List; + +public class LoadPasswordActivity extends AppCompatActivity { + private ActivityLoadPasswordBinding binding; + private PasswordAdapter passwordAdapter; + private Controller controller; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = ActivityLoadPasswordBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + // Add event onclick listener + addOnClickListenerOnButton(); + + // Make window fullscreen + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + } + + private void fillPasswordList() { + controller = new Controller(LoadPasswordActivity.this); + + List passwordList = controller.getAllPasswords(); + passwordAdapter = new PasswordAdapter(this, passwordList); + binding.passwordList.setAdapter(passwordAdapter); + } + + // Added all the onclick event listiners + private void addOnClickListenerOnButton() { + binding.passwordList.setOnItemClickListener((parent, view, position, id) -> { + PasswordModel selectedPassword = (PasswordModel) passwordAdapter.getItem(position); + + // Do something with the selectedPassword + Intent intent = new Intent(LoadPasswordActivity.this, ViewPasswordActivity.class); + intent.putExtra("id", selectedPassword.getId()); + startActivity(intent); + }); + } + + @Override + protected void onResume() { + super.onResume(); + fillPasswordList(); + } +} diff --git a/app/src/main/java/com/passwordmanager/ui/MainActivity.java b/app/src/main/java/com/passwordmanager/ui/MainActivity.java new file mode 100644 index 00000000..a80dd48d --- /dev/null +++ b/app/src/main/java/com/passwordmanager/ui/MainActivity.java @@ -0,0 +1,69 @@ +package com.passwordmanager.ui; + +import android.os.Bundle; +import android.content.Intent; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; +import android.view.LayoutInflater; + +import com.passwordmanager.R; +import com.passwordmanager.databinding.ActivityMainBinding; +// import com.passwordmanager.utils.Permissions; + +public class MainActivity extends AppCompatActivity { + + // private Permissions permissionsHandle; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + // Add event onclick listener + addOnClickListenerOnButton(binding); + + // Make window fullscreen + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + + /* Comment the code as permission is not need to write into app data dir. + // Check and request permission when the app is first opened + permissionsHandle = new Permissions(this); + if (!permissionsHandle.checkPermission()) permissionsHandle.requestPermission(); + */ + } + + /* Comment the code as permission is not need to write into app data dir. + @Override + public void onRequestPermissionsResult( + int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == Permissions.PERMISSION_REQUEST_CODE) { + if (permissionsHandle.isPermissionGranted(grantResults)) { + // Permission has been granted + Toast.makeText(this, getString(R.string.permission_granted), Toast.LENGTH_LONG).show(); + } else { + // Permission not granted + Toast.makeText(this, getString(R.string.permission_denied), Toast.LENGTH_LONG).show(); + } + } + } + */ + + // Added all the onclick event listiners + private void addOnClickListenerOnButton(ActivityMainBinding binding) { + binding.savePasswordBtn.setOnClickListener(v -> { + Intent savepasswordintent = new Intent(MainActivity.this, SavePasswordActivity.class); + startActivity(savepasswordintent); + }); + + binding.loadPasswordBtn.setOnClickListener(v -> { + Intent loadpasswordintent = new Intent(MainActivity.this, LoadPasswordActivity.class); + startActivity(loadpasswordintent); + }); + + binding.quitBtn.setOnClickListener(v -> { + finishAndRemoveTask(); + }); + } +} diff --git a/app/src/main/java/com/passwordmanager/ui/SavePasswordActivity.java b/app/src/main/java/com/passwordmanager/ui/SavePasswordActivity.java new file mode 100644 index 00000000..5a0283c3 --- /dev/null +++ b/app/src/main/java/com/passwordmanager/ui/SavePasswordActivity.java @@ -0,0 +1,49 @@ +package com.passwordmanager.ui; + +import android.os.Bundle; +import android.widget.Toast; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; + +import com.passwordmanager.R; +import com.passwordmanager.utils.Controller; +import com.passwordmanager.databinding.ActivitySavePasswordBinding; + +public class SavePasswordActivity extends AppCompatActivity { + private Controller controller; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ActivitySavePasswordBinding binding = ActivitySavePasswordBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + // Add event onclick listener + addOnClickListenerOnButton(binding); + + // Make window fullscreen + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + } + + // Added all the onclick event listiners + private void addOnClickListenerOnButton(ActivitySavePasswordBinding binding) { + binding.savePasswordBtn.setOnClickListener(v -> { + String domain = binding.inputDomain.getText().toString(); + String username = binding.inputUsername.getText().toString(); + String password = binding.inputPassword.getText().toString(); + String notes = binding.inputNotes.getText().toString(); + + controller = new Controller(SavePasswordActivity.this); + int res = controller.savePasswordEntity(domain, username, password, notes); + + if (res == -2) { + Toast.makeText(SavePasswordActivity.this, getString(R.string.warn_fill_form), Toast.LENGTH_SHORT).show(); + } + else if (res == -1) { + Toast.makeText(SavePasswordActivity.this, getString(R.string.fail_msg), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(SavePasswordActivity.this, getString(R.string.sucess_clause) + res, Toast.LENGTH_SHORT).show(); + } + }); + } +} diff --git a/app/src/main/java/com/passwordmanager/ui/UpdatePasswordActivity.java b/app/src/main/java/com/passwordmanager/ui/UpdatePasswordActivity.java new file mode 100644 index 00000000..b078dc41 --- /dev/null +++ b/app/src/main/java/com/passwordmanager/ui/UpdatePasswordActivity.java @@ -0,0 +1,75 @@ +package com.passwordmanager.ui; + +import android.os.Bundle; +import android.content.Intent; +import android.widget.Toast; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; +import android.view.LayoutInflater; + +import com.passwordmanager.R; +import com.passwordmanager.utils.Controller; +import com.passwordmanager.models.PasswordModel; +import com.passwordmanager.databinding.ActivityUpdatePasswordBinding; + +/* + Activity expects id as intent parameters. +*/ + +public class UpdatePasswordActivity extends AppCompatActivity { + private int passwordEnitityId = 0; + private Controller controller; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ActivityUpdatePasswordBinding binding = ActivityUpdatePasswordBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + Intent intent = getIntent(); + passwordEnitityId = intent.getIntExtra("id", -1); // -1 is a invalid id. + + if (passwordEnitityId == -1) { //invalid enitity + finish(); + } + + controller = new Controller(UpdatePasswordActivity.this); + PasswordModel passwordmodel = controller.getPasswordById(passwordEnitityId); + + // Put value in text view & edit texts + binding.tvId.setText(getString(R.string.id_prefix) + " " + passwordEnitityId); + binding.inputDomain.setText(passwordmodel.getDomain()); + binding.inputUsername.setText(passwordmodel.getUsername()); + binding.inputPassword.setText(passwordmodel.getPassword()); + binding.inputNotes.setText(passwordmodel.getNotes()); + + // Add event onclick listener + addOnClickListenerOnButton(binding); + + // Make window fullscreen + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + } + + // Added all the onclick event listiners + private void addOnClickListenerOnButton(ActivityUpdatePasswordBinding binding) { + binding.updatePasswordBtn.setOnClickListener(v -> { + // TODO: implement password update logic. + String newDomain = binding.inputDomain.getText().toString(); + String newUsername = binding.inputUsername.getText().toString(); + String newPassword = binding.inputPassword.getText().toString(); + String newNotes = binding.inputNotes.getText().toString(); + + int res = controller.updatePassword(passwordEnitityId, newDomain, newUsername, newPassword, newNotes); + + if (res == 1) { + Toast.makeText(UpdatePasswordActivity.this, getString(R.string.update_sucess_msg), Toast.LENGTH_SHORT).show(); + + finish(); + } else { + Toast.makeText(UpdatePasswordActivity.this, getString(R.string.something_went_wrong_msg), Toast.LENGTH_SHORT).show(); + } + + finish(); + }); + } +} diff --git a/app/src/main/java/com/passwordmanager/ui/ViewPasswordActivity.java b/app/src/main/java/com/passwordmanager/ui/ViewPasswordActivity.java new file mode 100644 index 00000000..c732c7b8 --- /dev/null +++ b/app/src/main/java/com/passwordmanager/ui/ViewPasswordActivity.java @@ -0,0 +1,87 @@ +package com.passwordmanager.ui; + +import android.os.Bundle; +import android.content.Intent; +import android.widget.Toast; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; +import android.view.LayoutInflater; + +import com.passwordmanager.R; +import com.passwordmanager.utils.Controller; +import com.passwordmanager.models.PasswordModel; +import com.passwordmanager.databinding.ActivityViewPasswordBinding; + +/* + Activity expects id, domain, username, password, notes, createdat, updatedat as intent parameters. +*/ + +public class ViewPasswordActivity extends AppCompatActivity { + private int passwordEnitityId = 0; + private ActivityViewPasswordBinding binding; + private Controller controller; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = ActivityViewPasswordBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + Intent intent = getIntent(); + passwordEnitityId = intent.getIntExtra("id", 1); // -1 is a invalid id. + + if (passwordEnitityId == -1) { //invalid enitity + finish(); + } + + controller = new Controller(ViewPasswordActivity.this); + + // Filling the textviews with data + // fillDataInTextview(); + + // Add event onclick listener + addOnClickListenerOnButton(); + + // Make window fullscreen + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + } + + private void fillDataInTextview() { + PasswordModel passwordmodel = controller.getPasswordById(passwordEnitityId); + + binding.tvDomain.setText(getString(R.string.domain_prefix) + " " + passwordmodel.getDomain()); + binding.tvUsername.setText(getString(R.string.username_prefix) + " " + passwordmodel.getUsername()); + binding.tvPassword.setText(getString(R.string.password_prefix) + " " + passwordmodel.getPassword()); + binding.tvNotes.setText(getString(R.string.notes_prefix) + " " + passwordmodel.getNotes()); + binding.tvCreatedAt.setText(getString(R.string.createdat_prefix) + " " + passwordmodel.getCreatedAt()); + binding.tvUpdatedAt.setText(getString(R.string.updatedat_prefix) + " " + passwordmodel.getUpdatedAt()); + } + + // Added all the onclick event listiners + private void addOnClickListenerOnButton() { + binding.updatePasswordBtn.setOnClickListener(v -> { + Intent viewpasswordintent = new Intent(ViewPasswordActivity.this, UpdatePasswordActivity.class); + viewpasswordintent.putExtra("id", passwordEnitityId); + startActivity(viewpasswordintent); + }); + + binding.deletePasswordBtn.setOnClickListener(v -> { + controller = new Controller(ViewPasswordActivity.this); + int res = controller.deletePassword(passwordEnitityId); + + if (res == 1) { + Toast.makeText(ViewPasswordActivity.this, getString(R.string.delete_sucess_msg), Toast.LENGTH_SHORT).show(); + + finish(); + } else { + Toast.makeText(ViewPasswordActivity.this, getString(R.string.something_went_wrong_msg), Toast.LENGTH_SHORT).show(); + } + }); + } + + @Override + protected void onResume() { + super.onResume(); + fillDataInTextview(); + } +} diff --git a/app/src/main/java/com/passwordmanager/ui/adapter/PasswordAdapter.java b/app/src/main/java/com/passwordmanager/ui/adapter/PasswordAdapter.java new file mode 100644 index 00000000..16f9bcdc --- /dev/null +++ b/app/src/main/java/com/passwordmanager/ui/adapter/PasswordAdapter.java @@ -0,0 +1,57 @@ +package com.passwordmanager.ui.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.BaseAdapter; + +import com.passwordmanager.models.PasswordModel; +import com.passwordmanager.databinding.PasswordListItemBinding; + +import java.util.List; + +public class PasswordAdapter extends BaseAdapter { + + private Context context; + private List passwordList; + + public PasswordAdapter(Context context, List passwordList) { + this.context = context; + this.passwordList = passwordList; + } + + @Override + public int getCount() { + return passwordList.size(); + } + + @Override + public Object getItem(int position) { + return passwordList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + PasswordListItemBinding binding; + + if (convertView == null) { + binding = PasswordListItemBinding.inflate(LayoutInflater.from(context), parent, false); + convertView = binding.getRoot(); + convertView.setTag(binding); + } else { + binding = (PasswordListItemBinding) convertView.getTag(); + } + + PasswordModel password = passwordList.get(position); + binding.tvDomain.setText(password.getDomain()); + binding.tvUsername.setText(password.getUsername()); + + return convertView; + } +} diff --git a/app/src/main/java/com/passwordmanager/utils/Controller.java b/app/src/main/java/com/passwordmanager/utils/Controller.java new file mode 100644 index 00000000..255be291 --- /dev/null +++ b/app/src/main/java/com/passwordmanager/utils/Controller.java @@ -0,0 +1,186 @@ +package com.passwordmanager.utils; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import java.util.List; +import java.util.ArrayList; + +import com.passwordmanager.database.MyDatabaseHelper; +import com.passwordmanager.models.PasswordModel; + +public class Controller { + private MyDatabaseHelper dbHelper; + + public Controller(Context context) { + dbHelper = new MyDatabaseHelper(context); + } + + public int savePasswordEntity(String domain, String username, String password, String notes) { + // Return -2 on empty parameter. + // Return -1 on error in SQL. + // Return +ve on success. + + if (domain.isEmpty() || username.isEmpty() || password.isEmpty()) { + return -2; + } + + SQLiteDatabase db = dbHelper.getWritableDatabase(); + + ContentValues values = new ContentValues(); + values.put("domain", domain); + values.put("username", username); + values.put("password", password); + if (!notes.isEmpty()) values.put("notes", notes); + + long newRowId = db.insert(MyDatabaseHelper.PASSWORDS_TABLE, null, values); + db.close(); + + return newRowId == -1 ? -1 : (int) newRowId; + } + + public PasswordModel getPasswordById(int id) { + SQLiteDatabase db = dbHelper.getReadableDatabase(); + + // Define the selection criteria + String selection = "id = ?"; + String[] selectionArgs = new String[]{String.valueOf(id)}; + + Cursor cursor = db.query( + MyDatabaseHelper.PASSWORDS_TABLE, + null, + selection, + selectionArgs, + null, null, null); + + PasswordModel passwordModel = null; + + if (cursor != null) { + if (cursor.moveToFirst()) { + String domain = cursor.getString(cursor.getColumnIndex("domain")); + String username = cursor.getString(cursor.getColumnIndex("username")); + String password = cursor.getString(cursor.getColumnIndex("password")); + String notes = cursor.getString(cursor.getColumnIndex("notes")); + String createdAt = cursor.getString(cursor.getColumnIndex("createdat")); + String updatedAt = cursor.getString(cursor.getColumnIndex("updatedat")); + + passwordModel = new PasswordModel(id, domain, username, password, notes, createdAt, updatedAt); + } + cursor.close(); + } + + db.close(); + return passwordModel; + } + + public PasswordModel getPasswordByUsernameAndDomain(String username, String domain) { + SQLiteDatabase db = dbHelper.getReadableDatabase(); + + // Define the selection criteria + String selection = "username = ? AND domain = ?"; + String[] selectionArgs = new String[]{username, domain}; + + Cursor cursor = db.query( + MyDatabaseHelper.PASSWORDS_TABLE, + null, + selection, + selectionArgs, + null, + null, + null + ); + + PasswordModel passwordModel = null; + + if (cursor != null) { + if (cursor.moveToFirst()) { + int id = cursor.getInt(cursor.getColumnIndex("id")); + String retrievedUsername = cursor.getString(cursor.getColumnIndex("username")); + String retrievedDomain = cursor.getString(cursor.getColumnIndex("domain")); + String password = cursor.getString(cursor.getColumnIndex("password")); + String notes = cursor.getString(cursor.getColumnIndex("notes")); + String createdAt = cursor.getString(cursor.getColumnIndex("createdat")); + String updatedAt = cursor.getString(cursor.getColumnIndex("updatedat")); + + passwordModel = new PasswordModel(id, retrievedDomain, retrievedUsername, password, notes, createdAt, updatedAt); + } + cursor.close(); + } + + db.close(); + return passwordModel; + } + + public List getAllPasswords() { + List passwordList = new ArrayList(); + SQLiteDatabase db = dbHelper.getReadableDatabase(); + Cursor cursor = db.query(MyDatabaseHelper.PASSWORDS_TABLE, null, null, null, null, null, null); + + if (cursor.moveToFirst()) { + do { + int id = cursor.getInt(cursor.getColumnIndexOrThrow("id")); + String domain = cursor.getString(cursor.getColumnIndexOrThrow("domain")); + String username = cursor.getString(cursor.getColumnIndexOrThrow("username")); + String password = cursor.getString(cursor.getColumnIndexOrThrow("password")); + String notes = cursor.getString(cursor.getColumnIndexOrThrow("notes")); + String createdAt = cursor.getString(cursor.getColumnIndexOrThrow("createdat")); + String updatedAt = cursor.getString(cursor.getColumnIndexOrThrow("updatedat")); + + PasswordModel passwordItem = new PasswordModel(id, domain, username, password, notes, createdAt, updatedAt); + passwordList.add(passwordItem); + } while (cursor.moveToNext()); + } + + cursor.close(); + db.close(); + + return passwordList; + } + + public int updatePassword(int id, String domain, String username, String password, String notes) { + // Return -2 on empty parameter. + // Return -1 on error in SQL. + // Return number of rows affected on success. + + if (domain.isEmpty() || username.isEmpty() || password.isEmpty()) { + return -2; + } + + SQLiteDatabase db = dbHelper.getWritableDatabase(); + + ContentValues values = new ContentValues(); + values.put("domain", domain); + values.put("username", username); + values.put("password", password); + values.put("notes", notes); + + int rowsAffected; + + try { + rowsAffected = db.update(MyDatabaseHelper.PASSWORDS_TABLE, values, "id = ?", new String[]{String.valueOf(id)}); + + if (rowsAffected > 0) { + String updateQuery = "UPDATE " + MyDatabaseHelper.PASSWORDS_TABLE + " SET updatedat = datetime('now') WHERE id = ?"; + db.execSQL(updateQuery, new Object[]{id}); + } + } catch (Exception e) { + db.close(); + return -1; // Error in SQL + } + db.close(); + + return rowsAffected == 0 ? -1 : rowsAffected; + } + + public int deletePassword(int id) { + // Return number of rows affected (should be 1 if the deletion is successful). + + SQLiteDatabase db = dbHelper.getWritableDatabase(); + int rowsDeleted = db.delete(MyDatabaseHelper.PASSWORDS_TABLE, "id = ?", new String[]{String.valueOf(id)}); + db.close(); + + return rowsDeleted; + } +} diff --git a/app/src/main/res/layout/activity_load_password.xml b/app/src/main/res/layout/activity_load_password.xml new file mode 100644 index 00000000..e86a016c --- /dev/null +++ b/app/src/main/res/layout/activity_load_password.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index a4d5b749..86de9d81 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -3,12 +3,61 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" - tools:context=".MainActivity"> + android:orientation="vertical" + tools:context=".ui.MainActivity" + android:padding="4sp" > + + + + + + + + + + + + + + + + + - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_save_password.xml b/app/src/main/res/layout/activity_save_password.xml new file mode 100644 index 00000000..fa1ceb26 --- /dev/null +++ b/app/src/main/res/layout/activity_save_password.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_update_password.xml b/app/src/main/res/layout/activity_update_password.xml new file mode 100644 index 00000000..7bad6921 --- /dev/null +++ b/app/src/main/res/layout/activity_update_password.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_view_password.xml b/app/src/main/res/layout/activity_view_password.xml new file mode 100644 index 00000000..99e7f3d7 --- /dev/null +++ b/app/src/main/res/layout/activity_view_password.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/password_list_item.xml b/app/src/main/res/layout/password_list_item.xml new file mode 100644 index 00000000..e36c7e35 --- /dev/null +++ b/app/src/main/res/layout/password_list_item.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5d258bd9..f4904f18 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,46 @@ Password Manager - - + v0.1.0-Alpha + + + Quit + + + Domain + Username + Password + Notes + + Save Password + Load Password + Update Password + Delete Password + + + Load Password + Save Password + View Password + View Password + + + Id: + Domain: + Username: + Password: + Notes: + CreatedAt: + UpdatedAt: + + Permission Granted Permission Denied + + + 404: Not Found!! + Warning: please fill the form first!! + Failed: please try again!! + Success: + Updated Successfully!! + Deleted Successfully!! + Something Went Wrong!! \ No newline at end of file diff --git a/installondevice.bat b/installondevice.bat new file mode 100644 index 00000000..418147d0 --- /dev/null +++ b/installondevice.bat @@ -0,0 +1,41 @@ +@REM Build & install on Android phone + +@REM Force continuation after Gradle command by capturing the output +call gradle build +if ERRORLEVEL 1 ( + echo Gradle build failed! Check build_output.txt for details. + exit /b 1 +) + +@REM Start ADB server +adb start-server +if ERRORLEVEL 1 ( + echo Failed to start ADB server! Exiting... + exit /b 1 +) + +@REM Check if a device is connected +adb devices +if ERRORLEVEL 1 ( + echo No device found! Make sure your device is connected and USB debugging is enabled. + adb kill-server + exit /b 1 +) + +@REM Install the APK on the connected device +adb install ./app/build/outputs/apk/release/app-universal-release.apk +if ERRORLEVEL 1 ( + echo APK installation failed! Exiting... + adb kill-server + exit /b 1 +) + +@REM Kill ADB server after installation +adb kill-server +if ERRORLEVEL 1 ( + echo Failed to stop ADB server! Exiting... + exit /b 1 +) + +echo Build and installation completed successfully! +exit /b 0 diff --git a/lint.xml b/lint.xml new file mode 100644 index 00000000..e1452c37 --- /dev/null +++ b/lint.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 8ce983c4..cfdf065f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,7 +3,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() - jcenter() // Warning: this repository is going to shut down soon + // jcenter() } } rootProject.name = "Password Manager"