14.adb查看真机log
adb logcat *:E // Error级别,其他可类比
15.锁定Activity的屏幕方向
在Manifest文件中对应Activity属性中添加: //在application中添加是不是整个应用都有效?不是
android:screenOrientation=“nosensor”
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就可以了。
/*
*Everything needs a context :(
*/
// 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 START
int 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:根据号码获取ContactId
public 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 start
public 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 start
public 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”; // 2
byte[] 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,即使为空也行:
总结到这里的时候,由于另外两个同事有其他的事情要忙,我接手了联系人模块和短信模块,做后续的修改。
时间不早了,今天就到这吧。
有不明白的地方,欢迎交流。
接手了同事的短信模块和联系人模块,他们完成了基础功能,我做后续修改和功能强化。
短信模块主要增加了了以下功能:
短信会话加密(九宫格手势密码和字符密保),短信收藏。
联系人模块,实现了以下功能:
联系人列表长按多选群发短信,联系人右侧快速字母定位联系人,并且把标星联系人(☆)显示在列表前,非字母开头(#)显示在列表后。
联系人模块有较多的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”>
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 比如 -s emulator-5554
就可以指定adb指令的目标。
在多个模拟器或者设备存在时很实用。
alex@alex-pc:~$ adb devices
List of devices attached
0123456789ABCDEF device
016B7EB20100B003 device
alex@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.p
ut(“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”>
44.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES
这样的问题主要是签名冲突造成的,比如你使用了ADB的debug权限签名,但后来使用标准sign签名后再安装同一个文件会出现这样的错误提示,解决的方法除了只有先老老实实从手机上卸载原有版本再进行安装,而adb install -r参数也无法解决这个问题。
45.cp命令移动隐藏文件
-a参数 所有文件,包括隐藏文件
-r参数 移动目录
所以备份时,直接cp -a -r 一步到位,省得一些点文件漏操作。
46.去除应用图标
去掉Manifest中Activity的对应标签:
47.修改framework内容后编译推入设备
./mk -t mm frameworks/base/
adb push /out/target/product//system/framework/framework.jar /system/framework
adb 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);
// 调整大小 START
int scaleWidth = 110;
int scaleHeight = 110;
Bitmap bitmap = Bitmap.createScaledBitmap(map, scaleWidth,
scaleHeight, true);
// 调整大小 END
ibAvatar.setImageBitmap(bitmap); // 参数由原来的map换成转换过的bitmap
}
}
52.调整Activity跳转动画的速度
改一下duration,500到300
<?xml version="1.0" encoding="utf-8"?><translate
android:duration=“300”
android:fromXDelta=“0”
android:toXDelta=“100%p” />
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);
}
}
}
private String notAlpha(String str) {
if (str == null) {
return “#”;
}
if (str.trim().length() == 0) {
return “#”;
}
// 正则表达式,判断首字母是否是英文字母
Pattern pattern = Pattern.compile(“1+$”);
if (pattern.matcher(str).matches()) {
return (str).toUpperCase();
} else {
return “#”;
}
}
56. 联系人列表右侧快速定位字母条监听
// 字母列触摸的监听器
private class ScrollBarListener implements
AlphabetScrollBar.OnTouchBarListener {
@Override
public 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();
// 遍历mArrayList
for (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)) {
// 将遍历到的元素重新组成一个list
SortEntry 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> {
@Override
protected Void doInBackground(Void… params) {
ArrayList ops = new ArrayList();
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;
}
@Override
protected void onPostExecute(Void result) {
if (m_dialogLoading != null) {
m_dialogLoading.dismiss();
finish();
}
}
@Override
protected void onPreExecute() {
m_dialogLoading = new ProgressDialog(MultiDeleteActivity.this);
m_dialogLoading.setProgressStyle(ProgressDialog.STYLE_SPINNER);// 设置风格为圆形进度条
m_dialogLoading.setMessage(“正在发送”);
m_dialogLoading.setCancelable(false);
m_dialogLoading.show();
}
@Override
protected 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 stub
super.onResume();
Intent intent = new Intent(this, MultiChoiseSecond.class);
startActivity(intent);
finish();
}
60.加快拨号面板号码匹配响应时间
// at com.android.dialer.dialpad.DialpadFragment.java
public 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);//czq
mHandler.postDelayed(mrunnable, 100); // zj
}
61.加快三个应用间Activity的跳转速度
android:launchMode=“singleTask”
62.让应用不显示在最近运行程序列表中
在主activity处设置属性:
android:excludeFromRecents=“true”
63.解决页面之间跳转时的短暂黑屏问题
<?xml version="1.0" encoding="utf-8"?>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
权限:
new Thread(new Runnable()
{
@Override
public 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左右滑动,今天总结一下:
同样由于是在源码环境下编译,所以代码中的资源引用之类地方的可能和常规的稍微不同。
// 滑动之后的回调方法
@Override
public 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;
}
@Override
public 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);
}
}
@Override
public 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 #D1EEEE
itemView.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) { // 向右call
itemView.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);
}
@Override
public 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 © 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
dial
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 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{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (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
}@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(CREATE_TABLE_SQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
//zj add START
public Cursor getCursorByNumber(String number) {
A-Za-z ↩︎