Sei sulla pagina 1di 34

/* LUCA'S ANDROID CHEAT SHEET */

//--------------------------------------------------------------------------------------------
//-- ARCHITETTURA ANDROID
//--------------------------------------------------------------------------------------------
/*
- Linux Kernel: fornisce i servizi di base come:
- filesystem
- gestione memoria e processi
- gestione interaccia di rete
- drivers delle periferiche
- inoltre forisce dei servizi specifici per android:
- gestione batteria
- gestione memoria condivisa
- low memory killer
- interprocess communication

- Hardware Abstraction layer: interfacce per utilizzare l'hardware ai livelli superiori


- audio
- bluethoot
- camera
- sensori
- ecc..

- Librerie Native c/c++


- le applicazioni sono compilate in bytecode
- un tool DX, unisce tutti i file bytecode in un singolo file (classes.dex)
- ART Virtual Machine esegue il file Dex

- ART e Dalvik VM:


- ART da api 21 in poi (> 5.0)
- Dalvik da api 1 a 20

- java API Framework: espone le funzionalità del SO tramite api java


- View System:
- fornisce elementi di base per le interfacce utente
- Content Providers:
- fornisce l'accesso ai dati di altre app
- Package Manager:
- gestisce l'installazione dell'app sul device mobile
- Activity Manager:
- gestisce il ciclo di vita delle applicazioni
- permette di passare da un applicazione all'altra

- System App: applicazione già presenti nel sistema (contatti, telefono, browser...)
*/

//--------------------------------------------------------------------------------------------
--
//-- LAYOUTS: definiscono l'aspetto grafico dell'interfaccia utente
//--------------------------------------------------------------------------------------------
--

/* LINEAR LAYOUT:
- posiziona gli elementi uno dopo l'altro a seconda del verso scelto:
android:orientation="vertical" oppure android:orientation="horizontal"

- ogni figlio presente nel layout ha l'attributo android:layout_weight che determina


quanto spazio deve occupare l'elemento nel layout.
Assegnando weights uguali ai figli, lo spazio sarà diviso ugualmente.
Assegnando weights differenti, maggiore spazio sarà assegnato ai figli con il weight
più alto

- Inoltre è possibile dividere in parti uguali lo spazio del layout tra i figli, inserendo
nei figli: android:layout_height="0dp"
android:layout_weight="1” per lo spazio verticale
oppure
android:layout_width="0dp"
android:layout_weight="1” per lo spazio orizzontale.
*/

/* RELATIVE LAYOUT

- android:layout_alignParentTop=”true"
– android:layout_centerVertical=”true"
– android:layout_below=”@id/other_object"
– android:layout_toRightOf=”@id/other_object”

- permette di specificare il posizionamento degli oggetti contenuti (figli), in modo


relativo
tramite: below, above specificando l'id

- permette di centrare verticalmente e/o orizontalmente i figli con:


- layout_centerVertical
- layout_centerHorizontal
*/

/* FRAME LAYOUT: contentitore */

/* GRID LAYOUT: griglia

- consente di specificare il numero di righe e colonne:


- android:columnCount="3"
- android:rowCount="4"
*/

//--------------------------------------------------------------------------------------------
--
//-- MISURE
//--------------------------------------------------------------------------------------------
--
/*
- px: pixel reali

- dp: density indipendent pixels (dip), in cui la dimensione è calcolata in base alla
densità
del display (rapporto tra dimensione e risoluzione del display)
i dp permettono di ottenere widget con dimensioni simile anche con display a
diverse risoluzioni

- sp: scale-indipendent pixels

- pt: punti (1/72 inch)

- mm: millimetri

- in: inches
*/

/*--------------------------------------------------------------------------------------------
--
-- LIST VIEW
----------------------------------------------------------------------------------------------
- ListView è un widget, bisogna inserirlo nel file xml di layout e collegarlo ad una
data source.
- è specifico per creare liste di elementi
- bisogna specificare il layout (xml) per il singolo elemento della lista
- la lista, di default, supporta lo scrolling.
*/

/* instanzia un adapter di tipo T, associando un layout ad ogni elemento della


lista(R.layout.list_item)
ed una dataSource, tipicamente un ArrayList.

In generale un adapter fa da ponte tra i dati ed una AdapterView, l'adapter fornisce


accesso ai
dati oltre ad essere responsabile della creazione di una View per ogni elemento presente
nel data-set.
*/
ArrayAdapter<T> adapter = new ArrayAdapter<T>(Context, R.layout.list_item, dataSource);

//aggiunge un elemento all'adapter o meglio alla sorgente dati contenuta nell'adapter.


adapter.add(Object);

/* setta l'adapter al ListView, automaticamente ogni elemento del dataSource sarà un elemento
della listView
*/
listView.setAdapter(adapter)

adapter.notifyDataSetChanged(); //aggiorna i valori presenti nell'adapter, aggiornando anche


la grafica.

/* bisogna aggiungere un OnItemClickListener al ListView per far in modo che ogni elemento sia
cliccabile, inoltre nel onItemClick si può definire il comportamento al click di un item.
Il listener è applicato all intero item, non è possibile quindi differenziare le varie
parti
che compongono un item.
*/
listView.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

//ritorna l'elemento della lista alla posizione specificata, in questo caso la


posizione
//dipende dalla posizione dell'elemento cliccato
listView.getItemAtPosition(position);
}
}

/* se l'item della lista è composto da più parti che richiedono un click singolo, bisogna
NON aggiungere un OnItemClickListener, ma aggiungere degli onClick (nel layout
dell'elemento della lista)
poi nell singolo onClick è possibile ottenere l'elemento con adapter.getItem(index) se si
ha un riferimento ad
adapter.
*/

//è possibile definire dei propri adapter


class CustomAdapter extends ArrayAdapter<Object> {

private LayoutInflater inflater;

//costrutture
public CustomAdapter(Context context, ...) {

//bisogna ottenere un LayoutInflater:


inflater = LayoutInflater.from(context);
}

@Override
public View getView(int position, View view, ViewGroup parent) {

if (view == null)
view = inflater.inflate(R.layout.list_element, null);

//getItem(position) ritorna l'elemento alla posizione: position

codice();

return view;
}
}

//EXPANDLABLE LIST VIEW

/* SCROLL VIEW
Una view che permette lo scrolling di ciò che contiene. Una ScrollView dovrebbe avere un
solo figlio all'interno
(es: LinearLayout, FrameLayout..) e supporta solo lo scrolling verticale. Usare
HorizontalScrollView per
lo scrolling orizzontale.

Se la dimensione verticale della scroll view è pari a meta dello schermo impostare:
android:fillViewport="true"
*/

//--------------------------------------------------------------------------------------------
--
//-- GRID VIEW:
//--------------------------------------------------------------------------------------------
--

//un grid view permette di visualizzare elementi in una griglia (scegliendo solo il numero
di colonne)
//bisogna usare un adapter per inserire gli elementi

/* attributi specifici:
- android:columnWidth="45dp"
- android:horizontalSpacing="3dp"
- android:numColumns="3"
- android:stretchMode="columnWidth" //spacingWidth, spacingWidthUniform
- android:verticalSpacing="3dp">

/*--------------------------------------------------------------------------------------------
--
-- BUTTON ONCLICK PROGRAMMATICO
----------------------------------------------------------------------------------------------
*/
//implementare nella classe corrente: View.OnClickListener oppure creare una nuova classe
interna

//fare l'override di:


@Override
public void onClick(View view) {

Button b = (Button) view;

//code...
}

//settare il listener al button (o widget)


button.setOnClickListener(this); //oppure la classe che ha implementato il listener

//oppure
button.setOnClickListener( new View.OnClickListener() {

@Override
public void onCLick(View view) {

//stuff...
}
} )

//CAMBIARE COLORE DI SFONDO AL BUTTON


//usare:
android:backgroundTint="#FFFFFF" //invece di background, perchè in questo modo viene
rispettata la forma del button

// Background, Icon-Image and Text in one Button


<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/home_btn_test"
android:drawableTop="@drawable/home_icon_test"
android:textColor="#FFFFFF"
android:id="@+id/ButtonTest"
android:paddingTop="32sp"
android:drawablePadding="-15sp"
android:text="this is text"></Button>

//android default icons: @android:drawable/icon_id

//--------------------------------------------------------------------------------------------
--
//-- EDIT TEXT
//--------------------------------------------------------------------------------------------
--
// nel file di layout, inserire:
android:inputType="xxx" //per specificare il tipo di input (es: text, passoword...)
// disabilitare edit text per renderlo non cliccabile e modificabile
android:clickable="false"
android:cursorVisible="false"
android:focusable="false"
android:focusableInTouchMode="false"

//VISIBILITà

android:visibility=gone //rende il layout (o widget) invisibile, inoltre non occupa spazio


