diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f867402..508e852 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,5 +68,15 @@ android:description="@string/service_description" android:exported="false" android:label="Monero Wallet Service" /> + + + + \ No newline at end of file diff --git a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java index fb48a05..9df51cf 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java @@ -17,19 +17,28 @@ package com.m2049r.xmrwallet; import android.content.Context; +import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; +import android.net.Uri; import android.nfc.NfcManager; import android.os.AsyncTask; import android.os.Bundle; +import android.support.annotation.Nullable; import android.support.design.widget.TextInputLayout; import android.support.v4.app.Fragment; +import android.support.v4.content.FileProvider; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.ShareActionProvider; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; @@ -56,6 +65,9 @@ import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor; import com.m2049r.xmrwallet.widget.ExchangeView; import com.m2049r.xmrwallet.widget.Toolbar; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -69,8 +81,8 @@ public class ReceiveFragment extends Fragment { private TextInputLayout etNotes; private ExchangeView evAmount; private TextView tvQrCode; - private ImageView qrCode; - private ImageView qrCodeFull; + private ImageView ivQrCode; + private ImageView ivQrCodeFull; private EditText etDummy; private ImageButton bCopyAddress; private Button bSubaddress; @@ -97,9 +109,9 @@ public class ReceiveFragment extends Fragment { tvAddress = view.findViewById(R.id.tvAddress); etNotes = view.findViewById(R.id.etNotes); evAmount = view.findViewById(R.id.evAmount); - qrCode = view.findViewById(R.id.qrCode); + ivQrCode = view.findViewById(R.id.qrCode); tvQrCode = view.findViewById(R.id.tvQrCode); - qrCodeFull = view.findViewById(R.id.qrCodeFull); + ivQrCodeFull = view.findViewById(R.id.qrCodeFull); etDummy = view.findViewById(R.id.etDummy); bCopyAddress = view.findViewById(R.id.bCopyAddress); bSubaddress = view.findViewById(R.id.bSubaddress); @@ -178,25 +190,25 @@ public class ReceiveFragment extends Fragment { } }); - qrCode.setOnClickListener(new View.OnClickListener() { + ivQrCode.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Helper.hideKeyboard(getActivity()); etDummy.requestFocus(); if (qrValid) { - qrCodeFull.setImageBitmap(((BitmapDrawable) qrCode.getDrawable()).getBitmap()); - qrCodeFull.setVisibility(View.VISIBLE); + ivQrCodeFull.setImageBitmap(((BitmapDrawable) ivQrCode.getDrawable()).getBitmap()); + ivQrCodeFull.setVisibility(View.VISIBLE); } else { evAmount.doExchange(); } } }); - qrCodeFull.setOnClickListener(new View.OnClickListener() { + ivQrCodeFull.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - qrCodeFull.setImageBitmap(null); - qrCodeFull.setVisibility(View.GONE); + ivQrCodeFull.setImageBitmap(null); + ivQrCodeFull.setVisibility(View.GONE); } }); @@ -228,6 +240,81 @@ public class ReceiveFragment extends Fragment { return view; } + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + private ShareActionProvider shareActionProvider; + + @Override + public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) { + inflater.inflate(R.menu.receive_menu, menu); + super.onCreateOptionsMenu(menu, inflater); + + // Locate MenuItem with ShareActionProvider + MenuItem item = menu.findItem(R.id.menu_item_share); + + // Fetch and store ShareActionProvider + shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item); + + shareActionProvider.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() { + @Override + public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) { + saveQrCode(); // save it only if we need it + return false; + } + }); + } + + private void setShareIntent() { + if (shareActionProvider != null) { + if (qrValid) { + shareActionProvider.setShareIntent(getShareIntent()); + } else { + shareActionProvider.setShareIntent(null); + } + } + } + + private void saveQrCode() { + if (!qrValid) throw new IllegalStateException("trying to save null qr code!"); + + File cachePath = new File(getActivity().getCacheDir(), "images"); + if (!cachePath.exists()) + if (!cachePath.mkdirs()) throw new IllegalStateException("cannot create images folder"); + File png = new File(cachePath, "QR.png"); + try { + FileOutputStream stream = new FileOutputStream(png); + Bitmap qrBitmap = ((BitmapDrawable) ivQrCode.getDrawable()).getBitmap(); + qrBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + stream.close(); + } catch (IOException ex) { + Timber.e(ex); + // make sure we don't share an old qr code + if (!png.delete()) throw new IllegalStateException("cannot delete old qr code"); + // if we manage to delete it, the URI points to nothing and the user gets a toast with the error + } + } + + private Intent getShareIntent() { + File imagePath = new File(getActivity().getCacheDir(), "images"); + File png = new File(imagePath, "QR.png"); + Uri contentUri = FileProvider.getUriForFile(getActivity(), + "com.m2049r.xmrwallet.fileprovider", png); + if (contentUri != null) { + Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file + shareIntent.setDataAndType(contentUri, getActivity().getContentResolver().getType(contentUri)); + shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); + shareIntent.putExtra(Intent.EXTRA_TEXT, bcData.getUriString()); + return shareIntent; + } + return null; + } + void enableSubaddressButton(boolean enable) { bSubaddress.setEnabled(enable); if (enable) { @@ -242,23 +329,23 @@ public class ReceiveFragment extends Fragment { Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show(); } - boolean qrValid = true; + private boolean qrValid = false; void clearQR() { if (qrValid) { - qrCode.setImageBitmap(null); + ivQrCode.setImageBitmap(null); qrValid = false; + setShareIntent(); if (isLoaded) tvQrCode.setVisibility(View.VISIBLE); } } void setQR(Bitmap qr) { - qrCode.setImageBitmap(qr); + ivQrCode.setImageBitmap(qr); qrValid = true; + setShareIntent(); tvQrCode.setVisibility(View.GONE); - Helper.hideKeyboard(getActivity()); - etDummy.requestFocus(); } @Override @@ -392,7 +479,7 @@ public class ReceiveFragment extends Fragment { return; } bcData = new BarcodeData(BarcodeData.Asset.XMR, address, null, notes, xmrAmount); - int size = Math.min(qrCode.getHeight(), qrCode.getWidth()); + int size = Math.max(ivQrCode.getWidth(), ivQrCode.getHeight()); Bitmap qr = generate(bcData.getUriString(), size, size); if (qr != null) { setQR(qr); @@ -422,8 +509,8 @@ public class ReceiveFragment extends Fragment { Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.RGB_565); bitmap = addLogo(bitmap); return bitmap; - } catch (WriterException e) { - e.printStackTrace(); + } catch (WriterException ex) { + Timber.e(ex); } return null; } diff --git a/app/src/main/res/menu/receive_menu.xml b/app/src/main/res/menu/receive_menu.xml new file mode 100644 index 0000000..2ce16b1 --- /dev/null +++ b/app/src/main/res/menu/receive_menu.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/filepaths.xml b/app/src/main/res/xml/filepaths.xml new file mode 100644 index 0000000..1464dd9 --- /dev/null +++ b/app/src/main/res/xml/filepaths.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file