2014년 9월 25일 목요일

Google Application Licensing (구글 앱 라이센싱 - LVL)

 이번에 유료앱을 하나 등록해 볼까 생각이 들어 구글 앱라이센싱을 적용해 보았다.
또 오래되면 잊어버릴지 모르니 일단 정리해 볼까 한다.

 물론 기본적으로 구글 개발자 페이지에 잘정리 되어있다. 그 내용을 바탕으로 요약 및 단축하여 작성하겠다.
- 구글 개발자 페이지 링크 : http://developer.android.com/google/play/licensing/index.html


1. LVL이란?

 우선 지금부터 사용할 라이브러리는 약어로 LVL(Licensing Verification Library)이다. 위의 페이지에 동작원리가 나와있다. 시간이 되면 한번 읽어보자. 안읽어봐도 적용하는데는 상관없지만 뭐 읽어두어 보자~~

 - LVL의 키 Interface
 총 두가지이다.







Policy
앱에 접속여부를 결정한다. 보통 서버혹은 캐쉬에저장된 데이터를 기반으로 결정하게된다. 커스텀으로 만들수도 있지만 기본적으로 구현된 객체를 사용하면된다.
LicenseCheckerCallback
어플리케이션의 접근을 관리한다. Policy 객체의 라이센스 응답에 의해 앱에서 취해주어야할 작업들을 연결해준다. 팝업을 띄운다던지, 구매페이지로 이동시킨다던지 하는 제어를 할 수 있다.


2. 사전 조건

 이 라이브러리가 나온지 아주 오래되어 사실 조건은 별다를게 없다.
 - API Level 3이상
 - 인터넷 억세스 가능(라이센스 서버와 통신하므로)


3. 환경설정

 - 에뮬레이터나 실기기 모두 가능하다고 나온다.
 - 일단 나는 실기기에서 바로 테스트 했다. 에뮬레이터에서 테스트 하려면 조금더 복잡한 과정이 필요하니 이부분은 필요하면 위의 개발자페이지를 참고하자.

 - LVL의 다운로드
 - 구글이 업데이트를 잘안한다. SDK Manager를 띄워서 아래의 항목을 확인한다.


 - Google Play Licensing Library 라고 보이는것이 설치되어있는지 확인하자.
 - 안되어있다면 설치
 - 받았다면, <sdk>/market_licensing/library/ 내부에 존재하는 라이브러리 소스를 import 해오든지 하여 library project로 이클립스 등에 가져온다.
 - 가져왔다면 내가 적용시키기 원하는 프로젝트의 프로퍼티에서 해당 라이브러리를 포함시켜준다.
 - 뭔말인고 하니 아래화면에서 추가해주면 된다는 이야기다.

 - 필자는 google-license라는 이름으로 가져와서 포함시켰다.
 - 이제 환경 설정은 끝났다.



4. 실제 적용

 - 실제적용은 위에서 받은 Library 패키지 안의 샘플을 보면 쉽게 적용이 가능하다.
 - 사실 샘플 그대로 써도 대부분 무방하다고 보면된다.

 - Manifest 파일.
<!-- Required permission to check licensing. -->
    <uses-permission android:name="com.android.vending.CHECK_LICENSE" />
 위의 권한을 추가해준다.

 - MainActivity
  가장 front의 액티비티만 고쳐주면된다. 즉 Manifest와 MainActivity 두 개만 수정하면됨

   > 멤버변수 선언부
      - 공개 키를 넣어주고, SALT에 임의의 숫자들을 넣어준다.
private static final String BASE64_PUBLIC_KEY = "디벨롭퍼 콘솔에서 생성된값을 여기에";
// Generate your own 20 random bytes, and put them here.
private static final byte[] SALT = new byte[] { -46, 11, 11, -128, -11,
-57, 74, -64, 51, 88, -95, -45, 77, -22, -36, -11, -11, 11, -11,89 };
private LicenseCheckerCallback mLicenseCheckerCallback;
private LicenseChecker mChecker;
private Handler mHandler;

   > onCreate 함수 내부.

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.activity_main);
mHandler = new Handler();
// Try to use more data here. ANDROID_ID is a single point of attack.
String deviceId = Secure.getString(getContentResolver(),Secure.ANDROID_ID);
// Library calls this when it's done.
mLicenseCheckerCallback = new MyLicenseCheckerCallback();
// Construct the LicenseChecker with a policy.
mChecker = new LicenseChecker(this, new ServerManagedPolicy(this,
new AESObfuscator(SALT, getPackageName(), deviceId)),BASE64_PUBLIC_KEY);
doCheck();
...
     > Inner Class 추가. Callback함수이다. 액티비티가 직접 implement 해도 된다.

private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow(int policyReason) {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
// Should allow user access.
displayResult(getString(R.string.allow));
}
public void dontAllow(int policyReason) {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
displayResult(getString(R.string.dont_allow));
displayDialog(policyReason == Policy.RETRY);
}
public void applicationError(int errorCode) {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
String result = String.format(
getString(R.string.application_error), errorCode);
displayResult(result);
}
}

     > Dialog Method들 추가. - 이부분은 dialog가 fragment로 구현되는 요즘 버전이 아닌 옛날 버전으로 되어있다. 구글에서 샘플업데이트를 안했다. 그래서 deprecated된 함수를 그대로 쓰고 있다. 각자 고쳐서 팝업 처리하도록 하자.
protected Dialog onCreateDialog(int id) {
final boolean bRetry = id == 1;
return new AlertDialog.Builder(this)
.setTitle(R.string.unlicensed_dialog_title)
.setMessage(
bRetry ? R.string.unlicensed_dialog_retry_body
: R.string.unlicensed_dialog_body)
.setPositiveButton(
bRetry ? R.string.retry_button : R.string.buy_button,
new DialogInterface.OnClickListener() {
boolean mRetry = bRetry;
public void onClick(DialogInterface dialog, int which) {
if (mRetry) {
doCheck();
} else {
Intent marketIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("http://market.android.com/details?id="
+ getPackageName()));
startActivity(marketIntent);
} }
})
.setNegativeButton(R.string.quit_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
finish();
}
}).create();
}
private void doCheck() {
// mCheckLicenseButton.setEnabled(false);
setProgressBarIndeterminateVisibility(true);
// mStatusText.setText(R.string.checking_license);
mChecker.checkAccess(mLicenseCheckerCallback);
}
private void displayResult(final String result) {
mHandler.post(new Runnable() {
public void run() {
// mStatusText.setText(result);
setProgressBarIndeterminateVisibility(false);
// mCheckLicenseButton.setEnabled(true);
}
});
}
private void displayDialog(final boolean showRetry) {
mHandler.post(new Runnable() {
public void run() {
setProgressBarIndeterminateVisibility(false);
showDialog(showRetry ? 1 : 0);
// mCheckLicenseButton.setEnabled(true);
}
});
}

    > 마지막으로 onDestroy를 Override하여 아래 코드 추가.

@Override
protected void onDestroy() {
super.onDestroy();
// License 적용시
mChecker.onDestroy();
}

5. 완료

  - 이로써 모든 적용이 끝났다. 사실 두개의 파일만 고치면 간단히 적용할 수 있다.
  - 테스트는 일단 알파 혹은 베타 버전으로 apk를 업로드하고 이것을 다운받아서 실행해본다.
  - 개발자 콘솔의 좌측 설정에서 계정세부정보 아래쪽을 보면 라이선스 테스트를 할 수 있다.
  - Not_licensed, Licensed등으로 바꾸면서 테스트 해본다.



댓글 없음:

댓글 쓰기