image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

Stockage de données persistantes

Manipulation de fichiers

Fichiers internes

Fichiers externes

Répertoires cache

Storage Access Framework (SAF)

Exemple : une activité qui propose de choisir un fichier parmi tous les fichiers d'un type MIME donné (e.g. image/jpeg) et qui envoie l'URI de ce fichier vers une autre activité

  1. Utilisation de SAF pour démarrer l'activité de choix de fichier pour un type MIME indiqué en cochant un bouton radio
  2. Récupération de toutes les activités installées supportant ACTION_SEND
  3. Affichage de ces activités sous la forme de boutons radio
  4. Envoi vers l'activité cochée de l'URI du fichier
FileChooser UI
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.filechooser

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.widget.RadioButton
import androidx.appcompat.app.AppCompatActivity
import fr.upem.coursand.R
import kotlinx.android.synthetic.main.activity_file_chooser.*

class FileChooserActivity : AppCompatActivity() {

    /** Return the MIME type for the selected radio button */
    private val selectedFileType: String get() {
        return when (fileTypeRadioGroup.checkedRadioButtonId) {
            R.id.imageRadioButton -> "image/*"
            R.id.videoRadioButton -> "video/*"
            R.id.AudioRadioButton -> "audio/*"
            R.id.textRadioButton -> "text/*"
            else -> "*/*"
        }
    }

    private val OPEN_REQUEST_CODE = 42

    private var selectedUri: Uri? = null
    private var activityMap: Map<String, Intent>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_file_chooser)

        // When we click on the button we open activity to choose a file matching the type selected with the radio button
        // using the Storage Access Framework
        chooserButton.setOnClickListener {
            Intent(Intent.ACTION_OPEN_DOCUMENT)
                    .addCategory(Intent.CATEGORY_OPENABLE)
                    .setType(selectedFileType)
                    .apply { startActivityForResult(this, OPEN_REQUEST_CODE) }
        }
        // we must write the onActivityResult callback
    }

    /** This methid is called back when we have the result of the file chosen with the SAF */
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == OPEN_REQUEST_CODE && data != null && resultCode == Activity.RESULT_OK) {
            selectedUri = data.data
            chosenFileView.text = "${data.data}"
            // build the share Intent
            val shareIntent = Intent(Intent.ACTION_SEND)
                    .setType(contentResolver.getType(selectedUri!!))
                    .putExtra(Intent.EXTRA_STREAM, selectedUri)
            // get all the intents for all the activities that can send this file
            activityMap = shareIntent.resolveIntent(this)
            Log.i(javaClass.name, "activity map $activityMap")
            if (selectedUri != null) {
                activityRadioGroup.removeAllViews() // remove all the radio buttons
                activityMap?.map { val rb = RadioButton(this); rb.text = it.key; rb.tag = it.key; rb }
                        ?.forEachIndexed { i, b -> b.id = i; activityRadioGroup.addView(b) }

                // configure the button to start the selectedActivity
                sendToActivityButton.apply {
                    isEnabled = true
                    setOnClickListener {
                        // find the selected radio button
                        val rb = activityRadioGroup.findViewById<RadioButton>(activityRadioGroup.checkedRadioButtonId)
                        // start the activity for the selected intent
                        activityMap?.get(rb.tag?: "")?.also { startActivity(it) }
                    }
                }
            }
        }
    }
}

Media Store

Exemple : une fonction en Kotlin créeant un enregistrement dans le MediaStore et retournant l'URI du fichier créé

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.screenrecorder

import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.provider.MediaStore
import java.io.File

enum class MediaType { IMAGE, AUDIO, VIDEO }

fun Context.saveInMediaStore(type: MediaType, mimeType: String, name: String, file: File, additionalMetadata: Map<String, String> = emptyMap()): Uri? {
    val contentValues = ContentValues()
    contentValues.put(MediaStore.MediaColumns.TITLE, name)
    contentValues.put(MediaStore.MediaColumns.DATE_ADDED, (System.currentTimeMillis() / 1000).toInt())
    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
    contentValues.put(MediaStore.MediaColumns.DATA, file.absolutePath)
    additionalMetadata.forEach { contentValues.put(it.key, it.value) }

    val resolver = contentResolver

    val contentUri = when (type) {
        MediaType.IMAGE -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        MediaType.AUDIO -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
        MediaType.VIDEO -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
    }
    return resolver.insert(contentUri, contentValues)
}

fun Context.openUri(uri: Uri) = contentResolver.openOutputStream(uri)

Sauvegarde des fichiers d'application

Préférences d'application

Exemple : utilisation des préférences pour mémoriser des informations d'authentification (nom d'utilisateur et mot de passe)

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.login;

import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import fr.upem.coursand.R;

/** An activity that asks the user to supply her login information.
 * The username and password are then sent back to the calling activity.
 * It illustrates bidirectional communication between activities.
 */
public class LoginActivity extends AppCompatActivity {

    private SharedPreferences prefs;
    private String service; // the service for which we login
    private EditText usernameView;
    private EditText passwordView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        usernameView = findViewById(R.id.usernameView);
        passwordView = findViewById(R.id.passwordView);

        // read the calling intent
        service = getIntent().getStringExtra("service");
        TextView serviceView = findViewById(R.id.serviceView);
        serviceView.setText(service); // display the service name

        // maybe we have already saved into the preferences of the activity the login info for this service
        // in this case we prefill the input fields with the stored data
        prefs = PreferenceManager.getDefaultSharedPreferences(this);
        String username0 = prefs.getString(service + ".username", null);
        String password0 = prefs.getString(service + ".password", null);
        if (username0 != null) usernameView.setText(username0);
        if (password0 != null) passwordView.setText(password0);

        // set the login info
        findViewById(R.id.loginButton).setOnClickListener( v -> {
            String username = usernameView.getText().toString();
            String password = passwordView.getText().toString();
            if (username.isEmpty() || password.isEmpty()) {
                Toast.makeText(this, "Missing information", Toast.LENGTH_LONG).show();
            } else {
                CheckBox rememberCheckBox = findViewById(R.id.rememberCheckBox);
                SharedPreferences.Editor editor = prefs.edit(); // open a transaction for the preferences
                if (rememberCheckBox.isChecked()) {
                    // we store the login info into the prefs
                    editor.putString(service + ".username", username);
                    editor.putString(service + ".password", password);
                    editor.putLong(service + ".timestamp", System.currentTimeMillis());
                } else {
                    // we must forget the information already (or not) stored
                    editor.remove(service + ".username");
                    editor.remove(service + ".password");
                    editor.remove(service + ".timestamp");
                }
                // finally we commit the prefs transaction
                editor.commit();

                // create a result intent and send it back to the calling activity
                Intent resultIntent = new Intent();
                resultIntent.putExtra("service", service);
                resultIntent.putExtra("username", username);
                resultIntent.putExtra("password", password);
                setResult(RESULT_OK, resultIntent);

                // the job is done, we quit the activity
                finish();
            }
        });
    }


}