編輯:關於Android編程
有了前面幾篇博文作為基礎(《Android之——AIDL小結》、《Android之——AIDL深入》、《Android之——自動掛斷電話的實現》),我將在這片博文中向大家介紹如何實現手機黑名單的功能。用過Android手機的用戶都知道,如果不想接聽一些人的電話或者接收一些人的短信,可以將這些人的手機號碼放入手機黑名單中,此時,將不會接收到這些人打進來的電話和發送進來的短信。那這些功能具體是如何實現的呢?就讓我們一起來實現這些功能吧。
以一個數據庫表來管理手機黑名單,對這張數據表的管理就是在管理手機黑名單,可以向這張數據表中的數據進行增、刪、改、查操作,從而實現對手機黑名單的管理,當數據表中存在的某一個號碼,向本機撥打電話或者發送短信的時候,我們可以攔截到相應的電話或者短信,自動掛斷電話或者不再提示本機相關顯示信息即可,同時,我們將手機是否開啟了黑名單功能保存在SharedPreferences中。
原理講完了,是不是很簡單呢?下面,我們就一起來動手實現一個手機黑名單的功能吧。
實現手機黑名單的功能,這裡我們的數據庫表很簡單,只有兩個字段,一個是數據表的id,一個是手機號碼字段,凡是這張表中有的數據,都是被拉入手機黑名單的號碼。
數據庫設計如圖所示:
這個類繼承自SQLiteOpenHelper,內部以一個單例模式來獲取SQLiteOpenHelper對象,同時在這個類中定義了數據庫的名稱和創建存儲數據的數據表。
具體實現如下:
package cn.lyz.mobilesafe.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; /** * 手機黑名單數據庫相關 * @author liuyazhuang * */ public class BlackNumberDBHelper extends SQLiteOpenHelper { private static SQLiteOpenHelper mInstance; private final static String name = blacknumber.db; public static SQLiteOpenHelper getInstance(Context context){ if(mInstance == null){ mInstance = new BlackNumberDBHelper(context, name, null, 1); } return mInstance; } private BlackNumberDBHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(create table blacknumber(_id integer primary key autoincrement,number text)); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } }
新建數據庫的操作類
這個類中主要是封裝了對數據庫的增、刪、改、查操作,我們可以直接調用這個類中的方法來實現對數據庫的增、刪、改、查操作,從而實現對手機黑名單的操作。
具體實現如下:
package cn.lyz.mobilesafe.dao; import java.util.ArrayList; import java.util.List; import cn.lyz.mobilesafe.db.BlackNumberDBHelper; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /** * 數據庫的操作類 * 封裝了對數據表的增刪改查操作 * @author liuyazhuang * */ public class BlackNumberDao { private SQLiteOpenHelper mOpenHelper; public BlackNumberDao(Context context) { // TODO Auto-generated constructor stub mOpenHelper = BlackNumberDBHelper.getInstance(context); } //添加黑名單 public void add(String number){ SQLiteDatabase db = mOpenHelper.getWritableDatabase(); if(db.isOpen()){ ContentValues values = new ContentValues(); values.put(number, number); db.insert(blacknumber, _id, values); db.close(); } } //判斷號碼是否是黑名單 public boolean isBlackNumber(String number){ boolean isExist = false; SQLiteDatabase db = mOpenHelper.getReadableDatabase(); if(db.isOpen()){ Cursor c = db.query(blacknumber, null, number = ? , new String[]{number}, null, null, null); if(c.moveToFirst()){ isExist = true; } c.close(); db.close(); } return isExist; } //根據號碼查詢id public int queryId(String number){ int _id = 0; SQLiteDatabase db = mOpenHelper.getReadableDatabase(); if(db.isOpen()){ Cursor c = db.query(blacknumber, new String[]{_id}, number = ? , new String[]{number}, null, null, null); if(c.moveToFirst()){ _id = c.getInt(0); } c.close(); db.close(); } return _id; } //刪除黑名單 public void delete(String number){ SQLiteDatabase db = mOpenHelper.getWritableDatabase(); if(db.isOpen()){ db.delete(blacknumber, number = ? , new String[]{number}); db.close(); } } //更新黑名單 public void update(int id,String number){ SQLiteDatabase db = mOpenHelper.getWritableDatabase(); if(db.isOpen()){ ContentValues values = new ContentValues(); values.put(number, number); db.update(blacknumber, values, _id = ? , new String[]{id+}); db.close(); } } //得到所有的黑名單 public ListfindAll(){ List blacknumbers = new ArrayList (); SQLiteDatabase db = mOpenHelper.getReadableDatabase(); if(db.isOpen()){ Cursor c = db.query(blacknumber, new String[]{number}, null, null, null, null, null); while(c.moveToNext()){ String number = c.getString(0); blacknumbers.add(number); } c.close(); db.close(); } return blacknumbers; } }
新建短信接收者SmsRecevier繼承BroadcastReceiver,這個類用來接收外界發送來的短信,在這個類中,首先攔截到發送短信的號碼,到黑名單數據庫中查詢是否存在這個號碼,如果存在,則說明當前發送短信的號碼為黑名單中的號碼,我們就把這個短信攔截掉,否則不做任何操作。
具體實現代碼如下:
package cn.lyz.mobilesafe.receiver; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.media.MediaPlayer; import android.telephony.SmsManager; import android.telephony.SmsMessage; import android.util.Log; import cn.lyz.mobilesafe.R; import cn.lyz.mobilesafe.dao.BlackNumberDao; import cn.lyz.mobilesafe.engine.GPSInfoService; /** * 短信接收者 * @author liuyazhuang * */ public class SmsRecevier extends BroadcastReceiver { private SharedPreferences sp; private DevicePolicyManager devicePolicyManager; private BlackNumberDao blackNumberDao; @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Log.i(i, 已經攔截到了短信); sp = context.getSharedPreferences(config, Context.MODE_PRIVATE); blackNumberDao = new BlackNumberDao(context); //判斷保護是否開啟 boolean isprotected = sp.getBoolean(isprotected, false); if(isprotected){ devicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); Object[] pdus = (Object[]) intent.getExtras().get(pdus); for(Object pdu:pdus){ String address = smsMessage.getDisplayOriginatingAddress(); //判斷是不是黑名單的一個短信 boolean isBlackNumber = blackNumberDao.isBlackNumber(address); if(isBlackNumber){ abortBroadcast(); } } } } }
新建黑名單電話服務類BlackNumberService繼承自Service,這是一個服務類,它會一直在Android後台運行,監聽來電是否為黑名單中的號碼。首先,這個類中的方法會先從SharedPreferences中獲取是否開啟了黑名單的信息,如果開啟了黑名單,則再從數據庫中查詢相關電話號碼是否為黑名單中的號碼,如果是,則自動掛斷電話(有關自動掛斷電話的功能請詳見《Android之——自動掛斷電話的實現》一文)。
具體代碼實現如下:
package cn.lyz.mobilesafe.service; import java.lang.reflect.Method; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.provider.CallLog.Calls; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import cn.lyz.mobilesafe.activity.BlackNumberListActivity; import cn.lyz.mobilesafe.dao.BlackNumberDao; import com.android.internal.telephony.ITelephony; /** * 黑名單電話服務 * @author liuyazhuang * */ public class BlackNumberService extends Service { private TelephonyManager tm; private MyPhoneStateListener listener; private BlackNumberDao blackNumberDao; private SharedPreferences sp; private NotificationManager nm; @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); sp = getSharedPreferences(config, Context.MODE_PRIVATE); listener = new MyPhoneStateListener(); tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); blackNumberDao = new BlackNumberDao(this); nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); } private final class MyPhoneStateListener extends PhoneStateListener{ private long startTime = 0; @Override public void onCallStateChanged(int state, String incomingNumber) { // TODO Auto-generated method stub super.onCallStateChanged(state, incomingNumber); switch (state) { case TelephonyManager.CALL_STATE_RINGING: //判斷來電黑名單是否開啟 boolean isblackstart = sp.getBoolean(isblacknumber, false); if(isblackstart){ boolean isBlackNumber = blackNumberDao.isBlackNumber(incomingNumber); if(isBlackNumber){ endCall(incomingNumber); return; } } startTime = System.currentTimeMillis(); break; case TelephonyManager.CALL_STATE_OFFHOOK: break; case TelephonyManager.CALL_STATE_IDLE: long endTime = System.currentTimeMillis(); //來電一聲響 if(endTime - startTime < 3000){ //發送通知 Notification notification = new Notification(android.R.drawable.stat_notify_missed_call, 攔截到來電一聲響, System.currentTimeMillis()); Intent intent = new Intent(getApplicationContext(),BlackNumberListActivity.class); intent.putExtra(number, incomingNumber); PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 100, intent, 0); notification.setLatestEventInfo(getApplicationContext(), 來電一聲響, 攔截到來電一聲響, contentIntent); notification.flags = Notification.FLAG_AUTO_CANCEL; nm.notify(100, notification); } break; default: break; } } } //掛斷電話 private void endCall(String incomingNumber){ try { Class clazz = Class.forName(android.os.ServiceManager); Method method = clazz.getMethod(getService, String.class); IBinder ibinder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE); ITelephony iTelephony = ITelephony.Stub.asInterface(ibinder); iTelephony.endCall(); //刪除通話記錄 通話記錄的保存是一個異步的操作,需要使用ContentObserver技術來實現 Uri uri = Calls.CONTENT_URI; getContentResolver().registerContentObserver(uri, true, new MyContentObserver(new Handler(),incomingNumber)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private final class MyContentObserver extends ContentObserver{ private String incomingNumber; public MyContentObserver(Handler handler, String incomingNumber) { super(handler); // TODO Auto-generated constructor stub this.incomingNumber = incomingNumber; } @Override public void onChange(boolean selfChange) { // TODO Auto-generated method stub super.onChange(selfChange); Uri uri = Calls.CONTENT_URI; String where = Calls.NUMBER + = ?; String[] selectionArgs = new String[]{incomingNumber}; getContentResolver().delete(uri, where, selectionArgs); //解除監聽 getContentResolver().unregisterContentObserver(this); } } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); //取消狀態監聽 tm.listen(listener, PhoneStateListener.LISTEN_NONE); } }
具體實現如下:
具體實現如下:
這個類是應用程序的入口類,是用戶直接可以看到的界面,在這個類中,我們首先找到頁面上的各個控件,設置相關的事件狀態,我們通過在這個類中設置上下文菜單來實現對黑名單的修改與刪除操作,通過長按黑名單列表來實現修改與刪除黑名單中相應號碼的操作。
具體實現如下:
package cn.lyz.mobilesafe.activity; import java.util.List; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import cn.lyz.mobilesafe.R; import cn.lyz.mobilesafe.adapter.BlackNumberAdapter; import cn.lyz.mobilesafe.dao.BlackNumberDao; /** * 手機黑名單的實現 * @author liuyazhuang * */ public class BlackNumberListActivity extends Activity implements OnClickListener{ private static final int MENU_UPDATE_ID = 0; private static final int MENU_DELETE_ID = 1; private TextView tv_add_blacknumber; private ListView lv_blacknumber; private TextView empty; private View view; private LayoutInflater mInflater; private EditText et_number_blacknumber_dialog; private Button bt_ok_blacknumber_dialog; private Button bt_cancel_blacknumber_dialog; private BlackNumberDao blackNumberDao; private AlertDialog dialog; private BlackNumberAdapter mAdapter; private int flag = 0; private final static int ADD = 1; private final static int UPDATE = 2; private String blacknumber; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); Log.i(i, on create ); blacknumber = getIntent().getStringExtra(number); setContentView(R.layout.blacknumber_list); tv_add_blacknumber = (TextView) findViewById(R.id.tv_add_blacknumber); lv_blacknumber = (ListView) findViewById(R.id.lv_blacknumber); empty = (TextView) findViewById(R.id.empty); mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = mInflater.inflate(R.layout.add_blacknumber_dialog, null); et_number_blacknumber_dialog = (EditText) view.findViewById(R.id.et_number_blacknumber_dialog); bt_ok_blacknumber_dialog = (Button) view.findViewById(R.id.bt_ok_blacknumber_dialog); bt_cancel_blacknumber_dialog = (Button) view.findViewById(R.id.bt_cancel_blacknumber_dialog); bt_ok_blacknumber_dialog.setOnClickListener(this); bt_cancel_blacknumber_dialog.setOnClickListener(this); //當listview沒有數據的顯示內容 lv_blacknumber.setEmptyView(empty); blackNumberDao = new BlackNumberDao(this); Listblacknumbers = blackNumberDao.findAll(); mAdapter = new BlackNumberAdapter(this, blacknumbers); lv_blacknumber.setAdapter(mAdapter); tv_add_blacknumber.setOnClickListener(this); //給一個控件注冊上下文菜單 registerForContextMenu(lv_blacknumber); if(blacknumber != null){ boolean isBlackNumber = blackNumberDao.isBlackNumber(blacknumber); if(!isBlackNumber){ ViewGroup parent = (ViewGroup) view.getParent(); if(parent != null){ parent.removeAllViews(); } et_number_blacknumber_dialog.setText(blacknumber); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(添加黑名單); builder.setView(view); dialog = builder.create(); dialog.show(); flag = ADD; } } } @Override protected void onNewIntent(Intent intent) { // TODO Auto-generated method stub super.onNewIntent(intent); Log.i(i, on new intent); blacknumber = intent.getStringExtra(number); if(blacknumber != null){ boolean isBlackNumber = blackNumberDao.isBlackNumber(blacknumber); if(!isBlackNumber){ ViewGroup parent = (ViewGroup) view.getParent(); if(parent != null){ parent.removeAllViews(); } et_number_blacknumber_dialog.setText(blacknumber); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(添加黑名單); builder.setView(view); dialog = builder.create(); dialog.show(); flag = ADD; } } } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { // TODO Auto-generated method stub super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, MENU_UPDATE_ID, 0, 更新黑名單號碼); menu.add(0, MENU_DELETE_ID, 0, 刪除黑名單號碼); } @Override public boolean onContextItemSelected(MenuItem item) { // TODO Auto-generated method stub AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) item.getMenuInfo(); int position = acmi.position; blacknumber = (String) mAdapter.getItem(position); int id = item.getItemId(); switch (id) { case MENU_UPDATE_ID: ViewGroup parent = (ViewGroup) view.getParent(); if(parent != null){ parent.removeAllViews(); } et_number_blacknumber_dialog.setText(blacknumber); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(更新黑名單); builder.setView(view); dialog = builder.create(); dialog.show(); flag = UPDATE; break; case MENU_DELETE_ID: blackNumberDao.delete(blacknumber); mAdapter.setBlacknumbers(blackNumberDao.findAll()); mAdapter.notifyDataSetChanged(); break; default: break; } return super.onContextItemSelected(item); } //按鈕點擊事件 public void onClick(View v) { // TODO Auto-generated method stub int id = v.getId(); switch (id) { case R.id.tv_add_blacknumber: ViewGroup parent = (ViewGroup) view.getParent(); if(parent != null){ parent.removeAllViews(); } AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(添加黑名單); builder.setView(view); dialog = builder.create(); dialog.show(); flag = ADD; break; case R.id.bt_ok_blacknumber_dialog: String number = et_number_blacknumber_dialog.getText().toString(); if(.equals(number)){ Toast.makeText(this, 黑名單號碼不能為空, 1).show(); }else{ boolean isBlackNumber = blackNumberDao.isBlackNumber(number); if(isBlackNumber){ Toast.makeText(this, 號碼已經存在於黑名單中, 1).show(); }else{ if(flag == ADD){ blackNumberDao.add(number); Toast.makeText(this, 黑名單號碼添加成功, 1).show(); }else{ int _id = blackNumberDao.queryId(blacknumber); blackNumberDao.update(_id, number); Toast.makeText(this, 黑名單號碼修改成功, 1).show(); } dialog.dismiss(); List blacknumbers = blackNumberDao.findAll(); mAdapter.setBlacknumbers(blacknumbers); mAdapter.notifyDataSetChanged();//讓listview自動刷新 } } break; case R.id.bt_cancel_blacknumber_dialog: dialog.dismiss(); break; default: break; } } }
最後,別忘了在AndroidManifest.xml中注冊相關權限
具體要注冊的權限如下:
程序運行圖
添加黑名單
黑名單添加成功
長按更新或刪除黑名單
更新黑名單號碼
更新黑名單號碼
修改成功
以不是黑名單的號碼向手機打電話
正常顯示來電
以黑名單號碼給手機來電
不顯示來電
正常號碼向手機發短信
正常提示短信信息
以黑名單中的號碼向手機發短信
不提示短信信息
至此,手機黑名單功能完成。
本實例中,為了方面,我把一些文字直接寫在了布局文件中和相關的類中,大家在真實的項目中要把這些文字寫在string.xml文件中,在外部引用這些資源,切記,這是作為一個Android程序員最基本的開發常識和規范,我在這裡只是為了方便直接寫在了類和布局文件中。
大家可以發現, 原生的Image控件無法實現等比放大後無丟失顯示。如: 有一張20x10的圖片, 要放入一個40x30的顯示區域內.1. cover模式(默認),圖片放大
前言這篇文章沒有什麼可看性,主要是源碼注釋太多,推薦自己看源碼,更容易理解些,在這裡主要介紹,其運作流程,貼代碼片段。自定義View要重寫三個方法:onMeasure,o
1.SQLite 輕量級 .dp文件多用於手機裡輕量級的 嵌入式的 關系數據模型。SQLiteOpenHelper負責創建打開更新關閉數據庫創建數據表SQLiteData
然而這個菜單效果只是普通的側拉效果 我們還可以實現抽屜式側滑菜單 就像這樣第一種效果第二種效果第三種效果第四種效果其它代碼都和上篇文章相同,只是在MyHorizontal