android:visibility=invisible //rende il layout (o widget) invisibile ma occupa spazio
android:visibility=visible //rende il layout (o widget) visibile (opzione attiva di
default)

//COLORE/BACKGROUND TRASPARENTE
android:backgroundTint="@android:color/transparent"

//--------------------------------------------------------------------------------------------
--
//-- IMAGE VIEW
//--------------------------------------------------------------------------------------------
--
// settare il il contenuto dell'imageView
android:src="id or @drawable/some_id"
//usare @drawable/ per i contenuti di default

/*--------------------------------------------------------------------------------------------
--
-- LAYOUT PROGRAMMATICO (creare/aggiungere widget ad un layout programmaticamente!)
--
-- definire un layout (es: GridLayout) in un file di layout, dopo, da codice sarranno settate
-- le proprietà al layout ed aggiunti elementi al layout
----------------------------------------------------------------------------------------------
*/

//GridLayout con righe e colonne custom


GridLayout grid = (GridLayout) findViewById(R.id.grid);
grid.setColumnCount(col);
grid.setRowCount(row);

//creare e aggiungere i Button alla griglia


Button[] buttons = new Button[row * col];

for (int i = 0; i < row * col; i++) {


buttons[i] = new Button(context); //creare un button (o widget)

//definire delle proprietà di layout da applicare ai widget da aggiungere,


//in questo caso i button.
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
GridLayout.LayoutParams.WRAP_CONTENT,
GridLayout.LayoutParams.WRAP_CONTENT
);

grid.addView(buttons[i], lp); //aggiunge il bottone (o widget) al GridLayout


}

//--------------------------------------------------------------------------------------------
--
//-- ACTIVITY LIFE CYCLE
//--------------------------------------------------------------------------------------------
--
/*
• Attività non esiste
1. onCreate() -> activity creata dal sistema
2. onStart() -> avviata e resa visibile
3. onResume() -> ripresa (dopo la onResume l'activity è visibile e interattiva)

• Attività in esecuzione
4. onPause() -> activity va in foreground (parzialmente visibile)
5. onStop() -> quando l'attività non è più visibile
6. onDestroy() -> activity terminata o distrutta dal sistema

• Attività non esiste (rimossa dalla memoria)


• Quandol’utente preme il pulsante “Home” sono chiamate:
- onPause()
- onStop()

• Quando si ritorna all’attività sono chiamate:


- onRestart()
- onStart()
- onResume()

• Quandol’utente ruota il dispositivo l’attività viene prima eliminata:


• onPause()
• onStop()
• onDestroy()
– e poi ricreata:
• onCreate()
• onStart()
• onResume()

• con onDestroy() si ha la perdita dello stato.

• Se un’attività può essere lanciata da più di un’altra attività si possono avere


istanze multiple

• Stati:
- Created: causato da onCreate()
- Started(visible): causato da onStart(), oppure onRestart() + onStart()
- Resumed(visible): causato da onResume()
- Paused(parzialmente visibile): causato da onPause()
- Stopped(hidden): causato da onStop()
- Destroyed(rimossa dalla memoria): causato da onDestroy()

• Passaggi di stato:
- Created -> Started: causato da onStart()
- Started -> Resumed: causato da onResume()
- Resumed -> Paused: causato da onPause()
- Paused -> Resumed: causato da onResume()
- Paused -> Stopped: causato da onStop()
- Stopped -> Started: causato da onRestart() + onStart()
- Stopped -> Destroyed: causato da onDestroy()
*/

// Si salva lo stato in onSaveInstanceState()

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {

// Salvare lo stato dell’app: facendo il put delle informazioni che si vogliono


salvare
savedInstanceState.putStringArrayList("LISTA_STRINGHE", array_di_stringhe);
savedInstanceState.putInt("CONTATORE", counter);

// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}

// Lo si recupera in onCreate()

@Override
protected void onCreate(Bundle savedInstanceState) {

if (savedInstanceState != null) {

//recupero degli oggetti inseriti nel Bundle


array_di_stringhe = savedInstanceState.getStringArrayList("LISTA_STRINGHE");
counter = savedInstanceState.getInt("CONTATORE");
}
}

/* lo si può recuperare anche in onRestoreInstanceState ma è questo è chiamato dopo onStart()


mentre onCreate viene chiamato prima di onStart(), per questo è preferibile recuperare lo
stato in onCreate().
*/
//-- BACKSTACK
/*
un task è un insieme di activity, un task è creato quando ad esempio si avvia un
app. A questo punto il task conterrà l'attività principale dell'app. La attività
di un task risiedono in un back-stack, ovvero quando un attivity viene mostrata
viene aggiunta al back-stack, quando un'altra attività sopraggiunge oscurando
quella precedente, la nuova sarà aggiunta al back-stack e si troverà al dì sopra
dell' attività precendere.

L'azione di premere il tasto back scatena il pop dell'ultima activity inserita nel
back-stack, premendo ulteriormente si estraggono tutte le varie activity fino a quando
si ritorna alla home screen del launcher, a questo punto il task è vuoto ed è eliminato.

Nel back-stack possono essere aggiunti anche le transazioni ai fragment, fare back
vuol dire annullare l'effetto della transazione eseguita.
*/

//--------------------------------------------------------------------------------------------
--
//-- INTENT
//--------------------------------------------------------------------------------------------
--
/*
Gli intent sono degli oggetti che mandano messaggi, racchiudono una azione e dei dati.
Possono essere di due tipi:
- intent espliciti: quando si conosce la componente, spesso usati all'interno della
propria app
- intent impliciti: usati quando non si conosce il nome completo della componente,
basta
specificare l'azione e i dati poi l'intent sarà inviato al sistema android che si
occupererà di risolvere l'intent e di trovare il componente adatto per esso. In
pratica
il sistema visiona il manifest delle varie applicazioni installate, nel manifest
cerca
per il tipo corretto di intent-filter.

• startActivity() -> lancia una nuova attivita


• startActivityForResult() -> lancia una nuova attivita, al termine di essa chiama
onActivityResult del chiamante
• broadcastIntent: spadisce l'intent in broadcast, sarà ricevuto da dei broadcast receiver
• startService o bindService: serve a comunicare con un servizio di background
*/

//intent che avvia una activity


Intent intent = new Intent(getApplicationContext(), Activity.class);
startActivity(intent);

//intent che avvia l'app dei contatti


Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);

//intent che mostra l'app di default per le mappe, con l'indirizzo inserito
[Uri.parse("geo:0,0?q=" + address)]
Intent geoIntent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=" +
address));

//è possibile avviare un activty tramite un oggetto Context


Context context = getApplicationContext();
context.startActivity(intent);

//o dall'activity stessa:


startActivity(intent);

//lancia l'intent e fornisce il risultato dell' intent alla activity che lo ha lanciato.
startActivityForResult(intent, REQUEST_CODE); //REQUEST_CODE è un intero (1)

/* il metodo startActivityForResult, ritona un risultato all'activity, per ottenere


questo risultato bisogna implementare onActivityResult nella Activity primaria:
*/
@Override
protected void onActivityResult(int request, int result, Intent data) {

if (request == REQUEST_CODE && result == Activity.RESULT_OK) {


codice();
}
}

//mentre nell'activity secondaria per mandare il risultato bisogna:


Intent intent = new Intent();
intent.putExtra(key, value);
setResult(resultCode, intent); //setta il risultato da mandare all'activity primaria
finish(); //termina l'activity corrente

//settare dei FLAG all'intent: permette di aggiungere ulteriori comportamenti all'intent

//fa in modo di non memorizzare nel backstack l'attività che si lancerà


intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

// INTENT COMPONENT: permette di specificare l'attività target (usare solo quando il target
è unico)
intent.setComponent();
intent.setClass();
intent.setClassName();

/*--------------------------------------------------------------------------------------------
---
//-- INTENT-FILTERS
//--------------------------------------------------------------------------------------------
---
Sono specificati nel manifest, in particolare ogni attività può specificare un
intent-filter,
questi indicano quali tipi di intent impliciti possono essere ricevuti dall'activity. Se
nessun intent implicito è specificato allora l'activity accetterà solo intent espliciti.

<action> specifica l'azione da eseguire


- ACTION_VIEW: dati che possono essere visualizzati dall'activity
- ACTION_SEND: dati che l'utente può condividere attraverso un'altra app

<data> uri che fa riferimento ai dati o il tipo MIME dei dati

<category> contiene informazioni aggiuntive riguardo al tipo di componente che può gestire
l'intent.
- CATEGORY_BROWSABLE: l'activity target consente di essere avviata da un web-browser.
- CATEGORY_LAUNCHER: l'activity è quella iniziale, mostrata nel launcher di sistema.

Il sistema android, leggendo queste componenti, può decidere quale componente avviare per
risolvere l'intent.

Per ricevere intent impliciti bisogna aggiungere all'intent-filter: CATEGORY_DEFAULT.


*/

