mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-04 17:28:42 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b01de1ad6e | ||
![]() |
5fb1bcb552 | ||
![]() |
b937ba38b6 | ||
![]() |
849718fdc7 | ||
![]() |
a6372f701d | ||
![]() |
12f135bb14 | ||
![]() |
16870fcbb9 | ||
![]() |
2fbd152fb3 | ||
![]() |
a7b178e024 | ||
![]() |
c5a035437b | ||
![]() |
75c550fd19 | ||
![]() |
1b680344b1 | ||
![]() |
3329636d32 | ||
![]() |
77e9bf7c43 | ||
![]() |
40f5c9365e | ||
![]() |
850ba7efef | ||
![]() |
548369ca0b | ||
![]() |
afc0d5b7bc |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@
|
||||
/app/alphaStagenet
|
||||
/app/prodStagenet
|
||||
/app/.cxx
|
||||
/monerujo.id
|
||||
|
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 708
|
||||
versionName "1.17.8 'Druk'"
|
||||
versionCode 713
|
||||
versionName "1.17.13 'Druk'"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
|
@@ -59,7 +59,7 @@ import com.m2049r.xmrwallet.model.NetworkType;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.service.WalletService;
|
||||
import com.m2049r.xmrwallet.util.ColorHelper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.util.DayNightMode;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||
@@ -78,7 +78,6 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -376,7 +375,7 @@ public class LoginActivity extends BaseActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(String walletName1, String password, boolean fingerprintUsed) {
|
||||
public void fail(String walletName) {
|
||||
}
|
||||
});
|
||||
} else { // this cannot really happen as we prefilter choices
|
||||
@@ -412,7 +411,7 @@ public class LoginActivity extends BaseActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(String walletName, String password, boolean fingerprintUsed) {
|
||||
public void fail(String walletName) {
|
||||
}
|
||||
});
|
||||
} else { // this cannot really happen as we prefilter choices
|
||||
@@ -707,11 +706,11 @@ public class LoginActivity extends BaseActivity
|
||||
break;
|
||||
case NetworkType_Testnet:
|
||||
toolbar.setSubtitle(getString(R.string.connect_testnet));
|
||||
toolbar.setBackgroundResource(ColorHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
|
||||
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
|
||||
break;
|
||||
case NetworkType_Stagenet:
|
||||
toolbar.setSubtitle(getString(R.string.connect_stagenet));
|
||||
toolbar.setBackgroundResource(ColorHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
|
||||
toolbar.setBackgroundResource(ThemeHelper.getThemedResourceId(this, R.attr.colorPrimaryDark));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("NetworkType unknown: " + net);
|
||||
@@ -1400,7 +1399,7 @@ public class LoginActivity extends BaseActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(String walletName, String password, boolean fingerprintUsed) {
|
||||
public void fail(String walletName) {
|
||||
}
|
||||
|
||||
});
|
||||
|
@@ -258,7 +258,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
||||
walletList.addAll(walletInfos);
|
||||
filterList();
|
||||
adapter.setInfos(displayedList);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
// deal with Gunther & FAB animation
|
||||
if (displayedList.isEmpty()) {
|
||||
|
@@ -524,7 +524,7 @@ public class NodeFragment extends Fragment
|
||||
.setNegativeButton(getString(R.string.label_cancel),
|
||||
(dialog, id) -> {
|
||||
closeDialog();
|
||||
nodesAdapter.dataSetChanged(); // to refresh test results
|
||||
nodesAdapter.setNodes(); // to refresh test results
|
||||
});
|
||||
|
||||
editDialog = alertDialogBuilder.create();
|
||||
@@ -588,7 +588,7 @@ public class NodeFragment extends Fragment
|
||||
if (nodeBackup == null) {
|
||||
nodesAdapter.addNode(nodeInfo);
|
||||
} else {
|
||||
nodesAdapter.dataSetChanged();
|
||||
nodesAdapter.setNodes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -36,10 +36,12 @@ import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.material.transition.MaterialContainerTransform;
|
||||
import com.m2049r.xmrwallet.data.UserNotes;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.Transfer;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
@@ -300,7 +302,7 @@ public class TxFragment extends Fragment {
|
||||
}
|
||||
tvTxXmrToKey.setText(key);
|
||||
tvDestinationBtc.setText(userNotes.xmrtoDestination);
|
||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " "+ userNotes.xmrtoCurrency);
|
||||
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " " + userNotes.xmrtoCurrency);
|
||||
switch (userNotes.xmrtoTag) {
|
||||
case "xmrto":
|
||||
tvXmrToSupport.setVisibility(View.GONE);
|
||||
@@ -329,6 +331,11 @@ public class TxFragment extends Fragment {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
final MaterialContainerTransform transform = new MaterialContainerTransform();
|
||||
transform.setDrawingViewId(R.id.fragment_container);
|
||||
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
||||
transform.setAllContainerColors(ThemeHelper.getThemedColor(getContext(), R.attr.colorSurface));
|
||||
setSharedElementEnterTransition(transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -36,12 +36,14 @@ import android.widget.ProgressBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.github.brnunes.swipeablerecyclerview.SwipeableRecyclerViewTouchListener;
|
||||
import com.google.android.material.transition.MaterialElevationScale;
|
||||
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
|
||||
import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
@@ -56,13 +58,14 @@ import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class WalletFragment extends Fragment
|
||||
implements TransactionInfoAdapter.OnInteractionListener {
|
||||
private TransactionInfoAdapter adapter;
|
||||
private NumberFormat formatter = NumberFormat.getInstance();
|
||||
private final NumberFormat formatter = NumberFormat.getInstance();
|
||||
|
||||
private TextView tvStreetView;
|
||||
private LinearLayout llBalance;
|
||||
@@ -79,7 +82,7 @@ public class WalletFragment extends Fragment
|
||||
|
||||
private Spinner sCurrency;
|
||||
|
||||
private List<String> dismissedTransactions = new ArrayList<>();
|
||||
private final List<String> dismissedTransactions = new ArrayList<>();
|
||||
|
||||
public void resetDismissedTransactions() {
|
||||
dismissedTransactions.clear();
|
||||
@@ -92,7 +95,7 @@ public class WalletFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
if (activityCallback.hasWallet())
|
||||
inflater.inflate(R.menu.wallet_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
@@ -103,6 +106,13 @@ public class WalletFragment extends Fragment
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_wallet, container, false);
|
||||
|
||||
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
|
||||
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
||||
setExitTransition(exitTransition);
|
||||
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
|
||||
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
||||
setReenterTransition(reenterTransition);
|
||||
|
||||
ivStreetGunther = view.findViewById(R.id.ivStreetGunther);
|
||||
tvStreetView = view.findViewById(R.id.tvStreetView);
|
||||
llBalance = view.findViewById(R.id.llBalance);
|
||||
@@ -123,7 +133,7 @@ public class WalletFragment extends Fragment
|
||||
List<String> currencies = new ArrayList<>();
|
||||
currencies.add(Helper.BASE_CRYPTO);
|
||||
currencies.addAll(Arrays.asList(getResources().getStringArray(R.array.currency)));
|
||||
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner_balance, currencies);
|
||||
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(Objects.requireNonNull(getContext()), R.layout.item_spinner_balance, currencies);
|
||||
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
sCurrency.setAdapter(spinnerAdapter);
|
||||
|
||||
@@ -154,7 +164,6 @@ public class WalletFragment extends Fragment
|
||||
dismissedTransactions.add(adapter.getItem(position).hash);
|
||||
adapter.removeItem(position);
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,25 +172,14 @@ public class WalletFragment extends Fragment
|
||||
dismissedTransactions.add(adapter.getItem(position).hash);
|
||||
adapter.removeItem(position);
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
|
||||
recyclerView.addOnItemTouchListener(swipeTouchListener);
|
||||
|
||||
|
||||
bSend.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
activityCallback.onSendRequest();
|
||||
}
|
||||
});
|
||||
bReceive.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
activityCallback.onWalletReceive();
|
||||
}
|
||||
});
|
||||
bSend.setOnClickListener(v -> activityCallback.onSendRequest(v));
|
||||
bReceive.setOnClickListener(v -> activityCallback.onWalletReceive(v));
|
||||
|
||||
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
@@ -204,6 +202,16 @@ public class WalletFragment extends Fragment
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
postponeEnterTransition();
|
||||
view.getViewTreeObserver().addOnPreDrawListener(() -> {
|
||||
startPostponedEnterTransition();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void showBalance(String balance) {
|
||||
tvBalance.setText(balance);
|
||||
final boolean streetMode = activityCallback.isStreetMode();
|
||||
@@ -261,24 +269,14 @@ public class WalletFragment extends Fragment
|
||||
@Override
|
||||
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||
if (isAdded())
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
exchange(exchangeRate);
|
||||
}
|
||||
});
|
||||
new Handler(Looper.getMainLooper()).post(() -> exchange(exchangeRate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
Timber.e(e.getLocalizedMessage());
|
||||
if (isAdded())
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
exchangeFailed();
|
||||
}
|
||||
});
|
||||
new Handler(Looper.getMainLooper()).post(() -> exchangeFailed());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -334,7 +332,13 @@ public class WalletFragment extends Fragment
|
||||
// Callbacks from TransactionInfoAdapter
|
||||
@Override
|
||||
public void onInteraction(final View view, final TransactionInfo infoItem) {
|
||||
activityCallback.onTxDetailsRequest(infoItem);
|
||||
final MaterialElevationScale exitTransition = new MaterialElevationScale(false);
|
||||
exitTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
||||
setExitTransition(exitTransition);
|
||||
final MaterialElevationScale reenterTransition = new MaterialElevationScale(true);
|
||||
reenterTransition.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
||||
setReenterTransition(reenterTransition);
|
||||
activityCallback.onTxDetailsRequest(view, infoItem);
|
||||
}
|
||||
|
||||
// called from activity
|
||||
@@ -352,7 +356,6 @@ public class WalletFragment extends Fragment
|
||||
list.add(info);
|
||||
}
|
||||
adapter.setInfos(list);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
updateStatus(wallet);
|
||||
}
|
||||
@@ -414,7 +417,6 @@ public class WalletFragment extends Fragment
|
||||
void setActivityTitle(Wallet wallet) {
|
||||
if (wallet == null) return;
|
||||
walletTitle = wallet.getName();
|
||||
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
|
||||
walletSubtitle = wallet.getAccountLabel();
|
||||
activityCallback.setTitle(walletTitle, walletSubtitle);
|
||||
Timber.d("wallet title is %s", walletTitle);
|
||||
@@ -438,7 +440,7 @@ public class WalletFragment extends Fragment
|
||||
balance = wallet.getBalance();
|
||||
unlockedBalance = wallet.getUnlockedBalance();
|
||||
refreshBalance();
|
||||
String sync = "";
|
||||
String sync;
|
||||
if (!activityCallback.hasBoundService())
|
||||
throw new IllegalStateException("WalletService not bound.");
|
||||
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
|
||||
@@ -479,9 +481,9 @@ public class WalletFragment extends Fragment
|
||||
|
||||
long getDaemonHeight(); //mBoundService.getDaemonHeight();
|
||||
|
||||
void onSendRequest();
|
||||
void onSendRequest(View view);
|
||||
|
||||
void onTxDetailsRequest(TransactionInfo info);
|
||||
void onTxDetailsRequest(View view, TransactionInfo info);
|
||||
|
||||
boolean isSynced();
|
||||
|
||||
@@ -493,7 +495,7 @@ public class WalletFragment extends Fragment
|
||||
|
||||
String getTxKey(String txId);
|
||||
|
||||
void onWalletReceive();
|
||||
void onWalletReceive(View view);
|
||||
|
||||
boolean hasWallet();
|
||||
|
||||
@@ -507,7 +509,7 @@ public class WalletFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof Listener) {
|
||||
this.activityCallback = (Listener) context;
|
||||
@@ -550,7 +552,7 @@ public class WalletFragment extends Fragment
|
||||
//TODO figure out why gunther disappears on return from send although he is still set
|
||||
if (enable) {
|
||||
if (streetGunther == null)
|
||||
streetGunther = ContextCompat.getDrawable(getContext(), R.drawable.ic_gunther_streetmode);
|
||||
streetGunther = ContextCompat.getDrawable(Objects.requireNonNull(getContext()), R.drawable.ic_gunther_streetmode);
|
||||
ivStreetGunther.setImageDrawable(streetGunther);
|
||||
} else
|
||||
ivStreetGunther.setImageDrawable(null);
|
||||
|
@@ -64,12 +64,14 @@ public class Node {
|
||||
return hostAddress.hashCode();
|
||||
}
|
||||
|
||||
// Nodes are equal if they are the same host address & are on the same network
|
||||
// Nodes are equal if they are the same host address:port & are on the same network
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Node)) return false;
|
||||
final Node anotherNode = (Node) other;
|
||||
return (hostAddress.equals(anotherNode.hostAddress) && (networkType == anotherNode.networkType));
|
||||
return (hostAddress.equals(anotherNode.hostAddress)
|
||||
&& (rpcPort == anotherNode.rpcPort)
|
||||
&& (networkType == anotherNode.networkType));
|
||||
}
|
||||
|
||||
static public Node fromString(String nodeString) {
|
||||
|
@@ -227,8 +227,7 @@ public class NodeInfo extends Node {
|
||||
if (response.isSuccessful()) {
|
||||
ResponseBody respBody = response.body(); // closed through Response object
|
||||
if ((respBody != null) && (respBody.contentLength() < 2000)) { // sanity check
|
||||
final JSONObject json = new JSONObject(
|
||||
respBody.string());
|
||||
final JSONObject json = new JSONObject(respBody.string());
|
||||
String rpcVersion = json.getString("jsonrpc");
|
||||
if (!RPC_VERSION.equals(rpcVersion))
|
||||
return false;
|
||||
|
@@ -332,7 +332,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
||||
send();
|
||||
}
|
||||
|
||||
public void fail(String walletName, String password, boolean fingerprintUsed) {
|
||||
public void fail(String walletName) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
bSend.setEnabled(sendCountdown > 0); // allow to try again
|
||||
});
|
||||
|
@@ -227,7 +227,7 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
|
||||
send();
|
||||
}
|
||||
|
||||
public void fail(String walletName, String password, boolean fingerprintUsed) {
|
||||
public void fail(String walletName) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
bSend.setEnabled(true); // allow to try again
|
||||
});
|
||||
|
@@ -36,6 +36,7 @@ import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.google.android.material.transition.MaterialContainerTransform;
|
||||
import com.m2049r.xmrwallet.OnBackPressedListener;
|
||||
import com.m2049r.xmrwallet.OnUriScannedListener;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
@@ -49,10 +50,12 @@ import com.m2049r.xmrwallet.layout.SpendViewPager;
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.Notice;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.widget.DotBar;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -309,7 +312,7 @@ public class SendFragment extends Fragment
|
||||
SparseArray<WeakReference<SendWizardFragment>> myFragments = new SparseArray<>();
|
||||
|
||||
public SpendPagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
}
|
||||
|
||||
public void addSuccess() {
|
||||
@@ -538,14 +541,18 @@ public class SendFragment extends Fragment
|
||||
enableNavigation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
final MaterialContainerTransform transform = new MaterialContainerTransform();
|
||||
transform.setDrawingViewId(R.id.fragment_container);
|
||||
transform.setDuration(getResources().getInteger(R.integer.tx_item_transition_duration));
|
||||
transform.setAllContainerColors(ThemeHelper.getThemedColor(Objects.requireNonNull(getContext()), R.attr.colorSurface));
|
||||
setSharedElementEnterTransition(transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.send_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2021 yorha-0x
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.layout;
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class DiffCallback<T> extends DiffUtil.Callback {
|
||||
|
||||
protected final List<T> mOldList;
|
||||
protected final List<T> mNewList;
|
||||
|
||||
public DiffCallback(List<T> oldList, List<T> newList) {
|
||||
this.mOldList = oldList;
|
||||
this.mNewList = newList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOldListSize() {
|
||||
return mOldList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewListSize() {
|
||||
return mNewList.size();
|
||||
}
|
||||
|
||||
public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);
|
||||
|
||||
public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
|
||||
}
|
@@ -25,11 +25,12 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.NodeInfo;
|
||||
import com.m2049r.xmrwallet.util.ColorHelper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
@@ -53,7 +54,7 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
||||
private final List<NodeInfo> nodeItems = new ArrayList<>();
|
||||
private final OnInteractionListener listener;
|
||||
|
||||
private Context context;
|
||||
private final Context context;
|
||||
|
||||
public NodeInfoAdapter(Context context, OnInteractionListener listener) {
|
||||
this.context = context;
|
||||
@@ -63,11 +64,34 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
||||
TS_FORMATTER.setTimeZone(tz);
|
||||
}
|
||||
|
||||
private static class NodeDiff extends DiffCallback<NodeInfo> {
|
||||
|
||||
public NodeDiff(List<NodeInfo> oldList, List<NodeInfo> newList) {
|
||||
super(oldList, newList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
final NodeInfo oldItem = mOldList.get(oldItemPosition);
|
||||
final NodeInfo newItem = mNewList.get(newItemPosition);
|
||||
return (oldItem.getTimestamp() == newItem.getTimestamp())
|
||||
&& (oldItem.isTested() == newItem.isTested())
|
||||
&& (oldItem.isValid() == newItem.isValid())
|
||||
&& (oldItem.getResponseTime() == newItem.getResponseTime())
|
||||
&& (oldItem.isSelected() == newItem.isSelected())
|
||||
&& (oldItem.getName().equals(newItem.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull
|
||||
ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_node, parent, false);
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_node, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@@ -82,25 +106,29 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
||||
}
|
||||
|
||||
public void addNode(NodeInfo node) {
|
||||
List<NodeInfo> newItems = new ArrayList<>(nodeItems);
|
||||
if (!nodeItems.contains(node))
|
||||
nodeItems.add(node);
|
||||
dataSetChanged(); // in case the nodeinfo has changed
|
||||
newItems.add(node);
|
||||
setNodes(newItems); // in case the nodeinfo has changed
|
||||
}
|
||||
|
||||
public void dataSetChanged() {
|
||||
Collections.sort(nodeItems, NodeInfo.BestNodeComparator);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setNodes(Collection<NodeInfo> data) {
|
||||
nodeItems.clear();
|
||||
if (data != null) {
|
||||
for (NodeInfo node : data) {
|
||||
if (!nodeItems.contains(node))
|
||||
nodeItems.add(node);
|
||||
}
|
||||
public void setNodes(Collection<NodeInfo> newItemsCollection) {
|
||||
List<NodeInfo> newItems;
|
||||
if (newItemsCollection != null) {
|
||||
newItems = new ArrayList<>(newItemsCollection);
|
||||
Collections.sort(newItems, NodeInfo.BestNodeComparator);
|
||||
} else {
|
||||
newItems = new ArrayList<>();
|
||||
}
|
||||
dataSetChanged();
|
||||
final NodeInfoAdapter.NodeDiff diffCallback = new NodeInfoAdapter.NodeDiff(nodeItems, newItems);
|
||||
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
|
||||
nodeItems.clear();
|
||||
nodeItems.addAll(newItems);
|
||||
diffResult.dispatchUpdatesTo(this);
|
||||
}
|
||||
|
||||
public void setNodes() {
|
||||
setNodes(nodeItems);
|
||||
}
|
||||
|
||||
private boolean itemsClickable = true;
|
||||
@@ -130,7 +158,7 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
||||
showStar();
|
||||
if (!nodeItem.isFavourite()) {
|
||||
nodeItem.setSelected(false);
|
||||
notifyDataSetChanged();
|
||||
setNodes(nodeItems);
|
||||
}
|
||||
});
|
||||
itemView.setOnClickListener(this);
|
||||
@@ -154,7 +182,7 @@ public class NodeInfoAdapter extends RecyclerView.Adapter<NodeInfoAdapter.ViewHo
|
||||
Helper.showTimeDifference(tvIp, nodeItem.getTimestamp());
|
||||
} else {
|
||||
tvIp.setText(getResponseErrorText(context, nodeItem.getResponseCode()));
|
||||
tvIp.setTextColor(ColorHelper.getThemedColor(tvIp.getContext(), R.attr.colorError));
|
||||
tvIp.setTextColor(ThemeHelper.getThemedColor(context, R.attr.colorError));
|
||||
}
|
||||
} else {
|
||||
tvIp.setText(context.getResources().getString(R.string.node_testing, nodeItem.getHostAddress()));
|
||||
|
@@ -23,7 +23,9 @@ import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
@@ -38,6 +40,7 @@ import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import timber.log.Timber;
|
||||
@@ -46,10 +49,10 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
|
||||
private final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
|
||||
private int outboundColour;
|
||||
private int inboundColour;
|
||||
private int pendingColour;
|
||||
private int failedColour;
|
||||
private final int outboundColour;
|
||||
private final int inboundColour;
|
||||
private final int pendingColour;
|
||||
private final int failedColour;
|
||||
|
||||
public interface OnInteractionListener {
|
||||
void onInteraction(View view, TransactionInfo item);
|
||||
@@ -58,7 +61,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
private final List<TransactionInfo> infoItems;
|
||||
private final OnInteractionListener listener;
|
||||
|
||||
private Context context;
|
||||
private final Context context;
|
||||
|
||||
public TransactionInfoAdapter(Context context, OnInteractionListener listener) {
|
||||
this.context = context;
|
||||
@@ -73,10 +76,32 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
DATETIME_FORMATTER.setTimeZone(tz);
|
||||
}
|
||||
|
||||
private static class TransactionInfoDiff extends DiffCallback<TransactionInfo> {
|
||||
|
||||
public TransactionInfoDiff(List<TransactionInfo> oldList, List<TransactionInfo> newList) {
|
||||
super(oldList, newList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
return mOldList.get(oldItemPosition).hash.equals(mNewList.get(newItemPosition).hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
final TransactionInfo oldItem = mOldList.get(oldItemPosition);
|
||||
final TransactionInfo newItem = mNewList.get(newItemPosition);
|
||||
return (oldItem.direction == newItem.direction)
|
||||
&& (oldItem.isPending == newItem.isPending)
|
||||
&& (oldItem.isFailed == newItem.isFailed)
|
||||
&& (Objects.equals(oldItem.notes, newItem.notes));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_transaction, parent, false);
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_transaction, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@@ -90,23 +115,26 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
return infoItems.size();
|
||||
}
|
||||
|
||||
public void setInfos(List<TransactionInfo> data) {
|
||||
// TODO do stuff with data so we can really recycle elements (i.e. add only new tx)
|
||||
// as the TransactionInfo items are always recreated, we cannot recycle
|
||||
infoItems.clear();
|
||||
if (data != null) {
|
||||
Timber.d("setInfos %s", data.size());
|
||||
infoItems.addAll(data);
|
||||
Collections.sort(infoItems);
|
||||
} else {
|
||||
public void setInfos(List<TransactionInfo> newItems) {
|
||||
if (newItems == null) {
|
||||
newItems = new ArrayList<>();
|
||||
Timber.d("setInfos null");
|
||||
} else {
|
||||
Timber.d("setInfos %s", newItems.size());
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
Collections.sort(newItems);
|
||||
final DiffCallback<TransactionInfo> diffCallback = new TransactionInfoAdapter.TransactionInfoDiff(infoItems, newItems);
|
||||
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
|
||||
infoItems.clear();
|
||||
infoItems.addAll(newItems);
|
||||
diffResult.dispatchUpdatesTo(this);
|
||||
}
|
||||
|
||||
public void removeItem(int position) {
|
||||
infoItems.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
List<TransactionInfo> newItems = new ArrayList<>(infoItems);
|
||||
if (newItems.size() > position)
|
||||
newItems.remove(position);
|
||||
setInfos(newItems); // in case the nodeinfo has changed
|
||||
}
|
||||
|
||||
public TransactionInfo getItem(int position) {
|
||||
@@ -139,7 +167,8 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
}
|
||||
|
||||
void bind(int position) {
|
||||
this.infoItem = infoItems.get(position);
|
||||
infoItem = infoItems.get(position);
|
||||
itemView.setTransitionName(context.getString(R.string.tx_item_transition_name, infoItem.hash));
|
||||
|
||||
UserNotes userNotes = new UserNotes(infoItem.notes);
|
||||
if (userNotes.xmrtoKey != null) {
|
||||
|
@@ -17,8 +17,6 @@
|
||||
package com.m2049r.xmrwallet.layout;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@@ -26,6 +24,11 @@ import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
|
||||
@@ -63,6 +66,24 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
|
||||
DATETIME_FORMATTER.setTimeZone(tz);
|
||||
}
|
||||
|
||||
private static class WalletInfoDiff extends DiffCallback<WalletManager.WalletInfo> {
|
||||
|
||||
public WalletInfoDiff(List<WalletManager.WalletInfo> oldList, List<WalletManager.WalletInfo> newList) {
|
||||
super(oldList, newList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
return mOldList.get(oldItemPosition).name.equals(mNewList.get(newItemPosition).name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
return mOldList.get(oldItemPosition).compareTo(mNewList.get(newItemPosition)) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
@@ -84,18 +105,19 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
|
||||
return infoItems.get(position);
|
||||
}
|
||||
|
||||
public void setInfos(List<WalletManager.WalletInfo> data) {
|
||||
// TODO do stuff with data so we can really recycle elements (i.e. add only new tx)
|
||||
// as the WalletInfo items are always recreated, we cannot recycle
|
||||
infoItems.clear();
|
||||
if (data != null) {
|
||||
Timber.d("setInfos %s", data.size());
|
||||
infoItems.addAll(data);
|
||||
Collections.sort(infoItems);
|
||||
} else {
|
||||
public void setInfos(List<WalletManager.WalletInfo> newItems) {
|
||||
if (newItems == null) {
|
||||
newItems = new ArrayList<>();
|
||||
Timber.d("setInfos null");
|
||||
} else {
|
||||
Timber.d("setInfos %s", newItems.size());
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
Collections.sort(newItems);
|
||||
final DiffCallback<WalletManager.WalletInfo> diffCallback = new WalletInfoAdapter.WalletInfoDiff(infoItems, newItems);
|
||||
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
|
||||
infoItems.clear();
|
||||
infoItems.addAll(newItems);
|
||||
diffResult.dispatchUpdatesTo(this);
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
@@ -110,35 +132,24 @@ public class WalletInfoAdapter extends RecyclerView.Adapter<WalletInfoAdapter.Vi
|
||||
tvName = itemView.findViewById(R.id.tvName);
|
||||
tvAddress = itemView.findViewById(R.id.tvAddress);
|
||||
ibOptions = itemView.findViewById(R.id.ibOptions);
|
||||
ibOptions.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (popupOpen) return;
|
||||
//creating a popup menu
|
||||
PopupMenu popup = new PopupMenu(context, ibOptions);
|
||||
//inflating menu from xml resource
|
||||
popup.inflate(R.menu.list_context_menu);
|
||||
popupOpen = true;
|
||||
//adding click listener
|
||||
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
if (listener != null) {
|
||||
return listener.onContextInteraction(item, infoItem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
//displaying the popup
|
||||
popup.show();
|
||||
popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss(PopupMenu menu) {
|
||||
popupOpen = false;
|
||||
}
|
||||
});
|
||||
ibOptions.setOnClickListener(view -> {
|
||||
if (popupOpen) return;
|
||||
//creating a popup menu
|
||||
PopupMenu popup = new PopupMenu(context, ibOptions);
|
||||
//inflating menu from xml resource
|
||||
popup.inflate(R.menu.list_context_menu);
|
||||
popupOpen = true;
|
||||
//adding click listener
|
||||
popup.setOnMenuItemClickListener(item -> {
|
||||
if (listener != null) {
|
||||
return listener.onContextInteraction(item, infoItem);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
//displaying the popup
|
||||
popup.show();
|
||||
popup.setOnDismissListener(menu -> popupOpen = false);
|
||||
|
||||
}
|
||||
});
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@ import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
@@ -484,7 +483,6 @@ public class Helper {
|
||||
}
|
||||
|
||||
etPassword.getEditText().addTextChangedListener(new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (etPassword.getError() != null) {
|
||||
@@ -508,17 +506,16 @@ public class Helper {
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(context.getString(R.string.label_ok), null)
|
||||
.setNegativeButton(context.getString(R.string.label_cancel),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
Helper.hideKeyboardAlways((Activity) context);
|
||||
cancelSignal.cancel();
|
||||
if (passwordTask != null) {
|
||||
passwordTask.cancel(true);
|
||||
passwordTask = null;
|
||||
}
|
||||
dialog.cancel();
|
||||
openDialog = null;
|
||||
(dialog, id) -> {
|
||||
action.fail(wallet);
|
||||
Helper.hideKeyboardAlways((Activity) context);
|
||||
cancelSignal.cancel();
|
||||
if (passwordTask != null) {
|
||||
passwordTask.cancel(true);
|
||||
passwordTask = null;
|
||||
}
|
||||
dialog.cancel();
|
||||
openDialog = null;
|
||||
});
|
||||
openDialog = alertDialogBuilder.create();
|
||||
|
||||
@@ -561,45 +558,37 @@ public class Helper {
|
||||
};
|
||||
}
|
||||
|
||||
openDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
@Override
|
||||
public void onShow(DialogInterface dialog) {
|
||||
if (fingerprintAuthAllowed && fingerprintAuthCallback != null) {
|
||||
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icFingerprint, null, null, null);
|
||||
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
|
||||
tvOpenPrompt.setVisibility(View.VISIBLE);
|
||||
FingerprintHelper.authenticate(context, cancelSignal, fingerprintAuthCallback);
|
||||
} else {
|
||||
etPassword.requestFocus();
|
||||
}
|
||||
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
String pass = etPassword.getEditText().getText().toString();
|
||||
if (passwordTask == null) {
|
||||
passwordTask = new PasswordTask(pass, false);
|
||||
passwordTask.execute();
|
||||
}
|
||||
}
|
||||
});
|
||||
openDialog.setOnShowListener(dialog -> {
|
||||
if (fingerprintAuthAllowed && fingerprintAuthCallback != null) {
|
||||
tvOpenPrompt.setCompoundDrawablesRelativeWithIntrinsicBounds(icFingerprint, null, null, null);
|
||||
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
|
||||
tvOpenPrompt.setVisibility(View.VISIBLE);
|
||||
FingerprintHelper.authenticate(context, cancelSignal, fingerprintAuthCallback);
|
||||
} else {
|
||||
etPassword.requestFocus();
|
||||
}
|
||||
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(view -> {
|
||||
String pass = etPassword.getEditText().getText().toString();
|
||||
if (passwordTask == null) {
|
||||
passwordTask = new PasswordTask(pass, false);
|
||||
passwordTask.execute();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// accept keyboard "ok"
|
||||
etPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
|
||||
String pass = etPassword.getEditText().getText().toString();
|
||||
if (passwordTask == null) {
|
||||
passwordTask = new PasswordTask(pass, false);
|
||||
passwordTask.execute();
|
||||
}
|
||||
return true;
|
||||
etPassword.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
|| (actionId == EditorInfo.IME_ACTION_DONE)) {
|
||||
String pass = etPassword.getEditText().getText().toString();
|
||||
if (passwordTask == null) {
|
||||
passwordTask = new PasswordTask(pass, false);
|
||||
passwordTask.execute();
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (Helper.preventScreenshot()) {
|
||||
@@ -613,7 +602,7 @@ public class Helper {
|
||||
public interface PasswordAction {
|
||||
void act(String walletName, String password, boolean fingerprintUsed);
|
||||
|
||||
void fail(String walletName, String password, boolean fingerprintUsed);
|
||||
void fail(String walletName);
|
||||
}
|
||||
|
||||
static private boolean processPasswordEntry(Context context, String walletName, String pass, boolean fingerprintUsed, PasswordAction action) {
|
||||
@@ -622,7 +611,7 @@ public class Helper {
|
||||
action.act(walletName, walletPassword, fingerprintUsed);
|
||||
return true;
|
||||
} else {
|
||||
action.fail(walletName, walletPassword, fingerprintUsed);
|
||||
action.fail(walletName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -667,8 +656,8 @@ public class Helper {
|
||||
}
|
||||
view.setText(msg);
|
||||
if (hours >= STALE_NODE_HOURS)
|
||||
view.setTextColor(ColorHelper.getThemedColor(view.getContext(), R.attr.colorError));
|
||||
view.setTextColor(ThemeHelper.getThemedColor(view.getContext(), R.attr.colorError));
|
||||
else
|
||||
view.setTextColor(ColorHelper.getThemedColor(view.getContext(), android.R.attr.textColorPrimary));
|
||||
view.setTextColor(ThemeHelper.getThemedColor(view.getContext(), android.R.attr.textColorPrimary));
|
||||
}
|
||||
}
|
||||
|
@@ -17,19 +17,26 @@
|
||||
package com.m2049r.xmrwallet.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import androidx.annotation.ColorInt;
|
||||
|
||||
public class ColorHelper {
|
||||
public class ThemeHelper {
|
||||
static public int getThemedResourceId(Context ctx, int attrId) {
|
||||
TypedArray styledAttributes = ctx.getTheme().obtainStyledAttributes(R.style.MyMaterialTheme, new int[]{attrId});
|
||||
return styledAttributes.getResourceId(0, 0);
|
||||
final TypedValue typedValue = new TypedValue();
|
||||
if (ctx.getTheme().resolveAttribute(attrId, typedValue, true))
|
||||
return typedValue.resourceId;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
static public int getThemedColor(Context ctx, int attrId) {
|
||||
TypedArray styledAttributes = ctx.getTheme().obtainStyledAttributes(R.style.MyMaterialTheme, new int[]{attrId});
|
||||
return styledAttributes.getColor(0, Color.BLACK);
|
||||
final TypedValue typedValue = new TypedValue();
|
||||
if (ctx.getTheme().resolveAttribute(attrId, typedValue, true))
|
||||
return typedValue.data;
|
||||
else
|
||||
return Color.BLACK;
|
||||
}
|
||||
}
|
@@ -42,7 +42,7 @@ import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||
import com.m2049r.xmrwallet.util.ColorHelper;
|
||||
import com.m2049r.xmrwallet.util.ThemeHelper;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.ServiceHelper;
|
||||
|
||||
@@ -180,7 +180,7 @@ public class ExchangeView extends LinearLayout {
|
||||
|
||||
// make progress circle gray
|
||||
pbExchange.getIndeterminateDrawable().
|
||||
setColorFilter(ColorHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant),
|
||||
setColorFilter(ThemeHelper.getThemedColor(getContext(), R.attr.colorPrimaryVariant),
|
||||
android.graphics.PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
sCurrencyA.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
|
@@ -3,7 +3,8 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:transitionName="@string/receive_transition_name">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etDummy"
|
||||
|
@@ -4,7 +4,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:transitionName="@string/send_transition_name">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llNotice"
|
||||
|
@@ -4,7 +4,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fillViewport="true">
|
||||
android:fillViewport="true"
|
||||
android:transitionName="@string/tx_details_transition_name">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@@ -141,6 +141,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="80dp"
|
||||
android:transitionGroup="true"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
tools:listitem="@layout/item_transaction" />
|
||||
|
||||
@@ -164,6 +165,7 @@
|
||||
android:layout_weight="1"
|
||||
android:enabled="false"
|
||||
android:text="@string/label_wallet_receive"
|
||||
android:transitionName="@string/receive_btn_transition_name"
|
||||
app:icon="@drawable/ic_hand" />
|
||||
|
||||
<Button
|
||||
@@ -175,6 +177,7 @@
|
||||
android:layout_weight="1"
|
||||
android:enabled="false"
|
||||
android:text="@string/label_wallet_send"
|
||||
android:transitionName="@string/send_btn_transition_name"
|
||||
app:icon="@drawable/ic_send" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -301,15 +301,15 @@
|
||||
<!-- Note for translators: new/changed text also in help_send -->
|
||||
|
||||
<string name="help_uri"><![CDATA[
|
||||
<h1>Using a payment link</h1>
|
||||
<p>You have started monerujo with a payment link. In order to send funds, please do the following:</p>
|
||||
<h1>Usando un vínculo de pago</h1>
|
||||
<p>Acabas de iniciar Monerujo con un vínculo de pago. Para poder enviar fondos, por favor haz lo siguiente:</p>
|
||||
<p>
|
||||
1. Open the wallet you want to spend from<br>
|
||||
2. Wait until the wallet is synced & the "Give" button appears<br>
|
||||
3. Touch the "Give" button
|
||||
1. Abre el monedero desde el cual quieras gastar<br>
|
||||
2. Espera hasta que esté sincronizado y el botón de "Enviar" aparezca<br>
|
||||
3. Presiona el botón de "Enviar"
|
||||
</p>
|
||||
<p>The payment details will be filled in. Check them and proceed like for any other transaction.</p>
|
||||
<p>Los detalles del pago ya estarán completos. Revísalos y prosigue como con cualquier otra transacción.</p>
|
||||
]]></string>
|
||||
|
||||
<string name="help_ok">Got it!</string> <!-- Note: "Got it" as in "I understand this" -->
|
||||
<string name="help_ok">¡Entendido!</string> <!-- Note: "Got it" as in "I understand this" -->
|
||||
</resources>
|
||||
|
@@ -47,7 +47,7 @@
|
||||
|
||||
<string name="service_progress">Guardando todo\n¡Puede llevar un tiempo!</string>
|
||||
|
||||
<string name="backup_success">Copia de seguridad satisfactoria</string>
|
||||
<string name="backup_success">Copia de seguridad exitosa</string>
|
||||
<string name="backup_failed">¡Copia de seguridad fallida!</string>
|
||||
<string name="archive_failed">¡Archivado fallido!</string>
|
||||
<string name="rename_failed">¡Cambio de nombre fallido!</string>
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
<string name="label_daemon">Nodo</string>
|
||||
<string name="status_wallet_loading">Cargando monedero…</string>
|
||||
<string name="status_wallet_unloaded">Monedero guardada</string>
|
||||
<string name="status_wallet_unloaded">Monedero guardado</string>
|
||||
<string name="status_wallet_unload_failed">¡Guardado de monedero fallido!</string>
|
||||
<string name="status_wallet_connecting">Conectando…</string>
|
||||
<string name="status_wallet_connect_failed">¡Conexión con el nodo fallida!\nComprueba el usuario/contraseña</string>
|
||||
@@ -78,7 +78,7 @@
|
||||
<string name="prompt_open_wallet">Abriendo el monedero…</string>
|
||||
<string name="bad_fingerprint">Huella no reconocida. Inténtalo nuevamente.</string>
|
||||
<string name="bad_password">¡Contraseña incorrecta!</string>
|
||||
<string name="bad_saved_password">La contraseña guardada es incorrecta.\nIngresela manualmente.</string>
|
||||
<string name="bad_saved_password">La contraseña guardada es incorrecta.\nIngrésala manualmente.</string>
|
||||
<string name="bad_wallet">¡El monedero no existe!</string>
|
||||
<string name="prompt_daemon_missing">¡La dirección del daemon debe estar configurada!</string>
|
||||
<string name="prompt_wrong_net">El monedero no coincide con la red seleccionada</string>
|
||||
@@ -90,27 +90,27 @@
|
||||
|
||||
<string name="xmr_unconfirmed_amount">+ %1$s XMR sin confirmar</string>
|
||||
|
||||
<string name="service_description">Servicio de monerujo</string>
|
||||
<string name="service_description">Servicio de Monerujo</string>
|
||||
|
||||
<string name="status_synced">Sincronizado:</string>
|
||||
<string name="status_remaining">bloques restantes</string>
|
||||
<string name="status_syncing">Escaneando:</string>
|
||||
|
||||
<string name="message_strorage_not_writable">¡No se puede escribir en el almacenamiento externo! ¡Pánico!</string>
|
||||
<string name="message_strorage_not_permitted">¡De verdad necesitamos esos permisos para el almacenamiento externo!</string>
|
||||
<string name="message_strorage_not_permitted">¡De verdad necesitamos ese permiso para el almacenamiento externo!</string>
|
||||
<string name="message_camera_not_permitted">Sin cámara = ¡Sin escaneo de QR!</string>
|
||||
|
||||
<string name="label_copy_viewkey">Clave de Vista</string>
|
||||
<string name="label_copy_address">Dirección Pública</string>
|
||||
<string name="message_copy_viewkey">¡Clave de vista copiada al portapapeles!</string>
|
||||
<string name="message_copy_address">¡Dirección del monedero copiada al portapapeles!</string>
|
||||
<string name="message_nocopy">¡La copia está desactivada por motivos de seguridad!</string>
|
||||
<string name="message_nocopy">¡Copia desactivada por motivos de seguridad!</string>
|
||||
|
||||
<string name="message_exchange_failed">¡No se ha podido obtener la tasa de cambio!\nUsa XMR/XMR o inténtalo de nuevo</string>
|
||||
|
||||
<string name="generate_title">Crear monedero</string>
|
||||
<string name="generate_name_hint">Nombre del monedero</string>
|
||||
<string name="generate_password_hint">Frase de Contraseña</string>
|
||||
<string name="generate_password_hint">Frase de contraseña</string>
|
||||
<string name="generate_fingerprint_hint">Permitir abrir usando huella dactilar</string>
|
||||
<string name="generate_fingerprint_warn"><![CDATA[
|
||||
<strong>Autenticación por huella</strong>
|
||||
@@ -209,9 +209,9 @@
|
||||
<string name="details_alert_no">¡Llévame de vuelta!</string>
|
||||
<string name="details_title">Detalles</string>
|
||||
|
||||
<string name="archive_alert_message">¡El monedero será copiada y después borrada!</string>
|
||||
<string name="archive_alert_yes">¡Sí, haz eso!</string>
|
||||
<string name="archive_alert_no">¡No gracias!</string>
|
||||
<string name="archive_alert_message">¡El monedero será copiado y después borrado!</string>
|
||||
<string name="archive_alert_yes">¡Sí, hazlo!</string>
|
||||
<string name="archive_alert_no">¡No, gracias!</string>
|
||||
|
||||
<string name="fab_create_new">Crear nuevo monedero</string>
|
||||
<string name="fab_restore_viewonly">Restaurar monedero de sólo vista</string>
|
||||
@@ -248,7 +248,7 @@
|
||||
<string name="label_send_btc_xmrto_key">Clave secreta SideShift.ai</string>
|
||||
<string name="label_send_btc_address">Dirección %1$s destino</string>
|
||||
<string name="label_send_btc_amount">Monto</string>
|
||||
<string name="send_xmrto_timeout">Oye, esperaste demasiado!</string>
|
||||
<string name="send_xmrto_timeout">Oye, tardaste demasiado!</string>
|
||||
<string name="label_copy_xmrtokey">Clave</string>
|
||||
<string name="message_copy_xmrtokey">¡Clave copiada al portapapeles!</string>
|
||||
<string name="send_send_label">Enviar mis preciados moneroj</string>
|
||||
@@ -258,7 +258,7 @@
|
||||
<string name="send_total_btc_label">Total (XMR)</string>
|
||||
<string name="send_amount">%1$s XMR</string>
|
||||
<string name="send_fee">+%1$s Comisión</string>
|
||||
<string name="about_whoami">Soy monerujo</string>
|
||||
<string name="about_whoami">Soy Monerujo</string>
|
||||
<string name="info_send_xmrto_success_order_label">Orden SideShift.ai</string>
|
||||
|
||||
<string name="info_xmrto_enabled">Pago en BTC activado, toca para más info.</string>
|
||||
@@ -342,65 +342,65 @@
|
||||
<!-- please verify this means "Receiver" or "Recipient" as in "Receiver in the transaction" -->
|
||||
<string name="send_address_hint">Receptor</string>
|
||||
|
||||
<string name="street_sweep_amount">EVERYTHING!</string> <!-- as in: "everything in the account" = "all the money" -->
|
||||
<string name="street_sweep_amount">TODO!</string> <!-- as in: "everything in the account" = "all the money" -->
|
||||
|
||||
<string name="menu_ledger_seed">Convert Ledger Seed</string>
|
||||
<string name="prompt_ledger_seed">Ledger Seed Words</string>
|
||||
<string name="prompt_ledger_phrase">Ledger Passphrase (optional)</string>
|
||||
<string name="bad_ledger_seed">Invalid Ledger Seed!</string>
|
||||
<string name="prompt_ledger_seed_warn">Entering your Ledger Seed here is a major security risk!</string>
|
||||
<string name="menu_ledger_seed">Convertir semilla de Ledger</string>
|
||||
<string name="prompt_ledger_seed">Palabras de la semilla de Ledger</string>
|
||||
<string name="prompt_ledger_phrase">Contraseña de Ledger (opcional)</string>
|
||||
<string name="bad_ledger_seed">¡Semilla de Ledger inválida!</string>
|
||||
<string name="prompt_ledger_seed_warn">¡Ingresar tu semilla de Ledger aquí implica un riesgo importante!</string>
|
||||
|
||||
<string name="label_restoreheight">Altura de Restauración</string> <!-- Restore Height -->
|
||||
|
||||
<string name="toast_ledger_start_app">Start Monero App on %1$s</string>
|
||||
<string name="toast_ledger_start_app">Arrancar aplicación de Monero en %1$s</string>
|
||||
|
||||
<string name="menu_rescan">Rescan!</string>
|
||||
<string name="menu_rescan">¡Re-escanear!</string>
|
||||
|
||||
<string name="onboarding_agree">I get it!</string>
|
||||
<string name="onboarding_button_next">Next</string>
|
||||
<string name="onboarding_button_ready">I\'m ready!</string>
|
||||
<string name="onboarding_agree">¡Entendido!</string>
|
||||
<string name="onboarding_button_next">Siguiente</string>
|
||||
<string name="onboarding_button_ready">¡Estoy listo!</string>
|
||||
|
||||
<string name="onboarding_welcome_title">Welcome to Monerujo!</string>
|
||||
<string name="onboarding_welcome_information">This app allows you to create and use Monero wallets. You can store your sweet moneroj in them.</string>
|
||||
<string name="onboarding_seed_title">Keep your seed safe</string>
|
||||
<string name="onboarding_seed_information">The seed grants full access to whoever has it. If you lose it, we cannot help you recover it and you lose your beloved moneroj.</string>
|
||||
<string name="onboarding_xmrto_title">Send Crypto</string>
|
||||
<string name="onboarding_xmrto_information">Monerujo has SideShift.ai support built-in. Just paste or scan a BTC, LTC, ETH, DASH or DOGE address and you\'ll be sending these cryptos by spending XMR.</string>
|
||||
<string name="onboarding_nodes_title">Nodes, your way</string>
|
||||
<string name="onboarding_nodes_information">Nodes connect you to the Monero network. Choose between public nodes or go full cypherpunk using your own.</string>
|
||||
<string name="onboarding_fpsend_title">Send with fingerprint</string>
|
||||
<string name="onboarding_fpsend_information">You\'re now able to send XMR with just your fingerprint if you enabled it. To request the password, just disable fingerprint access.</string>
|
||||
<string name="onboarding_welcome_title">¡Bienvenido a Monerujo!</string>
|
||||
<string name="onboarding_welcome_information">Esta aplicación te permite crear y usar monederos de Monero. Puedes guardar tus dulces moneroj en ellos.</string>
|
||||
<string name="onboarding_seed_title">Mantén segura tu semilla</string>
|
||||
<string name="onboarding_seed_information">La semilla otorga acceso total a quien la posee. Si la pierdes, nadie puede ayudarte a recuperarla y perderás tus preciados moneroj.</string>
|
||||
<string name="onboarding_xmrto_title">Enviar Criptos</string>
|
||||
<string name="onboarding_xmrto_information">Monerujo tiene SideShift.ai incorporado. Simplemente pega o escanea una dirección de BTC, LTC, ETH, DASH o DOGE y podrás enviar esas monedas usando tu XMR.</string>
|
||||
<string name="onboarding_nodes_title">Nodos, a tu manera</string>
|
||||
<string name="onboarding_nodes_information">Los nodos son tu conexión con la red de Monero. Elige entre usar nodos públicos, o ir totalmente ciberpunk conectándote al tuyo propio.</string>
|
||||
<string name="onboarding_fpsend_title">Enviar con tu huella</string>
|
||||
<string name="onboarding_fpsend_information">Ahora puedes enviar XMR con sólo tu huella dactilar si lo deseas. Para forzar el pedido de contraseña, simplemente desactiva la opción de huella.</string>
|
||||
|
||||
<string name="menu_daynight">Dark Mode</string>
|
||||
<string name="menu_daynight">Tema</string>
|
||||
<string-array name="daynight_themes">
|
||||
<item>Auto</item>
|
||||
<item>Day</item>
|
||||
<item>Night</item>
|
||||
<item>Automático</item>
|
||||
<item>Claro</item>
|
||||
<item>Oscuro</item>
|
||||
</string-array>
|
||||
<string name="gunther_says">There is nothing here\nPlease create or restore a wallet</string>
|
||||
<string name="gunther_says">No hay nada aquí.\nPor favor crea o restaura un monedero.</string>
|
||||
|
||||
<string name="menu_default_nodes">Restore default nodes</string>
|
||||
<string name="toast_default_nodes">Restoring already in progress…</string>
|
||||
<string name="menu_default_nodes">Restaurar los nodos por defecto</string>
|
||||
<string name="toast_default_nodes">Restauración ya en proceso…</string>
|
||||
|
||||
<string name="node_updated_now">Last Block: %1$d seconds ago</string>
|
||||
<string name="node_updated_mins">Last Block: %1$d minutes ago</string>
|
||||
<string name="node_updated_hours">Last Block: %1$d hours ago</string>
|
||||
<string name="node_updated_days">Last Block: %1$d days ago</string>
|
||||
<string name="node_updated_now">Último bloque hace %1$d segundos</string>
|
||||
<string name="node_updated_mins">Último bloque hace %1$d minutos</string>
|
||||
<string name="node_updated_hours">Último bloque hace %1$d horas</string>
|
||||
<string name="node_updated_days">Último bloque hace %1$d días</string>
|
||||
|
||||
<string name="shift_noquote">Cannot get quote</string>
|
||||
<string name="shift_checkamount">Check amount and try again</string>
|
||||
<string name="shift_noquote">No pudo obtenerse una cotización</string>
|
||||
<string name="shift_checkamount">Revisa el monto y trata nuevamente</string>
|
||||
|
||||
<string name="info_xmrto_ambiguous"><![CDATA[
|
||||
<b>Ambiguous address.</b><br/>
|
||||
<i>Please select the type above.</i>
|
||||
<b>Dirección ambigua.</b><br/>
|
||||
<i>Por favor selecciona la moneda deseada.</i>
|
||||
]]></string>
|
||||
|
||||
<string name="info_xmrto_help"><![CDATA[
|
||||
<b>Please enter or scan a %1$s address.</b><br/>
|
||||
<i>You'll send XMR and the receiver will get %2$s using the <b>SideShift.ai</b> service.</i>
|
||||
<b>Por favor ingresa o escanea una dirección %1$s.</b><br/>
|
||||
<i>Vas a enviar XMR y el receptor obtendrá %2$s a través de <b>SideShift.ai</b></i>
|
||||
]]></string>
|
||||
|
||||
<string name="info_xmrto_help_xmr"><![CDATA[
|
||||
<b>Please enter or scan a Monero address.</b>
|
||||
<b>Por favor ingresa o escanea una dirección de Monero.</b>
|
||||
]]></string>
|
||||
</resources>
|
||||
|
@@ -1,2 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
<resources>
|
||||
<integer name="tx_item_transition_duration">300</integer>
|
||||
</resources>
|
||||
|
@@ -471,4 +471,12 @@
|
||||
<item>Ethereum</item>
|
||||
<item>Litecoin</item>
|
||||
</string-array>
|
||||
|
||||
<string name="tx_item_transition_name" translatable="false">tx_transition_%1$s</string>
|
||||
<string name="tx_details_transition_name" translatable="false">tx_transition</string>
|
||||
<string name="receive_transition_name" translatable="false">receive_transition</string>
|
||||
<string name="receive_btn_transition_name" translatable="false">receive_btn_transition</string>
|
||||
<string name="send_transition_name" translatable="false">send_transition</string>
|
||||
<string name="send_btn_transition_name" translatable="false">send_btn_transition</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -36,7 +36,7 @@
|
||||
</style>
|
||||
|
||||
<style name="AppCard" parent="Widget.MaterialComponents.CardView">
|
||||
<item name="cardElevation">8dp</item>
|
||||
<item name="cardElevation">4dp</item>
|
||||
<item name="cardCornerRadius">1dp</item>
|
||||
</style>
|
||||
|
||||
|
Reference in New Issue
Block a user