Add the share menu icon to the ReceiveTransactionFragment toolbar, verify for the storage permission required to store a temp image of the QR code when the sare icon is tapped and create a temp image of the QR code to be used later to send a share intent to the Andriod System so that it shows the user a list of options for him to select his preferred one.

This commit is contained in:
Severiano Jaramillo 2018-12-20 20:28:31 -06:00
parent e3bea9f475
commit 62fdc6a3e4
7 changed files with 199 additions and 39 deletions

View file

@ -11,6 +11,8 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:name=".utils.BitsyApplication"

View file

@ -1,19 +1,21 @@
package cy.agorise.bitsybitshareswallet.fragments
import android.Manifest
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Color
import android.os.Bundle
import android.os.IBinder
import android.preference.PreferenceManager
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.*
import android.widget.AdapterView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
@ -28,6 +30,7 @@ import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.adapters.AssetsAdapter
import cy.agorise.bitsybitshareswallet.adapters.AutoSuggestAssetAdapter
import cy.agorise.bitsybitshareswallet.utils.Constants
import cy.agorise.bitsybitshareswallet.utils.Helper
import cy.agorise.bitsybitshareswallet.viewmodels.AssetViewModel
import cy.agorise.bitsybitshareswallet.viewmodels.UserAccountViewModel
import cy.agorise.graphenej.*
@ -51,6 +54,7 @@ class ReceiveTransactionFragment : Fragment(), ServiceConnection {
private val TAG = this.javaClass.simpleName
private val RESPONSE_LIST_ASSETS = 1
private val REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION = 100
/** Number of assets to request from the NetworkService to show as suggestions in the AutoCompleteTextView */
private val AUTO_SUGGEST_ASSET_LIMIT = 5
@ -88,6 +92,8 @@ class ReceiveTransactionFragment : Fragment(), ServiceConnection {
private var mShouldUnbindNetwork: Boolean = false
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
setHasOptionsMenu(true)
return inflater.inflate(R.layout.fragment_receive_transaction, container, false)
}
@ -333,6 +339,69 @@ class ReceiveTransactionFragment : Fragment(), ServiceConnection {
tvTo.text = txtAccount
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_receive_transaction, menu)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
if (item?.itemId == R.id.menu_share) {
verifyStoragePermission()
return true
}
return super.onOptionsItemSelected(item)
}
private fun verifyStoragePermission() {
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not already granted
requestPermissions(arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION)
} else {
// Permission is already granted
shareQRScreenshot()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
shareQRScreenshot()
} else {
// TODO extract string resource
Toast.makeText(context!!, "Storage permission is necessary to share QR codes.", Toast.LENGTH_SHORT).show()
}
return
}
}
/**
* This function takes a screenshot as a bitmap, saves it into a temporal cache image and then
* sends an intent so the user can select the desired method to share the image.
*/
private fun shareQRScreenshot() {
// Get Screenshot
val screenshot = Helper.loadBitmapFromView(container)
val imageUri = Helper.saveTemporalBitmap(context!!, screenshot)
// Prepare information for share intent
val subject = getString(R.string.msg__invoice_subject, mUserAccount?.name)
val content = tvPleasePay.text.toString() + "\n" +
tvTo.text.toString()
// Create share intent and call it
val shareIntent = Intent()
shareIntent.action = Intent.ACTION_SEND
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // temp permission for receiving app to read this file
shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri)
shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject)
shareIntent.putExtra(Intent.EXTRA_TEXT, content)
shareIntent.type = "*/*"
startActivity(Intent.createChooser(shareIntent, getString(R.string.text__share_with)))
}
override fun onResume() {
super.onResume()

View file

@ -0,0 +1,60 @@
package cy.agorise.bitsybitshareswallet.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.net.Uri
import android.util.Log
import android.view.View
import androidx.core.content.FileProvider
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
/**
* Contains methods that are helpful in different parts of the app
*/
class Helper {
companion object {
private val TAG = "Helper"
/**
* Creates and returns a Bitmap from the contents of a View, does not matter
* if it is a simple view or a ViewGroup like a ConstraintLayout or a LinearLayout.
*
* @param view The view that is gonna be pictured.
* @return The generated image from the given view.
*/
fun loadBitmapFromView(view: View): Bitmap {
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
fun saveTemporalBitmap(context: Context, bitmap: Bitmap): Uri {
// save bitmap to cache directory
try {
val cachePath = File(context.cacheDir, "images")
if (!cachePath.mkdirs())
// don't forget to make the directory
Log.d(TAG, "shareBitmapImage creating cache images folder")
val stream = FileOutputStream(cachePath.toString() + "/image.png") // overwrites this image every time
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
stream.close()
} catch (e: IOException) {
Log.d(TAG, "shareBitmapImage error: " + e.message)
}
// Send intent to share image+text
val imagePath = File(context.cacheDir, "images")
val newFile = File(imagePath, "image.png")
// Create and return image uri
return FileProvider.getUriForFile(context, "cy.agorise.FileProvider", newFile)
}
}
}

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

View file

@ -83,47 +83,57 @@
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/ivQR"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/spacing_different_topic"
android:layout_marginBottom="@dimen/spacing_different_topic"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@+id/tilAsset"
app:layout_constraintBottom_toTopOf="@+id/llText"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
<LinearLayout
android:id="@+id/llText"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ivQR">
android:layout_height="0dp"
android:background="?android:windowBackground"
app:layout_constraintTop_toBottomOf="@+id/tilAsset"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="@+id/tvPleasePay"
<ImageView
android:id="@+id/ivQR"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/spacing_different_topic"
android:layout_marginBottom="@dimen/spacing_different_topic"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/llText"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
<LinearLayout
android:id="@+id/llText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
tools:text="Please Pay: 12.25316 BTS"
android:textAppearance="@style/TextAppearance.Bitsy.Body1"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@id/tvTo"/>
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ivQR">
<TextView
android:id="@+id/tvTo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
tools:text="To: seventest-3"
android:textAppearance="@style/TextAppearance.Bitsy.Body1"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/tvPleasePay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
tools:text="Please Pay: 12.25316 BTS"
android:textAppearance="@style/TextAppearance.Bitsy.Body1"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@id/tvTo"/>
</LinearLayout>
<TextView
android:id="@+id/tvTo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
tools:text="To: seventest-3"
android:textAppearance="@style/TextAppearance.Bitsy.Body1"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_share"
android:icon="@drawable/ic_share"
android:title="@string/title_share"
app:showAsAction="always"/>
</menu>

View file

@ -56,6 +56,8 @@
<string name="text__asset">Asset</string>
<string name="template__please_pay">Please Pay: %1$s %2$s</string>
<string name="template__to">To: %1$s</string>
<string name="msg__invoice_subject">BiTSy invoice from %1$s</string>
<string name="text__share_with">Share with</string>
<!-- Settings -->
<string name="title_settings">Settings</string>
@ -79,5 +81,6 @@
<string name="title_search">Search</string>
<string name="title_filter">Filter</string>
<string name="title_export">Export</string>
<string name="title_share">Share</string>
</resources>