Android Dialer,Mms,Contacts源码修改笔记

来源:互联网 发布:淘宝返利最高的软件 编辑:程序博客网 时间:2024/05/15 11:31

转载请注明出处:周木水的CSDN博客 http://blog.csdn.net/zhoumushui


最近部门没什么大的需求要改,就让我们改一下Android 4.4的拨号器,联系人以及短信源码。以微信电话本为蓝本,进行布局定制和功能完善。我主要负责拨号模块,涉及拨号面板,通话记录和通话详情这几个修改点。到今天差不多已经完成,下面总结一下过程中遇到的一些问题以及解决步骤:

相应模块实现功能:

①通话记录:运营商显示,ListView条目向右滑动拨号、向左滑动发短信,相同联系人记录合并,

②通话详情:号码归属地显示,联系人标星收藏,加入黑名单来电拦截,显示联系人头像,发送名片,通话记录显示最近5条,超过5条显示More按钮,点击可查看全部记录

======================================================================================================

1.将服务器目录挂载到本地:

sshfs zhoumushui@168.168.0.55:../zhoumushui  localcode/


2.源码布局分析
res/layout/diapad.xml
包括数字1-9,0,*,#以及对应英文字母。
res/layout/diapad_key.xml
数字0-9的,*,#,字母的属性,大小和颜色
res/layout/dialpad_fragment.xml
包括拨号面板的号码输入框,退格。下面链接dialpad.xml。下面是通话记录,拨号键,overflow menu
res/layout/dialtacts_activity.xml
打开拨号的默认界面,未呼出dialpad
res/layout/phone_no_favorites.xml
文字提示“您收藏的联系人和经常通话的……”,“所有联系人”按钮
res/layout/call_log_fragment.xml
通话记录的过滤查看(全部,来电,去电,未接)按钮
res/layout/call_log_list_item.xml
(拨号首页)通话记录具体条目内容:头像QuickPhoto,名字Name,标签Label,通话类型call_type,通话时长和日期call_count_and_date,secondary_action_icon,
res/layout/call_detail.xml
通话详情界面,包括main_action_push_layer,header_text,main_action,call_and_sms_action等。
res/layout/mtk_dialer_search_item_view.xml
拨号界面输入号码后,页面上端匹配联系人条目的布局文件,包括qucik_contact_photo, name, labelAndNumber等。
res/menu/call_log_options.xml
通话记录列表actionBar上的操作按钮:选择资源,删除。
com/android/dialer/PhoneCallDetailsView.java
显示通话记录条目的具体内容:包括name,callTyepe,callTypeIcons,call_count_and_date,label

3.编译命令
整体编译:

./mk -t -l=*** -o=TARGET_BUILD_VARIANT=*** n

-t表示打印出编译log信息
模块编译:
./mk -t mm packages/apps/Dialer

输出文件夹:
out/target/product/***/system/priv-app/Dialer.apk


4.adb的一些操作

adb push xxxx.apk /system/app     将应用程序的apk文件push到手机中adb install xxxx.apk    安装appadb install -r ~/Dialer.apk      //-r 表示强制安装


5.".9.png"图片原理和制作
左侧和上方的黑线交叉的部分即可扩展区域
右侧和下方的黑线交叉的部分即内容显示区域(如做button背景图时,button上文字的显示区域)

6.让Android4.0以上机器的虚拟按键中显示menu键 
在Activity的onCreate中设置flag:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY,WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY); 

7.Visibility的三种属性
1) 可见(visible)
XML文件:android:visibility="visible"
Java代码:view.setVisibility(View.VISIBLE);
2) 不可见(invisible)
XML文件:android:visibility="invisible"
Java代码:view.setVisibility(View.INVISIBLE);
3) 隐藏(GONE)
XML文件:android:visibility="gone"
Java代码:view.setVisibility(View.GONE);


8.去掉ListView的分割线
去掉分割线,把divider的颜色设为透明就行:android:divider="#00000000"
点击之后不让被点击项变成橘黄色:android:listSelector="#00000000"


9.setClickable,setFocusable
setClickable()控制按钮是否可以被点击和点击之后触发监听器事件。
setFocusable()控制键盘是否可以获得这个按钮的焦点。


10.寻找文件中关键字

find -name "*.java" | xargs grep -n "ic_phone_dk"// 寻找代码文件中的关键字find -name "*.xml"  | xargs grep -n "ic-phone_dk"// 寻找布局文件中的关键字



11.去掉Activity间的跳转动画
①在values文件夹的styles.xml文件里面新建一个style

<style name="AlexNoAniTheme" parent="android:Theme"><item name="android:windowAnimationStyle">@style/noAnimation</item><item name="android:windowNoTitle">true</item></style><style name="noAnimation"><item name="android:activityOpenEnterAnimation">@null</item><item name="android:activityOpenExitAnimation">@null</item><item name="android:activityCloseEnterAnimation">@null</item><item name="android:activityCloseExitAnimation">@null</item><item name="android:taskOpenEnterAnimation">@null</item><item name="android:taskOpenExitAnimation">@null</item><item name="android:taskCloseEnterAnimation">@null</item><item name="android:taskCloseExitAnimation">@null</item><item name="android:taskToFrontEnterAnimation">@null</item><item name="android:taskToFrontExitAnimation">@null</item><item name="android:taskToBackEnterAnimation">@null</item><item name="android:taskToBackExitAnimation">@null</item></style>


②在AndroidManifest.xml中修改相应Activity的theme
<activity android:name=".HomeActivity" android:label="@string/app_name" android:theme="@style/AlexNoAniTheme"> 


或者将 <item name="android:windowAnimationStyle">@style/noAnimation</item>加入Activity原来的theme

源码修改过程中遇到不少或大或小的问题,接着昨天总结吧。


12.设置分割线
横线:

<View android:layout_width="fill_parent" android:layout_height="1px" android:background="?android:attr/listDivider" />


竖线:
<Viewandroid:layout_width="1px"android:layout_height="fill_parent"android:background="?android:attr/listDivider" />



13.调用系统发短信界面

Uri uri = Uri.parse("smsto:" + callLog.getNumber());      // callLog.getNumber()为手机号码Intent it = new Intent(Intent.ACTION_SENDTO, uri); //it.putExtra("sms_body", "这是填充短信内容"); ctx.startActivity(it);


14.adb查看真机log

adb logcat *:E// Error级别,其他可类比


15.锁定Activity的屏幕方向
在Manifest文件中对应Activity属性中添加:     //在application中添加是不是整个应用都有效?不是

android:screenOrientation="nosensor"    <!--ES706为竖屏显示-->


