/*
 *  This file is part of SWADroid.
 *
 *  Copyright (C) 2010 Juan Miguel Boyero Corral <juanmi1982@gmail.com>
 *
 *  SWADroid is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  SWADroid is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with SWADroid.  If not, see <http://www.gnu.org/licenses/>.
 */
package es.ugr.swad.swadroid.modules.downloads;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import es.ugr.swad.swadroid.Constants;
import es.ugr.swad.swadroid.R;
import es.ugr.swad.swadroid.gui.FontManager;
import es.ugr.swad.swadroid.gui.MenuActivity;
import es.ugr.swad.swadroid.gui.ProgressScreen;
import es.ugr.swad.swadroid.model.Group;
import es.ugr.swad.swadroid.model.GroupType;
import es.ugr.swad.swadroid.modules.courses.Courses;
import es.ugr.swad.swadroid.modules.groups.GroupTypes;
import es.ugr.swad.swadroid.modules.groups.Groups;
import es.ugr.swad.swadroid.modules.login.Login;
import es.ugr.swad.swadroid.modules.marks.GetMarks;
import es.ugr.swad.swadroid.modules.marks.Marks;
import es.ugr.swad.swadroid.utils.Utils;

/**
 * Activity to navigate through the directory tree of documents and to manage
 * the downloads of documents
 *
 * @author Helena Rodriguez Gijon <hrgijon@gmail.com>
 * @author Juan Miguel Boyero Corral <juanmi1982@gmail.com>
 */
public class DownloadsManager extends MenuActivity {
    /**
     * Class that contains the directory tree and gives information of each
     * level
     */
    private DirectoryNavigator navigator;

    /**
     * Specifies whether to display the documents or the shared area of the
     * subject 1 specifies documents area, 2 specifies shared area,
     * 3 specifies marks area
     */
    private int downloadsAreaCode = 0;
    /**
     * Specifies chosen group to show its documents
     * 0 -
     */
    private long chosenGroupCode = 0;
    /**
     * String that contains the xml files recevied from the web service
     */
    private String tree = null;

    /**
     * Downloads tag name for Logcat
     */
    private static final String TAG = Constants.APP_TAG + " Downloads";

    /**
     * List of group of the selected course to which the user belongs
     */
    private List<Group> myGroups;

    /**
     * Indicates if the groups has been requested
     */
    private boolean groupsRequested = false;

    /**
     * Indicates whether the refresh button was pressed
     */
    private boolean refresh = false;

    private TextView messageText;
    private GridView grid;
    private TextView currentPathText;
    private ProgressScreen mProgressScreen;

    private static Typeface iconFont;

    private String chosenNodeName = null;


    /**
     * fileSize stores the size of the last file name chosen to be downloaded
     */
    private long fileSize = 0;

    /**
     * Indicates the selected position in the groups spinner
     * by default the whole course is selected
     */
    private int groupPosition = 0;
    /**
     * Indicates if the menu no connection is visible
     */
    private boolean messageView = false;
    /**
     * Indicates that the current state should be saved in case the activity is brought to background
     */
    private boolean saveState = false;

    /**
     * Indicates if the state before the activity was brought to background has o not connection
     */
    private boolean previousConnection = false;

    private void init() {
        mProgressScreen.show();

        List<Group> allGroups = dbHelper.getGroups(Courses.getSelectedCourseCode());
        int nGroups = allGroups.size();

        if (!saveState) {
            if (nGroups != 0 || groupsRequested) { //groupsRequested is used to avoid continue requests of groups on courses that have not any group.
                myGroups = getFilteredGroups(); //only groups where the user is enrolled.
                int nMyGroups = myGroups.size();
                this.loadGroupsSpinner(myGroups);
                // the tree request must be explicit only when there are not any groups(where the user is enrolled), and therefore any Spinner.
                //in case there are groups(where the user is enrolled), it will be a spinner, and the tree request will be automatic made by OnItemSelectedListener
                if (nMyGroups == 0 && tree == null)
                    requestDirectoryTree();
            } else {
                Intent activity = new Intent(this, GroupTypes.class);
                activity.putExtra("courseCode", Courses.getSelectedCourseCode());
                startActivityForResult(activity, Constants.GROUPTYPES_REQUEST_CODE);
            }
        } else {
            myGroups = getFilteredGroups();
            this.loadGroupsSpinner(myGroups);
            this.previousConnection = Utils.connectionAvailable(getApplicationContext());

            if (previousConnection) {
                setMainView();
            } else {
                setNoConnectionView();
            }
        }
    }

