Tutorial e exemplos do Android DownloadManager

Nesta sessão vamos explorar a classe android.app.DownloadManager, como usá-la e por que é importante. Veremos questões como os tipos de downloads que você pode fazer com a classe DownloadManager, como fazer as requisições reais, como mostrar o progresso na barra de status via notificação, abrir arquivo baixado e até remover.

O que é o Gerenciador de Downloads?

_O MARKDOWN_HASH725ed1ec7cb22cb0a4b38d3dd7c624c7MARKDOWNHASH é um serviço de sistema que podemos usar para lidar com downloads de longa duração.

Por que baixar?

Não subestime a necessidade de downloads e o quão complexo pode ser. O estado da largura de banda na era atual implica que ainda não somos capazes, pelo menos em muitas partes do mundo, de obter os arquivos que precisamos sob demanda a qualquer momento. Os usuários não deixam a internet ligada o tempo todo, pois isso custa dinheiro e drena a bateria do dispositivo.

Portanto, ser capaz de baixar arquivos e armazená-los localmente é importante. Mas esta não é uma tarefa fácil de implementar corretamente. Especialmente fazê-lo corretamente e com eficiência. No entanto, é uma daquelas tarefas que podem ser absolutamente compartilhadas entre aplicativos. Na maioria das vezes, não há necessidade real de reinventar a roda ao fazer downloads http. Faz sentido ter uma classe simples de usar que possa fazer isso com eficiência e informar nosso aplicativo quando a tarefa estiver concluída.

Ser capaz de baixar dados é uma maneira poderosa de enriquecer nossos aplicativos, pois podemos obter arquivos da Internet que nosso aplicativo pode usar. Se o usuário remover o arquivo, podemos baixá-lo novamente.

Perguntas comuns

Aqui estão algumas das perguntas para nos permitir entender a classe DownloadManager.

Quais tipos de downloads são tratados pelo DownloadManager

Normalmente, existem vários protocolos para comunicação entre dispositivos. E certamente baixar um arquivo é apenas um formulário ou comunicação entre pelo menos dois dispositivos. Um dispositivo fornecendo um arquivo enquanto outro o recebe. Cuidado, o DownloadManager é usado para lidar apenas com downloads HTTP.

DownloadManager é especialmente útil se seus downloads forem de longa duração.

Onde ocorrem os downloads?

Onde em relação a threads. Bem, os downloads definitivamente ocorrerão em segundo plano.

Por que usar o DownloadManager?

Bem, quais são suas vantagens? Pois temos vários. Por exemplo

  1. Como dissemos, os downloads ocorrem em segundo plano. Na verdade, em segundo plano em um aplicativo do sistema. Isso significa que nosso aplicativo não precisa lidar com os downloads manualmente em nosso encadeamento principal, portanto, nossa interface do usuário é sempre responsiva. Este fato é bastante importante. O download de dados é uma das tarefas que os gadgets pessoais mais consomem tempo e recursos. No entanto, é uma daquelas tarefas que realmente tornam os dispositivos tão poderosos quanto são. Então nós temos que fazê-los. Mas temos que fazê-los em um thread em segundo plano e o DownloadManager definitivamente obedece a isso.
  2. Em segundo lugar, as interações HTTP são abstraídas de nós. Não precisamos nos preocupar com vários códigos e mensagens HTTP e possíveis falhas. Na verdade, até mesmo as tentativas são feitas em nosso nome. No entanto, essas novas tentativas podem ser mantidas por meio de alterações no estado de conectividade e reinicializações do sistema. O progresso pode ser mostrado para nós à medida que o download ocorre, tornando-o muito fácil de usar.

Instruções rápidas e trechos do DownloadManager

1. Como baixar vídeos usando a classe DownloadManager

