Commit dd65bf2a authored by Bartek Fabiszewski's avatar Bartek Fabiszewski
Browse files

Merge branch 'master' into android_11

parents 9f52f37f 922b76cb
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -17,8 +17,8 @@ android {
        applicationId 'net.fabiszewski.ulogger'
        minSdkVersion 19
        targetSdkVersion 30
        versionCode 301
        versionName '3.1'
        versionCode 302
        versionName '3.2'
    }

    buildTypes {
+45 −32
Original line number Diff line number Diff line
@@ -18,10 +18,10 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.MediaScannerConnection;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
@@ -40,13 +40,9 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import static android.content.ContentResolver.SCHEME_CONTENT;
import static android.content.ContentResolver.SCHEME_FILE;

class ImageHelper {
    private static final String TAG = ImageHelper.class.getSimpleName();
    private static final String MEDIA_ORIENTATION = "orientation";
    private static final String JPEG_MIME = "image/jpg";
    private static final int ORIENTATION_90 = 90;
    private static final int ORIENTATION_180 = 180;
    private static final int ORIENTATION_270 = 270;
@@ -148,22 +144,20 @@ class ImageHelper {
     */
    static Uri createImageUri(@NonNull Context context) {
        ContentValues values = new ContentValues();
        String title = getUniqueTitle(context);
        long timeMillis = System.currentTimeMillis();
        values.put(MediaStore.Images.Media.TITLE, title);
        values.put(MediaStore.Images.Media.DISPLAY_NAME, title);
        values.put(MediaStore.Images.Media.MIME_TYPE, JPEG_MIME);
        values.put(MediaStore.Images.Media.TITLE, getUniqueTitle(context));
        values.put(MediaStore.Images.Media.DATE_ADDED, timeMillis / 1000);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.Images.Media.DATE_TAKEN, timeMillis);
        }
        Uri collection;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.Images.Media.DATE_TAKEN, timeMillis);
            values.put(MediaStore.Images.Media.IS_PENDING, 0);
            collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
        } else {
            collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        }
        return context.getContentResolver().insert(collection, values);
        Uri imageUri = context.getContentResolver().insert(collection, values);
        if (Logger.DEBUG) { Log.d(TAG, "[createImageUri: " + imageUri + "]" ); }
        return imageUri;
    }

    /**
@@ -238,26 +232,32 @@ class ImageHelper {
     * Get file size
     * @param context Context
     * @param uri File URI
     * @return Size or zero if not known
     * @return Size or -1 if not known
     */
    static long getFileSize(@NonNull Context context, @NonNull Uri uri) {
        long fileSize = 0;
        final String scheme = uri.getScheme();
        if (SCHEME_CONTENT.equals(scheme)) {
            try (Cursor cursor = context.getContentResolver()
                    .query(uri, null, null, null, null)) {
                if (cursor != null) {
                    int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
                    cursor.moveToFirst();
                    fileSize = cursor.getLong(sizeIndex);
        final ContentResolver cr = context.getContentResolver();

        String[] projection = {OpenableColumns.SIZE};
        try (Cursor cursor = cr.query(uri, projection, null, null, null)) {
            if (cursor != null && cursor.moveToFirst()) {
                long size = cursor.getInt(0);
                if (size > 0) {
                    if (Logger.DEBUG) { Log.d(TAG, "[getFileSize (db): " + size + "]" ); }
                    return size;
                }

            }
        } else if (SCHEME_FILE.equals(scheme) && uri.getPath() != null) {
            File file = new File(uri.getPath());
            fileSize = file.length();
        } catch (Exception ignored) { }

        try (ParcelFileDescriptor parcelFileDescriptor = cr.openFileDescriptor(uri, "r")) {
            if (parcelFileDescriptor != null) {
                long size = parcelFileDescriptor.getStatSize();
                if (Logger.DEBUG) { Log.d(TAG, "[getFileSize (fd): " + size + "]" ); }
                return size;
            }
        if (Logger.DEBUG) { Log.d(TAG, "[getFileSize (" + scheme + "): " + fileSize + "]" ); }
        return fileSize;
        } catch (IOException ignored) { }

        return -1;
    }

    /**
@@ -273,6 +273,10 @@ class ImageHelper {
        if (fileMime == null) {
            String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());
            fileMime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.toLowerCase(Locale.ROOT));
            if (Logger.DEBUG) { Log.d(TAG, "[getFileMime (ext): " + fileMime + "]" ); }
        } else {
            fileMime = fileMime.replace("jpg", "jpeg");
            if (Logger.DEBUG) { Log.d(TAG, "[getFileMime (cr): " + fileMime + "]" ); }
        }
        return fileMime;
    }
@@ -424,9 +428,14 @@ class ImageHelper {
     * @param uri Image URI
     */
    static void galleryAdd(@NonNull Context context, @NonNull Uri uri) {
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, getUniqueTitle(context));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.Images.Media.IS_PENDING, 0);
        }
        ContentResolver cr = context.getContentResolver();
        String mime = cr.getType(uri);
        MediaScannerConnection.scanFile(context, new String[] {uri.getPath()}, new String[] {mime}, null);
        if (Logger.DEBUG) { Log.d(TAG, "[update " + uri + "]"); }
        cr.update(uri, values, null, null);
    }

    /**
@@ -435,6 +444,10 @@ class ImageHelper {
     * @param uri URI
     */
    static void getPersistablePermission(@NonNull Context context, @NonNull Uri uri) {
        try {
            context.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } catch (SecurityException e) {
            if (Logger.DEBUG) { Log.d(TAG, "[getPersistablePermission failed for " + uri + "]"); }
        }
    }
}
+7 −5
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ import android.graphics.Bitmap;
import android.location.Location;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
@@ -42,6 +41,9 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import static android.app.Activity.RESULT_OK;
import static android.content.Intent.EXTRA_LOCAL_ONLY;
import static android.content.Intent.EXTRA_MIME_TYPES;
import static android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;

