martedì 8 aprile 2014

Proximity Sensor con Bluetooth 2 in Android

Non avendo a disposizione un telefono con Bluetooth 4.0 (od LE che si dica) ho provato a ricreare un sistema di prossimita' legato a Bluetooth che faccia un po' il verso a IBeacon e simili

Per simulare un IBeacon ed un ricevitore bisogna prevedere un trasmettitore BT sempre visibile ed un ricevitore che sai in grado di misurare la qualita' del segnale di ricezione senza la necessita' di un precedente accoppiamento con il trasmettitore


Esempio di scoperta Bluetoorh

Android permette di calcolare la qualita' del segnale del ricevitore (RSSI) anche senza la necessita' di accoppiamento per tutte le versioni di Bluetooth mentre MacOSX e Linux permetto di essere impostati per avere bluetooth sempre visibile (senza timeout). Quindi c'e' tutto per fare la prova

Il programma per Android (prendendo come base il seguente link) e' il seguente

AndroidManifest.xml
----------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.luca.innocenti.bttester"
    android:versionCode="1"
    android:versionName="1.0" >
    
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.luca.innocenti.bttester.MainActivity"
            android:label="@string/app_name" 
            android:theme="@style/Theme.AppCompat">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            
        </activity>
    </application>

</manifest>
----------------------------------------------------------

fragment_main.xml
----------------------------------------------------------
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.luca.innocenti.bttester.MainActivity$PlaceholderFragment" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:text="Ricerca" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="90dp"
        android:text=" " />

</RelativeLayout>
----------------------------------------------------------

MainActivity.java
----------------------------------------------------------
package com.luca.innocenti.bttester;

import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.Fragment;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends ActionBarActivity {
private static TextView rssi_msg;

    private static BluetoothAdapter BTAdapter = BluetoothAdapter.getDefaultAdapter();


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

if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
        registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
         
   }
private final BroadcastReceiver receiver = new BroadcastReceiver(){
        
@Override
public void onReceive(Context context, Intent intent) {
 String action = intent.getAction();
           if(BluetoothDevice.ACTION_FOUND.equals(action)) {
               int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE);
               String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
               
              
               rssi_msg.setText(rssi_msg.getText() + name + " => " + rssi + "dBm\n");
           }
}
    };

@Override
public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {


public PlaceholderFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
rssi_msg = (TextView) rootView.findViewById(R.id.textView1);
Button cerca= (Button) rootView.findViewById(R.id.button1);
cerca.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
               BTAdapter.startDiscovery();
          }
       });
return rootView;
}
}

}
----------------------------------------------------------

A questo punto per completare l'opera e' sufficiente di richiamare la funzione startDiscovery all'interno di un timer e gestire un evento in funzione della qualita' del segnale e del nome dell'emettitore


NullPointerException su OnCreate di Android

Per molto tempo sono stato abituato a dichiarare i widget di Android direttamente nella funzione OnCreate con una sintassi di questo tipo


-------------------------------------------
public void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
 setContentView(R.layout.activity_main); 

Button cerca = (Button) findViewById(R.id.button1); 
cerca.setOnClickListener(new OnClickListener(){ 
                      public void onClick(View v) { 
                             //fai qualcosa
                        } 
                    });
 }
-------------------------------------------

con le nuove modifiche questa sintassi crea un NullPointerException (senza molte altre spiegazioni)
Adesso invece si le dichiarazioni devono essere spostati in OnCreateView()
--------------------------------------------
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,false);
msg = (TextView) rootView.findViewById(R.id.textView1);
Button cerca= (Button) rootView.findViewById(R.id.button1);
cerca.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
               //fai qualcosa;
          }
       });
return rootView;
}
--------------------------------------------
Per rendere visibile a tutto il programma la TextView si puo' dichiarare come static nella MainActivity

