Install apk update from firbase | Android Kotlin - android

I have an android app that not on Google play and I want it to get updates using APK on the server.
This code is working and checks for updates by searching for APK on Firebase storage with greater version than the current build and ending it to download
private fun checkUpdates(){
val storage = FirebaseStorage.getInstance()
val storageRef = storage.reference
val rootRef = storageRef.root
rootRef.listAll().addOnSuccessListener { listResult ->
for (item in listResult.items) {
// item is a StorageReference for a file
val fileName = item.name
val version = fileName.substring(fileName.indexOf("app_v") + 5, fileName.indexOf(".apk"))
if (version.replace(".", "") > BuildConfig.VERSION_CODE.toString().replace(".", "")) {
val builder = AlertDialog.Builder(this)
builder.setMessage(R.string.new_up_av)
builder.setPositiveButton(R.string.install) { _, _ ->
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_WRITE_STORAGE
)
download(version)
} else {
download(version)
}
}
val dialog = builder.create()
dialog.show()
}
else{
Toast.makeText(this, R.string.no_updates, Toast.LENGTH_LONG).show()
}
}
} .addOnFailureListener{
Toast.makeText(this, "ERROR", Toast.LENGTH_SHORT).show()
}
}
The download function is this (notice the comment on line 4):
private fun download(version: String) {
Toast.makeText(this, R.string.download_updates, Toast.LENGTH_SHORT).show()
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadUri = Uri.parse(//url for apk here.)
val request = DownloadManager.Request(downloadUri)
.setTitle(getString(R.string.download_updates))
.setDescription(getString(R.string.app_name))
val downloadId = downloadManager.enqueue(request)
// Check if the download was successful
if (downloadId == -1L) {
// Download failed, show an error message
Toast.makeText(this, "download failed", Toast.LENGTH_SHORT).show()
return
}
// Listen for the download to complete and install the update
val downloadReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
if (downloadId == id) {
installApk(context, "app_v$version.apk")
}
}
}
registerReceiver(downloadReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}
on line 4 i verified it works manually and the problems is showing also with sure-working APKs like WhatsApp.
the installApk function:
fun installApk(context: Context, fileName: String) {
val file = File(Environment.getExternalStorageDirectory().toString() + "/Download/" + filename)
val fileUri = FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(fileUri, "application/vnd.android.package-archive")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
}
provider in Manifest:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android: resource="#xml/provider_paths" />
</provider>
provider_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="downloads" path="Download/" />
</paths>
The problem: seems like its downloading, starting to install, and than shows an error occurred while parsing the package message. no logs.
(It works manually)
I have tried many other ways buts I couldn't figure what cussing the problem.
If someone fammiliar with intalling apk programmatically I looking for help.

Related

android - download base64 encoded pdf file and open it