    @Override
    protected void onStart() {
        super.onStart();

        // check Android 6 permission
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    Constants.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
        } else {
            init();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState != null) {
            this.saveState = savedInstanceState.getBoolean("saveState", false);
            if (saveState) {
                this.groupsRequested = true;
                this.previousConnection = savedInstanceState.getBoolean("previousConnection", false);
                this.chosenGroupCode = savedInstanceState.getLong("chosenGroupCode", 0);
                this.groupPosition = savedInstanceState.getInt("groupPosition", 0);
                if (previousConnection) {
                    this.tree = savedInstanceState.getString("tree");
                    String path = savedInstanceState.getString("path");
                    this.navigator = new DirectoryNavigator(getApplicationContext(), this.tree);
                    if ("/".equals(path)) {
                        int firstBar = path.indexOf('/', 0);
                        int nextBar = path.indexOf('/', firstBar + 1);
                        while (nextBar != -1) {
                            String dir = path.substring(firstBar + 1, nextBar);
                            this.navigator.goToSubDirectory(dir);
                            firstBar = nextBar;
                            nextBar = path.indexOf('/', firstBar + 1);
                        }
                    }
                }
            }

        }

        setContentView(R.layout.navigation);

        //Get Font Awesome typeface
        iconFont = FontManager.getTypeface(this, FontManager.FONTAWESOME);

