Contacts application porting

Source: Internet
Author: User

Migrating contacts from a system application to a common application

1. The first problem encountered was hiding the API problem.

There is a reason why Google hides some APIs (public classes, methods, or constants marked with @ hide. A major reason is that the Android system is still evolving. Android 2.3.4 is coming soon from 1.0 and 1.1. These hidden APIs may be unstable. Therefore, using hidden APIs means that programs are less compatible. Therefore, it is recommended that you do not use hidden APIs. However, some hidden APIs often play an unexpected role to realize some special functions or effects of Android applications.

You can find the method to hide the API using the Android system from the Internet (the reflection mechanism of Java is abandoned, and the workload is too large and too cumbersome ):

To use the Android system to hide the API, we need to get a file compiled and output by the Android system source code: Out \ target \ common \ OBJ \ java_libraries \ framework_intermediates \ Classes. Jar
Here I am using the file compiled by the source code of android2.3. This package contains all the system APIs, hidden and public.

Add jar: Right-click the project and choose function> build path> Configure.
Build path...-> libraries tab.

A. There should be a list. If you haven't added one, there should be only one item, that is, the android SDK that comes with the system, select and delete the SDK added by the system;

B. Click Add library-> User library and select the user library button. Create a new user library to import the classes. jar file and the system file;

C. Adjust the order and set classes. jar to the front. After adding the class, you can use the hidden APIs of the system.

To use the hidden API, you must:
Many APIs involve system permissions. For example, if the background Installation File requires the installer permission, this installer permission is not random and can be used only after Rom signature authentication. although you can add this permission to the configuration file, the tragedy is that you still cannot have this permission. In this regard, Google is doing a great job ..

I understand that Rom signature authentication will be related to a specific custom system, so I didn't try it.

There are many problems in the porting process, which are recorded here.

2. call history.

A. When you enter the call record, the unreceived call notification on the status bar will be canceled. There is no problem on 2.2, but an exception will be thrown because there is no permission on 2.3. That is to say, on 2.3, normal applications do not have the permission to cancel missed call notifications, configuration in XML is useless. 2.3 There is no way to strengthen permission control. Even cloud Assistant will also throw an exception in 2.3, and thus it is shameless to capture all exceptions here .. Operation failed, but no exception is thrown.

Since the system's "call history" has the permission to cancel missed call notifications, you can call the "call history" of the system ". The following records the system "call records" as B

Try 1: Start an invisible Activity B. I didn't expect a solution. I don't know if there is any;
Attempt 2: Start B directly to ensure that it has canceled the Missed Call Notification and is disabled through activitymanager before the user is visible. The effect is not very good, but it has always been realized. Use a thread to disable B through activitymanager after B is started. The Code is as follows:

Public class watcherthread extends thread {private context; Public watcherthread (context) {This. context = context;} string yourclassnmae = NULL; Public void run () {intent mintent = new intent (context, mycallactivity. class); startactivity (mintent); try {thread. sleep (200);} catch (exception e) {} activitymanager = (activitymanager) context. getsystemservice (activity_service); List <runningtaskinfo> runningtasks = activitymanager. getrunningtasks (5); For (runningtaskinfo mrunningtaskinfo: runningtasks) {string classname = mrunningtaskinfo. topactivity. getclassname (); If (classname. equals (yourclassnmae) {// 1. restartpackage has been discarded // activitymanager. restartpackage (mrunningtaskinfo. topactivity. getpackagename (); // 2. killbackgroundprocesses is available. To use restartpackage in 2.3, you still need to call killbackgroundprocesses to implement activitymanager. killbackgroundprocesses (mrunningtaskinfo. topactivity. getpackagename (); // you need to configure android to use the preceding two methods. manifest. permission # kill_background_processes permission // as classes has been introduced above. jar, you can also use the hide method forcestoppackage // you need to configure android. manifest. permission # force_stop_packages permission activitymanager. forcestoppackage (mrunningtaskinfo. topactivity. getpackagename ());}}}}

However, B was not closed after the attempt. Query by the keyword killbackgroundprocesses. Find a blog: alert, and do not kill the activity visible to the user. That is why my test does not close B, and the test forcestoppackage cannot kill the activity visible to the user. This attempt fails.

Try 3: Get an instance and end the activity through the instance.

Activity mactivity = instance; If (mactivity. isfinishing () {mactivity. Finish ();}

Question 3 is how to obtain the instance. activitymanager has limited information and cannot obtain the instance. I don't know how to get B's instance...

Failed to make the attempt. At present, I have to abort the operation to eat all the exceptions and do not process them.

 

B. The system can call the following actions by dialing:

Action_call (normal ),

Action_call_emergency (emergency call ),

Action_call_privileged (exclusive to the system ),

A common application can only call action_call to call a call. Therefore, the call operation in the entire application must be rewritten to call action_call.

The system API is as follows:

/**     * Activity Action: Perform a call to someone specified by the data.     * <p>Input: If nothing, an empty dialer is started; else {@link #getData}     * is URI of a phone number to be dialed or a tel: URI of an explicit phone     * number.     * <p>Output: nothing.     *     * <p>Note: there will be restrictions on which applications can initiate a     * call; most applications should use the {@link #ACTION_DIAL}.     * <p>Note: this Intent <strong>cannot</strong> be used to call emergency     * numbers.  Applications can <strong>dial</strong> emergency numbers using     * {@link #ACTION_DIAL}, however.     */    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)    public static final String ACTION_CALL = "android.intent.action.CALL";    /**     * Activity Action: Perform a call to an emergency number specified by the     * data.     * <p>Input: {@link #getData} is URI of a phone number to be dialed or a     * tel: URI of an explicit phone number.     * <p>Output: nothing.     * @hide     */    public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";    /**     * Activity action: Perform a call to any number (emergency or not)     * specified by the data.     * <p>Input: {@link #getData} is URI of a phone number to be dialed or a     * tel: URI of an explicit phone number.     * <p>Output: nothing.     * @hide     */    public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";

3. Contact List

Through this part, I found that many of my students have already done the job, but I can't help it. I just need to exercise myself. After the Code does not report an error, the following exception occurs during running:

I/ContactsListActivity( 1182): Called with action: com.android.contacts.action.LIST_DEFAULTE/DatabaseUtils(  183): Writing exception to parcelE/DatabaseUtils(  183): java.lang.UnsupportedOperationException: Only CrossProcessCursor cursors are supported across process for nowE/DatabaseUtils(  183): at android.database.CursorToBulkCursorAdaptor.<init>(CursorToBulkCursorAdaptor.java:97)E/DatabaseUtils(  183): at android.content.ContentProvider$Transport.bulkQuery(ContentProvider.java:179)E/DatabaseUtils(  183): at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:111)E/DatabaseUtils(  183): at android.os.Binder.execTransact(Binder.java:320)E/DatabaseUtils(  183): at dalvik.system.NativeStart.run(Native Method)E/DatabaseUtils(  183): Caused by: java.lang.ClassCastException: com.android.providers.contacts.ContactsProvider2$3E/DatabaseUtils(  183): at android.database.CursorToBulkCursorAdaptor.<init>(CursorToBulkCursorAdaptor.java:81)E/DatabaseUtils(  183): ... 4 moreW/AsyncQuery( 1182): java.lang.UnsupportedOperationException: Only CrossProcessCursor cursors are supported across process for nowD/AndroidRuntime( 1162): Shutting down VM

For more information about only crossprocesscursor cursors are ororted processing SS process for now, find the following link: 1. http://www.cnblogs.com/chenxian/archive/2011/03/15/1984501.html:

This is caused by Java polymorphism. The returned result is a cursor object, which is why downward transformation is insecure. When the provider transfers the cursor across processes, it will forcibly convert it to the crossprocesscursor type.... You can use sqlitequerybuilder.

Because it is the provider that calls the system, it is impossible to modify it. I can only find out the problem. I started to think it was too complicated to check the source code. My colleague reminded me that I wrote a demo to list the contacts and found that there was no cross-process problem. However, no problem is found. Later, I compared the demo and system code to list all contacts. The only difference is Uri:

Demo:

Uri uri=Contacts.CONTENT_URI;

System:

Uri = buildsectionindexeruri (contacts. content_uri); The returned URI. buildupon (). appendqueryparameter (contactcounts. Signature, "true"). Build ();

The following two links are found on the Internet:

Http://www.eoeandroid.com/thread-70806-1-1.html

Http://topic.csdn.net/u/20110406/17/cba2957e-c726-4a38-917d-ea34d761c75e.html
According to the above query, the problem is indeed on the URI. Actually, uri = contacts. content_uri was used before 2.2. Original article:

By default, all contacts in the query call mqueryhandler. startquery (query_token, null, Uri, projection, getcontactselection (), null, getsortorder (projection ));
Uri is obtained through the geturitoquery () method, and uri = buildsectionindexeruri (contacts. content_uri) is introduced );

Change URI to uri = contacts. content_uri; to query the contact.
However, the "a, B, c, d" header is not displayed in the contact list.

Then go to the source code and find this function buildsectionindexeruri (contacts. content_uri). The result is a Hierarchical UI (hierarchical acthierarchicaluri)

*******

"Buildsectionindexeruri" is already very obvious-index header, just too impetuous, the contact's letter header is like this.

Sort out the following URI process:

1. uri uri = geturitoquery (); 2. private URI geturitoquery () {Switch (mmode) {Case mode_default: Return contacts_content_uri_with_letter_counts;} 3. private Static final URI contacts_content_uri_with_letter_counts = buildsectionindexeruri (contacts. content_uri); 4. // obtain a URI with the letter header Private Static URI buildsectionindexeruri (URI) {return Uri. buildupon (). appendqueryparameter (contactcounts. addh Ss_book_index_extras, "true "). build () ;}5. // 4 is the method in the URI subclass. You can obtain a list URI with a letter header, if you are interested, you can check the builder and hierarchicaluri (inherited from the abstracthierarchicaluri mentioned above) subclasses in the URI source code.

In this case, follow up contactsprovider2 to view the query source code:

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder)

In the query, the source code after the query is complete is as follows:

     //1    Cursor cursor = query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);    if(readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {         //2         cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);    }

You have obtained the cursor. Check whether readbooleanqueryparameter () requires a letter header! This is where the problem is.

That is to say, there is no problem with the letter header. To include the letter header, you must use the processed cursor, which will cause the first problem: Only crossprocesscursor cursors are supported processing SS process for now. Finally, the root cause is found. I just haven't taken a good look at the specific implementation. Let's look back.


After reading the specific implementation and testing, the Code is as follows (after the above Code is added, before return ):

if(cursor instanceof CrossProcessCursor){Log.d("spare_H","1");}else if(cursor instanceof CursorWrapper){Log.d("spare_H","2");}

The test proves that cursor1 is of the crossprocesscursor type and cursor2 is of the cursorwrapper type. Therefore, when querying a cursor with a table header, data cannot be converted to crossprocesscursor, but it is indeed a cross-process query, eventually: Only crossprocesscursor
Cursors are supported into SS process for now!

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.