16.Toast常犯错误
 Toast.makeText(this, text, Toast.LENGTH_SHORT).show();  // 常犯错误:忘了调用 show 方法,调试的时候死活不见吐司,搞的晕头转向,生生没有看见ADT的高亮显示,结果是这么明显的错误


17. LinearLayout
也可以有onClick属性,也可以findById,哈哈。这个很好用啊。


18.ActionBar返回上一层

import android.app.ActionBar;


Activity onCreate()中:
ActionBar actionBar = getActionBar();actionBar.setHomeButtonEnabled(true);actionBar.setDisplayHomeAsUpEnabled(true);actionBar.setDisplayShowHomeEnabled(true);



public boolean onOptionsItemSelected(MenuItem item) 中添加一个case:
case android.R.id.home:finish();     break;


19.在Adapter中进行跳转Activity

不同于Activity,Adapter中的跳转用上Context就可以了。


<p><span style="font-size: 14px;">/* </span></p><p><span style="font-size: 14px;">*Everything needs a context :(</span></p><p><span style="font-size: 14px;">*/</span></p>// RecordAdapter.javaprivate Context ctx;对应跳转代码:Intent intent = new Intent(ctx,com.android.dialer.calllog.RecordDetail.class);intent.putExtra("mNum", callLog.getNumber());ctx.startActivity(intent);//以下代码是Activity跳转动画,可忽略:// add for animation STARTint version = Integer.valueOf(android.os.Build.VERSION.SDK);if (version > 5) {((Activity)ctx).overridePendingTransition(com.android.dialer.R.anim.push_left_in,com.android.dialer.R.anim.push_left_out);}// add for animation END


20.overflow menu内容显示图标
其实,overflow中的Action按钮应不应该显示图标,是由MenuBuilder这个类的setOptionalIconsVisible变量来决定的,如果我们在overflow被展开的时候将这个变量赋值为true,那么里面的每一个Action按钮对应的图标就都会显示出来了。赋值的方法仍然是用反射了,代码如下:

@Override  public boolean onMenuOpened(int featureId, Menu menu) {      if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {          if (menu.getClass().getSimpleName().equals("MenuBuilder")) {              try {                  Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);                  m.setAccessible(true);                  m.invoke(menu, true);              } catch (Exception e) {              }          }      }      return super.onMenuOpened(featureId, menu);  } 

 

21.View的setVisibiility()
android view setVisibility():
有三个参数:Parameters:visibility One of VISIBLE, INVISIBLE, or GONE,想对应的三个常量值:0、4、8
VISIBLE:0  意思是可见的
INVISIBILITY:4 意思是不可见的,但还占着原来的空间
GONE:8  意思是不可见的,不占用原来的布局空间


22.联系人接口
添加号码到联系人或新建联系人:

Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); intent.setType("vnd.android.cursor.item/contact"); intent.putExtra(Intents.Insert.PHONE, number); startActivity(intent);



直接添加联系人:
Intent intent = new Intent(Contacts.Intents.Insert.ACTION);intent.setType(Contacts.People.CONTENT_TYPE);intent.putExtra(Contacts.Intents.Insert.NAME, "zhangsan");intent.putExtra(Contacts.Intents.Insert.PHONE, number);intent.putExtra(Contacts.Intents.Insert.PHONE_TYPE,Contacts.PhonesColumns.TYPE_MOBILE);intent.putExtra(Contacts.Intents.Insert.EMAIL, "");intent.putExtra(Contacts.Intents.Insert.COMPANY,"http://orgcent.com");startActivity(intent);


=====
根据number获取ContactId,然后编辑指定联系人:
String str = getContactId(this, number);long contactId = Long.parseLong(str);//Toast.makeText(this, "contactId:"+contactId, 2).show();Intent intent = new Intent(Intent.ACTION_EDIT); intent.setData(ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI,contactId)); startActivity(intent);// zj:根据号码获取ContactIdpublic static String getContactId(Context context, String number) {Cursor c = null;try {c = context.getContentResolver().query(Phone.CONTENT_URI,new String[] { Phone.CONTACT_ID, Phone.NUMBER }, null,null, null);if (c != null && c.moveToFirst()) {while (!c.isAfterLast()) {if (PhoneNumberUtils.compare(number, c.getString(1))) {return c.getString(0);}c.moveToNext();}}} catch (Exception e) {// Log.e(TAG, "getContactId error:", e);} finally {if (c != null) {c.close();}}return null;}


今天就到这儿吧,明天继续。

晚上出去逛了一下,刚刚回来,虽然时间有点晚了,还是得继续啊,嘿嘿~


23.根据number获取联系人名字(若存在)

// zj add for CachedName -->RealName startpublic String getContactNameByPhoneNumber(String number) {if (TextUtils.isEmpty(number)) {return null;}final ContentResolver resolver = ctx.getContentResolver();Uri lookupUri = null;String[] projection = new String[] { PhoneLookup._ID,PhoneLookup.DISPLAY_NAME };Cursor cursor = null;try {lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,Uri.encode(number));cursor = resolver.query(lookupUri, projection, null, null, null);} catch (Exception ex) {ex.printStackTrace();try {lookupUri = Uri.withAppendedPath(android.provider.Contacts.Phones.CONTENT_FILTER_URL,Uri.encode(number));cursor = resolver.query(lookupUri, projection, null, null, null);} catch (Exception e) {e.printStackTrace();}}String name = null;if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));}cursor.close();return name;}


24.根据ContactId获取联系人头像

//Avatar startpublic byte[] getPhoto(String people_id) {String photo_id = null;String selection1 = ContactsContract.Contacts._ID + " = " + people_id;Cursor cur1 = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, selection1, null,null);if (cur1.getCount() > 0) {cur1.moveToFirst();photo_id = cur1.getString(cur1.getColumnIndex(ContactsContract.Contacts.PHOTO_ID));// Log.i(TAG, "photo_id:" + photo_id); // 如果没有头像,这里为空值}String selection = null;if (photo_id == null) {return null;} else {selection = ContactsContract.Data._ID + " = " + photo_id;}String[] projection = new String[] { ContactsContract.Data.DATA15 };Cursor cur = getContentResolver().query(ContactsContract.Data.CONTENT_URI, projection, selection, null,null);cur.moveToFirst();byte[] contactIcon = cur.getBlob(0);// Log.i(TAG, "conTactIcon:" + contactIcon);if (contactIcon == null) {return null;} else {return contactIcon;}}public void setPhoto(String contactId) {// 以下代码将字节数组转化成Bitmap对象,然后再ImageView中显示出来ImageButton ibAvatar = (ImageButton) this.findViewById(com.android.dialer.R.id.zj_detail_avatar);// String contactId = "1"; // 2byte[] photo = getPhoto(contactId);if (photo != null) {Bitmap map = BitmapFactory.decodeByteArray(photo, 0, photo.length);ibAvatar.setImageBitmap(map);}}//Avatar end