I'm trying to find a solution to simply download a base64 encoded pdf file of a webservice and open it with an preinstalled pdf viewer. My application targets Android R. I tried something like this but I dont't want a picker to open.
This is my code so far. It is just downloading the file and converts it to a bytearray. The next step should by saving the file and opening it.
lifecycleScope.launch {
withContext(Dispatchers.IO) {
try {
Snackbar.make(binding.root, getString(R.string.load_document_started), Snackbar.LENGTH_LONG).show()
val documentData = DocumentDao().get(document.id, montageOrder)
val docAsByte = Base64.decode(documentData.data, Base64.DEFAULT)
val currentDateString = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val fileName = document.documentType.
lowercase()
.replace("ä", "ae")
.replace("ü", "ue")
.replace("ö", "oe") +
"_" + currentDateString
val file = File(requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName)
val fileStream = FileOutputStream(file)
fileStream.write(docAsByte)
fileStream.close()
val target = Intent(Intent.ACTION_VIEW)
target.setDataAndType(Uri.fromFile(file), "application/pdf")
target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
val intent = Intent.createChooser(target, "Yolo");
startActivity(intent)
} catch (e: Exception) {
Log.e(TAG, "Dokument konnte nicht geladen werden: " + e.message, e)
Snackbar.make(binding.root, getString(R.string.exception_could_not_load_document), Snackbar.LENGTH_LONG).show()
}
}
}
This results in a FileUriExposedException
Another aproach was using the SAF
lateinit var docAsByte : ByteArray
private val createFileLauncher = registerForActivityResult(CreatePdfDocument()) { uri ->
lifecycleScope.launch {
withContext(Dispatchers.IO) {
val stream = requireContext().contentResolver.openOutputStream(uri)
stream?.write(docAsByte)
stream?.close()
val target = Intent(Intent.ACTION_VIEW)
target.setDataAndType(uri, "application/pdf")
target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
startActivity(target)
}
}
}
private fun setGui() {
_binding?.lvDocuments?.adapter = DocumentAdapter(requireContext(), montageOrder.documents)
_binding?.lvDocuments?.setOnItemClickListener { parent, _, position, _ ->
val document : Document = parent.getItemAtPosition(position) as Document
lifecycleScope.launch {
withContext(Dispatchers.IO) {
try {
val currentDateString = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val fileName = document.documentType.
lowercase()
.replace("ä", "ae")
.replace("ü", "ue")
.replace("ö", "oe") +
"_" +
montageOrder.orderNumber +
"_" +
currentDateString +
".pdf"
Snackbar.make(binding.root, getString(R.string.load_document_started), Snackbar.LENGTH_LONG).show()
val documentData = DocumentDao().get(document.id, montageOrder)
docAsByte = Base64.decode(documentData.data, Base64.DEFAULT)
createFileLauncher.launch(fileName)
} catch (e: Exception) {
Log.e(TAG, "Dokument konnte nicht geladen werden: " + e.message, e)
Snackbar.make(binding.root, getString(R.string.exception_could_not_load_document), Snackbar.LENGTH_LONG).show()
}
}
}
}
}
Everything works fine except for opening. But if I open the pdf via file explorer it works.
Found a thread online and solved it this way: https://www.py4u.net/discuss/614761
Add provider_paths.xml to xml resource folder
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
In your manifest add a FileProvider:
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>
Prepare to download files to any directory your app owns, such as getFilesDir(), getExternalFilesDir(), getCacheDir() or getExternalCacheDir().
val privateDir = context.getFilesDir()
Download file taking its progress into account (DIY):
val downloadedFile = myFancyMethodToDownloadToAnyDir(url, privateDir, fileName)
Copy it to Downloads folder:
private val DOWNLOAD_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val finalUri : Uri? = copyFileToDownloads(context, downloadedFile)
fun copyFileToDownloads(context: Context, downloadedFile: File): Uri? {
val resolver = context.contentResolver
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, getName(downloadedFile))
put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(downloadedFile))
put(MediaStore.MediaColumns.SIZE, getFileSize(downloadedFile))
}
resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
} else {
val authority = "${context.packageName}.provider"
val destinyFile = File(DOWNLOAD_DIR, getName(downloadedFile))
FileProvider.getUriForFile(context, authority, destinyFile)
}?.also { downloadedUri ->
resolver.openOutputStream(downloadedUri).use { outputStream ->
val brr = ByteArray(1024)
var len: Int
val bufferedInputStream = BufferedInputStream(FileInputStream(downloadedFile.absoluteFile))
while ((bufferedInputStream.read(brr, 0, brr.size).also { len = it }) != -1) {
outputStream?.write(brr, 0, len)
}
outputStream?.flush()
bufferedInputStream.close()
}
}
Once in download folder you can open file from app like this:
val authority = "${context.packageName}.provider"
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(finalUri, getMimeTypeForUri(finalUri))
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
} else {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
try {
context.startActivity(Intent.createChooser(intent, chooseAppToOpenWith))
} catch (e: Exception) {
Toast.makeText(context, "Error opening file", Toast.LENGTH_LONG).show()
}
//Kitkat or above
fun getMimeTypeForUri(context: Context, finalUri: Uri) : String =
DocumentFile.fromSingleUri(context, finalUri)?.type ?: "application/octet-stream"
//Just in case this is for Android 4.3 or below
fun getMimeTypeForFile(finalFile: File) : String =
DocumentFile.fromFile(it)?.type ?: "application/octet-stream"

Export Room database and attach to email Android Kotlin

I have the following code below for exporting a room database and then attaching it to an email. Currently the user first has to choose where they want the data saved before it can be attached.
Is there a way that I can do this without first having to ask the user where to save the database?
Here is my code:
fun exportDatabase() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.type = "*/*" // this line is a must when using ACTION_CREATE_DOCUMENT
startActivityForResult(
intent,
DATABASE_EXPORT_CODE
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
DATABASE_EXPORT_CODE -> {
val userChosenUri = data?.data
val inStream = getDatabasePath("app_database").inputStream()
val outStream = userChosenUri?.let { contentResolver.openOutputStream(it) }
inStream.use { input ->
outStream.use { output ->
output?.let { input.copyTo(it) }
Toast.makeText(this, "Data exported successfully", Toast.LENGTH_LONG).show()
val emailIntent = Intent(Intent.ACTION_SEND)
//Set type to email
emailIntent.type = "vnd.android.cursor.dir/email"
var toEmail: String = "whatever#gmail.com"
emailIntent.putExtra(Intent.EXTRA_EMAIL, toEmail)
emailIntent.putExtra(Intent.EXTRA_STREAM, userChosenUri)
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Data for Training Log")
startActivity(Intent.createChooser(emailIntent, "Send Email"))
}
}
}
else ->
Log.d("D001", "onActivityResult: unknown request code")
}
}
You need to use FileProvider. But FileProvider doesn't support transferring database files directly (Check here).
This can handled with:
Solution 1:
Create a custom FileProvider class that supports copying database files:
class DBFileProvider : FileProvider {
fun getDatabaseURI(c: Context, dbName: String?): Uri? {
val file: File = c.getDatabasePath(dbName)
return getFileUri(c, file)
}
private fun getFileUri(context: Context, file: File): Uri? {
return getUriForFile(context, "com.android.example.provider", file)
}
}
And request the FileProvider in manifest:
<application>
....
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.example.provider"
android:exported="false"
android:enabled="true"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>
And create provider_paths under res\xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="databases"
path="../" />
</paths>
Then to send this database file through the email:
public static void backupDatabase(AppCompatActivity activity) {
Uri uri = new DBFileProvider().getDatabaseURI(activity, "app_database.db");
sendEmail(activity, uri);
}
private fun sendEmail(activity: AppCompatActivity, attachment: Uri) {
val emailIntent = Intent(Intent.ACTION_SEND)
//Set type to email
emailIntent.type = "vnd.android.cursor.dir/email"
val toEmail = "whatever#gmail.com"
emailIntent.putExtra(Intent.EXTRA_EMAIL, toEmail)
emailIntent.putExtra(Intent.EXTRA_STREAM, attachment)
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Data for Training Log")
activity.startActivity(Intent.createChooser(emailIntent, "Send Email"))
}
Solution 2:
Copy the database file to a temp file to a directory supported by FileProvider like filesDir:
Get the database file using getDatabasePath
Copy the database file to a storage directory that is supported by FileProvider
Create the Uri of the new copied file using the FileProvider
fun backupDatabase(activity: AppCompatActivity) {
// Get the database file
val dbFile = activity.getDatabasePath("app_database.db")
try {
// Copy database file to a temp file in (filesDir)
val parent = File(activity.filesDir, "databases_temp")
val file = File(parent, "myDatabase")
dbFile.copyTo(file)
// Get Uri of the copied database file from filesDir to be used in email intent
val uri = getUri(activity.applicationContext, file)
// Send an email
sendEmail(activity, uri)
} catch (e: IOException) {
e.printStackTrace()
}
}
private fun getUri(context: Context, file: File): Uri {
var uri = Uri.fromFile(file)
// Using FileProvider for API >= 24
if (Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(
context,
"com.android.example.provider", file
)
}
return uri
}
Use the same manifest of solution 1. And adjust provider_paths under res\xml with the created temp dir:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="databases_temp"
path="/" />
</paths>
N.B: In both solutions, adjust the package name to yours.

