-
-
Notifications
You must be signed in to change notification settings - Fork 86
Export to Bitwarden json #830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.yogeshpaliyal.keypass.exporter | ||
|
||
import android.content.Context | ||
import androidx.compose.runtime.Composable | ||
import com.yogeshpaliyal.common.data.AccountModel | ||
import java.io.File | ||
|
||
interface AccountsExporter { | ||
fun getExporterTitle(): Int | ||
|
||
fun getExporterDesc(): Int | ||
|
||
fun export(context: Context, listOfAccounts: List<AccountModel>) : File? | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package com.yogeshpaliyal.keypass.exporter | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import android.os.Build | ||
import android.util.Log | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.material3.CircularProgressIndicator | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import androidx.compose.ui.window.Dialog | ||
import androidx.core.content.FileProvider | ||
import com.yogeshpaliyal.common.data.AccountModel | ||
import com.yogeshpaliyal.keypass.BuildConfig | ||
import com.yogeshpaliyal.keypass.R | ||
import org.json.JSONObject | ||
import java.io.File | ||
import java.time.LocalDateTime | ||
import java.time.format.DateTimeFormatter | ||
import java.util.UUID | ||
|
||
|
||
class BitwardenAccountsExporter : AccountsExporter { | ||
override fun getExporterTitle(): Int { | ||
return R.string.bitwarden_export | ||
} | ||
|
||
override fun getExporterDesc(): Int { | ||
return R.string.bitwarden_export_desc | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
override fun export(context: Context, listOfAccounts: List<AccountModel>) : File { | ||
val title = context.getString(getExporterTitle()).lowercase() | ||
val path = context.filesDir | ||
val letDirectory = File(path, "${title}_exports") | ||
letDirectory.mkdirs() | ||
|
||
val currentTime = LocalDateTime.now() | ||
val dateTime = currentTime.format(DateTimeFormatter.ofPattern("ddMMyyyyHHmmSS")) | ||
|
||
val exportFile: File = File(letDirectory, "${title}_export_${dateTime}.json") | ||
|
||
val uuid:UUID = UUID.randomUUID() | ||
Comment on lines
+47
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. File creation logic can also be same for all the exporters. |
||
|
||
var folderData : HashMap<String, Any?> = HashMap() | ||
folderData.put("id", uuid.toString()) | ||
folderData.put("name", "KeyPass") | ||
|
||
var itemsData : MutableList<HashMap<String, Any?>> = mutableListOf() | ||
listOfAccounts.forEach { | ||
var accountData : HashMap<String, Any?> = HashMap() | ||
var loginData : HashMap<String, Any?> = HashMap() | ||
var uriData : HashMap<String, Any?> = HashMap() | ||
accountData.put("folderId", uuid.toString()) | ||
accountData.put("type", 1) | ||
accountData.put("organizationId", it.uniqueId) | ||
accountData.put("id", it.id) | ||
accountData.put("name", it.title) | ||
accountData.put("notes", it.notes) | ||
accountData.put("favourite", false) | ||
accountData.put("fields", intArrayOf()) | ||
|
||
loginData.put("username", it.username) | ||
loginData.put("password", it.password) | ||
loginData.put("totp", it.secret) | ||
|
||
uriData.put("uri", it.site) | ||
uriData.put("match", null) | ||
|
||
loginData.put("uris", uriData) | ||
accountData.put("login", loginData) | ||
itemsData.add(accountData) | ||
} | ||
|
||
val exportData: HashMap<String, Any?> = HashMap() | ||
exportData.put("folders", listOf(folderData)) | ||
exportData.put("items", itemsData) | ||
|
||
val jsonString: String = JSONObject(exportData).toString() | ||
exportFile.writeText(jsonString) | ||
|
||
return exportFile | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package com.yogeshpaliyal.keypass.ui.exportPasswords | ||
|
||
import android.os.Build | ||
import androidx.activity.compose.rememberLauncherForActivityResult | ||
import androidx.activity.result.contract.ActivityResultContracts | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.lazy.LazyColumn | ||
import androidx.compose.foundation.lazy.items | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.livedata.observeAsState | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.platform.LocalContext | ||
import androidx.compose.ui.unit.dp | ||
import androidx.hilt.navigation.compose.hiltViewModel | ||
import com.yogeshpaliyal.keypass.R | ||
import com.yogeshpaliyal.keypass.exporter.BitwardenAccountsExporter | ||
import com.yogeshpaliyal.keypass.ui.commonComponents.DefaultBottomAppBar | ||
import com.yogeshpaliyal.keypass.ui.commonComponents.PreferenceItem | ||
import com.yogeshpaliyal.keypass.ui.home.DashboardViewModel | ||
import com.yogeshpaliyal.keypass.ui.redux.states.ExportPasswordState | ||
import java.io.BufferedWriter | ||
import java.io.OutputStream | ||
import java.io.OutputStreamWriter | ||
|
||
val listOfExporters = listOf(BitwardenAccountsExporter()) | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This as well |
||
@Composable | ||
fun PasswordExporter(state: ExportPasswordState, mViewModel: DashboardViewModel = hiltViewModel()) { | ||
var fileText : String = "" | ||
val context = LocalContext.current | ||
val listOfAccountsLiveData by mViewModel.mediator.observeAsState() | ||
|
||
val launcher = rememberLauncherForActivityResult( | ||
contract = ActivityResultContracts.CreateDocument("text/plain"), | ||
onResult = { | ||
val outputStream : OutputStream? = context.contentResolver.openOutputStream(it!!) | ||
val bufferedWriter : BufferedWriter = BufferedWriter(OutputStreamWriter(outputStream)) | ||
bufferedWriter.write(fileText) | ||
bufferedWriter.flush() | ||
bufferedWriter.close() | ||
} | ||
) | ||
|
||
Scaffold(bottomBar = { | ||
DefaultBottomAppBar() | ||
}) { | ||
LazyColumn( | ||
modifier = Modifier | ||
.padding(it) | ||
.fillMaxWidth(1f) | ||
.padding(16.dp) | ||
) { | ||
item { | ||
PreferenceItem( | ||
title = R.string.export_passwords_title, | ||
isCategory = true, | ||
removeIconSpace = true | ||
) | ||
} | ||
items(listOfExporters) { | ||
PreferenceItem( | ||
title = it.getExporterTitle(), | ||
summary = it.getExporterDesc(), | ||
removeIconSpace = true | ||
) { | ||
val exportedFile = it.export(context, listOfAccountsLiveData!!.toList()) | ||
fileText = exportedFile!!.readText() | ||
launcher.launch(exportedFile!!.name) | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
package com.yogeshpaliyal.keypass.ui.nav | ||
|
||
import android.os.Build | ||
import android.os.Bundle | ||
import android.view.WindowManager | ||
import androidx.activity.ComponentActivity | ||
import androidx.activity.compose.BackHandler | ||
import androidx.activity.compose.setContent | ||
import androidx.annotation.RequiresApi | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material3.Scaffold | ||
|
@@ -30,6 +32,7 @@ import com.yogeshpaliyal.keypass.ui.backupsImport.BackupImporter | |
import com.yogeshpaliyal.keypass.ui.changeDefaultPasswordLength.ChangeDefaultPasswordLengthScreen | ||
import com.yogeshpaliyal.keypass.ui.changePassword.ChangePassword | ||
import com.yogeshpaliyal.keypass.ui.detail.AccountDetailPage | ||
import com.yogeshpaliyal.keypass.ui.exportPasswords.PasswordExporter | ||
import com.yogeshpaliyal.keypass.ui.home.Homepage | ||
import com.yogeshpaliyal.keypass.ui.nav.components.DashboardBottomSheet | ||
import com.yogeshpaliyal.keypass.ui.nav.components.KeyPassBottomBar | ||
|
@@ -42,6 +45,7 @@ import com.yogeshpaliyal.keypass.ui.redux.states.BackupImporterState | |
import com.yogeshpaliyal.keypass.ui.redux.states.BackupScreenState | ||
import com.yogeshpaliyal.keypass.ui.redux.states.ChangeAppPasswordState | ||
import com.yogeshpaliyal.keypass.ui.redux.states.ChangeDefaultPasswordLengthState | ||
import com.yogeshpaliyal.keypass.ui.redux.states.ExportPasswordState | ||
import com.yogeshpaliyal.keypass.ui.redux.states.HomeState | ||
import com.yogeshpaliyal.keypass.ui.redux.states.KeyPassState | ||
import com.yogeshpaliyal.keypass.ui.redux.states.ScreenState | ||
|
@@ -131,6 +135,7 @@ fun Dashboard() { | |
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this as well |
||
@Composable | ||
fun CurrentPage() { | ||
val currentScreen by selectState<KeyPassState, ScreenState> { this.currentScreen } | ||
|
@@ -166,6 +171,8 @@ fun CurrentPage() { | |
} | ||
|
||
is BackupImporterState -> BackupImporter(state = it) | ||
|
||
is ExportPasswordState -> PasswordExporter(state = it) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.yogeshpaliyal.keypass.ui.redux.states | ||
|
||
import com.yogeshpaliyal.keypass.exporter.AccountsExporter | ||
|
||
data class ExportPasswordState( | ||
var selectedExporter : AccountsExporter? = null | ||
) : ScreenState() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<paths> | ||
<files-path path="bitwarden_exports/" name="bitwarden" /> | ||
</paths> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somehow can we remove this and add support for older version as well