Queremos baixar vídeos usando a classe DownloadManager no android. Queremos dar-lhes nomes apropriados e armazená-los em armazenamento externo.

    public void downloadvideo(String videoURL)
    {
        if(videoURL.contains(".mp4"))
        {
            File directory = new File(Environment.getExternalStorageDirectory()+File.separator+"My Videos");
            directory.mkdirs();
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(videoURL));
            int Number=pref.getFileName();
            request.allowScanningByMediaScanner();
            request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            File root = new File(Environment.getExternalStorageDirectory() + File.separator+"Facebook Videos");
            Uri path = Uri.withAppendedPath(Uri.fromFile(root), "Video-"+Number+".mp4");
            request.setDestinationUri(path);
            DownloadManager dm = (DownloadManager)getActivity().getSystemService(getActivity().DOWNLOAD_SERVICE);
            if(downloadlist.contains(videoURL))
            {
                Toast.makeText(getActivity().getApplicationContext(),"The Video is Already Downloading",Toast.LENGTH_LONG).show();
            }
            else
            {
                downloadlist.add(videoURL);
                dm.enqueue(request);
                Toast.makeText(getActivity().getApplicationContext(),"Downloading Video-"+Number+".mp4",Toast.LENGTH_LONG).show();
                Number++;
                pref.setFileName(Number);
            }

        }
    }

Primeiro, passamos o URL do vídeo como parâmetro. Digamos que queremos baixar apenas arquivos mp4, então fornecemos uma verificação booleana simples para verificar se ele contém mp4. Talvez você possa usar endsWith() se quiser em vez de contains().

Exemplos de gerenciadores de download

Vejamos alguns exemplos do DownloadManager.

Exemplo 1. Baixar arquivo, ver todos os downloads, excluir download

Neste exemplo completo queremos ver como baixar um arquivo da internet usando a classe downloadManager. Em seguida, podemos abrir o donwload clicando na notificação na barra do sistema ou internamente em nosso aplicativo. Além disso, podemos excluir o arquivo, visualizar todos os downloads etc.

Vídeo tutorial

Aqui temos um tutorial em vídeo para este exemplo.

Demonstração

Aqui está a demonstração do aplicativo.

Visão geral do aplicativo

  1. O usuário clica no botão de download.
  2. O download é iniciado.
  3. Enquanto isso, o progresso é mostrado na barra de status como uma notificação. A notificação também inclui o título e a descrição do download.
  4. O usuário é notificado através do texto da barra de status do sistema quando o download é concluído.
  5. Quando o usuário clica na notificação, o arquivo é aberto. Se não for encontrado, uma mensagem de brinde será exibida.
  6. Quando o usuário clica no botão View All, a visualização de todos os downloads é mostrada. Você pode ter alguns outros downloads enfileirados lá, alguns não do seu aplicativo.
  7. Quando o usuário clica no botão ‘Excluir’, nosso download, seja parcial ou completo, é excluído.
(uma). MainActivity.java

MainActivity como você pode imaginar é a nossa atividade de lançador. ele irá derivar do AppCompatActivity. É aqui que escrevemos todo o nosso código. Mas primeiro começamos fazendo várias importações. Esses incluem:

  1. DownloadManager – A classe responsável por nos permitir usar o gerenciador de downloads do sistema.
  2. Intent – Classe responsável por nos permitir abrir a visualização do gerenciador de downloads do sistema.
  3. Uri – Permite analisar um URL de string em um Uri do qual nosso arquivo pode ser baixado.
  4. Ambiente – Permite especificar o diretório público onde nosso arquivo baixado será armazenado.
  5. Toast – Permite mostrar mensagens rápidas.

Como fazer

Vejamos vários howTos.

(uma). Como inicializar o DownloadManager

Como um serviço do sistema, o DownloadManager não é instanciado diretamente. Em vez disso, vamos inicializá-lo usando o método getSystemService() e converter o objeto resultante na classe DownloadManager.

    private void initializeDownloadManager() {
        downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
    }

(b). Como criar uma solicitação de download

Dentro da classe DownloadManager há uma classe interna chamada de classe Request. Podemos usar essa classe para definir nossa solicitação HTTP. Nós fazemos isso convenientemente através do padrão do construtor.

        DownloadManager.Request request=new DownloadManager.Request(Uri.parse("https://raw.githubusercontent.com/Oclemy/SampleJSON/master/spacecrafts/voyager.jpg"));
        request.setTitle("Voyager")
                .setDescription("File is downloading...")
                .setDestinationInExternalFilesDir(this,
                        Environment.DIRECTORY_DOWNLOADS,fileName)
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

Claramente você pode ver que passamos o Uri em nosso construtor Request. Em seguida, defina o título, descrição, destino e visibilidade da notificação.

(c). Como enfileirar um download

Enfileirar um download significa adicioná-lo à fila de download do gerenciador de download. Em seguida, a fila será processada automaticamente pelo sistema. Você enfileira um download usando o método enqueue().

        downLoadId=downloadManager.enqueue(request);

