现在的位置: 首页 > 移动开发> 正文
Extending AIR for Android (4)
2011年07月02日 移动开发 暂无评论 ⁄ 被围观 3,302+

Application Licensing

Android provides APIs to help you enforce licensing policies for non-free apps in the Android Market. You might want to go read up on Android Licensing before you give this one a try.
To add Application Licensing to you AIR for Android application you first need to follow the steps outlined in the Android documentation. The broad steps are as follows:
1. Set up an Android Market publisher account

2. Install the Market Licensing Package in the Android SDK

3. Create a new LVL Android Library Project in Eclipse

4. Add a Library reference in the Android project to the LVL Android Library

5. Add the CHECK_LICENSE permission to your Android project’s manifest file:

<uses-permission android:name="com.android.vending.CHECK_LICENSE" />

After completing these set up steps, you are ready to update the MainApp Java class to handle validating the license:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.jamesward;   
import com.android.vending.licensing.AESObfuscator;
import com.android.vending.licensing.LicenseChecker;
import com.android.vending.licensing.LicenseCheckerCallback;
import com.android.vending.licensing.ServerManagedPolicy;   
import air.Foo.AppEntry;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings.Secure;   
 
public class MainApp extends AppEntry {
   private static final String BASE64_PUBLIC_KEY = "REPLACE WITH KEY FROM ANDROID MARKET PROFILE";       
   // Generate your own 20 random bytes, and put them here.    
   private static final byte[] SALT = new byte[] {        
  -45, 12, 72, -31, -8, -122, 98, -24, 86, 47, -65, -47, 33, -99, -55, -64, -114, 39, -71, 47    };       
   private LicenseCheckerCallback mLicenseCheckerCallback;    
   private LicenseChecker mChecker;    
   private Handler mHandler;       
  
   @Override   
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);        
   mHandler = new Handler();        
   String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);        
   mLicenseCheckerCallback = new MyLicenseCheckerCallback();        
  
   mChecker = new LicenseChecker(  this, new ServerManagedPolicy(this,
         new AESObfuscator(SALT, getPackageName(), deviceId)),  BASE64_PUBLIC_KEY);        
   mChecker.checkAccess(mLicenseCheckerCallback);    
   }       
  
   private void displayFault() {        
      mHandler.post(new Runnable() {          
         public void run() {                
      // Cover the screen with a messaging indicating there was a licensing problem                
      setContentView(R.layout.main);            
    }        
   });    
   }       
  
   private class MyLicenseCheckerCallback implements LicenseCheckerCallback {        
       public void allow() {            
        if (isFinishing()) {                
        // Don't update UI if Activity is finishing.                
     return;            
      }            
      // Should allow user access.        
    }           
   
    public void dontAllow() {            
        if (isFinishing()) {                
        // Don't update UI if Activity is finishing.                
        return;            
     }           
     displayFault();        
    }           
   
    public void applicationError(ApplicationErrorCode errorCode) {            
        if (isFinishing()) {                
         // Don't update UI if Activity is finishing.                
         return;            
     }        
  }    
 }       
 
 @Override   
 protected void onDestroy() {        
     super.onDestroy();        
     mChecker.onDestroy();    
 }
}
package com.jamesward;   
import com.android.vending.licensing.AESObfuscator;
import com.android.vending.licensing.LicenseChecker;
import com.android.vending.licensing.LicenseCheckerCallback;
import com.android.vending.licensing.ServerManagedPolicy;   
import air.Foo.AppEntry;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings.Secure;   

public class MainApp extends AppEntry {
   private static final String BASE64_PUBLIC_KEY = "REPLACE WITH KEY FROM ANDROID MARKET PROFILE";       
   // Generate your own 20 random bytes, and put them here.    
   private static final byte[] SALT = new byte[] {        
  -45, 12, 72, -31, -8, -122, 98, -24, 86, 47, -65, -47, 33, -99, -55, -64, -114, 39, -71, 47    };       
   private LicenseCheckerCallback mLicenseCheckerCallback;    
   private LicenseChecker mChecker;    
   private Handler mHandler;       
  
   @Override   
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);        
   mHandler = new Handler();        
   String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);        
   mLicenseCheckerCallback = new MyLicenseCheckerCallback();        
  
   mChecker = new LicenseChecker(  this, new ServerManagedPolicy(this,
         new AESObfuscator(SALT, getPackageName(), deviceId)),  BASE64_PUBLIC_KEY);        
   mChecker.checkAccess(mLicenseCheckerCallback);    
   }       
  
   private void displayFault() {        
      mHandler.post(new Runnable() {          
         public void run() {                
      // Cover the screen with a messaging indicating there was a licensing problem                
      setContentView(R.layout.main);            
    }        
   });    
   }       
  
   private class MyLicenseCheckerCallback implements LicenseCheckerCallback {        
       public void allow() {            
        if (isFinishing()) {                
        // Don't update UI if Activity is finishing.                
     return;            
      }            
      // Should allow user access.        
    }           
   
    public void dontAllow() {            
        if (isFinishing()) {                
        // Don't update UI if Activity is finishing.                
        return;            
     }           
     displayFault();        
    }           
   
    public void applicationError(ApplicationErrorCode errorCode) {            
        if (isFinishing()) {                
         // Don't update UI if Activity is finishing.                
         return;            
     }        
  }    
 }       
 
 @Override   
 protected void onDestroy() {        
     super.onDestroy();        
     mChecker.onDestroy();    
 }
}

Also add the following to a new res/layout/main.xml file in order to display an error when the license is denied:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
                android:orientation="vertical" 
                android:layout_width="fill_parent"    
                android:layout_height="fill_parent"    >   

       <TextView      android:layout_width="fill_parent"     
                              android:layout_height="wrap_content"     
                              android:text="@string/license_problem"    /> 
</LinearLayout>

The text to display uses a string resource named “license_problem”, which must be added to the res/values/strings.xml file:

<string name="license_problem">THERE WAS A PROBLEM LICENSING YOUR APPLICATION!</string>

When the application runs it will check for a valid license. If the license comes back as valid then the AIR application will start and run as usual. However, if there is an invalid license then the application will set the ContentView to the R.layout.main resource, which displays the error message defined in the “license_problem” resource. To simulate different responses you can change the “Test Response” in your Android Market profile.

The Gory Details

I’ve wrapped up a generated AppEntry class and its resources to make the process of extending AIR for Android fairly easy. If you are interested in seeing how that is done, I’ve posted all of the source code on github.

Here is an overview of the procedure:

1. Use the AIR SDK to create an AIR for Android APK file.
2. Use the dex2jar utility to convert the AppEntry dex classes into a JAR file.
3. Pull the resource classes out of the JAR file so that they don’t conflict with the new resources.
4. Use apktool to extract the original resources out of the AIR for Android APK file.
5. Create a single ZIP file containing the airbootstap.jar file, resources, AndroidManifest.xml file, and assets.
Now you can simply copy and paste those dependencies into your Android project.

Conclusion
Hopefully this article has helped you to better understand how you can extend AIR for Android applications with Android APIs. There are still a number of areas where this method can be improved. For instance, I am currently working with the Merapi Project developers to get Merapi working with my method of extending AIR for Android. That will provide a better bridging technique for communicating between the AIR application and Android APIs. So stay tuned for more information about that. And let me know if you have any questions!

原文作者: James Ward

给我留言

留言无头像?


×
腾讯微博