//--------------------------------------------------------------------------------------------
--
//-- PERMESSI
//--------------------------------------------------------------------------------------------
--
/* per proteggere risorse e dati e limtare l'accesso a:
- informazioni dell'utente (contatti...)
- servizi con costi (sms, chiamate, accesso a intenet...)
- risorse di sistema (camera, gps..)

i permessi vanno dichiarati nel manifest:


<uses-permission android:name = "android.permission.X" />

i permessi sono suddivisi in due categorie:


- normali: concessi senza chiedere nulla all'utente
- pericolosi: devono essere approvati dall'utente
- quando si installa l'app (API < 23)
- a runtime (API > = 23)
*/

//per richiedere i permessi:


requestPermissions(new String[]{ Manifest.permission.READ_CONTACTS },
PERMISSIONS_REQUEST_READ_CONTACTS);
//e leggere la risposta dell'utente
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[]
grantResults) {

if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS) {

if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { //grant del'


iesimo-permesso

// Permission is granted

} else {

//Permission negated
}
}
}

//--------------------------------------------------------------------------------------------
--
//-- DISPLAY
//--------------------------------------------------------------------------------------------
--

Display display = getWindowManager().getDefaultDisplay();


/*
getWindowManager() -> ritorna il windowManager che si occupa di creare schermate sull
display.

getDefaultDisplay() -> ritorna li display gestito dal windowsManager corrente.

Display display -> contiene informazioni sulla grandezza e densità del display logico
che rappresenta.
*/

//ottenere la dimensione del display in pixel


Point dimension = new Point();
display.getSize(dimension);

int width_px = dimension.x;


int height_px = dimension.y;

/*
display.getSize(Point) -> serve ad ottenere le dimensioni reali in pixel del display,
il metodo inserisce i valori di width ed height in un oggetto Point, richiamabili tramite
le proprietà di x e y di Point.
*/

//per il calcolo della densità del display:


DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);

float screen_density = metrics.density; //densità del display


/*
display.getMetrics(DisplayMetrics) -> ritorna una struttura contenente informazioni di
grandezza, densità e font scaling del display da cui è invocata.
*/

//convertire dimensioni in pixel del display in dip (pixel indipendent point)


int screenw_dp = (int)(width_px / screen_density);
int screenh_dp = (int)(height_px / screen_density);

//convertire le misure in cm a partire dai dip


float screenw_cm = 2.54f * screenw_dp / 160;
float screenh_cm = 2.54f * screenh_dp / 160;

//pixel to cm = (px * 2.54f) / (density * 160)


//cm to pixel = (cm * density * 160) / 2.54f

//determinare la rotazione corrente del display


int rotation = display.getRotation();
/*
ora bisogna confrontare il valore di "rotation" con le costanti intere:
Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180, Surface.ROTATION_270,
per determinare la rotazione.

landscape: if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270)


portrait: if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180)
*/

//CAMBIARE DIMENSIONE ALLA VIEW PROGRAMMATICAMENTE


view.setLayoutParams(new LayoutParams(width, height));

// oppure:
view.getLayoutParams().width = width;

//NOTA: utilizzare la clase appropriata di LayoutParams, ad esempio se il container della


//view è LinearLayout bisogna utilizzare LinearLayout.LayoutParams!

//--------------------------------------------------------------------------------------------
--
//-- DATA STORAGE
//--------------------------------------------------------------------------------------------
--

//-- SHARED PREFERENCES: dati privati, coppie chiave-valore


//--------------------------------------------------------------------------------------------
--

//ottenere un instanza di SharedPreferences per un singolo file


SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

//oppure: quando si vogliono usare più file, bisogna ottenere la SharedPreferences del file
specificato
SharedPreferences prefs = PreferenceManager.getSharedPreferences(filename);

//per recuperare i dati dalla SharedPreferences si usa getX(key, dafaultValue), dove X è:


Int, String, Boolean ec..
//dove key è la chiave con cui è stato memorizato il valore e defaultValue è un valore
specificato in caso
//la get non trovi nessun valore.
String stringa = prefs.getString(key, defaultValue);

//per srivere dati nella SharedPreferences bisogna farlo attraverso un editor:


SharedPreferences.Editor editor = prefs.edit(); //ottenere l'editor

//immettere i dati nell'edito con la putX(key, value)


editor.putString(key, value);

//al termine bisogna effettuare il commit per scrivere i valori (altrimenti non saranno
salvati)
editor.commit();

//usare per cancellare tutti i dati contenuti nella SharedPreferences corrente


editor.clear();

//-- FILE: possono essere privati dell'app o pubblici (accessibili da altre app)
//--------------------------------------------------------------------------------------------
--

//per ogni app il android prevede una directory privata in cui solo l'app può accedervi.

//permessi neccessari:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

//controlla se lo storage esterno (storage primario) è scrivibile


public boolean isExternalStorageWritable() {

String state = Environment.getExternalStorageState();

return Environment.MEDIA_MOUNTED.equals(state);
}
//controlla se lo storage primario (esterno) è leggibile
public boolean isExternalStorageReadable() {

String state = Environment.getExternalStorageState();

return (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state));
}

//Data Directory
String path = Environment.getDataDirectory().toString();

//Externa lStorage Directory


String path = Environment.getExternalStorageDirectory().toString();

//File Directory, è la directory privata dell'app


String path = getApplicationContext().getFilesDir().toString();

//External Storage Public Directory


String path = Environment.getExternalStoragePublicDirectory("").toString();

//per aprire il file in scrittura/lettura


FileOutputStream fos = openFileOutput(fileName, mode);
FileInputStream fis = openFileInput(fileName);

//mode:
Context.MODE_PRIVATE //file accessibile solo all'app (file privato)
Context.MODE_APPEND //permette l'append del file

//Restituisce la directory privata dell’app (dove vengono salvati i file)


getFilesDir()

//Restitusicela directory privata dell’app (dove vengono salvati i file)


getDir()

//cancella il file nello spazio privato


deleteFile()

//Restituisce un array di file, quelli presenti nello sèazio privato


filelist()

//i metodi: getFilesDir, getDir, deleteFile, filelist, sono metodi di Activity.

/* si possono creare dei file temporanei usando la directory cache,


ottenuta con getCacheDir(), è responsabilità dell'app cancellare i file
inoltre questa directory dovrebbe essere utilizzata per file piccoli (< 1MB)
android cancellera i file nella cartella cache in mancanza di spazio sul device
*/

//per scrivere un file


PrintWriter writer = new PrintWriter( new FileOutputStream(file));
writer.println(text);
writer.close();

//per leggere un file


Scanner scanner = new Scanner( new FileInputStream(file));
String text = "";
while (scanner.hasNext()) text += scanner.readLine() + "\n";
scanner.close();

//directory pubbliche: possono accedervi tutte le app


Enviroment.getExternalStoragePublicDirectory(type) //type: DIRECTORY_PICTURES,
DIRECTORY_MUSIC, DIRECTORY_RINGTONES,..

//-- DATABASE SQLITE: Dati strutturati in database privati (all'interno dell'app)


//--------------------------------------------------------------------------------------------
--
//usa il linguaggio SQL per le query

//per usare un database bisogna estendere SQLiteOpenHelper e ricavare il DB:


SQLiteDatabase db = DatabaseOpenHelper.getWritableDatabase();
//Esempio di DatabaseOpenHelper:
public class DatabaseOpenHelper extends SQLiteOpenHelper {

//nomi colonne database, utili per creare i CursorAdapter


final static String[] columns = {
SchemaDB.Table._ID,
SchemaDB.Table.COLUMN_NAME,
SchemaDB.Table.COLUMN_VOTO
};

final private static Integer VERSION = 1;


final private Context context;

public DatabaseOpenHelper(Context context) {


super(context, SchemaDB.Table.TABLE_NAME, null, VERSION);
this.context = context;
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_CMD); //esegue una query per creare la tabella
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// non serve in questo esempio, ma deve esserci
}

//Questo metodo serve per cancellare il database


void deleteDatabase() {
context.deleteDatabase(SchemaDB.Table.TABLE_NAME);
}
}

//Esempio di Schema del database:


public class SchemaDB {

//rende la classe non instanziabile


private SchemaDB() {}

//struttura tabella
public static abstract class Table implements BaseColumns {
public static final String TABLE_NAME = "studenti";
public static final String COLUMN_NAME = "nome";
public static final String COLUMN_VOTO = "voto";
}
}

//effettuare delle query con i Cursor:


private Cursor readSelectedEntries() {

// Specifichiamo le colonne che ci interessano


String[] projection = {
SchemaDB.Tavola._ID,
SchemaDB.Tavola.COLUMN_NAME,
SchemaDB.Tavola.COLUMN_VOTO
};

// Specifichiamo come le vogliamo ordinare le righe


String sortOrder = SchemaDB.Tavola.COLUMN_VOTO + " ASC";

// Definiamo la parte 'where' della query.


String selection = SchemaDB.Tavola.COLUMN_NAME + " LIKE ? "
+ " and "
+ SchemaDB.Tavola.COLUMN_VOTO + " >? "
;

// eventuali argomenti di selezione


String[] selectionArgs = {"Car%", "25"};

// Eseguiamo la query
Cursor cursor = db.query(
SchemaDB.Tavola.TABLE_NAME, // The table to query
projection, // The columns to return
selection, // The columns for the WHERE clause
selectionArgs, // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
sortOrder // The sort order
);

return cursor;
}
Cursor cursorSelected = readSelectedEntries();

//creare un adapter a partire dal Cursor ottenuto con la query


SimpleCursorAdapter adapter = new SimpleCursorAdapter(

getApplicationContext(), //context
R.layout.list_layout, //Layout della lista
cursorSelected, //Il cursore con i dati del database
DatabaseOpenHelper.columns, // String[] con i nomi delle colonne database
new int[]{R.id._id, R.id.name, R.id.voto}, //id dei campi nel layout
0 //flags
);

/* aggiungendo l'adapter ad un ListView, tramite il metodo getItemAtPosition(index) è


possibile
ottenere un SQLiteCursor, che sarà utilizzato per recuperare gli elementi dalla tabella
selezionata
*/
SQLiteCursor cursor = (SQLiteCursor) listView.getItemAtPosition(position);

cursor.getInt(columnIndex) //recupero dei dati in una data colonna


cursor.getString(columnIndex)

//se si conosce il nome della colonna, si può ottenere il suo indice:


cursor.getColumnIndex("column_name");

/*--------------------------------------------------------------------------------------------
--
-- BITMAP
----------------------------------------------------------------------------------------------
*/
ImageView imageView;
Bitmap bmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
imageView.setImageBitmap(bmap);

/*
BitmapFactory è una classe che permette di creare un oggetto Bitmap
a partire da varie fonti quali: file, stream o array di byte.

BitmapFactory.decodeResource(Resource, int) -> crea una bitmap a partire una


un percorso di risorse, con getResources() si ottiene quello di default dell app
e il valore intero rappresenta la risorsa, in questo caso un immagine: R.drawable.image
*/

//--------------------------------------------------------------------------------------------
--
//-- THREAD E ASYNCTASK
//--------------------------------------------------------------------------------------------
--

//-- THREAD: hanno un proprio program counter e stack ma condividono heap e memoria statica
//--------------------------------------------------------------------------------------------
--
/* i thread creati non possono modificare l'interfaccia grafica dell applicazione, solo il
solo il MainThread può farlo.

Quindi per farlo all'interno di un thread è necessario utilizzare il metodo


View.post(Runnable)
oppure Activity.runOnUiThread(Runnable).
*/
imageView.post(new Runnable() {

@Override
public void run() {
imageView.setImageBitmap(bmap);
}
});

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
imageView.setImageBitmap(bmap);
}
});

//-- ASYNCTASK - Thread Asincroni


//--------------------------------------------------------------------------------------------
--
/*
La classe generica e astratta AsyncTask<Params, Progress, Result> consente di effettuare
operazioni
(anche di modifica) sulla UI dell app, utilizzando gli UI Thread e senza manipolare Thread
o Handler.

La classe ha 3 parametri generici:


- Params: indica il tipo di parametro inviato al task al di sopra dell esecuzione.
- Progress: il tipo di dato usato per l'avanzamento, durante la computazione in
background(task).
- Result: il tipo di dato del risultato della computazione in background(task).

quando un task asincrono è eseguito, il task esegue 4 step (in pratica sono metodi da
implementare):

1.onPreExecute() -> è eseguito nel UI Thread (MainThread) prima che il task sia eseguito,
è utilizzato per eseguire codice di startup.

2.doInBackground(Params[]) -> invocato subito dopo onPreExecute(), qui vengono passati i


parametri
e qui si svolge la computazioni e deve essere restituito il risultato (tipo:
Result),in questo
metodo è possibile usare publishProgress(Progress[]) per inviare una o più unità del
progresso
al UI Thread durante la chiamata di onProgressUpdate().

3.onProgressupdate(Progress[]) invocato sul UI Thread dopo una publishProgress(), questo


metodo è utilizzato per mostrare nell interfaccia utente ogni forma di avanzamento
mentre il
task è ancora in esecuzione. Ad esempio può essere usato per animare una progressBar.

4.onPostExecute(Result) -> invocato sul UI Thread dopo che il task è terminato. Il


risultato
della computazione è passato come parametro.

è possibile anche cancellare un task invocando cancel(boolean), che conseguentemente


invocherà
il metodo onCancelled(Object).
*/

//AsyncTask che carica un immagine e utilizza una progress bar per mostrare l'avanzamento
del caricamento
class LoadImageTask extends AsyncTask<Integer, Integer, Bitmap> {
private Integer index = 1;

//eseguito nel MainThread prima di avviare il task


@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
}

//eseguito dopo onPreExecute()


@Override
protected Bitmap doInBackground(Integer... img_ids) {

// Load bitmap
Bitmap tmp = BitmapFactory.decodeResource(getResources(), img_ids[0]);
/* Simuliamo il ritardo */
for (int i = 1; i < 11; i++) {
sleep(500);
publishProgress(i * 10);
}

return tmp;
}

//invocato sul MainThread dopo aver invocato publishProgress(),


//utile per mostrare i cambiamenti della interfaccia grafica
@Override
protected void onProgressUpdate(Integer... values) {
progressBar.setProgress(values[0]);
}

//invocato sul MainThread dopo che il task è terminato


@Override
protected void onPostExecute(Bitmap result) {
progressBar.setVisibility(ProgressBar.INVISIBLE);
progressBar.setProgress(0);
imageView.setImageBitmap(result);
}
}

/*--------------------------------------------------------------------------------------------
--
-- FRAGMENTS
----------------------------------------------------------------------------------------------
-
Un fragment rappresenta una porzione dell interfaccia utente. E' possibile combinare più
fragments
in una singola activity. Ogni fragment ha un ciclo di vita (simile a quello dell activity) che
dipende dal ciclo di vita dell' activity. Un fragment deve avere il proprio layout e deve
essere
ospitato in una activity (risiederà in un ViewGroup).
I fragment possono essere aggiunti/rimossi durante l'esecuzione. Combinare fragment è utile
quando
abbiamo un device con un ampio schermo (interfacce utente dinamiche).
I fragment possono essere riutilizati in più activity.
*/

/*-- CICLO VITA DEI FRAGMENTS


//--------------------------------------------------------------------------------------------
---

• Fragment Start: • Fragment End:


--- - onAttach() onDetach() <-------
| - onCreate() onDestroy() |
--> - onCreateView() <-------------------------------------- onDestroyView() ---
- onActivityCreated()
- onStart() <------------------------------------------ onStop()
- onResume() ----------> Fragment is running ----------> onPause()

Normalmente bisogna implementare almeno:


- onCreate(): inizializzare il fragment (senza definire il layout)
- onCreateView(): fare l'inflate del layout, restituire una View
- onPause(): è chiamato per primo quando il fragment sta per essere
eliminato. Salvare eventuali cambiamenti nel fragment
altrimenti andranno persi.
*/

class MyFragment extends Fragment { //bisogna estendere la classe fragment

@Override //è l'equivalente di setContentView per l'activity


public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {

/*
viene impostato il layout del fragment,
applicando il layout specificato in xml (id)
*/
return inflater.inflate(R.layout.myFragment, container, false);
}
}

// LAYOUT FRAGMENT STATICO


//inserito in un file di layout
<fragment
android:id="@+id/fragmentID"
android:name="myFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

/* OPPURE DINAMICAMENTE*/

//richiedere il gestore dei frammenti per l'activity corrente


FragmentManager fragmentManager = getFragmentManager();

//iniziare una transazione


FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

//instanziare il fragment che si vuole aggiungere


MyFragment fragment = new MyFragment();

//aggiunge il fragment ad un container di frammenti, es: FrameLayout


fragmentTransaction.add(R.id.fragment_container, fragment);

//aggiunge il fragment al container e ne imposta un tag(FragmentTAG)


//fragmentTransaction.add(R.id.fragment_container, fragment, FragmentTAG);

//terminare la transazione, applicando le modifiche effettuate


fragmentTransaction.commit();

/* METODI UTILI */

//restituisce un fragment (se trovato) con il tag corrispondente


fragmentManager.findFragmentByTag(String)