Android Image Share to Another App Where is problem?

Im doing an app and i want to share photo just i saved to phone to another apps like instagram twitter.Im not able to do it and i cant see where the mistake is.Here is my code
`private fun getScreenshot(currentPage: Int){
QuickShot.of(requireActivity().findViewById<ConstraintLayout(currentPage))
.setResultListener(this)
.enableLogging()
.setFilename("screen")
.setPath("Spotibud")
.toJPG()
.save()
}
override fun onQuickShotSuccess(path: String?) {
Log.d(TAG, "onQuickShotSuccess: $path")
shareOnInstagram(path!!)
}
override fun onQuickShotFailed(path: String?, errorMsg: String?) {
Log.d(TAG, "onQuickShotFailed: $errorMsg")
}
private fun shareOnInstagram(path: String){
val stickerAssetUri: Uri = Uri.parse(path)
val intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM,stickerAssetUri)
type = "image/*"
}
startActivity(intent)
}`
and my log when app saves image
2021-02-18 17:28:08.750 16355-16355/com.example.contestifyfirsttry D/Home Fragment: onQuickShotSuccess: /Pictures/Spotibud/screen.jpg
also is there any code how i can see error.try catch not worked
now i found the solution this isnt best practice but im sure it makes you to see what you should do
private fun shareOnInstagram(path: String){
var file : File = File("/storage/emulated/0/Pictures/Spotibud/screen.jpg")
if (file.exists()){
Log.d(TAG, "shareOnInstagram: file exists")
val stickerAssetUri: Uri = Uri.fromFile(file)
val sourceApplication = "com.example.contestifyfirsttry"
val intent = Intent("com.instagram.share.ADD_TO_STORY")
intent.putExtra("source_application", sourceApplication)
intent.type = "image/*"
intent.putExtra("interactive_asset_uri", stickerAssetUri)
intent.putExtra("top_background_color", "#33FF33")
intent.putExtra("bottom_background_color", "#FF00FF")
val activity: Activity? = activity
activity!!.grantUriPermission(
"com.instagram.android", stickerAssetUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
)
if (activity!!.packageManager.resolveActivity(intent, 0) != null) {
activity!!.startActivityForResult(intent, 0)
}
}else{
Log.d(TAG, "shareOnInstagram: file dont exists")
}
}