Isso nos retornará um ID de download.

(d). Como remover/excluir arquivo baixado

Bem, você pode remover um arquivo baixado do gerenciador de downloads usando o método remove() da classe DownloadManager. Você passa o id de download.

    private void deleteDownloadedFile(){
        downloadManager.remove(downLoadId);
    }

Aqui está o código fonte completo.

package info.camposha.mrdownloadmanager;

import android.app.DownloadManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import java.io.FileNotFoundException;

public class MainActivity extends AppCompatActivity {

    private DownloadManager downloadManager;
    private String fileName=null;
    private long downLoadId;

    /**
     * Initialize download manager
     */
    private void initializeDownloadManager() {
        downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        fileName="Voyager";
    }

    /**
     * Set the title and desc of this download, to be displayed in notifications.
     */
    private void downloadFile(){
        DownloadManager.Request request=new DownloadManager.Request(Uri.parse("https://raw.githubusercontent.com/Oclemy/SampleJSON/master/spacecrafts/voyager.jpg"));
        request.setTitle("Voyager")
                .setDescription("File is downloading...")
                .setDestinationInExternalFilesDir(this,
                        Environment.DIRECTORY_DOWNLOADS,fileName)
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        //Enqueue the download.The download will start automatically once the download manager is ready
        // to execute it and connectivity is available.
        downLoadId=downloadManager.enqueue(request);
    }

    /**
     * Cancel downloads and remove them from the download manager.
     * If there is a downloaded file, partial or complete, it is deleted.
     */
    private void deleteDownloadedFile(){
        downloadManager.remove(downLoadId);
    }
    private void openOurDownload(){
       //we can open download here
    }

    /**
     * View all downloads in the downloadmanager
     */
    private void viewAllDownloads(){
        Intent intent=new Intent();
        intent.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS);
        startActivity(intent);
    }

    /**
     * Handle button clicks
     * @param view
     */
    public void clickView(View view){
        switch (view.getId()){
            case R.id.downloadBtn:
                downloadFile();
                break;
            case R.id.openDownloadBtn:
                openOurDownload();
                break;
            case R.id.viewDownloadsBtn:
                viewAllDownloads();
                break;
            case R.id.deleteBtn:
                deleteDownloadedFile();
                break;
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initializeDownloadManager();
    }
}
(b). atividade_main.xml

Precisamos de um layout para nossa MainActivity. Aqui está o código.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

android_layout_width="match_parent"
android_layout_height="match_parent"
    android_orientation="vertical"
android_background="#009688"
tools_context=".MainActivity">

<TextView
    android_id="@+id/headerTxt"
    android_layout_width="match_parent"
    android_layout_height="wrap_content"
    android_text="DownloadManager Example"
    android_textAlignment="center"
    android_fontFamily="casual"
    android_textStyle="bold"
    android_textAppearance="@style/TextAppearance.AppCompat.Large"
    android_textColor="@color/white" />

<ImageView
    android_layout_width="match_parent"
    android_layout_height="300dp"
    android_layout_centerHorizontal="true"
    android_src="@drawable/logo"
    android_contentDescription="@string/app_name"/>

<LinearLayout
    android_id="@+id/button_zone"
    android_layout_width="match_parent"
    android_layout_height="wrap_content"
    android_orientation="horizontal"
    android_layout_centerInParent="true">

    <Button
        android_id="@+id/downloadBtn"
        style="?android:attr/button"
        android_layout_width="0dp"
        android_layout_height="wrap_content"
        android_layout_weight="1"
        android_onClick="clickView"
        android_text="Download" />
    <Button
        android_id="@+id/viewDownloadsBtn"
        style="?android:attr/button"
        android_layout_width="0dp"
        android_layout_height="wrap_content"
        android_layout_weight="1"
        android_onClick="clickView"
        android_text="View All" />
</LinearLayout>

    <LinearLayout
        android_layout_width="match_parent"
        android_layout_height="wrap_content"
        android_orientation="horizontal"
        android_layout_centerInParent="true">
    <Button
        android_id="@+id/openDownloadBtn"
        style="?android:attr/button"
        android_layout_width="0dp"
        android_layout_height="wrap_content"
        android_layout_weight="1"
        android_onClick="clickView"
        android_text="Open" />

        <Button
            android_id="@+id/deleteBtn"
            style="?android:attr/button"
            android_layout_width="0dp"
            android_layout_height="wrap_content"
            android_layout_weight="1"
            android_onClick="clickView"
            android_text="Delete" />

