Me acabo de encontrar este problema al intentar probar una aplicación en modo "run". Investigando se soluciona en dos pasos:
1. borrando el fichero
2. haciendo un clean del proyecto en Eclipse.
android.permission.READ_PHONE_STATE
, modificar el estado del teléfono android.permission.MODIFY_PHONE_STATE
, y procesar llamadas entrantes android.permission.PROCESS_INCOMING_CALLS
. En este caso además la aplicación consta de un receiver y de una actividad. La actividad principal, BlacklistPhoneNumberActivity
se encarga de la parte gráfica de la aplicación, en la que se da de alta o de baja números de teléfono y consulta los números que están en lista negra en el preciso momento. El receiver BlackListPhoneNumberBroadcastReceiver
se encarga de recibir intents de llamada entrante y colgar esta llamada en caso de que ese nùmero de teléfono este dado de alta en lista negra.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.m607" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.PROCESS_INCOMING_CALLS" /> <receiver android:name=".BlackListPhoneNumberBroadcastReceiver" > <intent-filter android:priority="999" > <action android:name="android.intent.action.PHONE_STATE" /> </intent-filter> </receiver> <activity android:name=".BlackListPhoneActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
BlacklistPhoneNumberDBAdapter
que maneja el modelo de datos de la aplicación, con una clase interna DatabaseHelper
que extiende la clase SQLiteOpenHelper
y que sobreescribe los método onCreate y onUpgrade del Helper para la creación y actualización de la base de datos y que dará acceso a la apertura de conexión y cierre de la base de datos, así como acceso a la instancia de SQLiteDatabase
que maneja la persistencia de información en la base de datos.
BlacklistPhoneNumberDBAdapter
crea los métodos insertPhoneNumber(String) para persistir nuevos números en base de datos, deletePhoneNumber(String) para borrar números en base de datos, isPhoneNumberInBlacklist(String) para preguntar por un número en especial, getBlacklistPhoneNumbersAsString para la consulta de todos los números dados de alta en base de datos.
package com.m607.database; import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class BlacklistPhoneNumberDBAdapter { private static final String TAG = "BlacklistPhoneNumberDBAdapter"; private static final String DATABASE_NAME = "blacklistdb"; private static final String DATABASE_TABLE = "blacklistphonenumbers"; private static final int DATABASE_VERSION = 1; private static final String TXPHONENUMBER="tx_phonenumber"; private static final String DATABASE_CREATE = "create table "+DATABASE_TABLE+ "(_id integer primary key autoincrement, "+ TXPHONENUMBER+" text not null);"; private final String sqlSelectByNumber = "select " + TXPHONENUMBER+ " FROM " + DATABASE_TABLE + " where " + TXPHONENUMBER +"=?"; private final String sqlSelectNumbers = "select " + TXPHONENUMBER+ " FROM " + DATABASE_TABLE; private final String sqlDeleteNumber = "delete from " + DATABASE_TABLE + " where " + TXPHONENUMBER +"=?"; private final String sqlInsertBlacklistedPhoneNumber = "insert into " + DATABASE_TABLE + "("+ TXPHONENUMBER +") values (?)"; private final Context context; private DatabaseHelper DBHelper; private SQLiteDatabase db; public BlacklistPhoneNumberDBAdapter(Context ctx) { this.context = ctx; DBHelper = new DatabaseHelper(context); } public boolean isOpen(){ boolean isOpen = false; if(db.isOpen()){ isOpen=true; } return isOpen; } public Cursor getBlacklistPhoneNumber(String sPhoneNumber){ Log.d(TAG, sqlSelectByNumber); String[] argumentos = new String[1]; argumentos[0] = sPhoneNumber; Log.d(TAG, "argumentos: " + argumentos[0]); Cursor mCursor = db.rawQuery(sqlSelectByNumber, argumentos); if(mCursor!=null){ Log.d(TAG, "mCursor: " + mCursor); mCursor.moveToFirst(); } return mCursor; } public Cursor getBlacklistPhoneNumbers(){ Log.d(TAG, sqlSelectNumbers); Cursor mCursor = db.rawQuery(sqlSelectNumbers, null); if(mCursor!=null){ Log.d(TAG, "mCursor: " + mCursor); mCursor.moveToFirst(); } return mCursor; } public String getBlacklistPhoneNumbersAsString(){ Log.d(TAG, sqlSelectNumbers); String sConsulta=""; Cursor blacklistCursor =getBlacklistPhoneNumbers(); while (!blacklistCursor.isAfterLast()) { sConsulta += blacklistCursor.getString(0)+ "\n"; blacklistCursor.moveToNext(); } return sConsulta; } public void deletePhoneNumber(String sPhoneNumber){ Log.d(TAG, sqlDeleteNumber); String[] argumentos = new String[1]; argumentos[0] = sPhoneNumber; Log.d(TAG, "argumentos: " + argumentos[0]); db.execSQL(sqlDeleteNumber, argumentos); } public boolean isPhoneNumberInBlacklist(String sPhoneNumber){ Cursor blacklistCursor = getBlacklistPhoneNumber(sPhoneNumber); String sBlacklistedPhoneNumber; while (!blacklistCursor.isAfterLast()) { sBlacklistedPhoneNumber = blacklistCursor.getString(0); Log.d(TAG, "mCursor: " + "sBlacklistedPhoneNumber: "+ sBlacklistedPhoneNumber + " | sPhoneNumber: " + sPhoneNumber); if(sBlacklistedPhoneNumber!=null && sBlacklistedPhoneNumber.equalsIgnoreCase(sPhoneNumber)){ return true; } blacklistCursor.moveToNext(); } return false; } public void insertPhoneNumber(String sPhoneNumber){ String[] rowSet = new String[1]; rowSet[0]=sPhoneNumber; db.execSQL(sqlInsertBlacklistedPhoneNumber, rowSet); } //---opens the database--- public BlacklistPhoneNumberDBAdapter open() throws SQLException { db = DBHelper.getWritableDatabase(); return this; } //---closes the database--- public void close() { DBHelper.close(); } private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DATABASE_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS "+DATABASE_NAME); onCreate(db); } } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/insertar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/insertar" /> <EditText android:id="@+id/editor_texto" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="numberSigned" > </EditText> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/botonAniadir" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/aniadir" android:onClick="onClickAddphoneNumber" /> <Button android:id="@+id/botonBorrar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickDeletephoneNumber" android:text="@string/borrar" /> <Button android:id="@+id/botonConsulta" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickConsulta" android:text="@string/consulta" /> </LinearLayout> <EditText android:id="@+id/consulta" android:layout_width="match_parent" android:layout_height="match_parent" android:inputType="textMultiLine" android:enabled="false"> </EditText> </LinearLayout>
package com.m607; import com.m607.database.BlacklistPhoneNumberDBAdapter; import android.app.Activity; import android.app.Dialog; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class BlackListPhoneActivity extends Activity { /** Called when the activity is first created. */ private Button btAceptar; private Button btBorrar; private EditText etEditorTexto; private EditText etConsulta; private BlacklistPhoneNumberDBAdapter dbBlacklist; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); dbBlacklist = new BlacklistPhoneNumberDBAdapter(getBaseContext()); dbBlacklist.open(); setContentView(R.layout.main); btAceptar = (Button)findViewById(R.id.botonAniadir); btBorrar = (Button)findViewById(R.id.botonBorrar); etEditorTexto = (EditText)findViewById(R.id.editor_texto); etConsulta = (EditText)findViewById(R.id.consulta); } public void onClickAddphoneNumber(View view){ if(!dbBlacklist.isOpen()){ dbBlacklist.open(); } dbBlacklist.insertPhoneNumber(etEditorTexto.getText().toString()); Toast.makeText(getBaseContext(), "Añadido número " +etEditorTexto.getText().toString() , 1000); etEditorTexto.setText(""); } public void onClickDeletephoneNumber(View view){ if(!dbBlacklist.isOpen()){ dbBlacklist.open(); } dbBlacklist.deletePhoneNumber(etEditorTexto.getText().toString()); Toast.makeText(getBaseContext(), "Borrado n�mero " +etEditorTexto.getText().toString() , 1000); etEditorTexto.setText(""); } public void onClickConsulta(View view){ if(!dbBlacklist.isOpen()){ dbBlacklist.open(); } etConsulta.setText(dbBlacklist.getBlacklistPhoneNumbersAsString()); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); if(dbBlacklist.isOpen()){ dbBlacklist.close(); } } @Override protected void onNewIntent(Intent intent) { // TODO Auto-generated method stub super.onNewIntent(intent); } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); } @Override protected void onPostCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onPostCreate(savedInstanceState); } @Override protected void onPostResume() { // TODO Auto-generated method stub super.onPostResume(); } @Override protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { // TODO Auto-generated method stub super.onPrepareDialog(id, dialog, args); } @Override protected void onStart() { // TODO Auto-generated method stub super.onStart(); } @Override protected void onStop() { // TODO Auto-generated method stub super.onStop(); } }
package com.android.internal.telephony; public interface ITelephony { boolean endCall(); void answerRingingCall(); void silenceRinger(); }En el momento en que el BroadcastReceiver dispara un evento de llamada entrante se accede a este interfaz por Reflection, se compara el número de telefono con los que hay almacenados en la base de datos, y en caso de haber entrada, se cuelga la llamada.
package com.m607; import java.lang.reflect.Method; import com.android.internal.telephony.ITelephony; import com.m607.database.BlacklistPhoneNumberDBAdapter; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; public class BlackListPhoneNumberBroadcastReceiver extends BroadcastReceiver { private final String TAG = "BlackListPhoneNumberBroadcastReceiver"; private String telephonyServiceName = Context.TELEPHONY_SERVICE; private TelephonyManager telephonyManager; private BlackListPhoneNumerPhoneStateListener phoneStateListener; private BlacklistPhoneNumberDBAdapter dbAdapter; private ITelephony telephonyService; private void setTelephone(TelephonyManager tm) { try { if (telephonyService == null) { Class c = Class.forName(tm.getClass().getName()); Method m = c.getDeclaredMethod("getITelephony"); m.setAccessible(true); telephonyService = (ITelephony) m.invoke(tm); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void onReceive(Context context, Intent intent) { // insertar en bbdd 12345678 dbAdapter = new BlacklistPhoneNumberDBAdapter(context); dbAdapter.open(); dbAdapter.insert("12345678"); telephonyManager = (TelephonyManager) context .getSystemService(telephonyServiceName); phoneStateListener = new BlackListPhoneNumerPhoneStateListener(); telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); if (telephonyManager.getCallState() == TelephonyManager.CALL_STATE_RINGING) { // incoming phonecall, get nr String phoneNumber = telephonyManager.getLine1Number(); Log.d(TAG, "phone number on BR getLine1Number: callstate is ringing: " + phoneNumber); Bundle bundle = intent.getExtras(); String phoneNr = bundle.getString("incoming_number"); Log.d(TAG, "bundle.getString(incoming_number): " + phoneNr); if (dbAdapter.isPhoneNumberInBlacklist(phoneNr)) { Log.d(TAG, phoneNumber + " esta en lista negra. resultdata: " + getResultData()); setTelephone(telephonyManager); telephonyService.endCall(); } } else { Log.d(TAG, "phone number on BR: callstate is not ringing"); Integer myInt = telephonyManager.getCallState(); String error = "State NR:" + myInt.toString(); Log.d(TAG, "error: " + error); // if (getResultData()!=null) { // setResultData(null); // } } if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) { } } }El método setTelephone es el que recupera por reflection la instancia al objeto ITelephony, que posteriormente se usa para colgar la llamada, llamando al método getITelephony de la clase TelephonyManager.
package com.m607; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; public class BrujulaView extends View { private float direction; private Paint paint; public BrujulaView(Context context) { super(context); paint = new Paint(Paint.ANTI_ALIAS_FLAG); } public BrujulaView(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint(Paint.ANTI_ALIAS_FLAG); } public BrujulaView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); paint = new Paint(Paint.ANTI_ALIAS_FLAG); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); } @Override protected void onDraw(Canvas canvas) { paint.reset(); int w = getMeasuredWidth(); int h = getMeasuredHeight(); int r; if (w > h) { r = h / 2; } else { r = w / 2; } paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(5); paint.setColor(Color.WHITE); canvas.drawCircle(w / 2, h / 2, r, paint); paint.setColor(Color.RED); canvas.drawLine(w / 2, h / 2, (float) (w / 2 + r * Math.sin(-direction)), (float) (h / 2 - r * Math.cos(-direction)), paint); } public void update(float dir) { direction = dir; invalidate(); } }
package com.m607; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.widget.TextView; public class BrujulaActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private Sensor sensorAcelerometro; private Sensor sensorCampoMagnetico; private float[] valoresAcelerometro; private float[] valoresCamposMagnetico; private float[] matrizRotacion; private float[] matrizInclinacion; private float[] matrixValues; private TextView readingAzimuth, readingPitch, readingRoll; private BrujulaView brujulaView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); readingAzimuth = (TextView) findViewById(R.id.azimuth); readingPitch = (TextView) findViewById(R.id.pitch); readingRoll = (TextView) findViewById(R.id.roll); brujulaView = (BrujulaView) findViewById(R.id.brujula); sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); sensorAcelerometro = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); sensorCampoMagnetico = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); valoresAcelerometro = new float[3]; valoresCamposMagnetico = new float[3]; matrizRotacion = new float[9]; matrizInclinacion = new float[9]; matrixValues = new float[3]; } @Override protected void onResume() { sensorManager.registerListener(this, sensorAcelerometro, SensorManager.SENSOR_DELAY_NORMAL); sensorManager.registerListener(this, sensorCampoMagnetico, SensorManager.SENSOR_DELAY_NORMAL); super.onResume(); } @Override protected void onPause() { sensorManager.unregisterListener(this, sensorAcelerometro); sensorManager.unregisterListener(this, sensorCampoMagnetico); super.onPause(); } @Override public void onAccuracyChanged(Sensor arg0, int arg1) { // TODO Auto-generated method stub } @Override public void onSensorChanged(SensorEvent event) { switch (event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: System.arraycopy(event.values, 0, valoresAcelerometro, 0, event.values.length); break; case Sensor.TYPE_MAGNETIC_FIELD: System.arraycopy(event.values, 0, valoresCamposMagnetico, 0, event.values.length); break; } boolean exito = SensorManager.getRotationMatrix(matrizRotacion, matrizInclinacion, valoresAcelerometro, valoresCamposMagnetico); if (exito) { SensorManager.getOrientation(matrizRotacion, matrixValues); double azimuth = Math.toDegrees(matrixValues[0]); double pitch = Math.toDegrees(matrixValues[1]); double roll = Math.toDegrees(matrixValues[2]); readingAzimuth.setText("Azimuth: " + String.valueOf(azimuth)); readingPitch.setText("Pitch: " + String.valueOf(pitch)); readingRoll.setText("Roll: " + String.valueOf(roll)); brujulaView.update(matrixValues[0]); } } }Por último, el main.xml se ve así:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/azimuth" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <TextView android:id="@+id/pitch" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <TextView android:id="@+id/roll" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <View class="com.m607.BrujulaView" android:id="@+id/brujula" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </LinearLayout>Y el manifest de la aplicación:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.m607" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".BrujulaActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
<?xml version="1.0" encoding="utf-8" ?> <manifest android:versioncode="1" android:versionname="1.0" package="com.m607" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-sdk android:minsdkversion="10"> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name"> <activity android:configchanges="orientation|keyboardHidden" android:label="@string/app_name" android:name=".RealidadAumentadaActivity" android:screenorientation="landscape" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"> <intent-filter> <action android:name="android.intent.action.MAIN"> <category android:name="android.intent.category.LAUNCHER"> </category></action></intent-filter> </activity> </application> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> </uses-sdk>
package com.m607.orientation; import android.location.Location; import android.location.LocationListener; import android.os.Bundle; public class GPSListener implements LocationListener { Location localizacionActual; boolean cambioLocalizacion = false; public void onLocationChanged(Location location) { if (localizacionActual == null) { localizacionActual = location; cambioLocalizacion = true; } if (localizacionActual.getLatitude() == location.getLatitude() && localizacionActual.getLongitude() == location .getLongitude()) cambioLocalizacion = false; else cambioLocalizacion = true; localizacionActual = location; } public void onProviderDisabled(String provider) { } public void onProviderEnabled(String provider) { } public void onStatusChanged(String provider, int status, Bundle extras) { } }
... public void onCreate(Bundle savedInstanceState) { LocationManager locationManager; locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 100, 1, gpsListener); } ...
... SensorManager sensorMan = (SensorManager)ctx.getSystemService(Context.SENSOR_SERVICE); ...Posteriormenete, se recupera el sensor de orientación, indicando el tipo de sensor que se quiere recuperar SensorManager.SENSOR_ORIENTATION, el retardo con el que se recuperaran los cambios en la orientación, y el listener que implementa la acción en caso de que se detecte un cambio en la orientación.
... sensorMan.registerListener( listener, sensorMan.getDefaultSensor(SensorManager.SENSOR_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL); ...
SensorEventListener listenerOrientacion = new SensorEventListener(){ public void onAccuracyChanged(Sensor arg0, int arg1) {} public void onSensorChanged(SensorEvent evt) { if (evt.sensor.getType() == Sensor.TYPE_ORIENTATION) { direccion = evt.values[0]; } } };
direccion
la dirección sobre el eje x que tiene el dispositivo móvil en el momento de recibir el evento de cambio de dirección.
package com.m607.orientation; import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; public class Orientation { public SensorManager sensorManager; public float direccion; public Orientation(Context context){ SensorEventListener listenerOrientacion = new SensorEventListener(){ public void onAccuracyChanged(Sensor arg0, int arg1) {} public void onSensorChanged(SensorEvent evt) { if (evt.sensor.getType() == Sensor.TYPE_ORIENTATION) { direccion = evt.values[0]; } } }; sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); sensorManager.registerListener( listenerOrientacion, sensorManager.getDefaultSensor( SensorManager.SENSOR_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL); } }