25.Monkey调试
指定应用程序,并向其发送1000个伪随机事件:

 adb shell monkey -p com.android.dialer -v 1000

Monkey测试的停止条件
Monkey Test执行过程中在下列三种情况下会自动停止:
(1)如果限定了Monkey运行在一个或几个特定的包上,那么它会监测试图转到其它包的操作,并对其进行阻止。
(2)如果应用程序崩溃或接收到任何失控异常,Monkey将停止并报错。
(3)如果应用程序产生了应用程序不响应(application not responding)的错误,Monkey将会停止并报错。
通过多次并且不同设定下的Monkey测试才算它是一个稳定性足够的程序。


26.DDMS里面的Dump View Hierarchy for UI Automator

修改源码很痛苦的一件事就是分析每个界面的布局文件和代码文件分别是哪些。动则十几个package,分析起来很是头疼,这个工具可以小小的帮助我们一下。当然也可以用find命令查找资源在xml中的位置,然后在根据xml布局文件的名字在java中查找。以下是在网上找到的描述:
用来分析应用当前界面的View层次节点的,假设你现在是在用模拟器手机做调试,你用这个他就会构建一个你先在手机或模拟器显示界面的View的层次图,可以做一些性能的调优之类的。


27.putExtra获取到R.string中内容

it.putExtra("sms_body", this.getString(com.android.dialer.R.string.zj_name) + ":" + name + "\n" + this.getString(com.android.dialer.R.string.zj_number) + ":" + number);


28.通话记录去除重复记录,即同一个联系人只显示一条

去除相同数据

Uri uri = android.provider.CallLog.Calls.CONTENT_URI;String[] projection = { CallLog.Calls.DATE, CallLog.Calls.NUMBER,CallLog.Calls.TYPE, CallLog.Calls.CACHED_NAME,CallLog.Calls._ID, CallLog.Calls.DURATION, };asyncQuery.startQuery(0, null, uri, projection, "_id in (select max(_id)from calls group by number)", null,CallLog.Calls.DEFAULT_SORT_ORDER);


29.打开短信列表

Intent intent = new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_DEFAULT);intent.setType("vnd.android-dir/mms-sms");startActivity(intent);


30.短信数据库位置
文件 /data/data/com.android.providers.telephony/databases/mmssms.db 
这个数据库有13张表,sms表存了短信信息。

推荐一个查看真机里面数据库文件的方法,RE文件浏览器,很赞哦,不过要Root机子。


31.打开拨号界面

 Intent intent = new Intent(Intent.ACTION_DIAL,Uri.parse("tel:13850734494"));   intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);   startActivity(intent);


32.启动另外一个应用的Activity

