Skip to content

Commit

Permalink
Avoid Encrypting/Decrypting on the UI Thread & Show Progress for Text…
Browse files Browse the repository at this point in the history
… Encryption/Decryption
  • Loading branch information
Ammar64 authored and umutcamliyurt committed Oct 21, 2024
1 parent 2a16f86 commit b474aab
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.iml
.gradle
.idea
/local.properties
/.idea/caches
/.idea/libraries
Expand Down
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ android {

defaultConfig {
applicationId "com.nemesis.droidcrypt"
minSdk 30
minSdk 19
targetSdk 34
versionCode 1
versionName "1.0"
versionCode 2
versionName "1.1"

multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

Expand All @@ -29,7 +30,6 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}

// Add the dependenciesInfo block inside the android block
dependenciesInfo {
includeInApk false
includeInBundle false
Expand All @@ -43,4 +43,7 @@ dependencies {
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core

// Multidex support library
implementation 'androidx.multidex:multidex:2.0.1'
}
12 changes: 3 additions & 9 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,8 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.nemesis.droidcrypt">

<!-- Permissions for reading media files -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<!-- Permission for accessing selected photos -->
<uses-permission android:name="android.permission.ACCESS_SELECTED_PHOTOS" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<!-- Request legacy storage for compatibility -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="30"
Expand All @@ -22,6 +15,7 @@

<application
android:allowBackup="true"
android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
Expand Down
107 changes: 70 additions & 37 deletions app/src/main/java/com/nemesis/droidcrypt/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.provider.Settings;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.widget.ContentLoadingProgressBar;

import org.bouncycastle.crypto.generators.SCrypt;

Expand All @@ -38,7 +39,8 @@
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import android.util.Base64;
import java.util.Locale;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
Expand All @@ -47,7 +49,9 @@

public class MainActivity extends AppCompatActivity {

private EditText inputEditText, passwordEditText, outputEditText;
private EditText inputEditText, passwordEditText;
private TextView outputTextView;
private ContentLoadingProgressBar progress;
private CheckBox rememberPasswordCheckBox;
private static final int FILE_PICKER_REQUEST_CODE = 123;
private static final int PERMISSION_REQUEST_CODE = 124;
Expand All @@ -61,7 +65,8 @@ protected void onCreate(Bundle savedInstanceState) {

inputEditText = findViewById(R.id.inputEditText);
passwordEditText = findViewById(R.id.passwordEditText);
outputEditText = findViewById(R.id.outputEditText);
outputTextView = findViewById(R.id.outputTextView);
progress = findViewById(R.id.textEncryptProgress);
rememberPasswordCheckBox = findViewById(R.id.rememberPasswordCheckBox);

// Initialize SharedPreferences
Expand Down Expand Up @@ -130,16 +135,23 @@ public void performTextDecryption(View view) {
}

private void processText(boolean isEncryption) {
hideOutputText();
String inputText = inputEditText.getText().toString();
if (!inputText.isEmpty()) {
try {
String password = passwordEditText.getText().toString();
savePassword(password); // Save password if "Remember Password" is checked
String resultText = isEncryption ? encryptText(inputText, password) : decryptText(inputText, password);
outputEditText.setText(resultText);
} catch (Exception e) {
showError((isEncryption ? "Encryption" : "Decryption") + " failed: " + e.getMessage());
}
progress.show();
String password = passwordEditText.getText().toString();
savePassword(password); // Save password if "Remember Password" is checked
new Thread(() -> {
try {
String resultText = isEncryption ? encryptText(inputText, password) : decryptText(inputText, password);
runOnUiThread(() -> {
progress.hide();
showOutputText(resultText);
});
} catch (Exception e) {
showError((isEncryption ? "Encryption" : "Decryption") + " failed: " + e.getMessage());
}
}).start();
} else {
showToast("Please enter text to " + (isEncryption ? "encrypt" : "decrypt") + ".");
}
Expand All @@ -156,25 +168,28 @@ public void performFileDecryption(View view) {
private void processFile(boolean isEncryption) {
if (selectedFileUri != null) {
String password = passwordEditText.getText().toString();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);
showToast("Please enable the Manage All Files Access permission and try again.");
return;
new Thread(() -> {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
Uri uri = Uri.parse(String.format(Locale.ENGLISH, "package:%s", getApplicationContext().getPackageName()));
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri);
startActivity(intent);
showToast("Please enable the Manage All Files Access permission and try again.");
return;
}

byte[] processedBytes = isEncryption ? encryptFile(selectedFileUri, password) : decryptFile(selectedFileUri, password);
if (processedBytes != null) {
String fileName = isEncryption ? getFileName(selectedFileUri) + ".enc" : getFileNameWithoutExtension(selectedFileUri);
saveFile(processedBytes, fileName);
showToast("File " + (isEncryption ? "encrypted" : "decrypted") + " successfully.");
} else {
showError((isEncryption ? "Encryption" : "Decryption") + " failed. Check password or file.");
}
} catch (Exception e) {
showError((isEncryption ? "Encryption" : "Decryption") + " failed: " + e.getMessage());
}

byte[] processedBytes = isEncryption ? encryptFile(selectedFileUri, password) : decryptFile(selectedFileUri, password);
if (processedBytes != null) {
String fileName = isEncryption ? getFileName(selectedFileUri) + ".enc" : getFileNameWithoutExtension(selectedFileUri);
saveFile(processedBytes, fileName);
showToast("File " + (isEncryption ? "encrypted" : "decrypted") + " successfully.");
} else {
showError((isEncryption ? "Encryption" : "Decryption") + " failed. Check password or file.");
}
} catch (Exception e) {
showError((isEncryption ? "Encryption" : "Decryption") + " failed: " + e.getMessage());
}
}).start();
} else {
showToast("Please select a file to " + (isEncryption ? "encrypt" : "decrypt") + ".");
}
Expand Down Expand Up @@ -233,11 +248,11 @@ private String encryptText(String inputText, String password) throws GeneralSecu
Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, secretKey, null);
byte[] iv = cipher.getIV();
byte[] cipherText = cipher.doFinal(inputText.getBytes());
return Base64.getEncoder().encodeToString(concatenateArrays(salt, iv, cipherText));
return Base64.encodeToString(concatenateArrays(salt, iv, cipherText), Base64.DEFAULT);
}