Android studio image saving to external storage

So I've created a function to save photos to gallery with the name of the application, everything was working just fine, I worked on other stuff in the app (which are not related to this function) then when I wanted to use this function again it stopped working, here is my code
fun saveImage(itemImage: View, activity: Activity) {
var fileName: String
val imageFromView = getBitmapFromView(itemImage)
ByteArrayOutputStream().apply {
Bitmap.createBitmap(imageFromView).compress(Bitmap.CompressFormat.JPEG, 100, this)
fileName = UUID.nameUUIDFromBytes(this.toByteArray()).toString().replace("-", "")
}
val imageFile = File("${Environment.getExternalStorageDirectory().absolutePath}/ChatOut/$fileName.jpg/")
val direct = File("${Environment.getExternalStorageDirectory().absolutePath}/ChatOut/").apply {
if (!exists())
mkdirs()
}
if (!imageFile.exists()) {
File(direct, "$fileName.jpg").apply {
FileOutputStream(this).apply {
Bitmap.createBitmap(imageFromView).compress(Bitmap.CompressFormat.JPEG, 100, this)
flush()
close()
}
}.let {
activity.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
ContentValues().apply {
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATA, it.absolutePath)
}
)
}
Toast.makeText(activity, "saved", Toast.LENGTH_SHORT).show()
} else
Toast.makeText(activity, "Already saved", Toast.LENGTH_SHORT).show()
}
this function checks if an image is already saved or not, if not then it saves it, I am getting a
/storage/emulated/0/ChatOut/0fe4706621ce318fb4e7292e16bcfb17.jpg: open failed: ENOENT (No such file or directory)
I have usage permission in my manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I made sure that the permission is granted as well.
for some reason no matter what I do the mkdirs() returns false, it never succeeds in creating the file or the directory...
Please help, and thank you for your time.
I've solved my problem by applying the following code, in case someone else is having the same problem
fun saveImage(itemImage: View, activity: Activity) {
val fileName: String
val imageFromView = getBitmapFromView(itemImage)
ByteArrayOutputStream().apply {
imageFromView.compress(Bitmap.CompressFormat.JPEG, 100, this)
fileName = UUID.nameUUIDFromBytes(this.toByteArray()).toString().replace("-", "")
}
val imageFile = File("${activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)}/ChatOut/$fileName.jpg/")
if (!imageFile.exists()) {
val contentResolver = ContentValues().apply {
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATA, imageFile.absolutePath)
}
activity.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentResolver).apply {
imageFromView.compress(Bitmap.CompressFormat.JPEG, 100, activity.contentResolver.openOutputStream(this!!))
}
Toast.makeText(activity, "saved", Toast.LENGTH_SHORT).show()
} else
Toast.makeText(activity, "Already saved", Toast.LENGTH_SHORT).show()
}
fun getBitmapFromView(view: View): Bitmap {
return Bitmap.createBitmap(view.width, view.height,Bitmap.Config.ARGB_8888).apply {
Canvas(this).apply {
view.draw(this)
}
}
}

