Skip to content

Commit db2888f

Browse files
authored
Merge pull request #142 from TheCodeMonks/Share-As-Image
Share as image
2 parents acfceee + eeb3c98 commit db2888f

File tree

21 files changed

+4033
-35
lines changed

21 files changed

+4033
-35
lines changed

.idea/misc.xml

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ dependencies {
106106
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
107107
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
108108

109+
// activity & fragment ktx
110+
implementation "androidx.fragment:fragment-ktx:1.3.2"
111+
implementation "androidx.activity:activity-ktx:1.3.0-alpha05"
112+
implementation 'androidx.appcompat:appcompat:1.3.0-rc01'
109113

110114
// Navigation Components
111115
implementation "androidx.navigation:navigation-fragment-ktx:2.3.4"
@@ -118,6 +122,9 @@ dependencies {
118122
// Preference DataStore
119123
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha06"
120124

125+
// Lottie Animation Library
126+
implementation "com.airbnb.android:lottie:3.6.0"
127+
121128
// Hilt
122129
implementation "com.google.dagger:hilt-android:2.30.1-alpha"
123130
kapt "com.google.dagger:hilt-android-compiler:2.30.1-alpha"

app/src/main/AndroidManifest.xml

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ /*
4+
~ *
5+
~ * * MIT License
6+
~ * *
7+
~ * * Copyright (c) 2020 Spikey Sanju
8+
~ * *
9+
~ * * Permission is hereby granted, free of charge, to any person obtaining a copy
10+
~ * * of this software and associated documentation files (the "Software"), to deal
11+
~ * * in the Software without restriction, including without limitation the rights
12+
~ * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
~ * * copies of the Software, and to permit persons to whom the Software is
14+
~ * * furnished to do so, subject to the following conditions:
15+
~ * *
16+
~ * * The above copyright notice and this permission notice shall be included in all
17+
~ * * copies or substantial portions of the Software.
18+
~ * *
19+
~ * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
~ * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
~ * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
~ * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
~ * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
~ * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
~ * * SOFTWARE.
26+
~ *
27+
~ */
28+
~
29+
-->
30+
231
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
332
package="thecodemonks.org.nottzapp">
33+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
434

535
<application
636
android:name=".app.NotzzApp"
@@ -25,4 +55,4 @@
2555
android:resource="@array/preloaded_fonts" />
2656
</application>
2757

28-
</manifest>
58+
</manifest>

app/src/main/java/thecodemonks/org/nottzapp/ui/details/NotesDetailsFragment.kt

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,22 @@
2929

3030
package thecodemonks.org.nottzapp.ui.details
3131

32+
import android.Manifest
3233
import android.annotation.SuppressLint
34+
import android.content.Intent
35+
import android.content.pm.PackageManager
3336
import android.os.Bundle
34-
import android.view.*
37+
import android.view.LayoutInflater
38+
import android.view.Menu
39+
import android.view.MenuInflater
40+
import android.view.MenuItem
41+
import android.view.View
42+
import android.view.ViewGroup
3543
import android.widget.Toast
44+
import androidx.activity.result.contract.ActivityResultContracts
3645
import androidx.core.app.ShareCompat
46+
import androidx.core.content.ContextCompat
47+
import androidx.core.view.drawToBitmap
3748
import androidx.fragment.app.Fragment
3849
import androidx.fragment.app.activityViewModels
3950
import androidx.navigation.fragment.findNavController
@@ -42,6 +53,8 @@ import dagger.hilt.android.AndroidEntryPoint
4253
import thecodemonks.org.nottzapp.R
4354
import thecodemonks.org.nottzapp.databinding.FragmentNotesDetailsBinding
4455
import thecodemonks.org.nottzapp.ui.notes.NotesViewModel
56+
import thecodemonks.org.nottzapp.utils.saveBitmap
57+
import thecodemonks.org.nottzapp.utils.showOrHide
4558

4659
@AndroidEntryPoint
4760
class NotesDetailsFragment : Fragment(R.layout.fragment_notes_details) {
@@ -52,11 +65,59 @@ class NotesDetailsFragment : Fragment(R.layout.fragment_notes_details) {
5265
private lateinit var _binding: FragmentNotesDetailsBinding
5366
private val binding get() = _binding
5467

68+
// handle permission dialog
69+
private val requestLauncher =
70+
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
71+
if (isGranted) shareImage() else showErrorDialog()
72+
}
73+
74+
private fun showErrorDialog() = findNavController().navigate(
75+
NotesDetailsFragmentDirections.actionNotesDetailsFragmentToErrorDialog(
76+
"Image share failed!",
77+
"You have to enable storage permission to share transaction as Image"
78+
)
79+
)
80+
81+
private fun shareImage() {
82+
83+
if (!isStoragePermissionGranted()) {
84+
requestLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
85+
return
86+
}
87+
88+
// unHide watermark
89+
showLogo(true)
90+
val imageURI = binding.noteLayout.noteDetailLayout.drawToBitmap().let { bitmap ->
91+
// hide watermark
92+
showLogo(false)
93+
saveBitmap(requireActivity(), bitmap)
94+
} ?: run {
95+
Toast.makeText(requireContext(), "Error Occured", Toast.LENGTH_SHORT).show()
96+
return
97+
}
98+
99+
val intent = ShareCompat.IntentBuilder(requireActivity())
100+
.setType("image/jpeg")
101+
.setStream(imageURI)
102+
.intent
103+
104+
startActivity(Intent.createChooser(intent, null))
105+
}
106+
107+
private fun showLogo(isVisible: Boolean) = with(binding) {
108+
noteLayout.logo.showOrHide(isVisible)
109+
}
110+
111+
private fun isStoragePermissionGranted(): Boolean = ContextCompat.checkSelfPermission(
112+
requireContext(),
113+
Manifest.permission.WRITE_EXTERNAL_STORAGE
114+
) == PackageManager.PERMISSION_GRANTED
115+
55116
override fun onCreateView(
56117
inflater: LayoutInflater,
57118
container: ViewGroup?,
58119
savedInstanceState: Bundle?,
59-
): View? {
120+
): View {
60121
_binding = FragmentNotesDetailsBinding.inflate(inflater, container, false)
61122
return binding.root
62123
}
@@ -107,21 +168,13 @@ class NotesDetailsFragment : Fragment(R.layout.fragment_notes_details) {
107168
true
108169
}
109170

110-
R.id.action_share -> {
111-
val shareMsg = getString(
112-
R.string.share_message,
113-
args.notes.title,
114-
args.notes.description
115-
)
116-
117-
val intent = ShareCompat.IntentBuilder.from(requireActivity())
118-
.setType("text/plain")
119-
.setText(shareMsg)
120-
.intent
171+
R.id.action_share_text -> {
172+
shareText()
173+
true
174+
}
121175

122-
if (intent.resolveActivity(requireActivity().packageManager) != null) {
123-
startActivity(intent)
124-
}
176+
R.id.action_share_image -> {
177+
shareImage()
125178
true
126179
}
127180

@@ -135,4 +188,20 @@ class NotesDetailsFragment : Fragment(R.layout.fragment_notes_details) {
135188
it.noteET.text.toString()
136189
)
137190
}
191+
192+
@SuppressLint("StringFormatMatches")
193+
private fun shareText() = with(binding) {
194+
val shareMsg = getString(
195+
R.string.share_message,
196+
args.notes.title,
197+
args.notes.description
198+
)
199+
200+
val intent = ShareCompat.IntentBuilder(requireActivity())
201+
.setType("text/plain")
202+
.setText(shareMsg)
203+
.intent
204+
205+
startActivity(Intent.createChooser(intent, null))
206+
}
138207
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
*
3+
* *
4+
* * * MIT License
5+
* * *
6+
* * * Copyright (c) 2020 Spikey Sanju
7+
* * *
8+
* * * Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* * * of this software and associated documentation files (the "Software"), to deal
10+
* * * in the Software without restriction, including without limitation the rights
11+
* * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* * * copies of the Software, and to permit persons to whom the Software is
13+
* * * furnished to do so, subject to the following conditions:
14+
* * *
15+
* * * The above copyright notice and this permission notice shall be included in all
16+
* * * copies or substantial portions of the Software.
17+
* * *
18+
* * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* * * SOFTWARE.
25+
* *
26+
*
27+
*
28+
*/
29+
30+
package thecodemonks.org.nottzapp.ui.dialog
31+
32+
import android.os.Bundle
33+
import android.view.LayoutInflater
34+
import android.view.View
35+
import android.view.ViewGroup
36+
import android.view.WindowManager
37+
import androidx.navigation.fragment.navArgs
38+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
39+
import thecodemonks.org.nottzapp.databinding.ErrorDialogLayoutBinding
40+
41+
class ErrorDialog : BottomSheetDialogFragment() {
42+
private var _binding: ErrorDialogLayoutBinding? = null
43+
private val binding get() = _binding!!
44+
private val args: ErrorDialogArgs by navArgs()
45+
override fun onCreateView(
46+
inflater: LayoutInflater,
47+
container: ViewGroup?,
48+
savedInstanceState: Bundle?
49+
): View {
50+
_binding = ErrorDialogLayoutBinding.inflate(inflater, container, false)
51+
return binding.root
52+
}
53+
54+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
55+
super.onViewCreated(view, savedInstanceState)
56+
57+
binding.run {
58+
dialogTitle.text = args.title
59+
dialogMessage.text = args.message
60+
dialogButtonOk.setOnClickListener { dialog?.dismiss() }
61+
}
62+
}
63+
64+
override fun onStart() {
65+
super.onStart()
66+
dialog?.window?.setLayout(
67+
WindowManager.LayoutParams.MATCH_PARENT,
68+
WindowManager.LayoutParams.MATCH_PARENT
69+
)
70+
}
71+
72+
override fun onDestroy() {
73+
super.onDestroy()
74+
_binding = null
75+
}
76+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
*
3+
* *
4+
* * * MIT License
5+
* * *
6+
* * * Copyright (c) 2020 Spikey Sanju
7+
* * *
8+
* * * Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* * * of this software and associated documentation files (the "Software"), to deal
10+
* * * in the Software without restriction, including without limitation the rights
11+
* * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* * * copies of the Software, and to permit persons to whom the Software is
13+
* * * furnished to do so, subject to the following conditions:
14+
* * *
15+
* * * The above copyright notice and this permission notice shall be included in all
16+
* * * copies or substantial portions of the Software.
17+
* * *
18+
* * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* * * SOFTWARE.
25+
* *
26+
*
27+
*
28+
*/
29+
30+
package thecodemonks.org.nottzapp.utils
31+
32+
import android.app.Activity
33+
import android.content.ContentValues
34+
import android.graphics.Bitmap
35+
import android.net.Uri
36+
import android.os.Build
37+
import android.os.Environment
38+
import android.provider.MediaStore
39+
40+
@JvmField
41+
val DEFAULT_FILENAME = "${"Notzz" + System.currentTimeMillis()}.png"
42+
43+
fun saveBitmap(activity: Activity, bitmap: Bitmap, filename: String = DEFAULT_FILENAME): Uri? {
44+
val contentValues = ContentValues().apply {
45+
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
46+
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
47+
48+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
49+
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
50+
}
51+
}
52+
53+
val contentResolver = activity.contentResolver
54+
55+
val imageUri: Uri? = contentResolver.insert(
56+
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
57+
contentValues
58+
)
59+
60+
return imageUri.also {
61+
val fileOutputStream = imageUri?.let { contentResolver.openOutputStream(it) }
62+
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream)
63+
fileOutputStream?.close()
64+
}
65+
}

app/src/main/java/thecodemonks/org/nottzapp/utils/ViewExt.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ fun View.show() {
3838
fun View.hide() {
3939
visibility = View.GONE
4040
}
41+
42+
fun View.showOrHide(isVisible: Boolean) = if (isVisible) show() else hide()

app/src/main/res/drawable/ic_baseline_add.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@
3030
<vector xmlns:android="http://schemas.android.com/apk/res/android"
3131
android:width="24dp"
3232
android:height="24dp"
33-
android:tint="#FFFFFF"
33+
android:tint="@color/white"
3434
android:viewportWidth="24"
3535
android:viewportHeight="24">
3636
<path
37-
android:fillColor="@android:color/white"
37+
android:fillColor="@color/white"
3838
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
3939
</vector>

0 commit comments

Comments
 (0)