//rimuove il fragment
fragmentTransaction.remove(fragment)

//rimpiazza qualsiasi fragment ci sia nel container con newFragment


fragmentTransaction.replace(R.id.fragment_container, newFragment);

//scollega il fragment dalla UI


fragmentTransaction.detach(fragment)

//collega il fragment nella UI


fragmentTransaction.attach(fragment)

/* aggiunge il fragment al backStack quando sarà premuto back (mostrerà ciò che
precedeva il fragment ).
Il back-stack considera solo le activity, se si vogliono considerare
anche i fragmentes bisogna aggiungerli manualmente, altrimenti quando
si preme back sarà mostrata l'activity che precedeva il fragment omettendo
i cambiamenti nell ui effettuati dai fragment.

In al back-stack viene aggiunta la transazione (non il fragment).


*/
fragmentTransaction.addToBackStack(null); //String

//Esegue tutte le transazioni in coda


fragmentManager.executePendingTransactions();

/* ListFragment

E' un fragment che mostra una lista di items (legata a dei dati: array),
inoltre fornisce un handler utile quando l'utente seleziona un item della lista.
ListFragment opsita un ListView che può essere associato a diverse data sources e ne
eredita il comportamento (item layout, adapter...)
*/

/* Mettere in comunicazione Activity e Fragments:


Nella classe frament è possibile creare una Interfaccia, questa sarà implementata
dalla Activity (con conseguente override dei metodi)...
*/

/* consente al fragment di accedere ad una instanza di FragmentActivity che corrisponde


ad un wrapper per l'activity che ospita il fragment, consente di utilizzare metodi
come: findViewById(id); */
getActivity()

/* DETACH:
Scollega (detach) il frammento dato dalla UI. Il Frammento va in uno stato simile a
quando va nello back-stack,
ma il fragment è ancora attivamente gestito dal FragmentManager. Quando viene
effettuato il detach sul
fragment la sua gerarchia di view è eliminata.
Ciò vuol dire che la detach provoca solo la distruzione della view del fragment,
mentre il suo stato
continua ad essere gestito dal fragment manager.

REMOVE:
Rimuove un frammento esistente, se era aggiunto ad un container sarò rimossa anche la
sua view.
Inoltre il suo stato sarà rimosso dal FragmentManager.

ATTACH:
Effettua il re-attch del fragment di cui è stato fatto il detach in precedenza. Ciò
causa la
ri-creazione della sua view hierarchy che sarà collegata alla UI e mostrata solo le
il fragment
è stato aggiunto ad un container. Inoltre lo stato del fragment sarà gestio dal
FragmentManager.
*/

//--COMUNCAZIONE TRA ACTIVITY E FRAGMENT:

//----------------------------------------------------------------------------------------
--
//classe fragment che definisce un interfaccia:
class FragmentA extends Fragment {

private CallbackListener mCallback; //rifermento al'interfaccia che sarà recuperata


in onAttach()

@Override
public void onAttach(Context context) {
super.onAttach(context);

try {

/* ottiene un riferimento all'interfaccia dal context che rappresenta


un activity che ha implementato l'interfaccia di comunicazione del
fragment */
mCallback = (CallbackListener) context;

} catch (ClassCastException e) {

//lancia un eccezione in caso l'interfaccia non sia stata implementata


dall'activity.
throw new ClassCastException(context.toString() + " must implement
OnArticleSelectedListener");
}
}

//interfaccia di comunicazione
public interface CallbackListener {

public void callback(Object...params);


}
}

//activity che implementa l'interfaccia di comunicazione del fragment


class ActivityA extends Activity implements FragmentA.CallbackListener {
//override del metodo dell'interfaccia
@Override
public void callback(Object...params) {

}
}

/* creare un interfaccia nel fragment, recuperarla dall'activity, e farla implementare ad


essa consente di far comunicare il fragment con l'activity */

/*--------------------------------------------------------------------------------------------
--
-- NETWORKING
----------------------------------------------------------------------------------------------
-*/
//aggiungere al manifest.xml i permessi per l'accesso ad internet
//<uses-permission android:name="android.permission.INTERNET" />

//10.0.2.2 è l'indirizzo locale del' emulatore (equivalente a localhost)

//controllare sel il device dispone della connessione ad internet:


ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();

if (networkInfo != null && networkInfo.isConnected()) {

//cè connessione!
}

//recupero di una pagina web:


HttpURLConnection httpConnection = new java.net.URL(url).openConnection();
InputStream inputStream = httpConnection.getInputStream()
httpConnection.disconnect();

//per ottenere la pagina web in html:


HttpParams params = new BasicHttpParams();
HttpClientParams.setRedirecting(params, true);

HttpGet request = new HttpGet(URL); //URL: indirizzo del sito web


request.setParams(params);

ResponseHandler<String> responseHandler = new BasicResponseHandler();

try {

String result = httpClient.execute(request, responseHandler);

//il client richiede al server la pagina e il server


//risponde inviando il codice html della pagina.
return result;

} catch (IOException e) {
e.printStackTrace()
}

/* SOCKET:
conviene estendere AsyncTask per create un task asincrono
specifico per inviare i dati in rete
*/
class NetworkTask extends AsyncTask<Integer, Integer, String> {

@Override
protected void onPreExecute() {

//rende visibile una progressBar


progressBarDownload.setVisibility(ProgressBar.VISIBLE);
}

@Override
protected String doInBackground(Integer...values) {

String received_data = ""; //dati ricevuti


Socket socket = null; //socket per l'invio dei dati

//connessione:
try {

//ottengo l'indirizzo ip del server


InetAddress serverAddr = InetAddress.getByName(server);

//creo il socket per collegarmi al server in ascolto


socket = new Socket(serverAddr, port);

} catch (Exception e) {
e.printStackTrace();
return null;
}

if (socket == null)
return null; //connessione non stabilita

//invio dei dati


try {

//ottengo lo stream di output dal socket


PrintWriter out = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream())), true);

//invio i dati tramite il socket


out.println(strToSend);

} catch (Exception e) {
e.printStackTrace();
}

//Ricezione dei dati


try {

//ottengo lo stream di input dal socket


BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));

//lettura della prima linea dei dati ricevuti, per controllare se ci sono dati
String line;
if ((line = in.readLine()) == null) {
Log.d(TAG,"Errore");
}

int size = Integer.parseInt(line);


int c;

//lettura di tutti i dati ricevuti


while ( (c = in.read() ) > -1) {

received_data += (char) c;

//chiamare per permettere all' onProgressUpdate di aggiornare la progressBar


graficamente
publishProgress( 100 * (received_data.lengh() / size) );
}

} catch (Exception e) {
e.printStackTrace();
}

return received_data;
}

@Override
protected void onProgressUpdate(Integer... values) {

//serve ad aggiornare il valore del progresso della progressBar


progressBarDownload.setProgress(values[0]);
}
@Override //termine dell'esecuzione del task
protected void onPostExecute(String data) {

//nasconde la progress bar e la resetta


progressBarDownload.setVisibility(ProgressBar.INVISIBLE);
progressBarDownload.setProgress(0);
textResponse.setText(data);
}
}

/*--------------------------------------------------------------------------------------------
--
-- JSOUP: libreria per il parsing delle pagine html
----------------------------------------------------------------------------------------------
-*/
//recupera la pagine web, l'oggetto document rappresenta il DOM della pagina
Document doc = Jsoup.connect(url).get();

//recupera un elemento dal DOM tramite il suo id o tramite selezione


Element e = doc.getElementById("id");
Elements e = doc.select("[class=classe]"); //gruppo di elementi

/*--------------------------------------------------------------------------------------------
--
//-- GRAFICA, ANIMAZIONI E CUSTOM WIDGETS
//--------------------------------------------------------------------------------------------
---
Un’immagine può essere disegnata in
– un oggetto View
• grafica semplice, senza necessità di cambiamenti
– un oggeto Canvas
• grafica complessa, aggiornamenti frequenti

• Classe Drawable: rappresentaun oggetto che può essere disegnato


- ShapeDrawable – una forma
- BitmapDrawable – una matrice di pixels
- ColorDrawable – un colore (uniforme)

• Un oggetto Drawable deve essere inserito nell’oggetto View


– direttamentenel file XML
– in modo programmatico con View.setImageDrawable()

//--ANIMAZIONI: sono descritte da dei file xml in cui si specifica:


- rotazione
- traslazione
- scaling
- trasparenza
- velocità, punto di pivot, ecc...

• il file xml ha al seguente struttura:

<?xml version="1.0" encoding="utf-8"?>


<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >

<!-- gli effetti vanno posti all'interno -->

<!-- traparenza -->


<alpha
android:startOffset="4000"
android:duration="4000"
android:fromAlpha="1.0"
android:toAlpha="0.0" />

<!-- rotazione -->


<rotate
android:duration="4000"
android:fromDegrees="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="0"
android:toDegrees="720" />
<!-- traslazione -->
<translate
android:duration="3000"
android:fromXDelta="0"
android:fromYDelta="0"
android:interpolator="@android:anim/overshoot_interpolator"
android:startOffset="4000"
android:toXDelta="100"
android:toYDelta="100" />

<!-- scaling -->


<scale
android:duration="3000"
android:fromXScale="1"
android:fromYScale="1"
android:interpolator="@android:anim/anticipate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="7000"
android:toXScale="2"
android:toYScale="2" />
</set>

• La class Animation permette di leggere le animazioni dai file xml e di applicarle


alle ImageView. */