</LinearLayout>

</LinearLayout>
(c). AndroidManifest.xml

Em nosso AndroidManifest, precisamos adicionar permissões para conectividade com a Internet, bem como para gravar em armazenamento externo. Isso ocorre porque baixamos nossa imagem da Internet e gravamos em nosso armazenamento externo.

Aqui está meu arquivo AndroidManifest.xml completo:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    package="info.camposha.mrdownloadmanager">

    <uses-permission android_name="android.permission.INTERNET"/>
    <uses-permission android_name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android_allowBackup="true"
        android_icon="@mipmap/ic_launcher"
        android_label="@string/app_name"
        android_roundIcon="@mipmap/ic_launcher_round"
        android_supportsRtl="true"
        android_theme="@style/AppTheme">
        <activity android_name=".MainActivity">
            <intent-filter>
                <action android_name="android.intent.action.MAIN" />

                <category android_name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Download

Aqui estão os recursos de referência:

Não. Localização Ligação
1. GitHub Download direto
2. GitHub Navegar
3. YouTube Tutorial em vídeo
4. YouTube ProgrammingWizards TV Channel

Exemplo 2: Exemplo simples do DownloadManager de Kotlin Android

Um exemplo simples de gerenciador de download Kotlin de código aberto adequado para iniciantes absolutos.

Etapa 1: criar projeto

Comece criando um projeto AndroidStudio vazio.

Etapa 2: dependências

Nenhuma biblioteca especial de terceiros é necessária para este projeto.

Etapa 3: Layout do projeto

Projete seu layout MainActivity adicionando uma visualização de texto em um ConstraintLayout conforme mostrado abaixo:

atividade_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Etapa 4: escrever código

Comece adicionando importações:

import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.opengl.Visibility
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

Crie MainActivity estendendo AppCompatActivity:

class MainActivity : AppCompatActivity() {

Defina um ID de download:

    var downloadid: Long = 0

Substituir o retorno de chamada onCreate():

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

Crie um BroadcastReceiver anônimo e dentro dele substitua o onReceive() da seguinte forma:

        val new = object: BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
                if (id == downloadid) Log.d("DOWNLOAD", "DONE")
            }
        }

Crie um Uri de download:

        val uri = Uri.parse("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")

Inicialize um DownloadRequest:

        val request = DownloadManager.Request(uri).setDescription("DummyFile").setTitle("Dummy").setAllowedOverMetered(true).setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)

Enfileirar o download:

        val location = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
        downloadid = location.enqueue(request)

Registre o BroadcastReceiver:

        registerReceiver(new, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))

Aqui está o código completo:

MainActivity.kt

import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.opengl.Visibility
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

class MainActivity : AppCompatActivity() {

    var downloadid: Long = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val new = object: BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
                if (id == downloadid) Log.d("DOWNLOAD", "DONE")
            }
        }

        val uri = Uri.parse("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")

        val request = DownloadManager.Request(uri).setDescription("DummyFile").setTitle("Dummy").setAllowedOverMetered(true).setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)

        val location = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
        downloadid = location.enqueue(request)

        registerReceiver(new, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
    }
}

Etapa 5: adicionar permissões

No seu AndroidManifest adicione as seguintes permissões:

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

Correr

Copie o código ou baixe-o no link abaixo, construa e execute.

Referência

Seguem os links de referência:

Download Exemplo
Siga autor do código

Exemplo 3: Kotlin Android – Download dinâmico

Neste exemplo você aprende como baixar de qualquer link. O link é fornecido em tempo de execução por meio de um EditText.

Etapa 1: criar projeto

Comece criando um projeto AndroidStudio vazio.

Etapa 2: dependências

Nenhuma dependência de terceiros é necessária para este projeto.

Etapa 3: Layout do projeto

Adicione um EditText e Button no layout da sua atividade principal:

atividade_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/tilLink"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"

        android:hint="Введите ссылку"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etLink"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btnLoad"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="load"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tilLink" />

</androidx.constraintlayout.widget.ConstraintLayout>

Etapa 4: escrever código

