diff --git a/app/src/main/java/org/digitalcampus/oppia/activity/OfflineCourseImportActivity.java b/app/src/main/java/org/digitalcampus/oppia/activity/OfflineCourseImportActivity.java index f97d4e0b5..fd7416b3e 100644 --- a/app/src/main/java/org/digitalcampus/oppia/activity/OfflineCourseImportActivity.java +++ b/app/src/main/java/org/digitalcampus/oppia/activity/OfflineCourseImportActivity.java @@ -1,14 +1,12 @@ package org.digitalcampus.oppia.activity; -import static android.Manifest.permission.READ_EXTERNAL_STORAGE; - import android.app.Activity; import android.app.AlertDialog; import android.content.ClipData; import android.content.Intent; -import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.provider.OpenableColumns; @@ -19,13 +17,13 @@ import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; +import androidx.annotation.NonNull; import org.apache.commons.io.IOUtils; import org.digitalcampus.mobile.learning.R; import org.digitalcampus.mobile.learning.databinding.ActivityOfflineCourseImportBinding; import org.digitalcampus.oppia.adapter.OfflineCourseImportAdapter; +import org.digitalcampus.oppia.application.PermissionsManager; import org.digitalcampus.oppia.exception.CourseInstallException; import org.digitalcampus.oppia.listener.InstallCourseListener; import org.digitalcampus.oppia.listener.OnRemoveButtonClickListener; @@ -55,7 +53,6 @@ public class OfflineCourseImportActivity extends AppActivity implements InstallCourseListener, ScanMediaListener, OnRemoveButtonClickListener { - private static final int REQUEST_PERMISSION_READ_EXTERNAL_STORAGE = 1; private ActivityOfflineCourseImportBinding binding; private OfflineCourseImportAdapter adapter; private boolean coursesImported = false; @@ -104,8 +101,10 @@ public boolean onOptionsItemSelected(MenuItem item) { } private void launchFileExplorer() { - if (ContextCompat.checkSelfPermission(this, READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_READ_EXTERNAL_STORAGE); + final List notGrantedPerms = PermissionsManager.filterNotGrantedPermissions( + OfflineCourseImportActivity.this, PermissionsManager.OFFLINE_COURSE_IMPORT_PERMISSIONS_REQUIRED); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && !notGrantedPerms.isEmpty()) { + PermissionsManager.requestPermissions(OfflineCourseImportActivity.this, notGrantedPerms); } else { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); @@ -115,6 +114,12 @@ private void launchFileExplorer() { } } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + PermissionsManager.onRequestPermissionsResult(this, requestCode, permissions, grantResults); + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + private final ActivityResultLauncher someActivityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { diff --git a/app/src/main/java/org/digitalcampus/oppia/application/PermissionsManager.java b/app/src/main/java/org/digitalcampus/oppia/application/PermissionsManager.java index b047db81d..6f9866658 100644 --- a/app/src/main/java/org/digitalcampus/oppia/application/PermissionsManager.java +++ b/app/src/main/java/org/digitalcampus/oppia/application/PermissionsManager.java @@ -19,24 +19,29 @@ import android.Manifest; import android.app.Activity; +import android.app.AlertDialog; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Build; - -import androidx.annotation.NonNull; -import androidx.preference.PreferenceManager; - +import android.provider.Settings; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.preference.PreferenceManager; + import org.digitalcampus.mobile.learning.R; import org.digitalcampus.mobile.learning.databinding.ViewPermissionsExplanationBinding; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; public class PermissionsManager { @@ -59,6 +64,10 @@ public class PermissionsManager { Manifest.permission.POST_NOTIFICATIONS ); + public static final List OFFLINE_COURSE_IMPORT_PERMISSIONS_REQUIRED = Arrays.asList( + Manifest.permission.READ_EXTERNAL_STORAGE + ); + public static final int STARTUP_PERMISSIONS = 1; public static final int BLUETOOTH_PERMISSIONS = 2; public static final int STORAGE_PERMISSIONS = 3; @@ -76,6 +85,14 @@ private static void setAsked(SharedPreferences prefs, String permission) { prefs.edit().putBoolean(permission + "_asked", true).apply(); } + private static boolean isPermissionDeniedOnce(SharedPreferences prefs, String permission) { + return prefs.getBoolean(permission + "_denied", false); + } + + private static void setDenied(SharedPreferences prefs, String permission) { + prefs.edit().putBoolean(permission + "_denied", true).apply(); + } + public static boolean checkPermissionsAndInform(final Activity act, int perms) { ViewGroup container = act.findViewById(R.id.permissions_explanation); return checkPermissionsAndInform(act, perms, container); @@ -151,6 +168,10 @@ private static void showPermissionDescriptions(Context ctx, View container, fina } } + public static void requestPermission(final Activity act, String permission) { + requestPermissions(act, Collections.singletonList(permission)); + } + public static void requestPermissions(final Activity act, List permissions) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { act.requestPermissions(permissions.toArray(new String[0]), PERMISSIONS_REQUEST); @@ -203,15 +224,39 @@ public static boolean onRequestPermissionsResult(Context ctx, int requestCode, S for (int i = 0; i < permissions.length; i++) { setAsked(prefs, permissions[i]); - if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { + if (grantResults.length > 0 && grantResults[i] == PackageManager.PERMISSION_GRANTED) { Log.d("Permissions", "Permission Granted: " + permissions[i]); permissionsGranted++; - } else if (grantResults[i] == PackageManager.PERMISSION_DENIED) { + } else { Log.d("Permissions", "Permission Denied: " + permissions[i]); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) ctx, permissions[i])) { + setDenied(prefs, permissions[i]); + } else if (isPermissionDeniedOnce(prefs, permissions[i])) { + showPermissionDeniedDialog(ctx); + } + } } } } return permissions.length == permissionsGranted; } + public static void showPermissionDeniedDialog(Context context) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(context.getString(R.string.permissions_denied)); + builder.setMessage(context.getString(R.string.permissions_not_askable_message)); + builder.setPositiveButton(context.getString(R.string.app_settings), (dialog, which) -> PermissionsManager.openAppSettings(context)); + builder.setNegativeButton(context.getString(R.string.cancel), (dialog, which) -> {}); + + builder.create().show(); + } + + public static void openAppSettings(Context context) { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", context.getPackageName(), null); + intent.setData(uri); + context.startActivity(intent); + } + } diff --git a/app/src/main/java/org/digitalcampus/oppia/utils/storage/FileUtils.java b/app/src/main/java/org/digitalcampus/oppia/utils/storage/FileUtils.java index 3e0dc3a2d..aa8741245 100644 --- a/app/src/main/java/org/digitalcampus/oppia/utils/storage/FileUtils.java +++ b/app/src/main/java/org/digitalcampus/oppia/utils/storage/FileUtils.java @@ -87,11 +87,18 @@ public static void unzipFiles(Context context, String srcDirectory, String srcFi long availableStorage = Storage.getAvailableStorageSize(context); long uncompressedSize = 0; - try (ZipFile zipFile = new ZipFile(sourceFile)) { - Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); - uncompressedSize += zipEntry.getSize(); + try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(sourceFile))) { + byte[] buffer = new byte[4096]; + ZipEntry zipEntry; + + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + if (!zipEntry.isDirectory()) { + int bytesRead; + while ((bytesRead = zipInputStream.read(buffer)) != -1) { + uncompressedSize += bytesRead; + } + } + zipInputStream.closeEntry(); } } catch (IOException e) { e.printStackTrace(); diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index afc3448fe..666ff225e 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -280,7 +280,7 @@ Error precargando cuentas de usuario desde fichero CSV Nombre de usuario o email no encontrado\nInténtelo de nuevo Sin título - h + Precargadas %1$d nueva(s) cuenta(s) de usuario. Genial! Ha descargado todo el contenido multimedia de sus cursos en el dispositivo. Ordenar por @@ -305,6 +305,8 @@ La app necesita permisos adicionales para buscar otros dispositivos Bluetooth. acceder a la ubicación En las últimas versiones de Android se necesita este permiso para poder enlazar otros dispositivos con Bluetooth + Permiso denegado + Ajustes de la app Permitir cierre de sesión desde el menú principal Registro diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1a9de99e9..505c4b0a9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -368,6 +368,7 @@ needs the following permissions to function properly: Read and modify contents of the SD card Permissions needed + Permission denied &appName; needs access to the SD card to be able to store the downloaded media contents for the courses read phone state and identity &appName; needs access to the phone identity to get information about your device model and Android version @@ -377,6 +378,7 @@ You see this message because permissions were prompted before and you denied them and checked \"Don\'t ask again\" The app needs some additional permissions to scan for Bluetooth devices. Your API key expired. Please, log in again. + App Settings Privacy and Your Data Privacy policy