Android Download Manager updates App download to complete APK installation (compatible with Android 7.0)

Keywords: Android xml FileProvider encoding

Firstly, it adapts the open file mode of Android 7.0

Using FileProvider

Step one:
Register provider in Android Manifest. XML manifest file, because provider is also one of the four components of Android. It can be simply understood as a component that provides data to the outside world. This component is not frequently used in actual development, and the four components can be configured in the manifest file.

<application
   ...>
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.dl.install"
        android:grantUriPermissions="true"
        android:exported="false">
        <!--metadata-->
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

Be careful:

  • Export: Requirements must be false and security exceptions will be reported for true.
  • Grant UriPermissions: true, indicating grant URI temporary access rights.
  • The authorities'component identification, according to the rules of the river and lake, starts with the package name to avoid conflicts with other applications.
Step 2: Specify shared directories
In the configuration file above, android:resource="@xml/file_paths" refers to the current component referring to the file res/xml/file_paths.xml.
We need to create an xml directory under the resource (res) directory and then create a resource file named "file_paths" (the name can be arbitrary, as long as it is consistent with the resource referenced by the provider registered with manifest), which reads as follows:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <paths>
        <external-path path="" name="download"/>
    </paths>
</resources>

The root directory represented by <files-path/>: Context.getFilesDir()
The root directory represented by <external-path/>: Environment.getExternal Storage Directory ()
The root directory represented by <cache-path/>: getCacheDir()

The path = "" in the above code is of special significance. It is the code root directory, which means that you can share any file in the root directory and its subdirectories with other applications.

If you set path to path="pictures", it represents the pictures directory in the root directory (eg:/storage/emulated/0/pictures). If you share files outside the scope of the pictures directory with other applications, it is not possible.

Step 3: Use FileProvider
Now that we've done the above, we can use FileProvider. It will be explained in the comments in the next block of code.



Here's the download and installation logic

First create the Download Manager in the service

public class DownloadService extends Service {
    private DownloadFinishReceiver mReceiver;
    public DownloadService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();

        //Register and download completed broadcasting
        mReceiver = new DownloadFinishReceiver();
        registerReceiver(mReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new DownBinder();
    }

    class DownBinder extends Binder{
        public void startDownload (String downUrl) {
            //Delete existing apk packages
            File apkFile = new File(DownloadService.this.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Little bear.apk");
            if (apkFile.exists()) {
                apkFile.delete();
            }
            //Initialize Download Manager and start downloading
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downUrl));
            File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),"Little bear.apk");
            request.setDestinationUri(Uri.fromFile(file));
            DownloadManager mDownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
            mDownloadManager.enqueue(request);
        }
    }

    //Download completed broadcast
    private class DownloadFinishReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            //Android gets an intent for opening APK files
            Intent intent1 = new Intent(Intent.ACTION_VIEW);
            // Since Activity is not started in the Activity environment, set the following label
            intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            if(Build.VERSION.SDK_INT>=24) { //Is the interpretation version above 7.0?
                //Parametric 1 context, Parametric 2 Provider host address and file shared in configuration file with consistent parameter 3
                Uri apkUri =
                        FileProvider.getUriForFile(DownloadService.this, "com.example.dl.install",
                                new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),"Little bear.apk"));
                //Add this sentence to indicate that the file represented by the Uri is temporarily authorized for the target application
                intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intent1.setDataAndType(apkUri, "application/vnd.android.package-archive");
            }else{
                intent1.setDataAndType(Uri.fromFile(new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),"Little bear.apk")),
                        "application/vnd.android.package-archive");
            }
            DownloadService.this.startActivity(intent1);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mReceiver);
    }
}

Then in anctivity, there is only one button to simulate the binding layout file with service bind.

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn;
    private DownloadService.DownBinder binder;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
           binder = (DownloadService.DownBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

        //Connecting to services
        Intent intent = new Intent(MainActivity.this,DownloadService.class);
        startService(intent);
        bindService(intent,connection,BIND_AUTO_CREATE);

    }

    private void initView() {
        btn = (Button) findViewById(R.id.btn);

        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn:
                binder.startDownload("http://cdn.xiaoxiongyouhao.com/apps/androilas.apk");
                break;
        }
    }
}

Finally, add permission dynamic permission. I did not use dynamic permission here.

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

Below is the next rendering:




Posted by Goofan on Thu, 13 Dec 2018 13:27:23 -0800