ComponentName componetName = new ComponentName( //这个是另外一个应用程序的包名 "com.android.dialer", //这个参数是要启动的Activity "com.android.dialer.calllog.Record"); try { Intent intent = new Intent(); intent.setComponent(componetName); startActivity(intent); } catch (Exception e) { }


33.使用ComponentName启动另一个应用的Activity时出现java.lang.SecurityException: Permission Denial的解决方案:
原因分析:
在SDK版本eclair中(Level 5-7?),如果activity没有设定intent-filter则无法被外部程序启动!
解决办法:
给对应的activity添加intent-filter字段,且必须带上action,即使为空也行:

<intent-filter><action android:name=""/></intent-filter>


总结到这里的时候,由于另外两个同事有其他的事情要忙,我接手了联系人模块和短信模块,做后续的修改。

时间不早了,今天就到这吧。

有不明白的地方,欢迎交流。


接手了同事的短信模块和联系人模块,他们完成了基础功能,我做后续修改和功能强化。

短信模块主要增加了了以下功能:

短信会话加密(九宫格手势密码和字符密保),短信收藏。

联系人模块,实现了以下功能:

联系人列表长按多选群发短信,联系人右侧快速字母定位联系人,并且把标星联系人(☆)显示在列表前,非字母开头(#)显示在列表后。

联系人模块有较多的Bug,解决这些问题的过程也学到了不少。

话不多说,继续总结……


34.Cursor先获得制定列的下标,然后根据下标获得数据(不同系统的mmssms.db/sms标的列可能不同,i9250的原生4.3系统有17列,下标0-16.MTK的4.4.2有23列,下标0-22,多出几个自定义列)

int addressIndex = cur.getColumnIndex(“address”);int dateIndex = cur.getColumnIndex(“date”);int typeIndex = cur.getColumnIndex(“type”);int bodyIndex = cur.getColumnIndex(“body”);long address = Long.parseLong(cur.getString(addressIndex));long date = Long.parseLong(cur.getString(dateIndex));int type = Integer.parseInt(cur.getString(typeIndex));String body = cur.getString(bodyIndex);ZjLockMsg newMsg = new ZjLockMsg(threadId,address,date,type,body);


35.DATABASE_VERSION的问题
注意DATABASE_VERSION 值的问题,在程序运行时,如果改变了表的个数,再次运行时会出错.这是因为数据库改变时,会调用DatabaseHelper类.执行

super(context, DATABASE_NAME,null, DATABASE_VERSION ) 

这个方法,此时DATABASE_VERSION所对应的表的个数或者内容都是改变之前的, 这就造成异常的出现


36.使用Cursor使,读取里面的数据用到getColumnIndex()时报错:Index -1 requested, with a size of 1
仔细阅读过Cursor的文档,发现关于getColumnIndex()这个方法的说明如下:
public abstract int getColumnIndex (String columnName)
Since: API Level 1
Returns the zero-based index for the given column name, or -1 if the column doesn't exist. If you expect the column to exist use getColumnIndexOrThrow(String) instead, which will make the error more clear.
文档里清楚的表明:在要读取的列不存在的时候该方法会返回值“-1”。所以可知,以上报错可能是因为要get的列不存在,也可能是因为游标位置不对。后来发现,因为我在执行这个语句前没有执行“Cursor.moveToNext();”这个函数,导致游标还位于第一位置的前面,所以索引显示为“-1”,前面加上这句就没错了。
网上另有一种情况是用完Cursor没有关闭导致报此错误,所以也提醒各位用完Cursor要记得执行Cursor.close();


37.Your content must have a ListView whose id attribute is 'android.R.id.list'错误的解决办法

<ListView   android:id="@android:id/list" 或android:id="@id/android:list"  android:layout_width="fill_parent"  android:layout_height="wrap_content">  </ListView>  


38.模糊查找历史命令

history | grep -n "adb pull"


39.adb pull取设备中的数据库

adb pull /data/data/com.android.mms/databases/zj_msg ~/adb pull /data/data/com.android.providers.telephony/databases/mmssms.db


40.多个设备时adb操作
在adb的指令后面加上参数 -s <serialNumber> 比如 -s emulator-5554
就可以指定adb指令的目标。
在多个模拟器或者设备存在时很实用。

alex@alex-pc:~$ adb devicesList of devices attached 0123456789ABCDEF device016B7EB20100B003 devicealex@alex-pc:~$ adb -s 0123456789ABCDEF install -r mtk/zj/8382/out/target/product/esky82_tb_cn_kk/system/priv-app/Mms.apk


41.setBackgroundDrawable和setBackgroundColor的用法
设置背景图片,图片来源于drawable;

flightInfoPanel.setBackgroundDrawable(getResources().getDrawable(R.drawable.search_label_click));

转换字符串为int(颜色);
listItemView.deleteFilghtBg.setBackgroundColor(Color.parseColor("#F5F5DC"));


42.联系人标星收藏,即更改数据库starred字段值为1

ContentValues values = new ContentValues();values.put("starred", 1);this.getContentResolver().update(ContactsContract.RawContacts.CONTENT_URI, values,"contact_id = ?", new String[] { contactIdStr });


43.布局元素越界时,页面滚动
在LinearLayout外面包一层ScrollView即可,如下代码

ApiDemo 中关于如何使用ScrollView说明,请参考:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"android:scrollbars="none"></ScrollView>


44.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES

这样的问题主要是签名冲突造成的,比如你使用了ADB的debug权限签名,但后来使用标准sign签名后再安装同一个文件会出现这样的错误提示,解决的方法除了只有先老老实实从手机上卸载原有版本再进行安装,而adb install -r参数也无法解决这个问题。


45.cp命令移动隐藏文件

-a参数 所有文件,包括隐藏文件

-r参数 移动目录

所以备份时,直接cp -a -r 一步到位,省得一些点文件漏操作。


46.去除应用图标
去掉Manifest中Activity的对应标签:

<category android:name="android.intent.category.LAUNCHER" />


47.修改framework内容后编译推入设备

./mk -t mm frameworks/base/adb push ***/out/target/product/***/system/framework/framework.jar /system/frameworkadb push ***/out/target/product/***/system/framework/framework2.jar /system/framework

48.根据RawContactId获取contacts表中的lookup字段
public String getLookupKey(long contactId) {// Cursor cur = getContentResolver().query( // Raw Contacts表// ContactsContract.RawContacts.CONTENT_URI, null,// "contact_id = ?", new String[] { ""+contactId }, null);String lookupkey = "";// final Uri dataUri = Uri.withAppendedPath(// ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),// Contacts.Data.CONTENT_DIRECTORY);Cursor cursor = getContentResolver().query(Contacts.CONTENT_URI, null,"name_raw_contact_id = ?", new String[] { "" + contactId },null);if (cursor.moveToFirst()) {do {int starIndex = cursor.getColumnIndex(Contacts.LOOKUP_KEY);Log.e("LookupKey Index:", "" + starIndex);lookupkey = cursor.getString(starIndex); } while (cursor.moveToNext());}cursor.close();return lookupkey;}


这些内容多是我工作时,总结到Evernote的,可能不够详细,有什么不明白的欢迎交流。

今天就到这儿了,去洗个澡,明天又是新的一周,加油~


台风要来了,滴了些雨,顿时凉爽了很多。源码修改,继续。

有些Bug让人摸不着头脑,等发现了触发条件,就大致有了个分析方向,最后自然也可以找到原因。程序就是这么实在,什么反馈都是有原因的,真好。


49.解决ListView条目在应用初次启动时只能点击一次的情况,注释addFlags代码:

final Intent intent = new Intent(mPeopleActivity.this,ViewContactActivity.class);intent.setData(uri);//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);

50.在Adapter的onTouch监听中设置View的Down和Up事件:
if (event.getAction() == MotionEvent.ACTION_DOWN) {view.setBackgroundColor(Color.parseColor("#EBEBEB"));return false;} else if (event.getAction() == MotionEvent.ACTION_UP) {view.setBackgroundColor(Color.parseColor("#ffffff"));}

51.调整Bitmap对象的大小:

public void setPhoto(String contactId) {// 以下代码将字节数组转化成Bitmap对象,然后再ImageView中显示出来ImageButton ibAvatar = (ImageButton) this.findViewById(com.android.dialer.R.id.zj_detail_avatar);byte[] photo = getPhoto(contactId);if (photo != null) {Bitmap map = BitmapFactory.decodeByteArray(photo, 0, photo.length);// 调整大小 STARTint scaleWidth = 110;int scaleHeight = 110;Bitmap bitmap = Bitmap.createScaledBitmap(map, scaleWidth,scaleHeight, true);// 调整大小 ENDibAvatar.setImageBitmap(bitmap);      // 参数由原来的map换成转换过的bitmap}}

52.调整Activity跳转动画的速度
改一下duration,500到300
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" ><translateandroid:duration="300"android:fromXDelta="0"android:toXDelta="100%p" /></set>


53.联系人列表标星Tag显示★而不是字母逻辑:
// 获得当前姓名的拼音首字母String mfirstLetter = PinyinUtils.getPingYin(Utils.mPersons.get(position).mName).substring(0, 1).toUpperCase();String zjContactIdStr = Utils.mPersons.get(position).mID;boolean isStar = isStarred(zjContactIdStr);String firstLetter;Log.e("zjStar","position" + position + "\tcontactId"+ Utils.mPersons.get(position).mContactId + "\tName:"+ Utils.mPersons.get(position).mName + "\tisStar:"+ isStar);if (isStar) {firstLetter = "☆";} else {firstLetter = notAlpha(mfirstLetter);}

54.联系人标星Tag去重(多个标星归档只显示一个Tag)逻辑
// 如果是第1个联系人 那么letterTag始终要显示if (position == 0) {myViews.letterTag.setVisibility(View.VISIBLE);myViews.letterTag.setText(firstLetter);} else {if (isStar) {myViews.letterTag.setVisibility(View.GONE);} else {// 获得上一个姓名的拼音首字母String mfirstLetterPre = PinyinUtils.getPingYin(Utils.mPersons.get(position - 1).mName).substring(0, 1).toUpperCase();String firstLetterPre = notAlpha(mfirstLetterPre); // 比较一下两者是否相同if (firstLetter.equals(firstLetterPre)) {myViews.letterTag.setVisibility(View.GONE);} else {myViews.letterTag.setVisibility(View.VISIBLE);myViews.letterTag.setText(firstLetter);}}}

55.获得汉语拼音首字母
private String notAlpha(String str) {if (str == null) {return "#";}if (str.trim().length() == 0) {return "#";}// 正则表达式,判断首字母是否是英文字母Pattern pattern = Pattern.compile("^[A-Za-z]+$");if (pattern.matcher(str).matches()) {return (str).toUpperCase();} else {return "#";}}

56. 联系人列表右侧快速定位字母条监听
// 字母列触摸的监听器private class ScrollBarListener implementsAlphabetScrollBar.OnTouchBarListener {@Overridepublic void onTouch(String letter) {if (letter.compareTo("☆") == 0) {m_contactslist.setSelection(0); // 跳转到列表开始} else if (letter.compareTo("#") == 0) {int zjCount = m_contactslist.getChildCount();m_contactslist.setSelection(zjCount); // 跳转到列表最后位置} else {// 触摸字母列时,将联系人列表更新到首字母出现的位置int idx = Utils.binarySearch(letter);if (idx != -1) {m_contactslist.setSelection(idx);}}}}

57.搜索联系人过滤器
public void FilterSearch(String keyword) {mFilterList.clear();// 遍历mArrayListfor (int i = 0; i < Utils.mPersons.size(); i++) {// 如果遍历到List包含所输入字符串boolean isSearchNum = Utils.mPersons.get(i).mNum == null ? false: (Utils.mPersons.get(i).mNum.indexOf(keyword) > 0);if (isSearchNum// 为空时 报错|| isStrInString(Utils.mPersons.get(i).mPY, keyword)|| Utils.mPersons.get(i).mName.contains(keyword)|| isStrInString(Utils.mPersons.get(i).mFisrtSpell, keyword)) {// 将遍历到的元素重新组成一个listSortEntry entry = new SortEntry();entry.mName = Utils.mPersons.get(i).mName;entry.mID = Utils.mPersons.get(i).mID;entry.mOrder = i; // 在原Cursor中的位置entry.mPY = Utils.mPersons.get(i).mPY;entry.mNum = Utils.mPersons.get(i).mNum;mFilterList.add(entry);}}}

58.长按多选联系人群发短信逻辑

private class MultiSmsTask extends AsyncTask<Void, Integer, Void> {@Overrideprotected Void doInBackground(Void... params) {ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();for (int i = 0; i < ChooseContactsID.size(); i++) {// ops.add(ContentProviderOperation.newDelete(Uri.withAppendedPath(RawContacts.CONTENT_URI,// ChooseContactsID.get(i))).build());String contactId = ChooseContactsID.get(i);zjSmsNum = zjSmsNum + getNumByContactId(contactId)+";";}Log.e("zj", "zjSmsNum:" + zjSmsNum + " ZJSmsCount:"+ ChooseContactsID.size());// Uri uri = Uri.parse("smsto:" + zjSmsNum);Intent it = new Intent(Intent.ACTION_SENDTO, uri);startActivity(it);/** try { getContentResolver()* .applyBatch(ContactsContract.AUTHORITY, ops);* //Log.e("ZJSmsCount", "" + ChooseContactsID.size()); } catch* (RemoteException e) { // TODO Auto-generated catch block* e.printStackTrace(); } catch (OperationApplicationException e) {* // TODO Auto-generated catch block e.printStackTrace(); }*/return null;}@Overrideprotected void onPostExecute(Void result) {if (m_dialogLoading != null) {m_dialogLoading.dismiss();finish();}}@Overrideprotected void onPreExecute() {m_dialogLoading = new ProgressDialog(MultiDeleteActivity.this);m_dialogLoading.setProgressStyle(ProgressDialog.STYLE_SPINNER);// 设置风格为圆形进度条m_dialogLoading.setMessage("正在发送");m_dialogLoading.setCancelable(false);m_dialogLoading.show();}@Overrideprotected void onProgressUpdate(Integer... values) {}}

到此问题解决的差不多了,明天再写一篇估计就可以完结这个系列。以后遇到问题再补充,有不明白的地方欢迎交流。

今天就到这儿吧,学习的脚步不能停止,加油。


完结篇~

今天工作感觉挺累的,就没有加班在,早早的溜回来,看了会儿蓝牙的资料。不知不觉都9点了,开始~


59.B应用要用A应用的Activity2,但Activity2依赖于A应用的Activity1,也就是说Activity2要使用Activity1处理后的数据,这时候可以让B应用跳转到一个Activity3,Activity3和Activity1内容相同,但是在onResume中写下跳转到Activity2的逻辑:

protected void onResume() {// TODO Auto-generated method stubsuper.onResume();Intent intent = new Intent(this, MultiChoiseSecond.class);startActivity(intent);finish();}

60.加快拨号面板号码匹配响应时间

// at com.android.dialer.dialpad.DialpadFragment.javapublic void afterTextChanged(Editable input) {// When DTMF dialpad buttons are being pressed, we delay// SpecialCharSequencMgr sequence,// since some of SpecialCharSequenceMgr's behavior is too abrupt for the// "touch-down"// behavior.inputstring = input.toString();// mHandler.postDelayed(mrunnable, 2000);//czqmHandler.postDelayed(mrunnable, 100); // zj}

61.加快三个应用间Activity的跳转速度

android:launchMode="singleTask"


62.让应用不显示在最近运行程序列表中
在主activity处设置属性:

android:excludeFromRecents=“true”


63.解决页面之间跳转时的短暂黑屏问题

<?xml version="1.0" encoding="utf-8"?><resources> <style name="activityTheme" parent="@android:style/Theme"> <item name="android:windowIsTranslucent">true</item> (zj:这一个item就够了)</style> </resources>

64.最小化应用(注释代码为杀死应用)

// Intent intent = new Intent(Intent.ACTION_MAIN);// intent.addCategory(Intent.CATEGORY_HOME);// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);// startActivity(intent);// android.os.Process.killProcess(android.os.Process.myPid());Intent intent = new Intent(Intent.ACTION_MAIN);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.addCategory(Intent.CATEGORY_HOME);startActivity(intent);


65.获取Log
权限:

<uses-permission android:name=”android.permission.READ_LOGS” />

new Thread(new Runnable(){@Overridepublic void run(){Process logcatProcess = null;BufferedReader bufferedReader = null;try{/** 获取系统logcat日志信息 */logcatProcess = Runtime.getRuntime().exec(new String[] {“logcat”, “ActivityManager:I *:S”});bufferedReader = new BufferedReader(new InputStreamReader(logcatProcess.getInputStream()));String line;while ((line = bufferedReader.readLine()) != null){if (line.indexOf(“cat=[android.intent.category.HOME]“) > 0){Session.exit();}}}catch (Exception e){e.printStackTrace();}}}).start();

今天的内容相对较少,目前项目还存在一些待优化的问题,以后再跟进补充。


66.通话记录ListView实现左滑发短信和右滑打电话

下午公司篮球赛最后一场,超神队一分憾败。可惜,不过比赛归比赛,重要的是过程中的精彩。

通话记录向左滑动发短信,向右滑动打电话,微信电话本上有这个很便捷的操作,在项目的修改过程中,就仿造微信的交互方式,实现了ListView左右滑动,今天总结一下:

同样由于是在源码环境下编译,所以代码中的资源引用之类地方的可能和常规的稍微不同。

// 滑动之后的回调方法@Overridepublic void removeItem(RemoveDirection direction, int position) {String sildeNum = callLogs.get(position).getNumber(); // zj:获取滑动Item的号码switch (direction) {case RIGHT:tvTopBar.setBackgroundColor(Color.parseColor("#454545"));tvTopBar.setText(com.android.dialer.R.string.call_log_activity_title);RecordSlideListView.itemView.setBackgroundColor(Color.parseColor("#ffffff"));Uri uri = Uri.parse("tel:" + sildeNum);Intent intent = new Intent(Intent.ACTION_CALL, uri);startActivity(intent);break;case LEFT:// Toast.makeText(this, "向左短信  "+ position,// Toast.LENGTH_SHORT).show();// rlRecordBack.setBackgroundColor(Color.parseColor("#1C86EE"));tvTopBar.setBackgroundColor(Color.parseColor("#454545"));tvTopBar.setText(com.android.dialer.R.string.call_log_activity_title);RecordSlideListView.itemView.setBackgroundColor(Color.parseColor("#ffffff"));Uri uri2 = Uri.parse("smsto:" + sildeNum);Intent intent2 = new Intent(Intent.ACTION_SENDTO, uri2);startActivity(intent2);break;default:break;}}


自定义组件SlideListView:

package com.android.dialer.calllog;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.WindowManager;import android.widget.AdapterView;import android.widget.ListView;import android.widget.Scroller;import android.widget.TextView;public class RecordSlideListView extends ListView {private int slidePosition;private int downY;private int downX;private int screenWidth;public static View itemView;private Scroller scroller;private static final int SNAP_VELOCITY = 300;private VelocityTracker velocityTracker;private boolean isSlide = false;private int mTouchSlop;private RemoveListener mRemoveListener;private RemoveDirection removeDirection;public enum RemoveDirection {RIGHT, LEFT;}public RecordSlideListView(Context context) {this(context, null);}public RecordSlideListView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public RecordSlideListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);screenWidth = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();scroller = new Scroller(context);mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();}public void setRemoveListener(RemoveListener removeListener) {this.mRemoveListener = removeListener;}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: {addVelocityTracker(event);if (!scroller.isFinished()) {return super.dispatchTouchEvent(event);}downX = (int) event.getX();downY = (int) event.getY();slidePosition = pointToPosition(downX, downY);if (slidePosition == AdapterView.INVALID_POSITION) {return super.dispatchTouchEvent(event);}itemView = getChildAt(slidePosition - getFirstVisiblePosition());break;}case MotionEvent.ACTION_MOVE: {if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY|| (Math.abs(event.getX() - downX) > mTouchSlop && Math.abs(event.getY() - downY) < mTouchSlop)) {isSlide = true;}break;}case MotionEvent.ACTION_UP:recycleVelocityTracker();break;}return super.dispatchTouchEvent(event);}private void scrollRight() {// tvTopBar =// (TextView)findViewById(com.android.dialer.R.id.textViewRecord);// tvTopBar.setBackgroundColor(Color.parseColor("#32CD32"));// tvTopBar.setText(com.android.dialer.R.string.zj_slide_right_to_call);removeDirection = RemoveDirection.RIGHT;final int delta = (screenWidth + itemView.getScrollX());scroller.startScroll(itemView.getScrollX(), 0, -delta, 0,Math.abs(delta));postInvalidate();}private void scrollLeft() {removeDirection = RemoveDirection.LEFT;final int delta = (screenWidth - itemView.getScrollX());scroller.startScroll(itemView.getScrollX(), 0, delta, 0,Math.abs(delta));postInvalidate();}private void scrollByDistanceX() {if (itemView.getScrollX() >= screenWidth / 3) {scrollLeft();} else if (itemView.getScrollX() <= -screenWidth / 3) {scrollRight();} else {itemView.scrollTo(0, 0);// zj:恢复Topbar的字和背景颜色itemView.setBackgroundColor(Color.parseColor("#ffffff"));Record.tvTopBar.setBackgroundColor(Color.parseColor("#454545"));Record.tvTopBar.setText(com.android.dialer.R.string.call_log_activity_title);}}@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (isSlide && slidePosition != AdapterView.INVALID_POSITION) {addVelocityTracker(ev);final int action = ev.getAction();int x = (int) ev.getX();switch (action) {case MotionEvent.ACTION_MOVE:int deltaX = downX - x;downX = x;if (deltaX > 20) { // 向左sms #D1EEEEitemView.setBackgroundColor(Color.parseColor("#D1EEEE"));Record.tvTopBar.setBackgroundColor(Color.parseColor("#1C86EE"));Record.tvTopBar.setText(com.android.dialer.R.string.zj_slide_left_to_sms);} else if (deltaX < -20) { // 向右callitemView.setBackgroundColor(Color.parseColor("#D1EEEE"));Record.tvTopBar.setBackgroundColor(Color.parseColor("#32CD32"));Record.tvTopBar.setText(com.android.dialer.R.string.zj_slide_right_to_call);}itemView.scrollBy(deltaX, 0);return true; // 拖动的时候ListView不滚动// break;case MotionEvent.ACTION_UP:int velocityX = getScrollVelocity();if (velocityX > SNAP_VELOCITY) {scrollRight();} else if (velocityX < -SNAP_VELOCITY) {scrollLeft();} else {scrollByDistanceX();}recycleVelocityTracker();isSlide = false;break;}return true;}return super.onTouchEvent(ev);}@Overridepublic void computeScroll() {if (scroller.computeScrollOffset()) {itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());postInvalidate();if (scroller.isFinished()) {if (mRemoveListener == null) {throw new NullPointerException("RemoveListener is null, we should called setRemoveListener()");}itemView.scrollTo(0, 0);mRemoveListener.removeItem(removeDirection, slidePosition);}}}private void addVelocityTracker(MotionEvent event) {if (velocityTracker == null) {velocityTracker = VelocityTracker.obtain();}velocityTracker.addMovement(event);}private void recycleVelocityTracker() {if (velocityTracker != null) {velocityTracker.recycle();velocityTracker = null;}}private int getScrollVelocity() {velocityTracker.computeCurrentVelocity(1000);int velocity = (int) velocityTracker.getXVelocity();return velocity;}public interface RemoveListener {public void removeItem(RemoveDirection direction, int position);}}


代码有点长,Adapter的就先不贴了。


67.黑名单来电拦截自动挂断

准备工作:

添加一个名为android.telephony的包,里面有个NeighboringCellInfo.aidl,内容如下:

/* //device/java/android/android/content/Intent.aidl**** Copyright 2007, The Android Open Source Project**** Licensed under the Apache License, Version 2.0 (the "License");** you may not use this file except in compliance with the License.** You may obtain a copy of the License at****     http://www.apache.org/licenses/LICENSE-2.0**** Unless required by applicable law or agreed to in writing, software** distributed under the License is distributed on an "AS IS" BASIS,** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.** See the License for the specific language governing permissions and** limitations under the License.*/package android.telephony;parcelable NeighboringCellInfo;

新建包名为com.android.internal.telephony的包,新建文件ITelephony.aidl,文件内容如下:

/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.android.internal.telephony;import android.os.Bundle;import java.util.List;import android.telephony.NeighboringCellInfo;/** * Interface used to interact with the phone.  Mostly this is used by the  * TelephonyManager class.  A few places are still using this directly. * Please clean them up if possible and use TelephonyManager insteadl. * * {@hide} */interface ITelephony {    /**     * Dial a number. This doesn't place the call. It displays     * the Dialer screen.     * @param number the number to be dialed. If null, this     * would display the Dialer screen with no number pre-filled.     */    void dial(String number);    /**     * Place a call to the specified number.     * @param number the number to be called.     */    void call(String number);    /**     * If there is currently a call in progress, show the call screen.     * The DTMF dialpad may or may not be visible initially, depending on     * whether it was up when the user last exited the InCallScreen.     *     * @return true if the call screen was shown.     */    boolean showCallScreen();    /**     * Variation of showCallScreen() that also specifies whether the     * DTMF dialpad should be initially visible when the InCallScreen     * comes up.     *     * @param showDialpad if true, make the dialpad visible initially,     *                    otherwise hide the dialpad initially.     * @return true if the call screen was shown.     *     * @see showCallScreen     */    boolean showCallScreenWithDialpad(boolean showDialpad);    /**     * End call or go to the Home screen     *     * @return whether it hung up     */    boolean endCall();    /**     * Answer the currently-ringing call.     *     * If there's already a current active call, that call will be     * automatically put on hold.  If both lines are currently in use, the     * current active call will be ended.     *     * TODO: provide a flag to let the caller specify what policy to use     * if both lines are in use.  (The current behavior is hardwired to     * "answer incoming, end ongoing", which is how the CALL button     * is specced to behave.)     *     * TODO: this should be a oneway call (especially since it's called     * directly from the key queue thread).     */    void answerRingingCall();    /**     * Silence the ringer if an incoming call is currently ringing.     * (If vibrating, stop the vibrator also.)     *     * It's safe to call this if the ringer has already been silenced, or     * even if there's no incoming call.  (If so, this method will do nothing.)     *     * TODO: this should be a oneway call too (see above).     *       (Actually *all* the methods here that return void can     *       probably be oneway.)     */    void silenceRinger();    /**     * Check if we are in either an active or holding call     * @return true if the phone state is OFFHOOK.     */    boolean isOffhook();    /**     * Check if an incoming phone call is ringing or call waiting.     * @return true if the phone state is RINGING.     */    boolean isRinging();    /**     * Check if the phone is idle.     * @return true if the phone state is IDLE.     */    boolean isIdle();    /**     * Check to see if the radio is on or not.     * @return returns true if the radio is on.     */    boolean isRadioOn();    /**     * Check if the SIM pin lock is enabled.     * @return true if the SIM pin lock is enabled.     */    boolean isSimPinEnabled();    /**     * Cancels the missed calls notification.     */    void cancelMissedCallsNotification();     /**     * Supply a pin to unlock the SIM.  Blocks until a result is determined.     * @param pin The pin to check.     * @return whether the operation was a success.     */    boolean supplyPin(String pin);    /**     * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated     * without SEND (so <code>dial</code> is not appropriate).     *      * @param dialString the MMI command to be executed.     * @return true if MMI command is executed.     */    boolean handlePinMmi(String dialString);    /**     * Toggles the radio on or off.     */    void toggleRadioOnOff();    /**     * Set the radio to on or off     */    boolean setRadio(boolean turnOn);    /**     * Request to update location information in service state     */    void updateServiceLocation();    /**     * Enable location update notifications.     */    void enableLocationUpdates();    /**     * Disable location update notifications.     */    void disableLocationUpdates();    /**     * Enable a specific APN type.     */    int enableApnType(String type);    /**     * Disable a specific APN type.     */    int disableApnType(String type);    /**     * Allow mobile data connections.     */    boolean enableDataConnectivity();    /**     * Disallow mobile data connections.     */    boolean disableDataConnectivity();    /**     * Report whether data connectivity is possible.     */    boolean isDataConnectivityPossible();    Bundle getCellLocation();    /**     * Returns the neighboring cell information of the device.     */    List<NeighboringCellInfo> getNeighboringCellInfo();     int getCallState();     int getDataActivity();     int getDataState();}

广播:

/* * Add By ZJ For Blacklist */package com.android.dialer.calllog;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;public class BlacklistBroadcast extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stubif (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)){}else{Intent Sbintent = new Intent(context,com.android.dialer.calllog.BlacklistService.class);context.startService(Sbintent);}}}

DbHelper:

/* * Add By ZJ For Blacklist */package com.android.dialer.calllog;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase.CursorFactory;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class BlacklistDbHelper extends SQLiteOpenHelper{final String CREATE_TABLE_SQL ="create table blacklist(_id integer primary key autoincrement , number text)";public BlacklistDbHelper(Context context, String name,CursorFactory factory, int version) {super(context, name, factory, version);// TODO Auto-generated constructor stub}@Overridepublic void onCreate(SQLiteDatabase db) {// TODO Auto-generated method stubdb.execSQL(CREATE_TABLE_SQL);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stub}//zj add STARTpublic Cursor getCursorByNumber(String number) {String selectQuery = "SELECT * FROM blacklist where number = "+number;SQLiteDatabase db = this.getReadableDatabase();Cursor cursor = db.rawQuery(selectQuery, null);return cursor;}//zj add END}



Service:

/* * Add By ZJ For Blacklist */package com.android.dialer.calllog;import java.lang.reflect.Method;import com.android.internal.telephony.ITelephony;import android.app.Service;import android.content.Intent;import android.database.Cursor;import android.os.IBinder;import android.telephony.PhoneStateListener;import android.telephony.TelephonyManager;public class BlacklistService extends Service {BlacklistDbHelper dbHelper = new BlacklistDbHelper(this, "blacklist.db",null, 1);TelephonyManager tManager;CustomPhoneCallListener cpListener;public class CustomPhoneCallListener extends PhoneStateListener {@Overridepublic void onCallStateChanged(int state, String incomingNumber) {switch (state) {case TelephonyManager.CALL_STATE_IDLE:break;case TelephonyManager.CALL_STATE_OFFHOOK:break;case TelephonyManager.CALL_STATE_RINGING:System.out.println("this is 1");if (isBlock(incomingNumber)) {System.out.println("this is 2");try {Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);IBinder binder = (IBinder) method.invoke(null,new Object[] { TELEPHONY_SERVICE });ITelephony telephony = ITelephony.Stub.asInterface(binder);telephony.endCall();} catch (Exception e) {e.printStackTrace();}}break;}super.onCallStateChanged(state, incomingNumber);}public boolean isBlock(String number) {Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from blacklist", null);while (cursor.moveToNext()) {if (cursor.getString(1).equals(number)) {return true;}}return false;}}@Overridepublic IBinder onBind(Intent arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic void onStart(Intent intent, int startId) {// TODO Auto-generated method stubtManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);cpListener = new CustomPhoneCallListener();tManager.listen(cpListener, PhoneStateListener.LISTEN_CALL_STATE);super.onStart(intent, startId);}}

ListView:

/* * Add By ZJ For Blacklist */package com.android.dialer.calllog;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.content.ContentResolver;import android.content.ContentUris;import android.content.Intent;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.net.Uri;import android.os.Bundle;import android.provider.CallLog;import android.provider.Contacts;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ArrayAdapter;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;import android.app.ActionBar; // for ActionBarpublic class BlacklistView extends Activity {public int version = Integer.valueOf(android.os.Build.VERSION.SDK);BlacklistDbHelper dbHelper;@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(com.android.dialer.R.layout.zj_blacklist_result);dbHelper = new BlacklistDbHelper(this, "blacklist.db", null, 1);final ListView listview = (ListView) findViewById(com.android.dialer.R.id.zj_blacklist_view);listview.setAdapter(new ArrayAdapter<String>(this,com.android.dialer.R.layout.zj_blacklist_item, getData()));TextView tvHint = (TextView) findViewById(com.android.dialer.R.id.zj_balcklist_hint);TextView tvEmpty = (TextView) findViewById(com.android.dialer.R.id.zj_blacklist_empty);if (getData().isEmpty()) {// Toast.makeText(BlacklistView.this, "暂无黑名单", 5000).show();tvEmpty.setVisibility(View.VISIBLE);tvHint.setVisibility(View.GONE);// Intent intent = new Intent(BlacklistView.this,// com.android.dialer.calllog.BlacklistChoice.class);// intent.putExtra("number", "kong");// startActivity(intent);// finish();} else {tvEmpty.setVisibility(View.GONE);tvHint.setVisibility(View.VISIBLE);}listview.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {String phoneNum = arg0.getItemAtPosition(arg2).toString();dbHelper.getReadableDatabase().execSQL("delete from blacklist where number = ?",new String[] { phoneNum });onCreate(null);}});// actionBarActionBar actionBar = getActionBar();actionBar.setHomeButtonEnabled(true);actionBar.setDisplayHomeAsUpEnabled(true);actionBar.setDisplayShowHomeEnabled(true);}private List<String> getData() {List<String> data = new ArrayList<String>();Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from blacklist", null);while (cursor.moveToNext()) {data.add(cursor.getString(1));}cursor.close(); // zj:close cursorreturn data;}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(com.android.dialer.R.menu.zj_blacklist_view,menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {// add for action startcase android.R.id.home:finish();// add for animation startif (version > 5) {overridePendingTransition(com.android.dialer.R.anim.zj_right_in,com.android.dialer.R.anim.zj_right_out);}// add for animation endbreak;// add for action endcase com.android.dialer.R.id.zj_blacklist_clear:dbHelper.getReadableDatabase().execSQL("delete from blacklist");onCreate(null);break;default:break;}return true;}}


操作:

case com.android.dialer.R.id.zj_add_blacklist:// 添加到黑名单BlacklistDbHelper dbHelper = new BlacklistDbHelper(this,"blacklist.db", null, 1);boolean isBlack = isNumBlack(number);// ToDo:判断if (isBlack) { // 已经添加Toast.makeText(this, com.android.dialer.R.string.zj_alredy_add,Toast.LENGTH_SHORT).show();} else {dbHelper.getReadableDatabase().execSQL("insert into blacklist values(null , ?)",new String[] { number });ibAddBlack.setBackgroundDrawable(getResources().getDrawable(com.android.dialer.R.drawable.zj_block_red));Toast.makeText(this,com.android.dialer.R.string.zj_add_blacklist_ok,Toast.LENGTH_SHORT).show();}break;

// 联系人是否在黑名单中?public boolean isNumBlack(String number) {BlacklistDbHelper dbHelper = new BlacklistDbHelper(this,"blacklist.db", null, 1);Cursor countCur = dbHelper.getCursorByNumber(number);int numCount = 0;try {numCount = countCur.getCount(); // zj:查看是否已在黑名单Log.e("numCount", "" + numCount);} catch (Exception e) {// _db.onCreate(_db);} finally {countCur.close(); // zj close cursor}if (numCount > 0)return true;elsereturn false;}

// 加入或移出黑名单public void zjAddBlack(View v) {BlacklistDbHelper dbHelper = new BlacklistDbHelper(this,"blacklist.db", null, 1);boolean isBlack = isNumBlack(number);if (isBlack) {// 从黑名单移除号码dbHelper.getReadableDatabase().execSQL("delete from blacklist where number = ?",new String[] { number });ibAddBlack.setBackgroundDrawable(getResources().getDrawable(com.android.dialer.R.drawable.zj_block_grey));isBlack = false;Toast.makeText(this,com.android.dialer.R.string.zj_del_blacklist_ok,Toast.LENGTH_SHORT).show();} else if (!isBlack) {// 添加到黑名单dbHelper.getReadableDatabase().execSQL("insert into blacklist values(null , ?)",new String[] { number });ibAddBlack.setBackgroundDrawable(getResources().getDrawable(com.android.dialer.R.drawable.zj_block_red));isBlack = true;Toast.makeText(this,com.android.dialer.R.string.zj_add_blacklist_ok,Toast.LENGTH_SHORT).show();}}

67.来电界面InCallUI的布局修改:

有个客户要让来电界面适应他们提供的皮套,这样的话,就要根据皮套的开窗来调节来电界面。而来电界面并不是在Dialer代码中,而是在pacakage/apps/InCallUI中,调节对应的布局文件。

编译的时候也要注意,不可以单独模块编译InCallUI,模块编译Dialer就会把InCallUI编译进去了。

packages/apps/InCallUI/res/layout/primary_call_info.xml


2 0