//carica l'animazione restituendo un oggetto Animation


AnimationUtils.loadAnimation(Context, R.anim.rotazione);

//applica l'animazione all'imageView


imageView.startAnimation(Animation);

//quando si vuole eseguire del codice dopo il termine di una animazione:


imageView.postOnAnimationDelayed( new Runnable() { //richiede min API 16

@Override
public void run() {
imageView.setAlpha(0); //nasconde l'immagine
}

}, animation.getDuration() * 2); //delay

//AnimationListener: utile per gestire eventi di inizio, ripetizione e fine animazione


animation.setAnimationListener( new AnimationListener() {

@Override
public void onAnimationStart(Animation animation) {
//inizio dell'animazione
}

@Override
public void onAnimationRepeat(Animation animation) {
//ripetizione dell'animazione
}

@Override
public void onAnimationEnd(Animation animation) {
//fine dell'animazione
}
});
/*
//-- CUSTOM WIDGETS: oltre ai widget offerti da android è possibile crearne di propri

• Albero delle View:


- container View (ViewGroup): RelativeLayout, LinerarLayout.. sono delle view che ne
contengono altre
- tutti gli oggetti view (anche i widget) sono contenute dentro delle container view
in
maniera gerarchica (ad albero), ne è un esempio:

LinearLayout
|
--------------------------------
| | | |
ImageView Frame EditText LinearLayout
| |
RelativeLayout -----------
| | |
------------- Button Button
| |
Button ImageView

• Quando un widget ha bisogno di spazio, chiama requestLayout() che viene propagato


nell albero delle view. RequestLayout() inserisce un evento in coda (negli eventi di
ui),
quando l'evento viene processato, ogni container view ha la possibilità di interagire
con i figli.

• Questo processo ha 2 fasi che riguardano le view all'interno del container:


- misura delle view (implementata da tutte le view)
- posizionamento delle view (implementata solo dalle view container)

• Misura: da la possibilità alle view di chiedere lo spazio di cui hanno bisogno


- android chiama il metodo measure() sulla radice dell'albero, in cui a partire
dalla radice ogni container view chiede ai figli quando spazio hanno bisogno
- questa richiesta si propaga ricorsivamente
- un view deve implementare il metodo onMeasure() che sarà chiamato dal metodo
final measure().
- onMeasure potrebbe essere chiamata varie volte!
- MeasureSpec:
– MeasureSpec.EXACTLY
– MeasureSpec.AT_MOST
- MeasureSpec.UNSPECIFIED

• Posizionamento (onLayout): le view container comunicano, ad ogni view figlia, la


posizione effettiva;
- Dopo che la view è stata posizionata, questa verrà disegnata (metodo onDraw)
- Le container iew devono implementare onLayout
*/

//-- Esempio: widget Nota


public class Nota extends View {

private float x;
private float y;

public Nota(Context context, float x0, float y0) {


super(context);

x = x0;
y = y0;

setMinimumWidth(WIDTH);
setMinimumHeight(HEIGHT);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

super.onLayout(changed, left, top, right, bottom);


}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

//colore della nota


Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStrokeWidth(3.0f); //spessore della linea

//disegna le componenti della nota: un cerchio e una linea


canvas.drawCircle(x, y, RADIUS, paint);
canvas.drawLine(x, y - canvas.getHeight() / 2, x, y, paint);
}
}

//creare l'oggetto nota


Nota nota = new Nota(Context, x, y);

//per mostrarlo bisogna aggiungerlo ad una view container altrimenti non sarà eseguito
l'onDraw()
viewContainer.addView(nota); //viewContainer come: Frame, Linerar, Relative, Grid Layout...

//cancella (graficamente) il canvas e richiama l'onDraw, utile quando il canvas deve


cambiare posizione.
Canvas.invalidate();

//-- ON TOUCH LISTENER: implementare un onTouchListener in cui è possibile ottenere la x e y


del tocco
//--------------------------------------------------------------------------------------------
----
view.setOnTouchListener(new View.OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {

if (event.getAction() == MotionEvent.ACTION_DOWN) {

//recuperare le coordinate del tocco


int touch_x = event.getX();
int touch_y = event.getY();
}

return true;
}
});

/*--------------------------------------------------------------------------------------------
--
-- MULTI-TOUCH
----------------------------------------------------------------------------------------------
-
• MotionEvent: rappresenta un movimento registrato da una periferica (mouse, dita,
trackball...)
• Il movimento è rappresentato con:
- ACTION_CODE: codice cambiamento avvenuto
- ACTION_VALUES: Posizione e proprietà del movimento (tempo, sorgente, pressione...)

• Un MotionEvent rappresenta uno o più pointer, un Pointer è un singolo evento


- Ogni Pointer ha un iD unico per tutto il tempo in cui esiste

• MotionEvents ACTION_CODES:
– ACTION_DOWN: un dito tocca lo schermo ed è il primo
– ACTION_POINTER_DOWN: un dito tocca lo schermo ma non è il primo
– ACTION_MOVE: un dito che è sullo schermo si muove
– ACTION_POINTER_UP: un dito che è sullo schermo non lo tocca più
– ACTION_UP: l’ultimo dito sullo schermo viene alzato
*/

// Metodi di MotionEvent:

//restituisce l'ACTION_CODE dell'evento (da confrontare con ACTION_DOWN, ACTION_UP, ec...)


event.getActionMasked();

//restituisce il numero di pointer coinvolti nell'evento


event.getPointerCount();

//ritorna l'indice del pointer corrente


event.getActionIndex();

//restituisce i'ID del pointer specificato tramite il suo indice


event.getPointerId(int pointerIndex);

//ritorna le coordinate x, y del pointer specificato


event.getX(int pointerIndex);
event.getY(int pointerIndex);

//ritorna l'indice del pointer a partire dal suo ID


event.findPointerIndex(int pointerId);

//Esempio:
view.setOnTouchListener(new OnTouchListener() {

//il metodo onTouch è chiamato prima che la view sia notificata dell'evento
@Override
public boolean onTouch(View v, MotionEvent event) {

int pointerIndex;
int pointerID;

switch (event.getActionMasked())
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
pointerIndex = event.getActionIndex();
pointerID = event.getPointerId(pointerIndex);

//code

break;

//ecc...
}

return true; //true indica che l'evento è stato consumato, false altrimenti
}
});

/*--------------------------------------------------------------------------------------------
--
//-- GESTURE DETECTOR: permette di riconoscere le gesture
//--------------------------------------------------------------------------------------------
---

• Bisogna creare un GestureDetector che:


– implementa l'interfaccia GestureDetector.OnGestureListener
- fa l'override del metodo onTouchEvent:
- è chiamato in risposta ad un gesto
- e delega il riconoscimento del gesto al metodo GestureDetector.OnGestureListener

• GestureDetector è in grado di riconoscere alcuni gesti quali:


- pressione semplice
- doppia pressione (double click)
- fling (scorrimento)
*/

//Esempio: Riconoscimento gesture fling


GestureDetector gestureDetector = new GestureDetector(context, new
GestureDetector.SimpleOnGestureListener() {

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {

//do something
if (Math.abs(e1.getX() - e2.getX()) > X_OFFSET && velX > VEL_OFFSET)
return true; //evento consumato

return false; //evento non consumato


}
});

/* In generale implementando GestureDetector.SimpleOnGestureListener è possibile fare


l'override
di altre gesture come onDown, onScroll, onLongPress ecc...
Mentre se si implementa GestureDetector.OnGestureListener bisogna fare l'override di tutte
le gesture proposte (onDown, onShowPress, onSingleTapUp, onScroll....)
*/

/*--------------------------------------------------------------------------------------------
---
//-- VIEW ANIMATOR E VIEW FLIPPER
//--------------------------------------------------------------------------------------------
---
• ViewAnimator:
- è un view container di base (agisce come un Framelayout)
- può eseguire animazioni quando si passa da una view all'altra.

• ViewFlipper:
- sottoclasse di ViewAnimator
- crea animazioni tra due o più view contenute all'interno
- solo un figlio (view) per volta è visualizzato
- può anche cambiare view (tra le view figlie) ad intervalli regolari.

• ViewAnimator e ViewFlipper sono utilizzati nei file xml di layout.


- showNext(): mostra la prossima view
- showPrevious(): mostra la view precedente
- setInAnimation(Animation): setta l'animazione in entrata
- setOutAnimation(Animation): setta l'animazione in uscita
*/