Comece adicionando importações ao seu MainActivity.kt da seguinte forma:

import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.view.Gravity
import android.widget.PopupMenu
import android.widget.Toast
import com.google.android.material.textfield.TextInputEditText

Crie a atividade:

class MainActivity : AppCompatActivity() {

Defina campos de instância:

    private var binding: ActivityMainBinding? = null
    private var dm: DownloadManager? = null

Crie uma classe BroadcastReceiver anônima e implemente o método onReceive() da seguinte forma:

    private val broadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action
            if (DownloadManager.ACTION_DOWNLOAD_COMPLETE == action) {
                Toast.makeText(context, "Download Completed", Toast.LENGTH_SHORT).show()
            }
        }
    }

Configurar manipuladores de eventos:

    private fun setupListeners() {
        binding?.btnLoad?.setOnLongClickListener {
            val popup = PopupMenu(this, it)
            popup.inflate(R.menu.popup)
            popup.show()
            download()
            false
        }
    }

Aqui está a função que baixa o item:

    private fun download() {
        dm?.enqueue(
                DownloadManager.Request(Uri.parse(LINK_VIDEO))
                        .setAllowedNetworkTypes(
                                DownloadManager.Request.NETWORK_MOBILE or
                                        DownloadManager.Request.NETWORK_WIFI
                        )
                        .setTitle("Download File.mp4")
                        .setDescription("This is very important file")
                        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
//                        .setDestinationInExternalFilesDir(
//                                applicationContext , Environment.DIRECTORY_DOWNLOADS,   "managerDownload24.mp4"
//                        )
                        .setDestinationInExternalPublicDir(
                                Environment.DIRECTORY_DOWNLOADS,
                                "managerDownload24"
                        )
        )
    }

Cancele o registro do BroadcastReceiver no onCreate():

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(broadcastReceiver)
    }

Aqui está o código completo:

MainActivity.kt

