diff --git a/CHANGELOG.md b/CHANGELOG.md index 243dfd88d..b4e40a519 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Sending WalletConnect transaction with 0 energy if its payload is too large +### Removed +- Shielding – you can still enable and see your shielded balance and history, +but to unshield the funds CryptoX Concordium wallet must be used + ## [1.5.1] - 2024-03-18 ### Fixed diff --git a/app/src/main/java/com/concordium/wallet/core/authentication/Session.kt b/app/src/main/java/com/concordium/wallet/core/authentication/Session.kt index 5505da141..9cfd15d94 100644 --- a/app/src/main/java/com/concordium/wallet/core/authentication/Session.kt +++ b/app/src/main/java/com/concordium/wallet/core/authentication/Session.kt @@ -38,6 +38,14 @@ class Session(context: Context) { return filterPreferences.getHasShowFinalizationRewards(id) } + fun shieldingNoticeShown() { + authPreferences.setShieldingNoticeShown(true) + } + + fun isShieldingNoticeShown():Boolean { + return authPreferences.getShieldingNoticeShown() + } + fun hasSetupPassword(passcodeUsed: Boolean = false) { _isLoggedIn.value = true authPreferences.setHasSetupUser(true) diff --git a/app/src/main/java/com/concordium/wallet/data/model/AccountEncryptedAmount.kt b/app/src/main/java/com/concordium/wallet/data/model/AccountEncryptedAmount.kt index 5083f26ad..8d81973f7 100644 --- a/app/src/main/java/com/concordium/wallet/data/model/AccountEncryptedAmount.kt +++ b/app/src/main/java/com/concordium/wallet/data/model/AccountEncryptedAmount.kt @@ -7,4 +7,14 @@ data class AccountEncryptedAmount( val selfAmount: String, var selfAmountDecrypted: Long, val startIndex: Int -) : Serializable +) : Serializable { + fun isDefaultEmpty(): Boolean = + selfAmount == DEFAULT_EMPTY_ENCRYPTED_AMOUNT && + incomingAmounts.all { it == DEFAULT_EMPTY_ENCRYPTED_AMOUNT } + + + companion object { + const val DEFAULT_EMPTY_ENCRYPTED_AMOUNT = + "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } +} diff --git a/app/src/main/java/com/concordium/wallet/data/preferences/AuthPreferences.kt b/app/src/main/java/com/concordium/wallet/data/preferences/AuthPreferences.kt index 6e171d61d..fb0eed6e9 100644 --- a/app/src/main/java/com/concordium/wallet/data/preferences/AuthPreferences.kt +++ b/app/src/main/java/com/concordium/wallet/data/preferences/AuthPreferences.kt @@ -28,6 +28,15 @@ class AuthPreferences(val context: Context) : const val PREFKEY_IDENTITY_PENDING_ACKNOWLEDGED = "PREFKEY_IDENTITY_PENDING_ACKNOWLEDGED_" const val SEED_PHRASE = "SEED_PHRASE" const val SEED_PHRASE_ENCRYPTED = "SEED_PHRASE_ENCRYPTED" + const val PREFKEY_SHIELDING_NOTICE_SHOWN = "SHIELDING_NOTICE_SHOWN" + } + + fun setShieldingNoticeShown(value: Boolean) { + setBoolean(PREFKEY_SHIELDING_NOTICE_SHOWN, value) + } + + fun getShieldingNoticeShown(): Boolean { + return getBoolean(PREFKEY_SHIELDING_NOTICE_SHOWN, false) } fun setHasSetupUser(value: Boolean) { diff --git a/app/src/main/java/com/concordium/wallet/data/room/Account.kt b/app/src/main/java/com/concordium/wallet/data/room/Account.kt index 21fad02b5..3b6994557 100644 --- a/app/src/main/java/com/concordium/wallet/data/room/Account.kt +++ b/app/src/main/java/com/concordium/wallet/data/room/Account.kt @@ -1,10 +1,20 @@ package com.concordium.wallet.data.room -import androidx.room.* +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import androidx.room.TypeConverters import com.concordium.wallet.App -import com.concordium.wallet.data.model.* +import com.concordium.wallet.data.model.AccountBaker +import com.concordium.wallet.data.model.AccountDelegation +import com.concordium.wallet.data.model.AccountEncryptedAmount +import com.concordium.wallet.data.model.AccountReleaseSchedule +import com.concordium.wallet.data.model.CredentialWrapper +import com.concordium.wallet.data.model.IdentityAttribute +import com.concordium.wallet.data.model.ShieldedAccountEncryptionStatus +import com.concordium.wallet.data.model.TransactionStatus import com.concordium.wallet.data.room.typeconverter.AccountTypeConverters -import com.concordium.wallet.util.toBigInteger import com.google.gson.JsonObject import java.io.Serializable import java.math.BigInteger @@ -130,6 +140,18 @@ data class Account( return accountDelegation != null } + fun mayNeedUnshielding(): Boolean { + if (finalizedEncryptedBalance == null) { + return false + } + + val isShieldedBalanceUnknown = encryptedBalanceStatus == ShieldedAccountEncryptionStatus.ENCRYPTED + || encryptedBalanceStatus == ShieldedAccountEncryptionStatus.PARTIALLYDECRYPTED + val isShieldedBalancePositive = encryptedBalanceStatus == ShieldedAccountEncryptionStatus.DECRYPTED + && totalShieldedBalance.signum() > 0 + return isShieldedBalanceUnknown || isShieldedBalancePositive + } + fun getAtDisposalWithoutStakedOrScheduled(totalBalance: BigInteger): BigInteger { val stakedAmount: BigInteger = accountDelegation?.stakedAmount ?: accountBaker?.stakedAmount ?: BigInteger.ZERO diff --git a/app/src/main/java/com/concordium/wallet/ui/account/accountdetails/AccountDetailsActivity.kt b/app/src/main/java/com/concordium/wallet/ui/account/accountdetails/AccountDetailsActivity.kt index 5ec6fda3f..827ceec0d 100644 --- a/app/src/main/java/com/concordium/wallet/ui/account/accountdetails/AccountDetailsActivity.kt +++ b/app/src/main/java/com/concordium/wallet/ui/account/accountdetails/AccountDetailsActivity.kt @@ -518,16 +518,7 @@ class AccountDetailsActivity : BaseActivity(), EarnDelegate by EarnDelegateImpl( private fun updateButtonsSlider() { if (viewModelAccountDetails.isShielded) { binding.buttonsSlider.visibility = View.GONE - binding.buttonsShielded.visibility = View.VISIBLE - binding.sendShielded.setOnClickListener { - onSendShieldedClicked() - } - binding.unshield.setOnClickListener { - onShieldFundsClicked() - } - binding.receive.setOnClickListener { - onAddressClicked() - } + // Completely hide and disable shielding actions. return } binding.buttonsSlider.visibility = View.VISIBLE diff --git a/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/AccountsOverviewFragment.kt b/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/AccountsOverviewFragment.kt index a6a7ceef0..e9b54a9d1 100644 --- a/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/AccountsOverviewFragment.kt +++ b/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/AccountsOverviewFragment.kt @@ -8,30 +8,30 @@ import android.os.Process import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import com.concordium.wallet.App import com.concordium.wallet.R import com.concordium.wallet.core.arch.EventObserver import com.concordium.wallet.data.model.AppSettings import com.concordium.wallet.data.model.BakerStakePendingChange -import com.concordium.wallet.data.model.TransactionStatus import com.concordium.wallet.data.preferences.Preferences import com.concordium.wallet.data.room.Account import com.concordium.wallet.data.room.AccountWithIdentity import com.concordium.wallet.data.util.CurrencyUtil -import com.concordium.wallet.util.TokenUtil import com.concordium.wallet.databinding.FragmentAccountsOverviewBinding import com.concordium.wallet.ui.MainViewModel import com.concordium.wallet.ui.account.accountdetails.AccountDetailsActivity import com.concordium.wallet.ui.account.accountqrcode.AccountQRCodeActivity import com.concordium.wallet.ui.base.BaseFragment +import com.concordium.wallet.ui.cis2.SendTokenActivity import com.concordium.wallet.ui.common.delegates.EarnDelegate import com.concordium.wallet.ui.common.delegates.EarnDelegateImpl import com.concordium.wallet.ui.common.delegates.IdentityStatusDelegate import com.concordium.wallet.ui.common.delegates.IdentityStatusDelegateImpl import com.concordium.wallet.ui.identity.identitiesoverview.IdentitiesOverviewActivity import com.concordium.wallet.ui.identity.identityproviderlist.IdentityProviderListActivity -import com.concordium.wallet.ui.cis2.SendTokenActivity +import com.concordium.wallet.util.TokenUtil import java.math.BigInteger class AccountsOverviewFragment : BaseFragment(), @@ -160,7 +160,6 @@ class AccountsOverviewFragment : BaseFragment(), viewModel.accountListLiveData.observe(this) { accountList -> accountList?.let { accountAdapter.setData(it) - checkForUnencrypted(it) checkForClosingPools(it) } } @@ -187,6 +186,16 @@ class AccountsOverviewFragment : BaseFragment(), ) } } + viewModel.showShieldingNoticeLiveData.observe(this) { + childFragmentManager.fragments.forEach { fragment -> + if (fragment.tag == ShieldingNoticeDialogFragment.TAG && fragment is DialogFragment) { + fragment.dismissAllowingStateLoss() + } + } + + ShieldingNoticeDialogFragment() + .show(childFragmentManager, ShieldingNoticeDialogFragment.TAG) + } } private fun checkAppSettings(appSettings: AppSettings?) { @@ -293,60 +302,6 @@ class AccountsOverviewFragment : BaseFragment(), viewModel.loadPoolStatuses(poolIds) } - private fun checkForUnencrypted(accountList: List) { - accountList.forEach { - - val hasUnencryptedTransactions = - it.account.finalizedEncryptedBalance?.incomingAmounts?.isNotEmpty() - if ((hasUnencryptedTransactions != null && hasUnencryptedTransactions == true) - && it.account.transactionStatus == TransactionStatus.FINALIZED - && !App.appCore.session.isShieldedWarningDismissed(it.account.address) - && !App.appCore.session.isShieldingEnabled(it.account.address) - && encryptedWarningDialog == null - ) { - - val builder = AlertDialog.Builder(context) - builder.setTitle(getString(R.string.account_details_shielded_warning_title)) - builder.setMessage( - getString( - R.string.account_details_shielded_warning_text, - it.account.name - ) - ) - builder.setNegativeButton( - getString( - R.string.account_details_shielded_warning_enable, - it.account.name - ) - ) { _, _ -> - startShieldedIntroFlow(it.account) - encryptedWarningDialog?.dismiss() - encryptedWarningDialog = null - } - builder.setPositiveButton(getString(R.string.account_details_shielded_warning_dismiss)) { _, _ -> - App.appCore.session.setShieldedWarningDismissed( - it.account.address, - true - ) - encryptedWarningDialog?.dismiss() - encryptedWarningDialog = null - checkForUnencrypted(accountList) //Check for other accounts with shielded transactions - } - builder.setCancelable(true) - encryptedWarningDialog = builder.create()//.show() - encryptedWarningDialog?.show() - } - } - } - - private fun startShieldedIntroFlow(account: Account) { - val intent = Intent(activity, AccountDetailsActivity::class.java) - intent.putExtra(AccountDetailsActivity.EXTRA_ACCOUNT, account) - intent.putExtra(AccountDetailsActivity.EXTRA_SHIELDED, false) - intent.putExtra(AccountDetailsActivity.EXTRA_CONTINUE_TO_SHIELD_INTRO, true) - startActivityForResult(intent, REQUESTCODE_ACCOUNT_DETAILS) - } - private fun initializeViews() { mainViewModel.setTitle(getString(R.string.accounts_overview_title)) binding.includeProgress.progressLayout.visibility = View.VISIBLE diff --git a/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/AccountsOverviewViewModel.kt b/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/AccountsOverviewViewModel.kt index 8a89a1474..015ccff7b 100644 --- a/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/AccountsOverviewViewModel.kt +++ b/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/AccountsOverviewViewModel.kt @@ -63,6 +63,10 @@ class AccountsOverviewViewModel(application: Application) : AndroidViewModel(app val appSettingsLiveData: LiveData get() = _appSettingsLiveData + private val _showShieldingNoticeLiveData = MutableLiveData>() + val showShieldingNoticeLiveData: LiveData> + get() = _showShieldingNoticeLiveData + val localTransfersLoaded: MutableLiveData by lazy { MutableLiveData() } private val identityRepository: IdentityRepository @@ -169,8 +173,9 @@ class AccountsOverviewViewModel(application: Application) : AndroidViewModel(app if (identityCount == 0) { _stateLiveData.value = State.NO_IDENTITIES // Set balance, because we know it will be 0 - _totalBalanceLiveData.value = TotalBalancesData(BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, false) - if(notifyWaitingLiveData){ + _totalBalanceLiveData.value = + TotalBalancesData(BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, false) + if (notifyWaitingLiveData) { _waitingLiveData.value = false } } else { @@ -178,8 +183,9 @@ class AccountsOverviewViewModel(application: Application) : AndroidViewModel(app if (accountCount == 0) { _stateLiveData.value = State.NO_ACCOUNTS // Set balance, because we know it will be 0 - _totalBalanceLiveData.value = TotalBalancesData(BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, false) - if(notifyWaitingLiveData){ + _totalBalanceLiveData.value = + TotalBalancesData(BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, false) + if (notifyWaitingLiveData) { _waitingLiveData.value = false } } else { @@ -188,6 +194,7 @@ class AccountsOverviewViewModel(application: Application) : AndroidViewModel(app _waitingLiveData.value = false } updateSubmissionStatesAndBalances(notifyWaitingLiveData) + showUnshieldingNoticeIfNeeded() } } } @@ -254,5 +261,17 @@ class AccountsOverviewViewModel(application: Application) : AndroidViewModel(app return false } + private fun showUnshieldingNoticeIfNeeded() = viewModelScope.launch { + // Show the notice once. + if (App.appCore.session.isShieldingNoticeShown()) { + return@launch + } + + val anyAccountsMayNeedUnshielding = accountRepository.getAllDone() + .any(Account::mayNeedUnshielding) -} \ No newline at end of file + if (anyAccountsMayNeedUnshielding) { + _showShieldingNoticeLiveData.postValue(Event(true)) + } + } +} diff --git a/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/ShieldingNoticeDialogFragment.kt b/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/ShieldingNoticeDialogFragment.kt new file mode 100644 index 000000000..9ed583b18 --- /dev/null +++ b/app/src/main/java/com/concordium/wallet/ui/account/accountsoverview/ShieldingNoticeDialogFragment.kt @@ -0,0 +1,116 @@ +package com.concordium.wallet.ui.account.accountsoverview + +import android.content.ActivityNotFoundException +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDialogFragment +import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getSystemService +import androidx.core.view.isVisible +import androidx.lifecycle.lifecycleScope +import com.concordium.wallet.App +import com.concordium.wallet.BuildConfig +import com.concordium.wallet.R +import com.concordium.wallet.data.repository.AuthenticationRepository +import com.concordium.wallet.databinding.DialogShieldingNoticeBinding +import com.concordium.wallet.ui.common.delegates.AuthDelegate +import com.concordium.wallet.ui.common.delegates.AuthDelegateImpl +import kotlinx.coroutines.delay +import org.koin.android.ext.android.inject + +class ShieldingNoticeDialogFragment : + AppCompatDialogFragment(), + AuthDelegate by AuthDelegateImpl() { + + private lateinit var binding: DialogShieldingNoticeBinding + + private val authenticationRepository: AuthenticationRepository by inject() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = DialogShieldingNoticeBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + authenticationRepository.getSeedPhase().onSuccess { phrase -> + binding.copyPhraseButton.isVisible = true + binding.copyPhraseButton.setOnClickListener { + showAuthentication( + activity = requireActivity() as AppCompatActivity, + authenticated = { + val clipboard: ClipboardManager? = + getSystemService(requireContext(), ClipboardManager::class.java) + val clip = ClipData.newPlainText("Phrase", phrase) + clipboard?.setPrimaryClip(clip) + } + ) + } + } + + binding.continueWithOldWalletButton.setOnClickListener { + dismiss() + } + + binding.installCryptoxButton.setOnClickListener { + val cryptoXPackage = + if (BuildConfig.ENV_NAME == "prod_testnet") + "com.pioneeringtechventures.wallet.testnet" + else + "com.pioneeringtechventures.wallet" + + val intent = Intent(Intent.ACTION_VIEW).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + setData(Uri.parse("market://details?id=$cryptoXPackage")) + } + try { + startActivity(intent) + } catch (_: ActivityNotFoundException) { + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse("https://play.google.com/store/apps/details?id=$cryptoXPackage") + ) + ) + } + } + + // Track showing the notice once it is visible to the user. + viewLifecycleOwner.lifecycleScope.launchWhenStarted { + delay(500) + App.appCore.session.shieldingNoticeShown() + } + } + + override fun onStart() { + super.onStart() + dialog?.window?.apply { + setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + setBackgroundDrawable( + ContextCompat.getDrawable( + requireContext(), + R.drawable.bg_shielding_notice + ) + ) + } + } + + companion object { + const val TAG = "shielding-notice" + } +} diff --git a/app/src/main/java/com/concordium/wallet/ui/account/common/accountupdater/AccountUpdater.kt b/app/src/main/java/com/concordium/wallet/ui/account/common/accountupdater/AccountUpdater.kt index 5f90dd7b5..0d1645296 100644 --- a/app/src/main/java/com/concordium/wallet/ui/account/common/accountupdater/AccountUpdater.kt +++ b/app/src/main/java/com/concordium/wallet/ui/account/common/accountupdater/AccountUpdater.kt @@ -40,12 +40,6 @@ import retrofit2.HttpException import java.math.BigInteger class AccountUpdater(val application: Application, private val viewModelScope: CoroutineScope) { - - companion object { - const val DEFAULT_EMPTY_ENCRYPTED_AMOUNT = - "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - data class AccountSubmissionStatusRequestData( val deferred: Deferred, val account: Account @@ -571,14 +565,11 @@ class AccountUpdater(val application: Application, private val viewModelScope: C return output } - suspend fun lookupMappedAmount(key: String): String? { - if (DEFAULT_EMPTY_ENCRYPTED_AMOUNT.equals(key)) { - return 0.toString() - } - val result = encryptedAmountRepository.findByAddress(key)?.amount - - return result - } + suspend fun lookupMappedAmount(key: String): String? = + if (key == AccountEncryptedAmount.DEFAULT_EMPTY_ENCRYPTED_AMOUNT) + "0" + else + encryptedAmountRepository.findByAddress(key)?.amount suspend fun saveDecryptedAmount(key: String, amount: String?) { encryptedAmountRepository.insert(EncryptedAmount(key, amount)) diff --git a/app/src/main/java/com/concordium/wallet/ui/passphrase/recover/PassPhraseRecoverViewModel.kt b/app/src/main/java/com/concordium/wallet/ui/passphrase/recover/PassPhraseRecoverViewModel.kt index 77b62b54b..e1ad4cec6 100644 --- a/app/src/main/java/com/concordium/wallet/ui/passphrase/recover/PassPhraseRecoverViewModel.kt +++ b/app/src/main/java/com/concordium/wallet/ui/passphrase/recover/PassPhraseRecoverViewModel.kt @@ -49,14 +49,11 @@ class PassPhraseRecoverViewModel( fun setPredefinedPhraseForTesting(password: String) = viewModelScope.launch { if (BuildConfig.DEBUG) { - //AuthPreferences(getApplication()).setSeedPhrase("health smoke abandon middle outer method meadow sorry whale random cupboard thank album exclude idle month exit quarter shell portion eternal legal rent tonight") // testnet CBW-320 - val saveSuccess = AuthPreferences(getApplication()).tryToSetEncryptedSeedPhrase( + setSeedPhrase( "nothing ill myself guitar antique demise awake twelve fall victory grow segment bus puppy iron vicious skate piece tobacco stable police plunge coin fee", password - )// testnet - _saveSeedLiveData.value = saveSuccess - _validateLiveData.value = saveSuccess - + ) + _validateLiveData.value = true } } diff --git a/app/src/main/java/com/concordium/wallet/ui/passphrase/recoverprocess/RecoverProcessViewModel.kt b/app/src/main/java/com/concordium/wallet/ui/passphrase/recoverprocess/RecoverProcessViewModel.kt index 696e3f7a3..fe82f8d1a 100644 --- a/app/src/main/java/com/concordium/wallet/ui/passphrase/recoverprocess/RecoverProcessViewModel.kt +++ b/app/src/main/java/com/concordium/wallet/ui/passphrase/recoverprocess/RecoverProcessViewModel.kt @@ -302,7 +302,10 @@ class RecoverProcessViewModel(application: Application) : AndroidViewModel(appli totalShieldedBalance = BigInteger.ZERO, finalizedEncryptedBalance = accountBalance.finalizedBalance.accountEncryptedAmount, currentEncryptedBalance = accountBalance.currentBalance?.accountEncryptedAmount, - encryptedBalanceStatus = ShieldedAccountEncryptionStatus.ENCRYPTED, + if (accountBalance.finalizedBalance.accountEncryptedAmount.isDefaultEmpty()) + ShieldedAccountEncryptionStatus.DECRYPTED + else + ShieldedAccountEncryptionStatus.ENCRYPTED, totalStaked = accountBalance.finalizedBalance.accountBaker?.stakedAmount ?: BigInteger.ZERO, totalAtDisposal = BigInteger.ZERO, readOnly = false, diff --git a/app/src/main/res/drawable/bg_shielding_notice.xml b/app/src/main/res/drawable/bg_shielding_notice.xml new file mode 100644 index 000000000..766ad9c42 --- /dev/null +++ b/app/src/main/res/drawable/bg_shielding_notice.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ico_unshield.xml b/app/src/main/res/drawable/ico_unshield.xml new file mode 100644 index 000000000..16d03e062 --- /dev/null +++ b/app/src/main/res/drawable/ico_unshield.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/layout/dialog_shielding_notice.xml b/app/src/main/res/layout/dialog_shielding_notice.xml new file mode 100644 index 000000000..70481218d --- /dev/null +++ b/app/src/main/res/layout/dialog_shielding_notice.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + +