Amab
f45c57b456
## What changes were proposed in this pull request? * Fix permissions management on document download for Android 10+. Fixes #419 ## How was this patch tested? Manually. Reviewed-on: #421 Co-authored-by: Amab <juanmi1982@gmail.com> Co-committed-by: Amab <juanmi1982@gmail.com>
825 lines
30 KiB
Java
825 lines
30 KiB
Java
/*
|
|
* 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.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 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 androidx.annotation.NonNull;
|
|
import androidx.core.app.ActivityCompat;
|
|
import androidx.core.content.ContextCompat;
|
|
import androidx.core.content.FileProvider;
|
|
|
|
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 permissions
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
|
|
&& 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((v -> {
|
|
if (navigator != null) {
|
|
updateView(navigator.goToRoot());
|
|
}
|
|
}));
|
|
|
|
TextView parentButton = (TextView) this
|
|
.findViewById(R.id.parent_button);
|
|
parentButton.setOnClickListener((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(((parent, v, position, 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.
|
|
*/
|
|
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) {
|
|
super.onRequestPermissionsResult(requestCode, permissions, 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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|