import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.view.Gravity
import android.widget.PopupMenu
import android.widget.Toast
import com.google.android.material.textfield.TextInputEditText
import ru.trinitydigital.downloadmanager.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private var binding: ActivityMainBinding? = null
    private var dm: DownloadManager? = null

    private val broadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action
            if (DownloadManager.ACTION_DOWNLOAD_COMPLETE == action) {
                Toast.makeText(context, "Download Completed", Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding?.root)
        registerReceiver(broadcastReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
        dm = getSystemService(DOWNLOAD_SERVICE) as DownloadManager

        setupListeners()
    }

    private fun setupListeners() {
        binding?.btnLoad?.setOnLongClickListener {
            val popup = PopupMenu(this, it)
            popup.inflate(R.menu.popup)
            popup.show()
            download()
            false
        }
    }

    private fun download() {
        dm?.enqueue(
                DownloadManager.Request(Uri.parse(LINK_VIDEO))
                        .setAllowedNetworkTypes(
                                DownloadManager.Request.NETWORK_MOBILE or
                                        DownloadManager.Request.NETWORK_WIFI
                        )
                        .setTitle("Download File.mp4")
                        .setDescription("This is very important file")
                        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
//                        .setDestinationInExternalFilesDir(
//                                applicationContext , Environment.DIRECTORY_DOWNLOADS,   "managerDownload24.mp4"
//                        )
                        .setDestinationInExternalPublicDir(
                                Environment.DIRECTORY_DOWNLOADS,
                                "managerDownload24"
                        )
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(broadcastReceiver)
    }

    companion object {
        private const val LINK_VIDEO = "https://images.unsplash.com/photo-1569974507005-6dc61f97fb5c?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MXx8anBnfGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60"
    }
}

Manifesto Android

No AndroidManifest.xml adicione as seguintes permissões:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Correr

Copie o código ou baixe-o no link abaixo, construa e execute.

Referência

Seguem os links de referência:

Número Ligação
1. Download Exemplo
2. Siga autor do código

Exemplo 4: Kotlin DownloadManager com permissões de tempo de execução e corrotinas

Aqui está mais um exemplo de DownloadManager escrito em Kotlin. Isso utiliza Kotlin Coroutines. Também envolve a verificação das permissões necessárias em tempo de execução antes do download.

Etapa 1: criar projeto

Comece criando um projeto AndroidStudio vazio.

Etapa 2: dependências

Nenhuma dependência de terceiros é usada.

Etapa 3: Layout do projeto

Crie um layout simples com um botão e uma visualização de texto:

atividade_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerVertical="true"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:padding="12dp"
            android:textColor="@color/black"
            android:textSize="25sp" />

        <Button
            android:id="@+id/download_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:padding="12dp"
            android:text="DOWNLOAD PDF"
            android:textColor="@android:color/white" />

    </LinearLayout>

</RelativeLayout>

Etapa 4: escrever código

Comece adicionando importações:

import android.Manifest
import android.annotation.TargetApi
import android.app.DownloadManager
import android.content.Context
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File

Estenda a AppCompatActivity:`

class MainActivity : AppCompatActivity() {

Em seguida, defina um URL de download como um campo de instância:

    private var imageUrl = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"

Substitua o onCreate():

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

Quando o botão de download é clicado, antes de iniciar o download, verificamos a permissão:

        download_btn.setOnClickListener {
            // After API 23 (Marshmallow) and lower Android 10 you need to ask for permission first before save an image
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                askPermissions()
            } else {
                downloadImage(imageUrl)
            }
        }

Se concedido, iniciamos o download.

Aqui está a função para iniciar o download da imagem:

    private fun downloadImage(url: String) {
        val downloadManager = this.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

        val downloadUri = Uri.parse(url)

        val request = DownloadManager.Request(downloadUri).apply {
            setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
                .setAllowedOverRoaming(false)
                .setTitle(url.substring(url.lastIndexOf("/") + 1))
                .setDescription("abc")
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
                .setDestinationInExternalPublicDir(
                    Environment.DIRECTORY_DOWNLOADS,
                    url.substring(url.lastIndexOf("/") + 1)
                )

        }
        //use when just to download the file with getting status
        //downloadManager.enqueue(request)

        val downloadId = downloadManager.enqueue(request)
        val query = DownloadManager.Query().setFilterById(downloadId)

        lifecycleScope.launchWhenStarted {
            var lastMsg: String = ""
            var downloading = true
            while (downloading) {
                val cursor: Cursor = downloadManager.query(query)
                cursor.moveToFirst()
                if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
                    downloading = false
                }
                val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
                val msg: String? = statusMessage(url, File(Environment.DIRECTORY_DOWNLOADS), status)
                Log.e("DownloadManager", " Status is :$msg")
                if (msg != lastMsg) {
                    withContext(Dispatchers.Main) {
                        // Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
                        text_view.text = msg
                        //Log.e("DownloadManager", "Status is :$msg")
                    }
                    lastMsg = msg ?: ""
                }
                cursor.close()
            }
        }
    }

Aqui está a função que constrói e retorna o status de Download como uma string que pode ser exibida ao usuário:

    private fun statusMessage(url: String, directory: File, status: Int): String? {
        var msg = ""
        msg = when (status) {
            DownloadManager.STATUS_FAILED -> "Download has been failed, please try again"
            DownloadManager.STATUS_PAUSED -> "Paused"
            DownloadManager.STATUS_PENDING -> "Pending"
            DownloadManager.STATUS_RUNNING -> "Downloading..."
            DownloadManager.STATUS_SUCCESSFUL -> "PDF downloaded successfully in $directory" + File.separator + url.substring(
                url.lastIndexOf("/") + 1
            )
            else -> "There's nothing to download"
        }
        return msg
    }

A função a seguir permitirá que você solicite as permissões necessárias antes de prosseguir com o download:

    @TargetApi(Build.VERSION_CODES.M)
    fun askPermissions() {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // Permission is not granted
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
                )
            ) {
                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                AlertDialog.Builder(this)
                    .setTitle("Permission required")
                    .setMessage("Permission required to save photos from the Web.")
                    .setPositiveButton("Allow") { dialog, id ->
                        ActivityCompat.requestPermissions(
                            this,
                            arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                            MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE
                        )
                        finish()
                    }
                    .setNegativeButton("Deny") { dialog, id -> dialog.cancel() }
                    .show()
            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                    MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE
                )
                // MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE is an
                // app-defined int constant. The callback method gets the
                // result of the request.

            }
        } else {
            // Permission has already been granted
            downloadImage(imageUrl)
        }
    }

Em seguida, processe a solicitação de permissão resultando no seguinte retorno de chamada:

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    // permission was granted, yay!
                    // Download the Image
                    downloadImage(imageUrl)
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.

                }
                return
            }
            // Add other 'when' lines to check for other
            // permissions this app might request.
            else -> {
                // Ignore all other requests.
            }
        }
    }

Aqui está o código completo:

MainActivity.kt

import android.Manifest
import android.annotation.TargetApi
import android.app.DownloadManager
import android.content.Context
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File

class MainActivity : AppCompatActivity() {
    private var imageUrl = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    //private var imageUrl = "http://10.0.2.2:8087/getFile"

    //private var imageUrl = "https://www.orimi.com/pdf-test.pdf"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        download_btn.setOnClickListener {
            //http://localhost:8087/getFile
            //downloadImage("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")
            //downloadImage("http://localhost:8087/getFile")
            // After API 23 (Marshmallow) and lower Android 10 you need to ask for permission first before save an image
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                askPermissions()
            } else {
                downloadImage(imageUrl)
            }
        }
    }

    private fun downloadImage(url: String) {
        //val directory = File(Environment.DIRECTORY_PICTURES)

//        if (!directory.exists()) {
//            directory.mkdirs()
//        }

        val downloadManager = this.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

        val downloadUri = Uri.parse(url)

        val request = DownloadManager.Request(downloadUri).apply {
            setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
                .setAllowedOverRoaming(false)
                .setTitle(url.substring(url.lastIndexOf("/") + 1))
                .setDescription("abc")
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
                .setDestinationInExternalPublicDir(
                    Environment.DIRECTORY_DOWNLOADS,
                    url.substring(url.lastIndexOf("/") + 1)
                )

        }
        //use when just to download the file with getting status
        //downloadManager.enqueue(request)

        val downloadId = downloadManager.enqueue(request)
        val query = DownloadManager.Query().setFilterById(downloadId)

        lifecycleScope.launchWhenStarted {
            var lastMsg: String = ""
            var downloading = true
            while (downloading) {
                val cursor: Cursor = downloadManager.query(query)
                cursor.moveToFirst()
                if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
                    downloading = false
                }
                val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
                val msg: String? = statusMessage(url, File(Environment.DIRECTORY_DOWNLOADS), status)
                Log.e("DownloadManager", " Status is :$msg")
                if (msg != lastMsg) {
                    withContext(Dispatchers.Main) {
                        // Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
                        text_view.text = msg
                        //Log.e("DownloadManager", "Status is :$msg")
                    }
                    lastMsg = msg ?: ""
                }
                cursor.close()
            }
        }
    }

    private fun statusMessage(url: String, directory: File, status: Int): String? {
        var msg = ""
        msg = when (status) {
            DownloadManager.STATUS_FAILED -> "Download has been failed, please try again"
            DownloadManager.STATUS_PAUSED -> "Paused"
            DownloadManager.STATUS_PENDING -> "Pending"
            DownloadManager.STATUS_RUNNING -> "Downloading..."
            DownloadManager.STATUS_SUCCESSFUL -> "PDF downloaded successfully in $directory" + File.separator + url.substring(
                url.lastIndexOf("/") + 1
            )
            else -> "There's nothing to download"
        }
        return msg
    }

    @TargetApi(Build.VERSION_CODES.M)
    fun askPermissions() {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // Permission is not granted
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
                )
            ) {
                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                AlertDialog.Builder(this)
                    .setTitle("Permission required")
                    .setMessage("Permission required to save photos from the Web.")
                    .setPositiveButton("Allow") { dialog, id ->
                        ActivityCompat.requestPermissions(
                            this,
                            arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                            MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE
                        )
                        finish()
                    }
                    .setNegativeButton("Deny") { dialog, id -> dialog.cancel() }
                    .show()
            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                    MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE
                )
                // MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE is an
                // app-defined int constant. The callback method gets the
                // result of the request.

            }
        } else {
            // Permission has already been granted
            downloadImage(imageUrl)
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    // permission was granted, yay!
                    // Download the Image
                    downloadImage(imageUrl)
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.

                }
                return
            }
            // Add other 'when' lines to check for other
            // permissions this app might request.
            else -> {
                // Ignore all other requests.
            }
        }
    }

    companion object {
        private const val MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1
    }
}

Correr

Copie o código ou baixe-o no link abaixo, construa e execute.

Referência

Seguem os links de referência:

Download Exemplo
Siga autor do código