最新实战教程,让你了解Android自动化刷量、作弊与防作弊的那些事,案例:刷友盟统计、批量注册苹果帐号
首先,我把把昨天忘记了的两张设置向导的图片放出来先
我们从上面的第一张图片可以看到,我们有一个选择联系人这一操作,那么怎样才能获取到手机里面的联系人呢,其实方法有很多的,现在我们来讲一下我们这个项目里面用到的方法
其实要想知道怎样获取手机里面的联系人,去看一下Android自己的源码就知道的了
我们只要把platform/packages/providers/ContactsProvider这一部分的内容下载下来看看就可以的啦
下面是我下载之后,打开里面的AndroidManifest的内容
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.providers.contacts" android:sharedUserId="android.uid.shared" android:sharedUserLabel="@string/sharedUserLabel"> <permission android:name="com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL" android:label="@string/read_write_all_voicemail_label" android:description="@string/read_write_all_voicemail_description" android:permissionGroup="android.permission-group.PERSONAL_INFO" android:protectionLevel="signature" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.BIND_DIRECTORY_SEARCH" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" /> <uses-permission android:name="com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL" /> <application android:process="android.process.acore" android:label="@string/app_label" android:icon="@drawable/app_icon" android:allowBackup="false"> <provider android:name="ContactsProvider2" android:authorities="contacts;com.android.contacts" android:label="@string/provider_label" android:multiprocess="false" android:exported="true" android:readPermission="android.permission.READ_CONTACTS" android:writePermission="android.permission.WRITE_CONTACTS"> <path-permission android:pathPrefix="/search_suggest_query" android:readPermission="android.permission.GLOBAL_SEARCH" /> <path-permission android:pathPrefix="/search_suggest_shortcut" android:readPermission="android.permission.GLOBAL_SEARCH" /> <path-permission android:pathPattern="/contacts/.*/photo" android:readPermission="android.permission.GLOBAL_SEARCH" /> <grant-uri-permission android:pathPattern=".*" /> </provider> <provider android:name="CallLogProvider" android:authorities="call_log" android:syncable="false" android:multiprocess="false" android:exported="true" android:readPermission="android.permission.READ_CALL_LOG" android:writePermission="android.permission.WRITE_CALL_LOG"> </provider> <provider android:name="VoicemailContentProvider" android:authorities="com.android.voicemail" android:syncable="false" android:multiprocess="false" android:exported="true" android:permission="com.android.voicemail.permission.ADD_VOICEMAIL"> </provider> <!-- Handles database upgrades after OTAs, then disables itself --> <receiver android:name="ContactsUpgradeReceiver"> <!-- This broadcast is sent after the core system has finished booting, before the home app is launched or BOOT_COMPLETED is sent. --> <intent-filter> <action android:name="android.intent.action.PRE_BOOT_COMPLETED"/> </intent-filter> </receiver> <receiver android:name="PackageIntentReceiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_ADDED" /> <data android:scheme="package" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.PACKAGE_REPLACED" /> <data android:scheme="package" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.PACKAGE_REMOVED" /> <data android:scheme="package" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.PACKAGE_CHANGED" /> <data android:scheme="package" /> </intent-filter> </receiver> <receiver android:name="LocaleChangeReceiver"> <intent-filter> <action android:name="android.intent.action.LOCALE_CHANGED"/> </intent-filter> </receiver> <service android:name="VoicemailCleanupService"/> <activity android:name=".debug.ContactsDumpActivity" android:label="@string/debug_dump_title" android:theme="@android:style/Theme.Holo.Dialog" > <intent-filter> <action android:name="com.android.providers.contacts.DUMP_DATABASE"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <provider android:name=".debug.DumpFileProvider" android:authorities="com.android.contacts.dumpfile" android:exported="true"> </provider> </application> </manifest>
我们可以看一下第一个provider,里面就有我们想要的uri还有它已经写好了的类了,各位有兴趣的可以去看一下是如何实现的,
其实我们要获取的东西,都是存放在一个数据库里面的,我们不妨把那个数据库拿出来看一下,拿到那个数据库也很简单,只要在Eclipse里面的ddms界面里面,在右上边
那个contacts2.db就是我们想要的东西啦
我们可以把它导出来,然后用SQLite Database browser来打开它
获取手机里面的联系人,我们只要关注三个表就可以啦,一个是raw_contacts data还有一个mime_type这三个表就行啦
mime_type存放的就是一些类型,是电话,还是短信,还是E-mail这些
data就是存放一些数据的东西
raw_contacts就是存放一些命名什么的
这三个表的关系就是下面一样的啦
从上面的可以看到,我们可以通过raw_contacts拿到那个id还有display_name然后就去data那里找到对应的raw_contact_id,这样就可以获得对应的数据啦,然后还可以通过mimetype_id在mime_type表里面找出想要的类型
好啦,原理就是这样啦,下面我们来看一下代码怎么写
我们先要用一个model类来存放我们的数据
com.xiaobin.security.domain.ContactInfo
package com.xiaobin.security.domain; public class ContactInfo { private String name; private String phone; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }
然后呢,我们就可以写我们获得手机联系人的代码啦
新建一个类com.xiaobin.security.engine.ContactInfoService
package com.xiaobin.security.engine; import java.util.ArrayList; import java.util.List; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; import com.xiaobin.security.domain.ContactInfo; public class ContactInfoService { private Context context; public ContactInfoService(Context context) { this.context = context; } public List<ContactInfo> getContactInfos() { List<ContactInfo> infos = new ArrayList<ContactInfo>(); ContactInfo info; ContentResolver contentResolver = context.getContentResolver(); //在源码的AndroidManifest里面可以看到这些uri Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); Uri dataUri = Uri.parse("content://com.android.contacts/data"); Cursor cursor = contentResolver.query(uri, null, null, null, null); while(cursor.moveToNext()) { info = new ContactInfo(); String id = cursor.getString(cursor.getColumnIndex("_id")); String name = cursor.getString(cursor.getColumnIndex("display_name")); info.setName(name); //通过raw_contacts里面的id拿到data里面对应的数据 Cursor dataCursor = contentResolver.query(dataUri, null, "raw_contact_id = ? ", new String[] {id}, null); while(dataCursor.moveToNext()) { String type = dataCursor.getString(dataCursor.getColumnIndex("mimetype")); //根据类型,只要电话这种类型的数据 if(type.equals("vnd.android.cursor.item/phone_v2")) { String number = dataCursor.getString(dataCursor.getColumnIndex("data1"));//拿到数据 info.setPhone(number); } } dataCursor.close(); infos.add(info); info = null; } cursor.close(); return infos; } }
因为我们是点击按钮,然后打开一个activity,然后选择好了之后,再把值返回到原来的activity那里的,所以我们就要用到startActivityForResult这个方法啦
好,我们来看一下setup_guide3这个向导页面的逻辑是怎样写的
com.xiaobin.security.ui.SetupGuide3Activity
package com.xiaobin.security.ui; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.xiaobin.security.R; public class SetupGuide3Activity extends Activity implements OnClickListener { private Button bt_next; private Button bt_pervious; private Button bt_select; private EditText et_phoneNumber; private SharedPreferences sp; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.setup_guide3); sp = getSharedPreferences("config", Context.MODE_PRIVATE); bt_next = (Button) findViewById(R.id.bt_guide_next); bt_pervious = (Button) findViewById(R.id.bt_guide_pervious); bt_select = (Button) findViewById(R.id.bt_guide_select); bt_next.setOnClickListener(this); bt_pervious.setOnClickListener(this); bt_select.setOnClickListener(this); et_phoneNumber = (EditText) findViewById(R.id.et_guide_phoneNumber); } //重写这个方法,从acitivty里面拿到数据 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); //resultCode是乃至区分拿到的activity是从那一个activity里面拿到的 if(data != null) { String number = data.getStringExtra("number"); et_phoneNumber.setText(number); } } @Override public void onClick(View v) { switch(v.getId()) { case R.id.bt_guide_select : Intent selectIntent = new Intent(this, SelectContactActivity.class); //启动一个activity来获取数据,获取到的数据是在重写的onActivityResult这个方法里面拿到的 startActivityForResult(selectIntent, 1); break; case R.id.bt_guide_next : String number = et_phoneNumber.getText().toString().trim(); if(number.equals("")) { Toast.makeText(this, "安全号码不能为空", Toast.LENGTH_SHORT).show(); } else { Editor editor = sp.edit(); editor.putString("number", number); editor.commit(); Intent intent = new Intent(this, SetupGuide4Activity.class); finish(); startActivity(intent); //这个是定义activity切换时的动画效果的 overridePendingTransition(R.anim.translate_in, R.anim.translate_out); } break; case R.id.bt_guide_pervious : Intent i = new Intent(this, SetupGuide2Activity.class); finish(); startActivity(i); //这个是定义activity切换时的动画效果的 overridePendingTransition(R.anim.alpha_in, R.anim.alpha_out); break; default : break; } } }
我们还要新建一个类用来做为选择的呢
com.xiaobin.security.ui.SelectContactActivity
package com.xiaobin.security.ui; import java.util.List; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import com.xiaobin.security.R; import com.xiaobin.security.domain.ContactInfo; import com.xiaobin.security.engine.ContactInfoService; public class SelectContactActivity extends Activity { private ListView lv; private List<ContactInfo> infos; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.select_contact); infos = new ContactInfoService(this).getContactInfos(); lv = (ListView) findViewById(R.id.lv_select_contact); lv.setAdapter(new SelectContactAdapter()); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String number = infos.get(position).getPhone(); Intent intent = new Intent(); intent.putExtra("number", number); //把要返回的数据设置进去,便通过onActivityResult(int, int, Intent)拿到 setResult(1, intent); finish(); } }); } //================================================================================= private class SelectContactAdapter extends BaseAdapter { @Override public int getCount() { return infos.size(); } @Override public Object getItem(int position) { return infos.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ContactInfo info = infos.get(position); View view; ContactViews views; if(convertView == null) { views = new ContactViews(); view = View.inflate(SelectContactActivity.this, R.layout.contact_item, null); views.tv_name = (TextView) view.findViewById(R.id.tv_contact_name); views.tv_number = (TextView) view.findViewById(R.id.tv_contact_number); views.tv_name.setText("联系人:" + info.getName()); views.tv_number.setText("联系电话:" + info.getPhone()); view.setTag(views); } else { view = convertView; } return view; } } private class ContactViews { TextView tv_name; TextView tv_number; } }
这个类是非常简单的,只有一个listView
大家也可以看到,这个类里面有一个adapter,这个是为了方便,所以写在里面的,而且那个listview我们也用到了上一次的优化方法
如果有什么不了解的,可以回去看一下我们前面的那个Android项目实战--手机卫士06--GridView的优化与修改Button的显示样式
好啦,下面是activity的布局文件,还有listview的布局文件
select_contact.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" android:orientation="vertical" > <ListView android:id="@+id/lv_select_contact" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
contact_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/tv_contact_name" android:layout_width="match_parent" android:layout_height="wrap_content"/> <TextView android:id="@+id/tv_contact_number" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
好啦,获取联系人的操作,我们也差不多完成的啦,现在还要加上两个权限
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
到现在为止,获取联系人的操作就完成的啦,
因为之前都是清一色的切换效果,所以我现写多了两个切换效果,大家可以看看,也是很简单的
至于怎么写那些效果,那就要看一下我们的前一篇文章啦
translate_in.xml
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="100%p" android:toXDelta="0" android:duration="400" > <!-- 100%p是指从看不到的地方进入到0这个地方 --> </translate>
translate_out.xml
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:toXDelta="-100%p" android:duration="400" > <!-- 100%p是指从看不到的地方进入到0这个地方 --> </translate>
好啦,今天要说的基本上已经说完的啦
如果有什么不明白的,或有什么指导的,欢迎留言