1

My goal is to download files with a Service (please, you may suggest a better option) in the background without having to interact with the app such that a download can be triggered (startService) whenever it receives a broadcast that a new text has been copied to clipboard and the text has a downloadable extension.

The implementation below uses PR Downloader(Library) to download the file but the file is not downloaded.

I usually get an Error and i Logged the response.

onError. isServerError = false. isConnectionError = true

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.package.name">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        ...>

        <activity
            android:name=".Activities.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">
            <intent-filter>
                ...
            </intent-filter>
        </activity>

        ...
        <service android:name=".DownloadService" />
    </application>
</manifest>

MainActivity.kt

class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val clipBoard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
            clipBoard.addPrimaryClipChangedListener {
                val primaryClip = clipBoard.primaryClip

                if (primaryClip == null || primaryClip.itemCount > 0 && primaryClip.getItemAt(0).text == null)
                    return@addPrimaryClipChangedListener  
                val serviceIntent = Intent(this, DownloadService::class.java)
                val clip = primaryClip.getItemAt(0).text.toString()
                if (clip.contains(".mp3")) {
                    Log.d("MainActivity","$clip")
                    serviceIntent.putExtra(DOWNLOAD_URL_KEY, clip)
                    serviceIntent.putExtra(DOWNLOAD_FILE_NAME_KEY, getFileName(clip))
                    startService(serviceIntent)
                }
            }
        }
    }

DownloadService.kt

class DownloadService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val url = intent?.getStringExtra(DOWNLOAD_URL_KEY)
        val fileName = intent?.getStringExtra(DOWNLOAD_FILE_NAME_KEY)
        Log("onStartCommand. FileName = $fileName Download URL = $url")

        if (url != null)
            downoadFile(url, fileName)

        return super.onStartCommand(intent, flags, startId)
    }
    fun downoadFile(download_url: String, fileName: String?) {
        val dirPath ="/Music Downloader/$fileName.mp3"
        val downloadId = PRDownloader.download(download_url, dirPath, fileName)
                .build()
                .setOnStartOrResumeListener {

                }
                .setOnPauseListener {}
                .setOnCancelListener {}
                .setOnProgressListener { progress ->

                    Log("Progress = ${((progress.currentBytes / progress.totalBytes) * 100) as Int}%")
                }
                .start(object : OnDownloadListener {
                    override fun onError(error: com.downloader.Error?) {
                        Log("onError. ServerError = ${error?.isServerError}. ConnectionError = ${error?.isConnectionError}")
                    }

                    override fun onDownloadComplete() {
                        Log("onDownloadComplete")
                    }
                })
        Log("dirPath = $dirPath, download_url = $download_url, Download ID = $downloadId")

    }
}

I have also tried using Fetch but same result - file not downloading.

But i noticed that getting and parsing JSON/Xml file worked with VOLLEY (:) I know it's 2018. I'll move to Retrofit, i promise).

Please, what am i doing wrong? How can i download a file using a service.

UPDATE

I Tried running the code in the main activity, and it still didn't work. I am testing with android 8.1 and i guess something is wrong with the library PR-Downloader

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        PRDownloader.initialize(getApplicationContext())
        val download_url = "https://images.pexels.com/photos/658687/pexels-photo-658687.jpeg?cs=srgb&dl=beautiful-bloom-blooming-658687.jpg"
      download(download_url)
}
fun downoad(url: String, fileName: String) {
        val dirPath = "/File Downloader/" + fileName
        val downloadId = PRDownloader.download(url, dirPath, fileName)
                .build()
                .setOnStartOrResumeListener {

                }
                .setOnPauseListener {}
                .setOnCancelListener {}
                .setOnProgressListener { progress ->

                    Log("Progress = ${((progress.currentBytes / progress.totalBytes) * 100) as Int}%")
                }
                .start(object : OnDownloadListener {
                    override fun onError(error: com.downloader.Error?) {
                        Log("onError. isServerError = ${error?.isServerError}. isConnectionError = ${error?.isConnectionError}")
                    }

                    override fun onDownloadComplete() {
                        Log("onDownloadComplete")
                    }
                })
    }
