feat: import settings on login page

This commit is contained in:
rebelonion 2024-02-04 03:59:52 -06:00
parent d80b250650
commit 7a67fbb980
6 changed files with 123 additions and 4 deletions

View file

@ -1,14 +1,25 @@
package ani.dantotsu.home
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import ani.dantotsu.R
import ani.dantotsu.connections.anilist.Anilist
import ani.dantotsu.databinding.FragmentLoginBinding
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.settings.saving.internal.PreferenceKeystore
import ani.dantotsu.settings.saving.internal.PreferencePackager
import ani.dantotsu.statusBarHeight
import ani.dantotsu.toast
import com.google.android.material.textfield.TextInputEditText
import eltos.simpledialogfragment.SimpleDialog
class LoginFragment : Fragment() {
@ -21,6 +32,9 @@ class LoginFragment : Fragment() {
savedInstanceState: Bundle?
): View {
_binding = FragmentLoginBinding.inflate(layoutInflater, container, false)
val layoutParams = binding.importSettingsButton.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin += statusBarHeight
binding.importSettingsButton.layoutParams = layoutParams
return binding.root
}
@ -29,5 +43,89 @@ class LoginFragment : Fragment() {
binding.loginDiscord.setOnClickListener { openLinkInBrowser(getString(R.string.discord)) }
binding.loginGithub.setOnClickListener { openLinkInBrowser(getString(R.string.github)) }
binding.loginTelegram.setOnClickListener { openLinkInBrowser(getString(R.string.telegram)) }
val openDocumentLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
if (uri != null) {
try {
val jsonString = requireActivity().contentResolver.openInputStream(uri)?.readBytes()
?: throw Exception("Error reading file")
val name = DocumentFile.fromSingleUri(requireActivity(), uri)?.name ?: "settings"
//.sani is encrypted, .ani is not
if (name.endsWith(".sani")) {
passwordAlertDialog() { password ->
if (password != null) {
val salt = jsonString.copyOfRange(0, 16)
val encrypted = jsonString.copyOfRange(16, jsonString.size)
val decryptedJson = PreferenceKeystore.decryptWithPassword(
password,
encrypted,
salt
)
if(PreferencePackager.unpack(decryptedJson))
println("Settings imported")
restartApp()
} else {
toast("Password cannot be empty")
}
}
} else {
val decryptedJson = jsonString.toString(Charsets.UTF_8)
if(PreferencePackager.unpack(decryptedJson))
restartApp()
}
} catch (e: Exception) {
e.printStackTrace()
toast("Error importing settings")
}
}
}
binding.importSettingsButton.setOnClickListener {
openDocumentLauncher.launch(arrayOf("*/*"))
}
}
private fun passwordAlertDialog(callback: (CharArray?) -> Unit) {
val password = CharArray(16).apply { fill('0') }
// Inflate the dialog layout
val dialogView = LayoutInflater.from(requireActivity()).inflate(R.layout.dialog_user_agent, null)
dialogView.findViewById<TextInputEditText>(R.id.userAgentTextBox)?.hint = "Password"
val subtitleTextView = dialogView.findViewById<TextView>(R.id.subtitle)
subtitleTextView?.visibility = View.VISIBLE
subtitleTextView?.text = "Enter your password to decrypt the file"
val dialog = AlertDialog.Builder(requireActivity(), R.style.MyPopup)
.setTitle("Enter Password")
.setView(dialogView)
.setPositiveButton("OK", null)
.setNegativeButton("Cancel") { dialog, _ ->
password.fill('0')
dialog.dismiss()
callback(null)
}
.create()
dialog.window?.setDimAmount(0.8f)
dialog.show()
// Override the positive button here
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val editText = dialog.findViewById<TextInputEditText>(R.id.userAgentTextBox)
if (editText?.text?.isNotBlank() == true) {
editText.text?.toString()?.trim()?.toCharArray(password)
dialog.dismiss()
callback(password)
} else {
toast("Password cannot be empty")
}
}
}
private fun restartApp() {
val intent = Intent(requireActivity(), requireActivity().javaClass)
requireActivity().finish()
startActivity(intent)
}
}

View file

@ -74,7 +74,6 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
lateinit var binding: ActivitySettingsBinding
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller()
private var cursedCounter = 0
private var tempPassword: CharArray? = null
@OptIn(UnstableApi::class)
@SuppressLint("SetTextI18n", "Recycle")

View file

@ -242,6 +242,13 @@ object PrefManager {
)
}
/**
* @param prefs Map of preferences to import
* @param prefLocation Location to import to
* @return true if successful, false if error
*/
@Suppress("UNCHECKED_CAST")
fun importAllPrefs(prefs: Map<String, *>, prefLocation: Location): Boolean {
val pref = getPrefLocation(prefLocation)

View file

@ -64,14 +64,17 @@ class PreferencePackager {
}
/**
* @return true if successful, false if error
*/
private fun unpackagePreferences(map: Map<String, Map<String, *>>): Boolean {
var failed = false
var success = true
map.forEach { (location, prefMap) ->
val locationEnum = locationFromString(location)
if (!PrefManager.importAllPrefs(prefMap, locationEnum))
failed = true
success = false
}
return failed
return success
}
private fun locationFromString(location: String): Location {

View file

@ -12,6 +12,17 @@
android:visibility="gone"
tools:ignore="ContentDescription" />
<Button
android:id="@+id/importSettingsButton"
android:layout_width="111dp"
android:layout_height="64dp"
android:layout_gravity="top|end"
android:backgroundTint="?attr/colorPrimaryContainer"
android:fontFamily="@font/poppins_bold"
android:text="@string/import_settings"
android:textColor="?attr/colorOnPrimaryContainer"
app:cornerRadius="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"

View file

@ -654,5 +654,6 @@
<string name="share_username_in_crash_reports">Share username in crash reports</string>
<string name="pinned_sources">Pinned Sources</string>
<string name="import_export_settings">Import/Export Settings</string>
<string name="import_settings">Import Settings</string>
</resources>