how to download and install an APK file by an intent?

I am making my app to be updated via my own file server instead of Google Play-store. However, it does not work well. After confirming to "update", APK file is downloaded, it is not opened correctly. Please check the demonstration https://youtu.be/qDSGZ9fQ1Oo
class MainActivity : Activity() {
private fun checkUpdate(){
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET,
"https://myserver/release.json",
null,
Response.Listener { response ->
if(response.getInt("version") > versionCode){
val builder = AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog_Alert)
builder.setTitle("Update to v" + response.getString("version") + " ?")
builder.setMessage(response.getString("note"))
builder.setPositiveButton("Yes") { _, _ ->
downloadUpdate(response.getString("version"))
}
builder.setNegativeButton("No") { _, _ ->
showUserInteraction()
}
builder.setCancelable(false)
builder.show()
}else{
showUserInteraction()
}
},
Response.ErrorListener{ _ ->
showUserInteraction()
}
)
requestQueue.add(jsonObjectRequest)
}
private fun downloadUpdate(versionCode: String) {
registerReceiver(onDownloadComplete(), IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
val request = DownloadManager
.Request(Uri.parse("https://myserver/app-release.apk"))
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "myapp_v" + versionCode + ".apk")
downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request)
}
private class onDownloadComplete: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val c = downloadManager.query(DownloadManager.Query().setFilterById(downloadId))
if(c != null){
c.moveToFirst()
val fileUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))
val mFile = File(Uri.parse(fileUri).path!!)
val fileName = mFile.absolutePath
context.unregisterReceiver(this)
val intent = Intent(Intent.ACTION_VIEW)
var contentUri: Uri
if (SDK_VER >= Build.VERSION_CODES.N) {
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
contentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", File(fileName))
}else{
contentUri = Uri.fromFile(File(fileName))
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
intent.setDataAndType(contentUri, "application/vnd.android.package-archive")
startActivity(context, intent, null)
}
}
}
}
May anyone please point out my mistake? Thanks.
You need to add below permission in your manifest.xml file.
If an app uses a targetSdkLevel of 26 or above and prompts the user to install other apps, the manifest file needs to include the REQUEST_INSTALL_PACKAGES permission:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
You can see below link why it's needed
Link1
Link2

Categories

Resources