public class WaypointFragment extends Fragment implements LoggerTask.LoggerTaskCallback, ImageTask.ImageTaskCallback {

@@ -268,10 +270,8 @@ public class WaypointFragment extends Fragment implements LoggerTask.LoggerTaskC
        if (takePictureIntent.resolveActivity(requireContext().getPackageManager()) != null) {
            photoUri = ImageHelper.createImageUri(requireContext());
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            int flags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION;
            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
            int flags = FLAG_GRANT_WRITE_URI_PERMISSION|FLAG_GRANT_READ_URI_PERMISSION|FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
            takePictureIntent.addFlags(flags);
            }
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }
@@ -384,6 +384,8 @@ public class WaypointFragment extends Fragment implements LoggerTask.LoggerTaskC
        String[] mimeTypes = { "image/jpeg", "image/gif", "image/png", "image/x-ms-bmp" };
        intent.putExtra(EXTRA_MIME_TYPES, mimeTypes);
        intent.putExtra(EXTRA_LOCAL_ONLY, true);
        int flags = FLAG_GRANT_READ_URI_PERMISSION|FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
        intent.addFlags(flags);
        try {
            startActivityForResult(intent, REQUEST_IMAGE_OPEN);
        } catch (ActivityNotFoundException e) {
+7 −4
Original line number Diff line number Diff line
@@ -280,9 +280,12 @@ class WebHelper {
            // text part size
            length += data.length;
            // file size
            long fileSize = ImageHelper.getFileSize(context, uri);
            if (fileSize <= UPLOAD_SIZE_MAX && fileSize > 0) {
                String headers = String.format(MULTIPART_FILE_TEMPLATE, PARAM_IMAGE, fileMime);
                length += headers.getBytes(StandardCharsets.UTF_8).length + delimiter.length;
            length += ImageHelper.getFileSize(context, uri);
                length += fileSize;
            }
            // closing delimiter
            length += delimiter.length + 2;
        }
@@ -305,7 +308,7 @@ class WebHelper {
            return;
        }
        long fileSize = ImageHelper.getFileSize(context, uri);
        if (fileSize > UPLOAD_SIZE_MAX || fileSize == 0) {
        if (fileSize > UPLOAD_SIZE_MAX || fileSize <= 0) {
            if (Logger.DEBUG) { Log.d(TAG, "[Skipping file, wrong size: " + fileSize + "]"); }
            return;
        }
+33 −33
Original line number Diff line number Diff line
@@ -21,35 +21,35 @@
    <string name="submit">Envoyer</string>
    <string name="ok">Ok</string>
    <string name="settings">Réglages</string>
    <string name="pref_username_title">Nom d\'utilisateur</string>
    <string name="pref_username_summary">Nom d\'utilisateur sur le serveur distant</string>
    <string name="pref_username_title">Nom dutilisateur</string>
    <string name="pref_username_summary">Nom dutilisateur sur le serveur distant</string>
    <string name="pref_pass_title">Mot de passe</string>
    <string name="pref_pass_summary">Mot de passe sur le serveur distant</string>
    <string name="pref_host_title">Adresse du serveur</string>
    <string name="pref_host_summary">Adresse racine du serveur distant</string>
    <string name="pref_mintime_title">Temps minimum</string>
    <string name="pref_mintime_summary">Temps minimum entre les mises à jour de localisation</string>
    <string name="pref_mintime_summary">Temps minimum entre chaque enregistrement de position</string>
    <string name="pref_mindistance_title">Distance minimum</string>
    <string name="pref_mindistance_summary">Distance minimum entre les mises à jour de localisation</string>
    <string name="pref_mindistance_summary">Distance minimum entre chaque enregistrement de position</string>
    <string name="pref_minaccuracy_title">Précision minimum</string>
    <string name="pref_minaccuracy_summary">Précision minimum de la position</string>
    <string name="pref_imagesize_title">Taille d\'image</string>
    <string name="pref_imagesize_summary">Largeur/hauteur maximale des images téléchargées en pixels. En cas de sous-échantillonnage, les images redimensionnées seront temporairement stockées dans le dossier d\'application avant d\'être téléverséess. Si vous choisissez l\'option \"taille native\", aucune copie ne sera faite, les fichiers originaux doivent donc être disponibles pendant le téléchargement.</string>
    <string name="pref_imagesize_title">Taille dimage</string>
    <string name="pref_imagesize_summary">Largeur/hauteur maximale des images téléchargées en pixels. En cas de sous-échantillonnage, les images redimensionnées seront temporairement stockées dans le dossier dapplication avant dêtre téléverséess. Si vous choisissez loption \"taille native\", aucune copie ne sera faite, les fichiers originaux doivent donc être disponibles pendant le téléchargement.</string>
    <string name="pref_livesync_title">Synchronisation immédiate</string>
    <string name="pref_livesync_summary">Mise à jour directe de la position sur le serveur. Nécessite un nom d\'utilisateur, un mot de passe et une adresse de serveur valide.</string>
    <string name="pref_livesync_summary">Mise à jour directe de la position sur le serveur. Nécessite un nom dutilisateur, un mot de passe et une adresse de serveur valide.</string>
    <string name="pref_autostart_title">Enregistrement automatique</string>
    <string name="pref_autostart_summary">L\'application enregistrera au démarrage du téléphone</string>
    <string name="pref_autostart_summary">Lapplication enregistrera au démarrage du téléphone</string>
    <string name="pref_external_title">Autoriser les actions externes</string>
    <string name="pref_external_summary">Permet de recevoir des commandes d\'autres applications pour faciliter l\'automatisation et la planification des tâches.</string>
    <string name="pref_external_summary">Permet de recevoir des commandes dautres applications pour faciliter lautomatisation et la planification des tâches.</string>
    <string name="pref_provider_title">Fournisseur de position</string>
    <string name="pref_provider_summary">La localisation peut être fournie par le fournisseur GPS, le réseau ou les deux. Utiliser les deux fournisseurs donnera de meilleurs résultats mais utilisera plus de batterie.</string>
    <string name="pref_provider_summary">Le positionnement peut être fourni par le GPS, le réseau ou les deux. Utiliser les deux fournisseurs donnera de meilleurs résultats mais utilisera plus de batterie.</string>
    <string name="pref_units_title">Unités</string>
    <string name="pref_units_summary">Unités préférées pour afficher le résumé de la piste</string>
    <string name="pref_units_metric">Mètre</string>
    <string name="pref_units_summary">Unités préférées pour afficher le résumé de la trace</string>
    <string name="pref_units_metric">Métrique</string>
    <string name="pref_units_imperial">Impérial</string>
    <string name="pref_units_nautical">Système nautique</string>
    <string name="pref_auto_name_title">Nom par défaut de la trace</string>
    <string name="pref_auto_name_summary" formatted="false">Modèle pour le nouveau nom d\'une trace. Les modèles suivants seront remplacés par les éléments de la date actuelle : %y (année), %m (mois), %d (jour), %H (heure), %M (minute), %S (seconde). Si vous avez besoin d\'utiliser le caractère de pourcentage littéral, utilisez %%.</string>
    <string name="pref_auto_name_summary" formatted="false">Modèle pour le nouveau nom dune trace. Les modèles suivants seront remplacés par les éléments de la date actuelle : %y (année), %m (mois), %d (jour), %H (heure), %M (minute), %S (seconde). Si vous avez besoin dutiliser le caractère de pourcentage littéral, utilisez %%.</string>
    <string name="label_status">Statut</string>
    <string name="label_location">Position</string>
    <string name="label_synchronization">Synchronisation</string>
@@ -57,16 +57,16 @@
    <string name="tracking_started">Enregistrement démarré</string>
    <string name="uploading_started">Le téléversement a débuté</string>
    <string name="nothing_to_synchronize">Rien à synchroniser</string>
    <string name="no_positions">La trace actuelle n\'a pas de position</string>
    <string name="no_positions">La trace actuelle na pas de position</string>
    <string name="warning">Attention</string>
    <string name="notsync_warning">La piste actuelle contient des positions non synchronisées. Définir une nouvelle piste supprimera ces données.</string>
    <string name="clear_warning">Êtes-vous sûr de vouloir supprimer la piste actuelle et toutes ses données de l\'appareil ? Aucune donnée sur le serveur ne sera supprimée.</string>
    <string name="no_track_warning">Merci de d\'abord créer une nouvelle trace</string>
    <string name="notsync_warning">La trace actuelle contient des positions non synchronisées. Définir une nouvelle trace supprimera ces données.</string>
    <string name="clear_warning">Êtes-vous sûr de vouloir supprimer la trace actuelle et toutes ses données de lappareil ? Aucune donnée sur le serveur ne sera supprimée.</string>
    <string name="no_track_warning">Merci de dabord créer une nouvelle trace</string>
    <string name="empty_trackname_warning">Le nom de la trace ne peut pas être vide</string>
    <string name="logger_running_warning">Merci d\'arrêter l\'enregistrement actuel</string>
    <string name="logger_running_warning">Merci darrêter lenregistrement actuel</string>
    <string name="about">A propos</string>
    <string name="about_description">Simple moniteur de position avec suivi en direct et envoi des traces sur son propre <xliff:g id="server_link_open"><![CDATA[<a href=\"https://github.com/bfabiszewski/ulogger-server\">]]></xliff:g>serveur μlogger<xliff:g id="server_link_close"><![CDATA[</a>]]></xliff:g>.</string>
    <string name="about_description2">Pour plus d\'informations et soutien, aller sur <xliff:g id="app_link_open"><![CDATA[<a href="https://github.com/bfabiszewski/ulogger-android">]]></xliff:g>page d\'accueil de μlogger<xliff:g id="app_link_close"><![CDATA[</a>]]></xliff:g>.</string>
    <string name="about_description2">Pour plus dinformations et soutien, aller sur <xliff:g id="app_link_open"><![CDATA[<a href="https://github.com/bfabiszewski/ulogger-android">]]></xliff:g>page daccueil de μlogger<xliff:g id="app_link_close"><![CDATA[</a>]]></xliff:g>.</string>
    <string name="about_version">Version %s</string>
    <string name="about_license">License : GPL</string>
    <string name="uploading_failed">Échec du téléversement :</string>
@@ -81,11 +81,11 @@
    <string name="unit_kilometer">km</string>
    <string name="unit_mile">mi.</string>
    <string name="provide_valid_url">Merci de remplir une adresse de serveur valide</string>
    <string name="provide_user_pass_url">Merci d\'entrer un nom d\'utilisateur, un mot de passe et une adresse de serveur</string>
    <string name="e_illegal_state">Erreur d\'état : %s</string>
    <string name="provide_user_pass_url">Merci dentrer un nom dutilisateur, un mot de passe et une adresse de serveur</string>
    <string name="e_illegal_state">Erreur détat : %s</string>
    <string name="e_illegal_redirect">Redirection illégale : %d</string>
    <string name="e_auth_failure">Erreur d\'authentification : %d</string>
    <string name="e_http_code">Code d\'erreur HTTP : %d</string>
    <string name="e_auth_failure">Erreur dauthentification : %d</string>
    <string name="e_http_code">Code derreur HTTP : %d</string>
    <string name="e_server_response">Erreur de réponse du serveur</string>
    <string name="e_unknown_host">Hôte inconnu : %s</string>
    <string name="e_bad_url">URL incorrect : %s</string>
@@ -97,38 +97,38 @@
    <string name="using_network">Utilisant le réseau GSM</string>
    <string name="using_gps">Utilisant le GPS</string>
    <string name="export">Exporter en GPX</string>
    <string name="waypoint">Point d\'intérêt</string>
    <string name="waypoint">Point dintérêt</string>
    <string name="nothing_to_export">Rien à exporter</string>
    <string name="export_done">Piste sauvegardée avec succès</string>
    <string name="export_failed">Échec de l\'export</string>
    <string name="unknown_track">Piste inconnue</string>
    <string name="export_failed">Échec de lexport</string>
    <string name="unknown_track">Trace inconnue</string>
    <string name="export_started">Export débuté</string>
    <plurals name="label_positions_behind">
        <item quantity="one">%d position derrière</item>
        <item quantity="one">%d position en attente</item>
        <item quantity="other">%d positions en attente</item>
    </plurals>
    <string name="track_server_setup_warning">Si vous modifiez la configuration du serveur, vous ne pourrez peut-être pas synchroniser la piste en cours.</string>
    <string name="track_server_setup_warning">Si vous modifiez la configuration du serveur, vous ne pourrez peut-être pas synchroniser la trace en cours.</string>
    <string name="pref_mintime_other">Intervalle de temps minimum en secondes</string>
    <string name="pref_mindistance_other">Distance minimum en mètres</string>
    <string name="pref_minaccuracy_other">Précision minimum en mètres</string>
    <string name="pref_imagesize_other">Largeur/hauteur maximale de l\'image en pixels</string>
    <string name="pref_imagesize_other">Largeur/hauteur maximale de limage en pixels</string>
    <string name="unit_nmile">nm</string>
    <string name="pref_cat_location">Suivi de la position</string>
    <string name="pref_cat_server">Serveur</string>
    <string name="pref_cat_other">Autre</string>
    <string name="illegal_template_warning">Le modèle contient des caractères illégaux</string>
    <string name="cannot_open_picker">Impossible d\'ouvrir le dossier</string>
    <string name="e_open_out_stream">Impossible d\'ouvrir le flux de sortie</string>
    <string name="cannot_open_picker">Impossible douvrir le dossier</string>
    <string name="e_open_out_stream">Impossible douvrir le flux de sortie</string>
    <string name="provider_gps">GPS</string>
    <string name="provider_network">Réseau</string>
    <string name="provider_gps_network">Réseau et GPS</string>
    <string name="logger_task_failure">Impossible d\'obtenir la position</string>
    <string name="logger_task_failure">Impossible dobtenir la position</string>
    <string name="take_photo">Prendre une photo</string>
    <string name="from_library">Choisir dans la galerie</string>
    <string name="accuracy_meters">Précision de %d m</string>
    <string name="meters_asl">%s m</string>
    <string name="label_position">Position</string>
    <string name="label_waypoint_description">Description du point d\'intérêt</string>
    <string name="label_waypoint_description">Description du point dintérêt</string>
    <string name="label_add_photo">Ajouter une photo</string>
    <string name="button_save">Sauvegarder</string>
    <string name="tracking">Enregistrement</string>
Loading