EdgeDev
  • 2,376
  • 2
  • 20
  • 37

2 Answers2

0

Turned out the problem is your download_url , it seems "images.pexels.com" is not allowing connections to their photos. I tested below code (which is a downloader in pure java which i implemented) and I got Connection.refused exceptions. Try your own code with https://html.com/wp-content/uploads/flamingo.jpg or https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg and see what you will get

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    download().execute("https://images.pexels.com/photos/658687/pexels-photo-658687.jpeg",
            Environment.getExternalStorageDirectory().path+"/myFolder/", "pexels-photo-658687.jpeg")
}

class download : AsyncTask<String, String, String>() {
    override fun doInBackground(vararg p0: String?): String? {
        val url = URL(p0[0])
        val buf = ByteArray(1024)
        var byteRead: Int
        val p = File(p0[1])
        p.mkdirs()
        val f = File(p0[1], p0[2])
        f.createNewFile()
        var outStream = BufferedOutputStream(
                FileOutputStream(p0[1] + p0[2]))

        val conn = url.openConnection()
        var `is` = conn.getInputStream()
        byteRead = `is`!!.read(buf)
        while (byteRead != -1) {
            outStream.write(buf, 0, byteRead)
            byteRead = `is`.read(buf)
        }
        `is`.close()
        outStream.close()
        return null;
    }
}
DNA.h
  • 823
  • 8
  • 17
  • The situation is that, i don't want to build a custom download manager from scratch. and the pexels url isn't the issue and their url isn't blocked – EdgeDev Aug 25 '18 at 11:24
0

Use DownloadManager class:

private var dm: DownloadManager? = null
companion object {
        lateinit var receiver: BroadcastReceiver
    }

call this method inside your onCreate:

private fun initbroadCastReceiver() {

        receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                val action = intent.action
                if (DownloadManager.ACTION_DOWNLOAD_COMPLETE == action) {
                    val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)
                    val query = Query()
                    query.setFilterById(enqueue)
                    val c = dm!!.query(query)
                    if (c.moveToFirst()) {
                        val columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS)
                        if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                            isDownload = true
                            downloadImage.setImageResource(R.drawable.icon_download_grey)
                            downloadImage.setBackground(ContextCompat.getDrawable(baseActivity, R.drawable.circle_background_grey))
                            SQLHelper.updateData(bookEntity.id)
                            showToast("Download completed")
                        }
                    }
                }
            }
        }
    }

onClick for Download:

downloadImage.setOnClickListener(View.OnClickListener
        {
            if (bookEntity.isDownload == 0 && isDownload == false) {
                if (Misc.isNetworkAvailable(baseActivity)) {
                    dm = baseActivity.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
                    val request = Request(
                            Uri.parse(EnvironmentConstant.BOOK_DOWNLOAD_URL + bookEntity.id))
                            .setTitle(""Title)
                            .setDestinationInExternalFilesDir(baseActivity,path)
                            .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
                    enqueue = dm!!.enqueue(request)
                } else {
                    showAlertDialog()
                }
            } else {
                alreadyDownloadAlert()
            }
        })
Erselan Khan
  • 692
  • 6
  • 13
  • the problem with android DownloadManager is that downloaded files are deleted once the app is uninstalled from API from android 6.0 – EdgeDev Aug 27 '18 at 14:23
  • actually android creates a folder for downloadManager for particular app and all downloaded files are goes there. But you can change the path of the downloaded files. check this https://stackoverflow.com/questions/16773348/set-custom-folder-android-download-manager – Erselan Khan Aug 27 '18 at 17:29
  • Thanks for the input but check this out -> https://stackoverflow.com/a/26332295/6563022 – EdgeDev Aug 28 '18 at 07:11
  • you should use this method setDestinationInExternalPublicDir – Erselan Khan Aug 28 '18 at 07:49
  • with that you can only use specified folders like Downloads, Music and Pictures. You can't create a separate file – EdgeDev Aug 28 '18 at 19:24