Rotation proof songs downloading app with AsyncTask

In this post we are going to make a simple yet beautiful Rotation proof songs downloading app with AsyncTask. What do i mean by Rotation proof? As we know when user rotate the screen our activity is destroyed and recreated and this will cause our downloading to stop if user rotate the screen. This tutorial is a little complicated so i recommend you to read this tutorial first

  1. JSON parsing in android using PHP and MySqli
  2. Working with internal storage – Android 2016

If you are a intermediate and have knowledge of AsyncTask, Fragments and Storage so move forward

Rotation proof songs downloading app with AsyncTask – App Demo

Project structure

project structure for rotation proof songs downloading app with asynctask

strings.xml

Add songs url & songs name to string resource file

<resources>
    <string name="app_name">DownloadAppAsyncTask</string>
    <string-array name="songs_name">
        <item>Khu Te Bar</item>
        <item>Take Your Sandals Off</item>
        <item>Saturday Saturday</item>
        <item>Honedo Batiya</item>
        <item>Don\'t Look At Me</item>
        <item>Proper Patola</item>
        <item>Dj Waley Babu</item>
        <item>Fateh</item>
        <item>Baagi Jatt</item>
        <item>Yaar, 17</item>
        <item>Chull</item>
    </string-array>
    <string-array name="songs_url">
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_008.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_005.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_002.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_009.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_003.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_001.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_004.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_010.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_010.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_007.mp3</item>
        <item>http://h.saavncdn.com/191/SONY_886445480191_01_006.mp3</item>
    </string-array>
</resources>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edittext"
        android:ems="10"/>

    <ProgressBar
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:visibility="gone"
        android:id="@+id/progressbar"
        android:indeterminate="false" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Download"
        android:id="@+id/button"
        android:onClick="downloadSong"/>

    <ListView
        android:id="@+id/listview"
        android:entries="@array/songs_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

activity_main for rotation proof songs downloading app with asynctask

MainActivity.java

in MainActivity.java we will instanciate the NonUiFragment when activity is created for the first time & retain that NonUiFragment when activity is created for subsequent item.

NonUiFragment.java

on onActivityCreated method we will add setRetainInstance(true); which will make this fragment indestructible which means this fragment will be not created again when we rotate the screen.

package com.hackerkernel.downloadappasynctask;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class NonUiFragment extends Fragment {
    public MyTask myTask;
    private Activity activity;

    public NonUiFragment(){

    }

    public void beginTask(String... ars){
        myTask = new MyTask(activity);
        myTask.execute(ars);
    }
    
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        this.activity = activity;
        if (myTask != null){
            myTask.onAttach(activity);
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        if (myTask != null){
            myTask.onDetach();
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        Log.d("HUS","HUS: onCreate");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.d("HUS", "HUS: onCreateView");
        return null;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d("HUS", "HUS: onActivityCreated");
        //Make this fragment indestructible
        setRetainInstance(true);
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d("HUS", "HUS: onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d("HUS", "HUS: onResume");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d("HUS", "HUS: onStop");
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d("HUS", "HUS: onSaveInstanceState");
    }

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        super.onViewStateRestored(savedInstanceState);
        Log.d("HUS", "HUS: onViewStateRestored");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d("HUS", "HUS: onDestroyView");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("HUS", "HUS: onDestroy");
    }
}

MyTask.java

This method will download the songs from the given url and store it External storage

package com.hackerkernel.downloadappasynctask;

import android.app.Activity;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

//Async task to download song
class MyTask extends AsyncTask<String, Integer, Boolean> {
    private int contentlength = -1;
    private int counter = 0;
    private int current = 0;

    private Activity activity;

    public MyTask(Activity activity){
        this.activity = activity;
    }

    public void onAttach(Activity activity){
        this.activity = activity;
    }

    public void onDetach(){
        activity = null;
    }

    @Override
    protected void onPreExecute() {
        if (activity != null){
            ((MainActivity)activity).showProgressbar();
        }
    }

    @Override
    protected Boolean doInBackground(String... params) {
        boolean success = false;
        URL downloadUrl = null;
        HttpURLConnection connection = null;
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        File file = null;

        try {
            downloadUrl = new URL(params[0]);
            connection = (HttpURLConnection) downloadUrl.openConnection();
            contentlength = connection.getContentLength();
            inputStream = connection.getInputStream();
            file = new File(Environment.getExternalStorageDirectory()
                    .getAbsolutePath()
                    + "/" + Uri.parse(params[0]).getLastPathSegment());
            Log.d("", "HUS: " + file.getAbsolutePath());
            fileOutputStream = new FileOutputStream(file);
            int read = -1;
            byte[] buffer = new byte[1024];
            while ((read = inputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, read);
                //Log.d("HUS","HUS: "+read);
                //update counter
                counter += read;

                publishProgress(counter);
            }
            success = true;

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return success;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        if (activity != null){
            current = (int) (((double) values[0] / contentlength) * 100);
            ((MainActivity)activity).updateProgressbar(current);
        }


    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        if (activity != null){
            ((MainActivity)activity).hideProgressbar();
        }
    }
}

AndroidManifest.xml

Added internet & storage permissions to the manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hackerkernel.downloadappasynctask">
    
    <!-- permission -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- permission -->
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        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>

Leave a Reply

Your email address will not be published. Required fields are marked *