/*--------------------------------------------------------------------------------------------
---
//-- MUSIC PLAYER
//--------------------------------------------------------------------------------------------
---
• AudioManager: controlla le sorgenti audio
- gestisce il volume
- è ottenuto tramite: Context.getSystemService(Context.AUDIO_SERVICE)

• MediaPlayer: play di audio e video

• Sorgente dati
– Risorse locali
– URI (interni)
– URL

• Audio Focus: il canale di output è unico, l'audio focus permette di gestire l'accesso
contemporaneo, in quanto:
- una app richiede l'audio focus per usare l'audio
- se lo perde deve smettere di suonare o abbassare il proprio volume.
*/

//Esempio: semplice music player

//ottenere l'audioManager dal context


AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);

//aumenta di 1, il volume del brano


public void increaseVolume() {

//restituisce il volume corrente e lo aumenta di 1


int volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + 1;

//applica il nuovo volume


audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
}

//seleziona un brano e crea l'oggetto MediaPlayer


public void selezionaBrano(String nomeRisorsaBrano) {

//recupera l'ID della risorsa res/raw


int id = getResources().getIdentiFier(nomeRisorsaBrano, "raw", getPackageName());

if (id != 0) { //risorsa trovata, altrimenti id = 0

//crea un MediaPlayer a partire dal Contesto e id risorsa


player = MediaPlayer.crate(context, id);
}
}

//avvia il brano
public void play() {

if (player != null)
player.start();
}

//ferma il brano, rilasciando le risorse


public void stop() {

if (player != null)
{
player.stop(); //ferma la musica
player.release(); //rilascia le risorse

//oppure fare MediaPlayer.create con il brano corrente


player = null;
}
}

//mette il pausa il brano


public void pause() {

if (player != null)
player.pause();
}

//Play di un file da URL


String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

//posiziona l'audio al secondo specificato (msec)


player.seekTo(int msec);

//ritorna un booleano a seconda se un brano è in esecuzione o no


player.isPlaying();

//setta il loop al brano


player.setLooping(boolean loop);

//listener che gestisce l'evento del termine del brano


player.setOnCompletionListener( new OnCompletionListener() {

@Override
public void onCompletion(MediaPlayer arg0) {

//do something..
}
} );

/*--------------------------------------------------------------------------------------------
---
//-- SENSORI
//--------------------------------------------------------------------------------------------
---

• SensorManager:
- permette di capire quali sensori sono disponibili
- fornisce le caratterstiche del singolo sensore (range, accuratezza...)
- permette di leggere i dati grezzi del sensore
- permette di usare listener sui cambiamenti dei dati

• Alcuni tipi di sensori:


- TYPE_ACCELEROMETER: misura le forze in m/s^2 applicate al device sugli assi x,y,z
- TYPE_AMBIENT_TEMPERATURE: misura la temperatura dell'ambiente in gradi centigradi
- TYPE_GRAVITY: misura la forza di gravita sui 3 assi (x, y, z)
- TYPE_GYROSCOPE: misura le velocità di rotazioni in rad/s nelle direzioni: x, y, z
- TYPE_MAGNETIC_FIELD: misura il campo magnetico sui 3 assi
- TYPE_ORIENTATION: misura la rotazione riferita ai 3 assi
- TYPE_RELATIVE_HUMIDITY: misura la % di umiditò dell'ambiente
- TYPE_LIGHT: misura la luminosità dell'ambiente

• il sistema di riferimento rimane fisso, in caso di rotazione del device cambieranno


solo i valori letti sugli assi

• Velocità di campionamento (costanti di SensorManager):


- SENSOR_DELAY_NORMAL: 0,2s
- SENSOR_DELAY_GAME: 0,02s
- SENSOR_DELAY_UI: 0,06s
- SENSOR_FASTEST: 0s
*/

//Esempio: uso dell'accellerometro

//l'activity deve implementare l'interfaccia SensorEventListener


public class SensorActivity extends Activity implements SensorEventListener {

private SensorManager sensorManager;


private Sensor accelerometer;

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

//ottenere dal context un riferimento a SensorManager


sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

//ottenere tramite il SensorManager un riferimento al Sensore richiesto


accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

//bisogna controllare se il sensore è presente nel device


if (accelerometer == null) {

finish(); //termina l'activity, il sensore richiesto non è presente


}
}

@Override
protected void onResume() {
super.onResume();

//viene registrato il listener per la lettura dei dati dal sensore


//dove this è la classe che implementa l'interfaccia SensorEventListener
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
}

@Override
protected void onPause() {
super.onPause();

//viene rimosso il listener per la lettura de i dati


sensorManager.unregisterListener(this);
}

/* utilizzare onResume e onPause per registrare e poi rimuovere il listener consente di


risparmiare batteria quando l'activity è in stato di pausa */

//metodo richiamato ogni qual volta si verifica un evento che riguarda i sensori
@Override
public void onSensorChanged(SensorEvent event) {

if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

//lettura dei valori dal sensore


float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
//do something
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}
}

/*--------------------------------------------------------------------------------------------
---
//-- NOTIFICHE: permettono di informare l'utente al di fuori dell'interfaccia grafica dell'app
//--------------------------------------------------------------------------------------------
---
• Ne sono un esempio:
- Toast
- Dialog
- Notification Area (notifiche che compaiono nella status bar)
*/

//-- Toast: mostra un messaggio in un rettangolo con i bordi arrotondati


Toast.makeText(Context, Message, Toast.LENGTH_SHORT ).show();

//-- Toast Custom:


public void showCustomToast() {

Toast toast = new Toast(getApplicationContext());

//setta la proprietà gravity che determina la posizione a video del toast


toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);

//setta la durata a video del toast


toast.setDuration(Toast.LENGTH_LONG);

//fa l'inflate del layout specificare in un file di layout xml (R.layout.custom_toast)


toast.setView(getLayoutInflater().inflate(R.layout.custom_toast, null));

//mostra il toast a video


toast.show();
}

//-- Dialog: sono dei messaggi in cui possono esserci anche bottoni
public void showDialog(View v) {

DialogInterface.OnClickListener dialogListener = new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {

switch (which)
{
case DialogInterface.BUTTON_POSITIVE:

//codice per la risposta positiva (bottone: SI)


break;

case DialogInterface.BUTTON_NEGATIVE:

//codice per la risposta negativa (bottone: NO)


break;
}
}
};

//costruisce il dialog da mostrare


new AlertDialog.Builder(context)
.seTitle("titolo alert")
.setMessage("Messaggio del dialog")
.setPositiveButton("Si", dialogClickListener) //azione positiva (titolo bottone,
listener)
.setNegativeButton("No", dialogClickListener) //azione negativa (titolo bottone,
listener)
.show() //mostra il dialog
;
}

//-- Notifiche:
public void showNotification(View v) {

//costruisce la notifica
Notification.Builder notificationBuilder = new
Notification.Builder(getApplicationContext())
.setTicker("Messaggio breve")
.setSmallIcon(R.drawable.icon) //icona
.setAutoCancel(true)
.setContentTitle("Titolo notifica")
.setContentText("Testo della notifica")
;

// ottenere un NotificationManager dal context (servizio di sistema)


NotificationManager notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);

//passa la notifica al NotificationManager e la mostra


notificationManager.notify(1, notificationBuilder.build() ); //il numero 1 rappresenta
l'id della notifica
}

// cancellare una notifica a partire dal suo id


public void cancelNotification(View v) {

NotificationManager notificationManager = (NotificationManager)


getSystemService(Context.NOTIFICATION_SERVICE);

//cancella la notifica con ID pari a 1


notificationManager.cancel(1);
}

/*--------------------------------------------------------------------------------------------
---
//-- ALARMS: permettono di eseguire intent in funzione di specifici eventi
//--------------------------------------------------------------------------------------------
---

• Un app che usa un alarm può eseguire del codice anche se l'aplicazione è terminata
• Un alarm è attivo anche se il telefono va in modalità sleep:
- l'alarm può causare la ripresa dell'attività
- oppure potrà essere gestito quando il telefono ritornerà in modalità normale

• Gli alarms rimango attivi fino a quando:


- vengono cancellati
- la periferiche viene spenta

• Esempi di alarm:
- controllo periodico dei messaggi non spediti
- rendere la periferica non visibile (bluetooth) dopo un certo periodo
*/
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

//creare un alarm
alarmManager.set(int type, long triggerAtTime, PendingIntent i);
alarmManager.setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent
i);

