diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index aea1919..998a495 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
android:installLocation="auto">
+
@@ -27,6 +28,12 @@
+
+
+
+
+
+
val databaseFolder = Constants.getDatabaseFolder(requireContext())
if (createNewIdentity(databaseFolder, identityName, password)) {
- (binder as LoginActivity.ActivityLauncher).launch(identityName)
+ (binder as LoginActivity.ActivityLauncher).launch()
success = true
}
}
diff --git a/app/src/main/java/sushi/hardcore/aira/LoginActivity.kt b/app/src/main/java/sushi/hardcore/aira/LoginActivity.kt
index 33b8809..bc5036b 100644
--- a/app/src/main/java/sushi/hardcore/aira/LoginActivity.kt
+++ b/app/src/main/java/sushi/hardcore/aira/LoginActivity.kt
@@ -9,21 +9,18 @@ import sushi.hardcore.aira.background_service.AIRAService
import java.io.File
class LoginActivity : AppCompatActivity() {
- private external fun getIdentityName(databaseFolder: String): String?
-
companion object {
const val NAME_ARG = "identityName"
const val BINDER_ARG = "binder"
- private external fun initLogging()
- init {
- System.loadLibrary("aira")
- initLogging()
- }
+ }
+
+ init {
+ AIRADatabase.init()
}
inner class ActivityLauncher: Binder() {
- fun launch(identityName: String) {
- startMainActivity(identityName)
+ fun launch() {
+ startMainActivity()
}
}
@@ -38,13 +35,13 @@ class LoginActivity : AppCompatActivity() {
}
}
val isProtected = AIRADatabase.isIdentityProtected(databaseFolder)
- val name = getIdentityName(databaseFolder)
+ val name = AIRADatabase.getIdentityName(databaseFolder)
if (AIRAService.isServiceRunning) {
- startMainActivity(null)
+ startMainActivity()
} else if (name != null && !isProtected) {
if (AIRADatabase.loadIdentity(databaseFolder, null)) {
AIRADatabase.clearCache()
- startMainActivity(name)
+ startMainActivity()
} else {
Toast.makeText(this, R.string.identity_load_failed, Toast.LENGTH_SHORT).show()
}
@@ -62,11 +59,10 @@ class LoginActivity : AppCompatActivity() {
}
}
- private fun startMainActivity(identityName: String?) {
+ private fun startMainActivity() {
val mainActivityIntent = Intent(this, MainActivity::class.java)
mainActivityIntent.action = intent.action
mainActivityIntent.putExtras(intent)
- mainActivityIntent.putExtra(NAME_ARG, identityName)
startActivity(mainActivityIntent)
finish()
}
diff --git a/app/src/main/java/sushi/hardcore/aira/LoginFragment.kt b/app/src/main/java/sushi/hardcore/aira/LoginFragment.kt
index a9ad56b..d39760a 100644
--- a/app/src/main/java/sushi/hardcore/aira/LoginFragment.kt
+++ b/app/src/main/java/sushi/hardcore/aira/LoginFragment.kt
@@ -42,7 +42,7 @@ class LoginFragment : Fragment() {
binding.buttonLogin.setOnClickListener {
if (AIRADatabase.loadIdentity(databaseFolder, binding.editPassword.text.toString().toByteArray())) {
AIRADatabase.clearCache()
- (binder as LoginActivity.ActivityLauncher).launch(name)
+ (binder as LoginActivity.ActivityLauncher).launch()
} else {
Toast.makeText(activity, R.string.identity_load_failed, Toast.LENGTH_SHORT).show()
}
diff --git a/app/src/main/java/sushi/hardcore/aira/MainActivity.kt b/app/src/main/java/sushi/hardcore/aira/MainActivity.kt
index b167067..bccb3d1 100644
--- a/app/src/main/java/sushi/hardcore/aira/MainActivity.kt
+++ b/app/src/main/java/sushi/hardcore/aira/MainActivity.kt
@@ -95,11 +95,6 @@ class MainActivity : ServiceBoundActivity() {
setContentView(binding.root)
setSupportActionBar(binding.toolbar.toolbar)
- val identityName = intent.getStringExtra(LoginActivity.NAME_ARG)
- identityName?.let {
- initToolbar(it)
- }
-
val openedToShareFile = intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE
onlineSessionAdapter = SessionAdapter(this)
@@ -157,12 +152,10 @@ class MainActivity : ServiceBoundActivity() {
airaService.uiCallbacks = uiCallbacks
airaService.isAppInBackground = false
refreshSessions()
- if (AIRAService.isServiceRunning) {
- airaService.identityName?.let { initToolbar(it) }
- } else {
- airaService.identityName = identityName
+ if (!AIRAService.isServiceRunning) {
startService(serviceIntent)
}
+ initToolbar(airaService.identityName)
}
override fun onServiceDisconnected(name: ComponentName?) {}
}
diff --git a/app/src/main/java/sushi/hardcore/aira/SettingsActivity.kt b/app/src/main/java/sushi/hardcore/aira/SettingsActivity.kt
index a7401a4..e9307ab 100644
--- a/app/src/main/java/sushi/hardcore/aira/SettingsActivity.kt
+++ b/app/src/main/java/sushi/hardcore/aira/SettingsActivity.kt
@@ -27,6 +27,7 @@ import sushi.hardcore.aira.utils.StringUtils
class SettingsActivity: AppCompatActivity() {
class MySettingsFragment(private val activity: AppCompatActivity): PreferenceFragmentCompat() {
+ private lateinit var databaseFolder: String
private lateinit var airaService: AIRAService
private val avatarPicker = AvatarPicker(activity) { picker, avatar ->
if (::airaService.isInitialized) {
@@ -37,6 +38,7 @@ class SettingsActivity: AppCompatActivity() {
displayAvatar(avatar)
}
private lateinit var identityAvatarPreference: Preference
+ private lateinit var startAtBootSwitch: SwitchPreferenceCompat
override fun onAttach(context: Context) {
super.onAttach(context)
@@ -45,10 +47,13 @@ class SettingsActivity: AppCompatActivity() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
+ databaseFolder = Constants.getDatabaseFolder(activity)
findPreference("identityAvatar")?.let { identityAvatarPreference = it }
+ startAtBootSwitch = findPreference("startAtBoot")!!
+ updateStartAtBootSwitch(AIRADatabase.isIdentityProtected(databaseFolder))
val paddingPreference = findPreference("psecPadding")
paddingPreference?.isPersistent = false
- AIRADatabase.getIdentityAvatar(Constants.getDatabaseFolder(activity))?.let { avatar ->
+ AIRADatabase.getIdentityAvatar(databaseFolder)?.let { avatar ->
displayAvatar(avatar)
}
Intent(activity, AIRAService::class.java).also { serviceIntent ->
@@ -68,9 +73,9 @@ class SettingsActivity: AppCompatActivity() {
avatarPicker.launch()
}
val dialogBinding = ChangeAvatarDialogBinding.inflate(layoutInflater)
- val avatar = AIRADatabase.getIdentityAvatar(Constants.getDatabaseFolder(activity))
+ val avatar = AIRADatabase.getIdentityAvatar(databaseFolder)
if (avatar == null) {
- dialogBinding.avatar.setTextAvatar(airaService.identityName!!)
+ dialogBinding.avatar.setTextAvatar(airaService.identityName)
} else {
dialogBinding.avatar.setImageAvatar(avatar)
dialogBuilder.setNegativeButton(R.string.remove) { _, _ ->
@@ -112,7 +117,7 @@ class SettingsActivity: AppCompatActivity() {
findPreference("identityPassword")?.setOnPreferenceClickListener {
val dialogView = layoutInflater.inflate(R.layout.dialog_password, null)
val oldPasswordEditText = dialogView.findViewById(R.id.old_password)
- val isIdentityProtected = AIRADatabase.isIdentityProtected(Constants.getDatabaseFolder(activity))
+ val isIdentityProtected = AIRADatabase.isIdentityProtected(databaseFolder)
if (!isIdentityProtected) {
oldPasswordEditText.visibility = View.GONE
}
@@ -123,24 +128,24 @@ class SettingsActivity: AppCompatActivity() {
.setTitle(R.string.change_password)
.setPositiveButton(R.string.ok) { _, _ ->
val newPassword = newPasswordEditText.text.toString().toByteArray()
- if (newPassword.isEmpty()) {
- if (isIdentityProtected) { //don't change password if identity is not protected and new password is blank
- changePassword(isIdentityProtected, oldPasswordEditText, null)
+ val newPasswordConfirm = newPasswordConfirmEditText.text.toString().toByteArray()
+ if (newPassword.contentEquals(newPasswordConfirm)) {
+ if (newPassword.isEmpty()) {
+ if (isIdentityProtected) { //don't change password if identity is not protected and new password is blank
+ changePassword(isIdentityProtected, oldPasswordEditText, null)
+ }
+ } else {
+ changePassword(isIdentityProtected, oldPasswordEditText, newPassword)
}
} else {
- val newPasswordConfirm = newPasswordConfirmEditText.text.toString().toByteArray()
- if (newPassword.contentEquals(newPasswordConfirm)) {
- changePassword(isIdentityProtected, oldPasswordEditText, newPassword)
- } else {
- AlertDialog.Builder(activity, R.style.CustomAlertDialog)
- .setMessage(R.string.password_mismatch)
- .setTitle(R.string.error)
- .setPositiveButton(R.string.ok, null)
- .show()
- }
- newPassword.fill(0)
- newPasswordConfirm.fill(0)
+ AlertDialog.Builder(activity, R.style.CustomAlertDialog)
+ .setMessage(R.string.password_mismatch)
+ .setTitle(R.string.error)
+ .setPositiveButton(R.string.ok, null)
+ .show()
}
+ newPassword.fill(0)
+ newPasswordConfirm.fill(0)
}
.setNegativeButton(R.string.cancel, null)
.show()
@@ -188,7 +193,13 @@ class SettingsActivity: AppCompatActivity() {
} else {
null
}
- if (!AIRADatabase.changePassword(Constants.getDatabaseFolder(activity), oldPassword, newPassword)) {
+ if (AIRADatabase.changePassword(databaseFolder, oldPassword, newPassword)) {
+ val isNowIdentityProtected = newPassword != null
+ updateStartAtBootSwitch(isNowIdentityProtected)
+ if (isIdentityProtected && !isNowIdentityProtected ) {
+ startAtBootSwitch.isChecked = true
+ }
+ } else {
AlertDialog.Builder(activity, R.style.CustomAlertDialog)
.setMessage(R.string.change_password_failed)
.setTitle(R.string.error)
@@ -197,6 +208,15 @@ class SettingsActivity: AppCompatActivity() {
}
oldPassword?.fill(0)
}
+
+ private fun updateStartAtBootSwitch(isIdentityProtected: Boolean) {
+ startAtBootSwitch.isEnabled = !isIdentityProtected
+ startAtBootSwitch.summary = getString(if (isIdentityProtected) {
+ R.string.start_at_boot_summary_identity_protected
+ } else {
+ R.string.start_at_boot_summary
+ })
+ }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
diff --git a/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt b/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt
index 6655a65..af9293b 100644
--- a/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt
+++ b/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt
@@ -89,6 +89,7 @@ class AIRAService : Service() {
}
override fun onServiceLost(serviceInfo: NsdServiceInfo?) {}
}
+ lateinit var identityName: String
val savedMsgs = mutableMapOf>()
val pendingMsgs = mutableMapOf>()
val savedNames = mutableMapOf()
@@ -96,7 +97,6 @@ class AIRAService : Service() {
val notSeen = mutableListOf()
var uiCallbacks: UiCallbacks? = null
var isAppInBackground = true
- var identityName: String? = null
inner class AIRABinder : Binder() {
fun getService(): AIRAService = this@AIRAService
@@ -500,14 +500,12 @@ class AIRAService : Service() {
}
}
MESSAGE_SEND_NAME -> {
- identityName?.let {
- val tellingName = Protocol.name(it)
- for (session in sessions.values) {
- try {
- session.encryptAndSend(tellingName, usePadding)
- } catch (e: SocketException) {
- e.printStackTrace()
- }
+ val tellingName = Protocol.name(identityName)
+ for (session in sessions.values) {
+ try {
+ session.encryptAndSend(tellingName, usePadding)
+ } catch (e: SocketException) {
+ e.printStackTrace()
}
}
}
@@ -540,6 +538,7 @@ class AIRAService : Service() {
}
}
}
+ identityName = AIRADatabase.getIdentityName(Constants.getDatabaseFolder(this))!!
val contactList = AIRADatabase.loadContacts()
if (contactList == null) {
contacts = HashMap(0)
@@ -773,9 +772,7 @@ class AIRAService : Service() {
}
}
Protocol.ASK_PROFILE_INFO -> {
- identityName?.let { name ->
- session.encryptAndSend(Protocol.name(name), usePadding)
- }
+ session.encryptAndSend(Protocol.name(identityName), usePadding)
AIRADatabase.getIdentityAvatar(Constants.getDatabaseFolder(this))?.let { avatar ->
session.encryptAndSend(Protocol.avatar(avatar), usePadding)
}
diff --git a/app/src/main/java/sushi/hardcore/aira/background_service/SystemBroadcastReceiver.kt b/app/src/main/java/sushi/hardcore/aira/background_service/SystemBroadcastReceiver.kt
new file mode 100644
index 0000000..0599d67
--- /dev/null
+++ b/app/src/main/java/sushi/hardcore/aira/background_service/SystemBroadcastReceiver.kt
@@ -0,0 +1,36 @@
+package sushi.hardcore.aira.background_service
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import androidx.preference.PreferenceManager
+import sushi.hardcore.aira.AIRADatabase
+import sushi.hardcore.aira.Constants
+
+class SystemBroadcastReceiver: BroadcastReceiver() {
+ init {
+ AIRADatabase.init()
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
+ if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("startAtBoot", true) && !AIRAService.isServiceRunning) {
+ val databaseFolder = Constants.getDatabaseFolder(context)
+ val isProtected = AIRADatabase.isIdentityProtected(databaseFolder)
+ val name = AIRADatabase.getIdentityName(databaseFolder)
+ if (name != null && !isProtected) {
+ if (AIRADatabase.loadIdentity(databaseFolder, null)) {
+ AIRADatabase.clearCache()
+ val serviceIntent = Intent(context, AIRAService::class.java)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ context.startForegroundService(serviceIntent)
+ } else {
+ context.startService(serviceIntent)
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/native/Cargo.toml b/app/src/main/native/Cargo.toml
index ed0e178..5ac18c9 100644
--- a/app/src/main/native/Cargo.toml
+++ b/app/src/main/native/Cargo.toml
@@ -11,21 +11,21 @@ jni = { version = "0.19", default-features = false }
crate-type = ["dylib"]
[dependencies]
-rand = "0.8.3"
-rand-7 = {package = "rand", version = "0.7.3"}
-lazy_static = "1.4.0"
-rusqlite = { version = "0.25.1", features = ["bundled"] }
+rand = "0.8"
+rand-7 = {package = "rand", version = "0.7"}
+lazy_static = "1.4"
+rusqlite = { version = "0.25", features = ["bundled"] }
ed25519-dalek = "1" #for singing
x25519-dalek = "1.1" #for shared secret
-sha2 = "0.9.3"
-hkdf = "0.11.0"
-aes-gcm = "0.9.0" #PSEC
-aes-gcm-siv = "0.10.0" #Database
-hmac = "0.11.0"
-hex = "0.4.3"
-strum_macros = "0.20.1" #display enums
+sha2 = "0.9"
+hkdf = "0.11"
+aes-gcm = "0.9" #PSEC
+aes-gcm-siv = "0.10" #Database
+hmac = "0.11"
+hex = "0.4"
+strum_macros = "0.21" #display enums
uuid = { version = "0.8", features = ["v4"] }
-scrypt = "0.7.0"
-zeroize = "1.2.0"
-log = "0.4.14"
-android_log = "0.1.3"
+scrypt = "0.7"
+zeroize = "1.4"
+log = "0.4"
+android_log = "0.1"
diff --git a/app/src/main/native/src/lib.rs b/app/src/main/native/src/lib.rs
index 101151f..806fd33 100644
--- a/app/src/main/native/src/lib.rs
+++ b/app/src/main/native/src/lib.rs
@@ -53,7 +53,7 @@ fn slice_to_jvalue<'a>(env: JNIEnv, input: &'a [u8]) -> JValue<'a> {
}
#[no_mangle]
-pub extern fn Java_sushi_hardcore_aira_LoginActivity_00024Companion_initLogging(_: JNIEnv, _: JClass) -> jboolean {
+pub extern fn Java_sushi_hardcore_aira_AIRADatabase_initLogging(_: JNIEnv, _: JClass) -> jboolean {
bool_to_jboolean(android_log::init("AIRA Native").is_ok())
}
@@ -76,7 +76,7 @@ pub extern fn Java_sushi_hardcore_aira_CreateIdentityFragment_createNewIdentity(
#[no_mangle]
-pub extern fn Java_sushi_hardcore_aira_LoginActivity_getIdentityName(env: JNIEnv, _: JClass, database_folder: JString) -> jobject {
+pub extern fn Java_sushi_hardcore_aira_AIRADatabase_getIdentityName(env: JNIEnv, _: JClass, database_folder: JString) -> jobject {
*match Identity::get_identity_name(&jstring_to_string(env, database_folder)) {
Ok(name) => *env.new_string(name).unwrap(),
Err(e) => {
diff --git a/app/src/main/res/drawable/ic_blur.xml b/app/src/main/res/drawable/ic_blur.xml
index 64429b3..c8a5f4b 100644
--- a/app/src/main/res/drawable/ic_blur.xml
+++ b/app/src/main/res/drawable/ic_blur.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/app/src/main/res/drawable/ic_shuttle.xml b/app/src/main/res/drawable/ic_shuttle.xml
new file mode 100644
index 0000000..ae7360f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_shuttle.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7e511e4..2fd14b1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -104,6 +104,10 @@
Pending messages:
Sending pending messages…
Stop
+ App
+ Start AIRA service at boot
+ If disabled, you won\'t receive messages until you open the app manually.
+ Only available if identity is not protected by a password.
Send file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e44f790..6bb4bb2 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -34,6 +34,17 @@
+
+
+
+
+
+