11.13: Un “blog” personale per Android sfruttando Firebase: AddPostActivity pt. 1 (prelevare immagini dalla galleria)

Condividi su:

La nostra app inizia a prendere forma, abbiamo il codice necessario a portare l’utente fino alla schermata di inserimento post, ma arrivato lì non può fare più nulla, quindi iniziamo a dedicarci a questa activity.





Per prima cosa implementiamo il codice per il setup dell’intera activity:

public class AddPostActivity extends AppCompatActivity {
    private ImageButton mImageButton;
    private EditText mTitlePost;
    private EditText mDescPost;
    private Button mSubmitButt;
    private DatabaseReference mPostRef;
    private FirebaseUser mUser;
    private FirebaseAuth mAuth;

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

        /*
         * Recuperiamo l'istanza di autenticazione, l'utente corrente e
         * il riferimento alla posizione dei dati.
         * Per questa compattiamo il codice richiamando tutti i metodi in cascata
         */
        mAuth = FirebaseAuth.getInstance();
        mUser = mAuth.getCurrentUser();
        mPostRef = FirebaseDatabase.getInstance().getReference().child("Blog");

        /*
         * Recuperiamo le istanze di tutti i widget presenti nella UI dell'activity
         */
        mImageButton = (ImageButton) findViewById(R.id.addImageButton);
        mTitlePost = (EditText) findViewById(R.id.titleEditText);
        mDescPost = (EditText) findViewById(R.id.postDescriptionET);
        mSubmitButt = (Button) findViewById(R.id.submitPost);

        //Settiamo il listener sul pulsante di invio per richiamare un apposito metodo alla sua pressione
        mSubmitButt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startPosting();
            }
        });
    }

    private void startPosting() {
        //Recuperiamo il titolo e la descrizione dalle EditText
        String titleVal = mTitlePost.getText().toString().trim();
        String descVal = mDescPost.getText().toString().trim();

        if (!TextUtils.isEmpty(titleVal) && !TextUtils.isEmpty(descVal)){
            //ToDo: implement final code
            
            //Creiamo un oggetto blog fittizio per testare il funzionamento del codice
            Blog post = new Blog("Title", "description", "imageurl", "timestamp", "userid");
            //Memorizziamo l'oggetto Blo nel database e monitoriamo l'esito dell'operazione
            mPostRef.setValue(post).addOnSuccessListener(new OnSuccessListener<Void>() {
                @Override
                public void onSuccess(Void aVoid) {
                    Toast.makeText(AddPostActivity.this, getResources().getString(R.string.item_added), Toast.LENGTH_LONG).show();
                }
            });
        }
    }
}

Al momento non abbiamo fatto nulla di speciale, ci siamo limitati ad istanziare tutti gli oggetti di cui abbiamo bisogno e a gestire l’evento click sul pulsante di invio.
Nel recuperare le stringhe all’interno delle EditText ci siamo avvalsi anche del metodo “trim()” che non fa altro che ritornare una stringa con eventuali spazi superflui rimossi.
Molti elementi per costruire l’oggetto blog da salvare nel database non li abbiamo ancora, quindi ai fini di test ne creiamo uno fittizio e salviamo questo. All’azione di salvataggio abbiamo collegato un apposito listener che non fa altro che monitorare lo stato dell’operazione. Il metodo “onSuccess()” verrà invocato nel momento in cui l’operazione avrà successo.

Eseguendo l’app e provando ad inserire un nuovo post (abbiamo inserito dei controlli sui valori prelevati dalle EditText, quindi anche se non utilizzati, va inserito qualcosa), questo dovrebbe essere salvato all’interno del database:

Database

Gli intent impliciti: richiamare la galleria per recuperare un’immagine dal dispositivo

Per continuare con l’implementazione di questa classe, ci troviamo di fronte alla prima criticità. Il post richiede anche un’immagine, che per forza di cose dovrà essere prelevata tra quelle presenti all’interno del dispositivo.

Android mette a disposizione il meccanismo degli intent che di fatto permetto di richiedere l’esecuzione di un’operazione da parte di un’altra componente. Finora abbiamo utilizzato esclusivamente intent espliciti e li abbiamo sfruttati per passare da un’activity all’altra. Questi sono detti espliciti poiché noi definiamo sia il mittente che il destinatario, una volta creato l’intent lo diamo in pasto ad un apposito metodo ed il risultato è l’avvio di una nuova activity.