/*--------------------------------------------------------------------------------------------
---
//-- BROADCASTS
//--------------------------------------------------------------------------------------------
---

• Le applicazioni andoid possono inviare e ricevere messaggi di broadcast provenienti dal


sistema o da altre app. In modo simile al pattern Public-Subscribe, i broadcast sono
inviati quando un evento di interesse occorre. Le app possono inviare dei custom
broadcast,
ad esempio per notificare di un qualcosa ad altre app.
• Le app possono registrarsi per ricevere uno specifico broadcast. Quando un broadcast è
inviato, il sistema instrada, automaticamente, i broadcast alle app che hanno una
sottoscrizione per quel perticolare tipo di broadcast.
• In generale i broadcast possono essere usati come un sistema di messaggistica tra le
app e
al di fuori del normale flusso dell'utente.

• BroadcastReceiver: serve per ricevere e reagire ad eventi (rappresentati da intents)


- un receiver deve registrarsi (usando registerReceiver()), dichiarando gli eventi ai
quali è interessato.
- un receiver riceve l'intent (evento) tramite il metodo onReceive(Context c, Intent
i)
chiamato dal sistema android per notificare il receiver.

• La registrazione può avvenire:


- staticamente nel manifest:
- il ricevitore è registrato durante il boot del sistema o quando l'app è
installata.
<receiver>
android:name="my_receiver"
android:exported="false"
<intent-filter>
<action android:name="xxx.MY_ACTION" />
</intent-filter>
</receiver>

- dinamicamente: il receiver sarà registato quando di chiama:


- LocalBroadcastManager.registerReceiver(): per i broadcast locali all'app
- Context.registerReceiver(): per i broadcast system-wide
- la registrazione può essere revocata con unregisterReceiver()

• Spedire i messaggi:
- sendBroadcast(Intent i): disponibile in LocalBroadcastManager e Context
- sendBroadcast(Intent i, String permission): disponibile sono in Context, inoltre
verrà consegnato solo ai receiver che hanno il permesso (dichiarato dall'app nel
manifest)
*/

//Esempio: Broadcast local

//AndroidManifest.xml: aggiungere il receiver statico


<application>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".Receiver"
android:exported="false" >
<intent-filter>
<action android:name="my.app.BROADCAST_CODE" />
</intent-filter>
</receiver>
</application>

//classe receiver:
public class Receiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

//event received - do something...


}

//nell'activity:
sendBroadcast( new Intent("my.app.BROADCAST_CODE") ); //invia il broadcast

//registrare un receiver dinamico:


BroadcastReceiver receiver = new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent intent) {

////event received - do something...


}
}

//intent filter con il codice dell'evento


IntentFilter intentFilter = new IntentFilter("my.app.BROADCAST_CODE");
//new IntentFilter(Intent.ACTION_TIME_TICK); invia l'intent ogni minuto

//registra un receiver dinamicamente sull'evento specificato dal intentFilter


registerReceiver(receiver, intentFilter);

//cancella il receiver
unregisterReceiver(receiver);

//eventi globali:
Intent.ACTION_TIME_TICK
Intent.AIRPLANE_MODE
Intent.BATTERY_LOW
Intent.DATA_SMS_RECEIVED
Intent.DATE_CHANGED
Intent.DEVICE_STORAGE_LOW
Intent.TIMEZONE_CHANGED
Intent.USER_PRESENT
Intent.WALLPAPER_CHANGED

/*--------------------------------------------------------------------------------------------
---
//-- CONTENT PROVIDERS
//--------------------------------------------------------------------------------------------
---

• I Content Providers gestiscono l'accesso ad insiemi di dati strutturati, incapsulano il


dato e forniscono un meccanismo per definirne la sicurezza.
• I Content Providers sono l'interfaccia standard che collega i dati in un processo con il
codice in esecuzione su un altro processo.

• Funzionamento:
- Un ContentResolver è utilizzato (nel contesto dell'app) per comunicare con il
provider
(oggetto instanza di una classe che implementa ContentProvider).
- L'oggetto Provider riceve le richieste di dati dai clients, effettua la richiesta e
ritorna il risutato.
- Bisogna implementare un proprio provider se si è interessati a condividere dati con
altre applicazioni (o per fornire suggerimenti di ricerca custom, o per copiare e
incollare dati complessi dalla propria applicazione ad un altra).

• ContentProvider: rappresentano dei contenitori di dati progettatti per essere


condivisibili dalle applicazioni.
- un ContentResolver offre dei comani SQL-Like, oltre ad inviare notifiche sul
cambiamento dei dati. Chiamando Context.getContentResolver() si recupera un
riferimento ad un ContentResolver.
- i dati contenuti in un ContentProvider sono memorizzati in tabelle, è possibile
far riferimento ad uno specifico ContentProvider tramite un URI:
- es: content://authority/path/id
- authority: specifica il content provider
- path: specifica la tabella
- id: specifica un particolare record

• ContentProvider standard:
- Browser (info su bookmarks, history)
- Call Log (info sulle chiamate)
- Contact (info sui contatti presenti in rubrica)
- Media (lista dei file multimediali utilizzabili)
- UserDictionary (lista delle parola digitate)
- ec..

• Per ottenere i dati si usano Query e Cursor:


- ContentResolver.query(
Uri uri,
String[] projection, //colonne
String selection, //SQL selection
String[] args, //SQl args
String sortOrder) //ordinamento
- Restituisce un Cursor che permette di iterare sull'insieme dei record restituiti
dalla query.
*/

/*--------------------------------------------------------------------------------------------
---
//-- SERVICES
//--------------------------------------------------------------------------------------------
---

• Un Service è un componenti applicativo capace di compiere lunghe operazioni in


backgroud,
senza fornire un interfaccia grafica (non interagisce con l'utente).
• Un Service continua la sua esecuzione in background anche se l'utente chiude l'app.
• inoltre un componente può effettuare il bind con un service per interagire con esso e
può effettuare Interprocess Communication (IPC).
• Un service può gestire una transazione in rete, interagire con un content provider,
ecc..

• Context.startService(Intent i): fa partire il service, che può continuare la sua


esecuzione fino a quando il device è acceso.
- per default il Service gira nel main thread dell'app che lo ha fatto partire.

• Le componenti che vogliono interagire con un service devono effetturare un bind con
esso:
- Context.bindService(Intent service, ServiceConnection conn, int flags);
- il binding permette di inviare richieste e ricevere risposte.
- se al momento della richiesta il bind service non è ancora attivo allora:
- viene fatto partire,
- e rimane attivo fino a quando c'è almeno un client connesso.

• Bisogna dichiarare il service nel Manifest:


<application>

<service
android:name="my_service"
android:label="my service label" />

</application>
*/

//Esempio service:

//classe del service


public class MyService extends Service {

private int serviceID;

@Override
public void onCreate() {
super.onCreate();

//onCreate code
}

//metodo chiamato quando il servizio deve essere avviato


@Override
public int onStartCommand(Intent i, int flags, int id) {

serviceID = id; //id del servizio

//code..

//Non fa ripartire automaticamente il servizio se viene fermato


return START_NOT_STICKY;
}

//chiamato quando il servizio termina


@Override
public void onDestroy() {

//rilasciare le risorse
}

@Override
public IBinder onBind(Intent arg0) {

return null;
}
}

//fermare un service
Service.stopSelf();

//client:
public class Client extends Activity {

private Intent serviceIntent;

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

//crea un intent per il servizio


serviceIntent = new Intent(getApplicationContext(), MyService.class);
}

public void startService(View v) {

//avvia il servizio
startService(serviceIntent);
}

public void stopService(View v) {

//ferma il servizio
stopService(serviceIntent);
}

/*--------------------------------------------------------------------------------------------
--
-- CONTATTI
----------------------------------------------------------------------------------------------
-*/
// inserire nel manifest i permessi per leggere i contatti
<uses-permission android:name="android.permission.READ_CONTACTS" />

//--------------------------------------------------------------------------------------------
---
//-- RESOURCES
//--------------------------------------------------------------------------------------------
---

//ottenere un riferimento di un drawable tramite il suo id (int)


Drawable d = getResources().getDrawable(R.drawable.image));

//ottenere una bitmap da un ImageView


Bitmap bitmap = ((BitmapDrawable)image.getDrawable()).getBitmap();

/* setta l'orientamento dell'activity in base a quello specificato anche se il device non


è nella posizione indicata da codice */
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

//--------------------------------------------------------------------------------------------
---
//-- HANDLER: eseguire del codice DOPO UN CERTO DELAY
//--------------------------------------------------------------------------------------------
---
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {

@Override
public void run() {

//CODICE ESEGUITO DOPO 100ms


}
}, 100);

Potrebbero piacerti anche