        TextView homeButton = (TextView) this
                .findViewById(R.id.home_button);
        homeButton.setOnClickListener((new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (navigator != null) {
                    updateView(navigator.goToRoot());
                }
            }

        }));

        TextView parentButton = (TextView) this
                .findViewById(R.id.parent_button);
        parentButton.setOnClickListener((new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (navigator != null) {
                    updateView(navigator.goToParentDirectory());
                }
            }

        }));

        //Set Font Awesome typeface
        homeButton.setTypeface(iconFont);
        parentButton.setTypeface(iconFont);

        downloadsAreaCode = getIntent().getIntExtra("downloadsAreaCode",
                Constants.DOCUMENTS_AREA_CODE);

        messageText = (TextView) this.findViewById(R.id.messageText);

        grid = (GridView) this.findViewById(R.id.gridview);
        grid.setOnItemClickListener((new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View v,
                                    int position, long id) {

                DirectoryItem node = navigator.getDirectoryItem(position);
                if (node.getFileCode() == -1) //it is a directory therefore navigates into it
                    updateView(navigator.goToSubDirectory(position));
                else { //it is a files therefore gets its information through web service GETFILE
                    chosenNodeName = node.getName();
                    fileSize = node.getSize();
                    File f = new File(Constants.DOWNLOADS_PATH + File.separator + chosenNodeName);

                    //If a student is requesting the marks, gets the marks and call the Marks module
                    if((downloadsAreaCode == Constants.MARKS_AREA_CODE)
                            && (Login.getLoggedUser().getUserRole() == Constants.STUDENT_TYPE_CODE)) {

                        requestGetMarks(node.getFileCode());
                    } else { //Otherwise treat as a regular file
                        if (isDownloaded(f)) {
                            viewFile(f);
                        } else {
                            AlertDialog fileInfoDialog = createFileInfoDialog(node.getName(), node.getSize(), node.getTime(), node.getPublisher(), node.getFileCode(), node.getLicense());
                            fileInfoDialog.show();
                        }
                    }
                }
            }
        }));

        View mDirectoryNavigatorView = findViewById(R.id.directoryNavigator);
        View mProgressScreenView = findViewById(R.id.progress_screen);

        mProgressScreen = new ProgressScreen(mProgressScreenView, mDirectoryNavigatorView,
                getString(R.string.loadingMsg), this);

        setupActionBar();
    }

    @Override
    public void onBackPressed() {
        if ((navigator != null) && (!navigator.isRootDirectory())) {
            //If current directory is not the root, go to parent directory
            updateView(navigator.goToParentDirectory());
        } else {
            //If current directory is the root, exit module
            super.onBackPressed();
        }
    }
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
    	//If back button is pressed, go to parent directory
	    if ((keyCode == KeyEvent.KEYCODE_BACK))
	    {
	    	if (navigator != null) {
	    		//If current directory is not the root, go to parent directory
	    		if (!navigator.isRootDirectory()) {
	                updateView(navigator.goToParentDirectory());
	             //If current directory is the root, exit module
	    		} else {
	    			return super.onKeyDown(keyCode, event);
	    		}
            }
		
		    return true;	
	    }
	
	    return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("saveState", this.saveState);
        if (this.saveState) {
            outState.putBoolean("previousConnection", this.previousConnection);
            outState.putLong("chosenGroupCode", this.chosenGroupCode);
            outState.putInt("groupPosition", this.groupPosition);
            if (this.previousConnection) {
                outState.putString("tree", this.tree);
                outState.putString("path", this.navigator.getPath());
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            Intent activity;

            switch (requestCode) {
                // After get the list of courses, a dialog is launched to choice the
                // course
                case Constants.DIRECTORY_TREE_REQUEST_CODE:
                    tree = data.getStringExtra("tree");

                    if(refresh && !messageView) {
                        refresh();
                        refresh = false;
                    }

                    setMainView();
                    break;
                case Constants.GETFILE_REQUEST_CODE:
                    //if the sd card is not busy, the file can be downloaded
                    if (this.checkMediaAvailability() == 2) {
                        Log.i(TAG, "External storage is available");
                        String url = data.getExtras().getString("link");
                        downloadFile(Constants.DIRECTORY_SWADROID, url, fileSize);
                    } else { //if the sd card is busy, it shows a alert dialog
                        Log.i(TAG, "External storage is NOT available");
                        AlertDialog.Builder builder = new AlertDialog.Builder(this);
                        AlertDialog dialog;
                        builder.setTitle(R.string.sdCardBusyTitle);
                        builder.setMessage(R.string.sdCardBusy);
                        builder.setIcon(R.drawable.ic_dialog_alert);
                        builder.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                dialog.dismiss();
                            }
                        });
                        dialog = builder.create();
                        dialog.show();
                    }
                    break;
                case Constants.GETMARKS_REQUEST_CODE:
                    activity = new Intent(this, Marks.class);
                    activity.putExtra("content", GetMarks.getMarks());
                    startActivityForResult(activity, Constants.MARKS_REQUEST_CODE);
                    break;
                case Constants.GROUPS_REQUEST_CODE:
                    groupsRequested = true;
                    myGroups = getFilteredGroups(); //only groups where the user is enrolled.
                    this.loadGroupsSpinner(myGroups);
                    if (myGroups.size() == 0) {
                        requestDirectoryTree();
                    } else {
                        mProgressScreen.hide();
                    }
                    break;
                case Constants.GROUPTYPES_REQUEST_CODE:
                    activity = new Intent(this, Groups.class);
                    activity.putExtra("courseCode", Courses.getSelectedCourseCode());
                    startActivityForResult(activity, Constants.GROUPS_REQUEST_CODE);
                    break;
            }

        } else {
            if (refresh) {
                refresh = false;
            }

            mProgressScreen.hide();
        }
    }

    /**
     * Having connection is mandatory for the Download Module.
     * Therefore when there is not connection, the grid of nodes is disabled and instead it is showed an info messages
     */
    private void setNoConnectionView() {
        getSupportActionBar().setSubtitle(Courses.getSelectedCourseShortName());

        this.saveState = true;
        this.previousConnection = false;

        mProgressScreen.hide();

        messageView = true;

        messageText.setText(R.string.noConnectionMsg);
        messageText.setVisibility(View.VISIBLE);

        grid.setVisibility(View.GONE);

        this.findViewById(R.id.groupSpinner).setVisibility(View.GONE);
    }

    /**
     *  Documents:
     *
     *  Show a warning on Documents screen if there are no documents currently available.
     *
     *  Marks:
     *
     *  Show a warning on Marks screen if student ID has not yet been verified by the teacher or
     *  there are no marks currently available.
     */
    private void setNoDocumentsView() {
        int resNoDocuments;

        if(downloadsAreaCode == Constants.MARKS_AREA_CODE) {
            if (Login.getCurrentUserRole() == Constants.STUDENT_TYPE_CODE)
                resNoDocuments = R.string.noMarksStudentMsg;
            else {
                resNoDocuments = R.string.noMarksTeacherMsg;
            }
        } else {
            resNoDocuments = R.string.noDocumentsMsg;
        }

        this.findViewById(R.id.groupSpinner).setVisibility(View.GONE);

        getSupportActionBar().setSubtitle(Courses.getSelectedCourseShortName());

        this.saveState = true;
        this.previousConnection = true;

        grid.setVisibility(View.GONE);

        mProgressScreen.hide();

        messageText.setText(resNoDocuments);
        messageText.setVisibility(View.VISIBLE);

        messageView = true;
    }

    /**
     * This method set the grid of nodes visible and paints the directory tree in its root node
     */
    private void setMainView() {
        currentPathText = (TextView) this.findViewById(R.id.path);

        ArrayList<DirectoryItem> items;
        if (!(this.saveState && this.previousConnection)) {
            navigator = new DirectoryNavigator(getApplicationContext(), tree);
            items = navigator
                    .goToRoot();
        } else {
            items = navigator.goToCurrentDirectory();
        }

        if(items.isEmpty()) {
            setNoDocumentsView();
        } else {
            messageView = false;
            messageText.setVisibility(View.GONE);

            mProgressScreen.hide();

            currentPathText.setText(navigator.getPath());
            grid.setAdapter(new NodeAdapter(this, items));
            grid.setVisibility(View.VISIBLE);
        }

        //this is used for the activity restart in case it was taken background
        this.saveState = true;
        this.previousConnection = true;
    }

    /**
     * This method is called after the new file tree is received when the refresh button is pressed
     */
    private void refresh() {
        if (navigator != null) {
            navigator.refresh(tree);
        }
    }

    /**
     * When the user moves into a new directory, this method updates the set of new directories and files and paints it
     */
    private void updateView(ArrayList<DirectoryItem> items) {
        if(!items.isEmpty()) {
            currentPathText.setText(navigator.getPath());
            ((NodeAdapter) grid.getAdapter()).change(items);
        }
    }

    /**
     * Get the list of the groups of the course with a documents zone to whom the user belongs
     */
    private List<Group> getFilteredGroups() {
        List<Group> currentGroups = dbHelper.getGroups(Courses.getSelectedCourseCode());
        //remove groups that do not have a file zone assigned
        int j = 0;
        while (j < currentGroups.size()) {
            if (currentGroups.get(j).getDocumentsArea() != 0 && currentGroups.get(j).isMember())
                ++j;
            else
                currentGroups.remove(j);
        }
        return currentGroups;
    }

    /**
     * If there are not groups to which the user belong in the database, it makes the request
     */
    private void loadGroupsSpinner(List<Group> currentGroups) {

        if (!currentGroups.isEmpty()) { //there are groups in the selected course, therefore the groups spinner should be loaded
            Spinner groupsSpinner = (Spinner) this.findViewById(R.id.groupSpinner);
            groupsSpinner.setVisibility(View.VISIBLE);

            ArrayList<String> spinnerNames = new ArrayList<>(currentGroups.size() + 1);
            spinnerNames.add(getString(R.string.course) + "-" + Courses.getSelectedCourseShortName());
            for (Group g : currentGroups) {
                GroupType gType = dbHelper.getGroupTypeFromGroup(g.getId());
                spinnerNames.add(getString(R.string.group) + "-" + gType.getGroupTypeName() + " " + g.getGroupName());
            }

            ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, spinnerNames);
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            groupsSpinner.setAdapter(adapter);
            groupsSpinner.setOnItemSelectedListener(new onGroupSelectedListener());
            groupsSpinner.setSelection(groupPosition);
        } else {
            this.findViewById(R.id.groupSpinner).setVisibility(View.GONE);

            getSupportActionBar().setSubtitle(Courses.getSelectedCourseShortName());
        }
    }

    /**
     * Listener associated with the spinner. With a new group / course is selected, it is requested the right file tree
     */
    private class onGroupSelectedListener implements OnItemSelectedListener {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position,
                                   long id) {
            //if the position is 0, it is chosen the whole course. Otherwise a group has been chosen
            //position - 0 belongs to the whole course
            long newGroupCode = position == 0 ? 0 : myGroups.get(position - 1).getId();
            if (chosenGroupCode != newGroupCode || tree == null) {
                chosenGroupCode = newGroupCode;
                groupPosition = position;
                requestDirectoryTree();
            }

        }

        @Override
        public void onNothingSelected(AdapterView<?> arg0) {
        }

    }

    /**
     * Method to request files tree to SWAD thought the web services GETDIRECTORYTREE
     */

    private void requestDirectoryTree() {
        Intent activity;
        activity = new Intent(this, DirectoryTreeDownload.class);
        activity.putExtra("treeCode", downloadsAreaCode);
        activity.putExtra("groupCode", (int) chosenGroupCode);
        startActivityForResult(activity, Constants.DIRECTORY_TREE_REQUEST_CODE);
    }

    /**
     * It checks if the external storage is available
     *
     * @return 0 - if external storage can not be read either wrote <br/>
     *         1 - if external storage can only be read <br/>
     *         2 - if external storage can be read and wrote <br/>
     */

    private int checkMediaAvailability() {
        String state = Environment.getExternalStorageState();
        int returnValue;
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            // We can read and write the media
            returnValue = 2;
        } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            // We can only read the media
            returnValue = 1;
        } else {
            // Something else is wrong. It may be one of many other states, but all we need
            //  to know is we can neither read nor write
            returnValue = 0;
        }
        return returnValue;
    }
    
    /**
     * Check if a file is already downloaded or not
     * @param f File to check
     * @return False if not downloaded, True otherwise.
     */
    private boolean isDownloaded(File f) {
        return f.exists() && f.length() == fileSize;
    }
    
    /**
     * Start an intent to view the file
     * @param f The file to view.
     */
    private void viewFile(File f) {
        String mime = URLConnection.guessContentTypeFromName(f.getAbsolutePath());
        String authority = "es.ugr.swad.swadroid.fileprovider";
        Intent viewFileIntent = new Intent();

        try {
            viewFileIntent.setAction(Intent.ACTION_VIEW);
            viewFileIntent.setDataAndType(FileProvider.getUriForFile(this, authority, f), mime);
            viewFileIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            startActivity(viewFileIntent);
        } catch (ActivityNotFoundException e) {
            showDialog(R.string.errorMsgExecutionOperation, R.string.errorNoAppForIntent);
        }
    }
    
    /**
     * it initializes the download the file from the url @a url and stores it in the directory name @directory
     *
     * @param directory - directory where the downloaded file will be stored
     * @param url       - url from which the file is downloaded
     * @param fileSize  - file size of the file. It is used to show the download progress in the notification
     */
    private void downloadFile(String directory, String url, long fileSize) {
        // Check if external storage is available
        int storageState = checkMediaAvailability(); 
        if (storageState == 2) {
            new FileDownloaderAsyncTask(this, this.chosenNodeName)
            .execute(directory, url);
        } else {
            Toast.makeText(this, R.string.sdCardBusyTitle, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Method to request info file identified with @a fileCode to SWAD thought the web services GETFILE
     *
     * @param fileCode file code
     */
    private void requestGetFile(long fileCode) {
        Intent activity;
        activity = new Intent(this, GetFile.class);
        activity.putExtra("fileCode", fileCode);
        startActivityForResult(activity, Constants.GETFILE_REQUEST_CODE);
    }

    /**
     * Method to request info file identified with @a fileCode to SWAD thought the web services GETMARKS
     *
     * @param fileCode file code
     */
    private void requestGetMarks(long fileCode) {
        Intent activity;
        activity = new Intent(this, GetMarks.class);
        activity.putExtra("fileCode", fileCode);
        startActivityForResult(activity, Constants.GETMARKS_REQUEST_CODE);
    }

    /**
     * Method that shows information file and allows its download
     * It has a button to confirm the download. If It is confirmed  getFile will be requested to get the link
     */
    private AlertDialog createFileInfoDialog(String name, long size, long time, String uploader,
            long fileCode, String license) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        AlertDialog dialog;
        final long code = fileCode;
        this.fileSize = size;

        Date d = new Date(time * 1000);
        java.text.DateFormat dateShortFormat = android.text.format.DateFormat.getDateFormat(this);
        java.text.DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(this);

        String uploaderName;
        if (uploader.compareTo("") != 0)
            uploaderName = uploader;
        else
            uploaderName = this.getResources().getString(R.string.unknown);

        StringBuilder message;
        
        Resources res = getResources();
        
        message = new StringBuilder(res.getString(R.string.fileTitle))
                .append(" ")
                .append(name)
                .append("\n")
                .append(getString(R.string.sizeFileTitle))
                .append(" ")
                .append(DownloadFactory.humanReadableByteCount(size, true))
                .append("\n")
                .append(res.getString(R.string.uploaderTitle))
                .append(" ")
                .append(uploaderName)
                .append("\n")
                .append(res.getString(R.string.licenseType))
                .append(" ")
                .append(license)
                .append("\n")
                .append(res.getString(R.string.creationTimeTitle))
                .append(" ")
                .append(dateShortFormat.format(d))
                .append("  ")
                .append(timeFormat.format(d));
        
        builder.setTitle(name);
        builder.setMessage(message);
        builder.setPositiveButton(R.string.downloadFileTitle, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                requestGetFile(code);
            }
        });
        builder.setNegativeButton(R.string.cancelMsg, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.dismiss();
            }
        });

        dialog = builder.create();
        return dialog;
    }

    /**
     * Launches an action when refresh button is pushed.
     * <p/>
     * The listener onClick is set in action_bar.xml
     *
     * @param v Actual view
     */
    public void onRefreshClick(View v) {
        refresh = true;

        Intent activity = new Intent(this, GroupTypes.class);
        activity.putExtra("courseCode", Courses.getSelectedCourseCode());
        startActivityForResult(activity, Constants.GROUPTYPES_REQUEST_CODE);
    }

    /**
     * Set up the {@link android.app.ActionBar}, if the API is available.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void setupActionBar() {
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        switch (downloadsAreaCode) {
            case Constants.DOCUMENTS_AREA_CODE:
                setTitle(R.string.documentsDownloadModuleLabel);
                break;
            case Constants.SHARE_AREA_CODE:
                setTitle(R.string.sharedsDownloadModuleLabel);
                break;
            case Constants.MARKS_AREA_CODE:
                setTitle(R.string.marksModuleLabel);
                break;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        switch (requestCode) {
            case Constants.PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    init();
                } else {
                    setResult(Activity.RESULT_CANCELED);
                    finish();
                }
            }
        }
    }
}