private String decryptText(String inputText, String password) throws GeneralSecurityException {
byte[] inputBytes = Base64.getDecoder().decode(inputText);
byte[] inputBytes = Base64.decode(inputText, Base64.DEFAULT);
byte[] salt = Arrays.copyOfRange(inputBytes, 0, 16);
byte[] iv = Arrays.copyOfRange(inputBytes, 16, 28);
byte[] cipherText = Arrays.copyOfRange(inputBytes, 28, inputBytes.length);
Expand Down Expand Up @@ -268,7 +283,10 @@ private byte[] generateSalt() {
}

private byte[] concatenateArrays(byte[]... arrays) {
int totalLength = Arrays.stream(arrays).mapToInt(array -> array.length).sum();
int totalLength = 0;
for( int i = 0 ; i < arrays.length ; i++ ) {
totalLength += arrays[i].length;
}
byte[] result = new byte[totalLength];
int currentIndex = 0;
for (byte[] array : arrays) {
Expand Down Expand Up @@ -309,7 +327,7 @@ private byte[] readBytes(InputStream inputStream) throws IOException {
}

public void copyToClipboard(View view) {
String outputText = outputEditText.getText().toString();
String outputText = outputTextView.getText().toString();
if (!outputText.isEmpty()) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Output Text", outputText);
Expand All @@ -323,6 +341,7 @@ public void copyToClipboard(View view) {
showToast("Output text is empty.");
}
}

public void forgetEverything(View view) {
// Clear SharedPreferences
SharedPreferences.Editor editor = sharedPreferences.edit();
Expand All @@ -332,14 +351,24 @@ public void forgetEverything(View view) {
passwordEditText.setText(""); // Clear the password EditText
// Clear input and output fields
inputEditText.setText("");
outputEditText.setText("");
outputTextView.setText("");

// Clear remember password checkbox
rememberPasswordCheckBox.setChecked(false);

showToast("All data forgotten.");
}

public void showOutputText(String text) {
outputTextView.setVisibility(View.VISIBLE);
outputTextView.setText(text);
}

public void hideOutputText() {
outputTextView.setVisibility(View.INVISIBLE);
outputTextView.setText("");
}

public void pasteInput(View view) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard != null && clipboard.hasPrimaryClip()) {
Expand All @@ -357,10 +386,14 @@ public void pasteInput(View view) {
}

private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
runOnUiThread(() -> {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
});
}

private void showError(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
runOnUiThread(() -> {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
});
}
}
24 changes: 20 additions & 4 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="match_parent"
Expand Down Expand Up @@ -157,18 +158,33 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"/>

<EditText
android:id="@+id/outputEditText"
<TextView
android:id="@+id/outputTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/outputLabel"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:hint="Output will be displayed here"
android:inputType="textMultiLine"
android:minLines="4"
android:maxLines="10"
android:scrollbars="vertical"/>
android:textSize="18sp"
android:textStyle="bold"
android:textIsSelectable="true"
android:scrollbars="vertical"
android:visibility="invisible"/>

<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/textEncryptProgress"
style="?android:attr/progressBarStyleLarge"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignTop="@id/outputTextView"
android:layout_centerHorizontal="true"
android:visibility="gone"
/>



</RelativeLayout>
</ScrollView>

0 comments on commit b474aab

Please sign in to comment.