implement eUICC profile downloading
hope that this actually works...
This commit is contained in:
parent
ca637da5ee
commit
df46ed883b
|
@ -17,7 +17,8 @@
|
|||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.OpenEUICC">
|
||||
android:theme="@style/Theme.OpenEUICC"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="true">
|
||||
|
|
|
@ -20,4 +20,8 @@ val <T> T.slotId: Int where T: Fragment, T: EuiccFragmentMarker
|
|||
|
||||
val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccFragmentMarker
|
||||
get() =
|
||||
(requireActivity().application as OpenEUICCApplication).euiccChannelRepo.availableChannels[slotId]
|
||||
(requireActivity().application as OpenEUICCApplication).euiccChannelRepo.availableChannels[slotId]
|
||||
|
||||
interface EuiccProfilesChangedListener {
|
||||
fun onEuiccProfilesChanged()
|
||||
}
|
|
@ -17,7 +17,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class EuiccManagementFragment : Fragment(), EuiccFragmentMarker {
|
||||
class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesChangedListener {
|
||||
companion object {
|
||||
fun newInstance(slotId: Int): EuiccManagementFragment =
|
||||
newInstanceEuicc(EuiccManagementFragment::class.java, slotId)
|
||||
|
@ -55,6 +55,10 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker {
|
|||
refresh()
|
||||
}
|
||||
|
||||
override fun onEuiccProfilesChanged() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun refresh() {
|
||||
binding.swipeRefresh.isRefreshing = true
|
||||
|
|
|
@ -2,14 +2,22 @@ package im.angry.openeuicc.ui
|
|||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
import com.truphone.lpa.progress.DownloadProgress
|
||||
import im.angry.openeuicc.R
|
||||
import im.angry.openeuicc.databinding.FragmentProfileDownloadBinding
|
||||
import im.angry.openeuicc.util.setWidthPercent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.lang.Exception
|
||||
|
||||
class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.OnMenuItemClickListener {
|
||||
companion object {
|
||||
|
@ -22,6 +30,8 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O
|
|||
private var _binding: FragmentProfileDownloadBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var downloading = false
|
||||
|
||||
private val barcodeScannerLauncher = registerForActivityResult(ScanContract()) { result ->
|
||||
result.contents?.let { content ->
|
||||
val components = content.split("$")
|
||||
|
@ -46,13 +56,13 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O
|
|||
binding.toolbar.apply {
|
||||
setTitle(R.string.profile_download)
|
||||
setNavigationOnClickListener {
|
||||
dismiss()
|
||||
if (!downloading) dismiss()
|
||||
}
|
||||
setOnMenuItemClickListener(this@ProfileDownloadFragment)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean =
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean = downloading ||
|
||||
when (item.itemId) {
|
||||
R.id.scan -> {
|
||||
barcodeScannerLauncher.launch(ScanOptions().apply {
|
||||
|
@ -61,6 +71,10 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O
|
|||
})
|
||||
true
|
||||
}
|
||||
R.id.ok -> {
|
||||
startDownloadProfile()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
@ -75,4 +89,56 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O
|
|||
it.setCanceledOnTouchOutside(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startDownloadProfile() {
|
||||
val server = binding.profileDownloadServer.editText!!.let {
|
||||
it.text.toString().trim().apply {
|
||||
if (isEmpty()) {
|
||||
it.requestFocus()
|
||||
return@startDownloadProfile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val code = binding.profileDownloadCode.editText!!.let {
|
||||
it.text.toString().trim().apply {
|
||||
if (isEmpty()) {
|
||||
it.requestFocus()
|
||||
return@startDownloadProfile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloading = true
|
||||
|
||||
binding.profileDownloadServer.editText!!.isEnabled = false
|
||||
binding.profileDownloadCode.editText!!.isEnabled = false
|
||||
|
||||
binding.progress.isIndeterminate = true
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
doDownloadProfile(server, code)
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, "Error downloading profile")
|
||||
Log.d(TAG, Log.getStackTraceString(e))
|
||||
Toast.makeText(context, R.string.profile_download_failed, Toast.LENGTH_LONG).show()
|
||||
} finally {
|
||||
if (parentFragment is EuiccProfilesChangedListener) {
|
||||
(parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun doDownloadProfile(server: String, code: String) = withContext(Dispatchers.IO) {
|
||||
channel.lpa.downloadProfile("1\$${server}\$${code}", DownloadProgress().apply {
|
||||
setProgressListener { _, _, percentage, _ ->
|
||||
binding.progress.isIndeterminate = false
|
||||
binding.progress.progress = (percentage * 100).toInt()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
10
app/src/main/res/drawable/ic_check_black.xml
Normal file
10
app/src/main/res/drawable/ic_check_black.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
</vector>
|
|
@ -16,6 +16,27 @@
|
|||
app:layout_constraintWidth_percent="1"
|
||||
app:navigationIcon="?homeAsUpIndicator" />
|
||||
|
||||
<View
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@id/toolbar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/profile_download_server"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -6,4 +6,10 @@
|
|||
android:icon="@drawable/ic_scan_black"
|
||||
android:title="@string/profile_download_scan"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/ok"
|
||||
android:icon="@drawable/ic_check_black"
|
||||
android:title="@string/profile_download_ok"
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
15
app/src/main/res/raw/symantec_gsma_rspv2_root_ci1
Normal file
15
app/src/main/res/raw/symantec_gsma_rspv2_root_ci1
Normal file
|
@ -0,0 +1,15 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICSTCCAe+gAwIBAgIQbmhWeneg7nyF7hg5Y9+qejAKBggqhkjOPQQDAjBEMRgw
|
||||
FgYDVQQKEw9HU00gQXNzb2NpYXRpb24xKDAmBgNVBAMTH0dTTSBBc3NvY2lhdGlv
|
||||
biAtIFJTUDIgUm9vdCBDSTEwIBcNMTcwMjIyMDAwMDAwWhgPMjA1MjAyMjEyMzU5
|
||||
NTlaMEQxGDAWBgNVBAoTD0dTTSBBc3NvY2lhdGlvbjEoMCYGA1UEAxMfR1NNIEFz
|
||||
c29jaWF0aW9uIC0gUlNQMiBSb290IENJMTBZMBMGByqGSM49AgEGCCqGSM49AwEH
|
||||
A0IABJ1qutL0HCMX52GJ6/jeibsAqZfULWj/X10p/Min6seZN+hf5llovbCNuB2n
|
||||
unLz+O8UD0SUCBUVo8e6n9X1TuajgcAwgb0wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
|
||||
EwEB/wQFMAMBAf8wEwYDVR0RBAwwCogIKwYBBAGC6WAwFwYDVR0gAQH/BA0wCzAJ
|
||||
BgdngRIBAgEAME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9nc21hLWNybC5zeW1h
|
||||
dXRoLmNvbS9vZmZsaW5lY2EvZ3NtYS1yc3AyLXJvb3QtY2kxLmNybDAdBgNVHQ4E
|
||||
FgQUgTcPUSXQsdQI1MOyMubSXnlb6/swCgYIKoZIzj0EAwIDSAAwRQIgIJdYsOMF
|
||||
WziPK7l8nh5mu0qiRiVf25oa9ullG/OIASwCIQDqCmDrYf+GziHXBOiwJwnBaeBO
|
||||
aFsiLzIEOaUuZwdNUw==
|
||||
-----END CERTIFICATE-----
|
|
@ -12,4 +12,6 @@
|
|||
<string name="profile_download_server">Server (RSP / SM-DP+)</string>
|
||||
<string name="profile_download_code">Activation Code</string>
|
||||
<string name="profile_download_scan">Scan QR Code</string>
|
||||
<string name="profile_download_ok">Download</string>
|
||||
<string name="profile_download_failed">Failed to download eSIM. Check your activation / QR code.</string>
|
||||
</resources>
|
9
app/src/main/res/xml/network_security_config.xml
Normal file
9
app/src/main/res/xml/network_security_config.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
<certificates src="@raw/symantec_gsma_rspv2_root_ci1"/>
|
||||
<certificates src="system"/>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
Loading…
Reference in a new issue