Se invece non sappiamo chi potrebbe essere il destinatario?
Ritorniamo alla nostra app, abbiamo bisogno che la galleria di sistema ci fornisca un immagine. Per eseguire un’operazione del genere basta confezionare un intent e richiedere l’azione col metodo “startActivityForResult()”, questo aprirà l’activity della galleria, l’utente selezionerà l’immagine e questa verrà ritornata all’activity chiamante. Nulla di straordinario, ci siamo già occupati di questo scambio di dati tra activity. Il problema è un altro, come si chiama l’activity della galleria che permette la selezione di un immagine tra quelle presenti sul dispositivo? Non è difficile scovarla, ma l’utente che userà la nostra app potrebbe decidere di usare un’altra app per la gestione della galleria, senza considerare il fatto che ogni produttore preinstalla la propria. È in queste situazioni che vengono in aiuto gli intent impliciti, dove non specifichiamo il destinatario della richiesta, ma quello di cui necessitiamo. Questo meccanismo permette alle varie app, di sistema e non, di comunicare e scambiarsi dati senza conoscere le specifiche l’una dell’altra. È grazie a ciò se ad esempio possiamo prelevare un contatto dalla rubrica, senza dover scrivere classi apposite, ma semplicemente chiedendo che il componente adibito a tale scopo lo faccia per noi. Allo stesso modo possiamo prelevare un’immagine dalla memoria del dispositivo senza premurarci minimamente di accedere alla memoria e ricercare le immagini. Lo farà la galleria per noi. Infatti vedremo che la nostra app non richiederà alcun permesso di accesso alla memoria.

La prima operazione da fare è settare un listener sull’ImageButton, poiché sarà cliccando su quella che l’utente potrà aggiungere l’immagine al post:

mImageButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //Creiamo un intent implicito
        Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
        //Specifichiamo il tipo di contenuto di cui abbiamo bisogno
        galleryIntent.setType("image/*");
        //Avviamo l'activity attraverso l'intent e ci aspettiamo un dato di ritorno
        startActivityForResult(galleryIntent, GALLERY_CODE);
    }
});

All’evento click creiamo un intent implicito, dove appunto specifichiamo ciò di cui abbiamo bisogno, piuttosto che il destinatario. Queste richieste sono codificate in apposite costanti, con “ACTION_GET_CONTENT” ad esempio permettiamo all’utente di selezionare un particolare tipo di dato e riceverlo come risposta all’activity chiamante, proprio quello di cui abbiamo bisogno. Per consultare la lista di tutte le costanti e la loro funzione, basta consultare la documentazione. Successivamente, col metodo “setType()” specifichiamo il tipo di dato che vogliamo riceve. Il parametro che specifica il tipo di dato dovrà essere in un formato particolare: MIME type. Un elenco completo lo potete reperire facilmente su internet, con “image/*” stabiliamo che necessitiamo di un’immagine, di qualsiasi formato essa sia. Infine richiamiamo il metodo per avviare la nuova activity, aspettandoci una risposta sfruttiamo “startActivityForResult()”, che quindi oltre all’intent vuole anche un request code, che identificherà la nostra richiesta. Questo l’abbiamo semplicemente codificato in una costante tra i field:

private static final int GALLERY_CODE = 1;

Se ricordate, quando l’activity chiamante si aspetta un dato di ritorno, va implementato anche il metodo “onActivityResult()”, che sarà appunto quello che verrà invocato quando l’activity chiamata renderà disponibile il dato di ritorno. Sarà qui che scriveremo il codice necessario per salvare l’immagine ricevuta:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    /*
     * Questo metodo viene invocato quando l'activity chiamata ritorna il dato richiesto
     */
    if (requestCode == GALLERY_CODE && resultCode == RESULT_OK){
        //Preleviamo il dato ricevuto e lo settiamo sulla ImageButton
        mImageUri = data.getData();
        mImageButton.setImageURI(mImageUri);
    }
}

Prima di eseguire qualunque operazione verifichiamo il request code e il result code, così da essere certi che la risposta viene da chi ha ricevuto la nostra richiesta e che l’operazione sia andata a buon fine. L’immagine la ritroviamo nel parametro “data”. Per prelevarla ci affidiamo ad un particolare tipo di oggetto: URI. L’Uniform Resource Identifier è una sequenza di caratteri che identifica univocamente una risorsa generica, quindi semplicemente stiamo prelevando l’identificativo univoco dell’immagine.
Avendo l’URI dell’immagine non resta da fare altro che settarla sull’ImageButton. Ovviamente non è una risorsa interna all’app, quindi si sfrutterà il metodo apposito. Anche l’oggetto in cui salviamo l’URI ci siamo premurati di dichiararlo tra i field dell’activity:

private Uri mImageUri;




Non abbiamo finito qui, abbiamo fatto solo il minimo indispensabile, ma al momento fermiamoci, continueremo con il prossimo articolo.

Condividi su:

label, , ,

About the author