--------------------------------------------
public class MainActivity extends ActionBarActivity {
private static TextView rssi_msg;
--------------------------------------------

Un altro aspetto che e' cambiato nel modo di programmare Android e' la presenza di due file di layout (ovvero il classico activity_main.xml e fragment_main.xml). I componenti visivi devono essere posti su fragment_main.xml

Per cambiare il tema dell'applicazione si puo' modificare il file AndroidManifest.xml come
--------------------------------------------
        <activity
            android:name="com.luca.innocenti.bttester.MainActivity"
            android:label="@string/app_name" 
            android:theme="@style/Theme.AppCompat">

MTP per Android

Un piccolo riassunto per salvare e copiare file dai dispositivi Android che usano il protocollo MTP per la connessione via USB



Windows
per collegarsi via MTP e' necessario avere l'apposito driver Media Transfer Protocol Porting Kit che si scarica da questo link

Mac OS X
Per Mac si usa il programma Android File Transfer che si scarica da questo link


Linux (Debian/Ubuntu)
Per Linux ovviamente le cose si complicano
Sotto Ubuntu 12.04 quando si connette un dispositivo Android MTP (come Nexus 7 2012) questo compare nel file manager, si riesce a sfogliare il contenuto ma tutti i tentativi di copiare file sono inutili

Alcuni hanno trovato soluzione installando gmtp ma per me e' stato un insuccesso
La soluzione invece e' derivata dall'impiego di go-mtpfs (github)
Per Ubuntu c'e' una soluzione piu' semplice

sudo add-apt-repository ppa:webupd8team/unstable
sudo apt-get update
sudo apt-get install go-mtpfs
sudo groupadd fusesudo gpasswd -a luca fuse

a questo punto si riavvia e si puo' accedere all'unita' MTP montandola come segue
go-mtpfs /home/luca/nexus7

per la cronaca il sistema funziona perfettamente da linea di comando (per esempio da Midnight Commander) ma ha problemi a copiare trascinando le icone dal file manager di Gnome

lunedì 7 aprile 2014

Debug Mode e Play Store

Dal 28 marzo sono mutate le regole per inserire le applicazioni su Play Store ed in particolare non sono piu' ammessi programmi con le istruzioni di debug

Su alcuni forum indicano come soluzione di inserire la direttiva
<application ....  android:debuggable="false"

nel file manifest ma cio' genera un errore.

 La soluzione sta nel selezionare il progetto dall'albero a sinistra di Eclipse, poi Android Tools ed infine Export Signed Application Package


In questo modo l'applicazione viene accettata da Play Store

Soil Color Chart per IOS

Vista la semplicita' ho effettuato il porting di Soil Color Chart anche per IOS mediante Phonegap
In questo caso la compilazione e' stata estremamente lineare
Il codice sorgente puo' essere scaricata da questo link







Soil Color Chart 2 in Android con Phonegap

Basandosi sul codice HTML5 usato per FirefoxOS ed usando Phonegap ho effettuato il porting di Soil Color Chart per Android (l'applicazione e' disponibile sul PlayStore a questo indirizzo mentre il codice sorgente puo' essere scaricato qui)



I passi sono piuttosto semplico
prima di crea il progetto Phonegap

phonegap create soilcolor  com.soil.color.chart.two SoilColorChart

cd soilcolor/www

si copiano i file dell'applicazione HTML5 in www
poi

cd ..
phonegap build android

aprendo il progetto mi e' comparso il seguente errore

conversion to dalvik format failed with error 1
che pero' e' stato risolto effettuando un semplice Project/Clean (dai menu' di Eclipse)






Per terminare e' sufficiente personalizzare le icone del progetto Android nella directory /res

venerdì 4 aprile 2014

Soil Color Chart per Firefox OS

Una nuova versione di Soil Color Chart per Firefox OS utilizzando PhotoSwipe e Mobile JQuery estendendo la tabella dei colori

Link all'applicazione









Opencv camera calibration in cpp

Oltre che con uno script Python come visto qui la calibrazione della camera si puo' fare anche con il programma in CPP Questo il proce...