스마트기기개발관련/안드로이드 개발

[android] 안드로이드 android 내 Account 조회, 추가, 안드로이드 Authenticator Service 만들기 관련

AlrepondTech 2016. 7. 14. 11:53
반응형


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


출처: http://cranix.net/346



안드로이드 Authenticator Service 만들기 (1) - 계정등록하기




안드로이드에서 ID/PW 는 어떻게 저장할까요?

어플리케이션 별로 알아서 저장해도 상관은 없지요.

 

그러나 안드로이드에서는 이러한 로그인 정보를 통합적으로 관리 할 수 있도록 Authenticator Service 를 제공합니다.

안드로이드의 설정에 “Accounts & Sync” 메뉴에 가보면 이렇게 관리되는 계정을 확인 할 수 있습니다.

 

아마 대부분 Google 계정만 등록되어 있을텐데 이번 포스트에서는 내가 만든 Authenticator Service 를 등록해 보도록 하겠습니다.

 

 

먼저 설정의 “Accounts & Sync” 메뉴의 계정리스트에 나오도록 해야하는데 이것은 서비스 구현으로 이루어 집니다.

 

1. manifest 에 서비스 등록하기

   - AndroidManifest.xml 파일에 다음과 같이 서비스를 등록합니다.

<service android:name=".authenticator.AuthenticationService"> 
<intent-filter>
<action
android:name="android.accounts.AccountAuthenticator"
/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator"
/>
</service>


 

2. xml 리소스 등록하기

   - xml/authenticator.xml 파일을 만들고 아래 내용을 입력합니다.

<?xml version="1.0" encoding="utf-8"?>


<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="net.cranix.android.cranixsyncsample"
android:icon="@drawable/icon"
android:smallIcon="@drawable/icon"
android:label="@string/app_name"
/>


 

3. sample 서비스 만들기

   - 일단 등록이 되는지 확인을 위해 아래와같이 빈 Service 를 만듭니다.

package net.cranix.android.cranixsyncsample.authenticator;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class AuthenticationService extends Service {
@Override
public void onCreate() {
// TODO Auto-generated method stub
}

@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}

}


 

   - 그후 설정에 “Accounts & Sync” 에 가보면 등록이 되어 있는것을 볼 수 있습니다.


 

 

자신이 추가한 계정을 클릭하면 아무 반응이 없는것을 알 수 있습니다.

서비스를 구현하지 않았으니 당연한 결과 입니다.

 

3번에서 만든 서비스를 구현해서 로그인 정보 기록이나 나아가서 주소록 싱크 어플 등을 만들수 있게 됩니다.

다음에는 이 서비스를 실제로 구현하는 방법을 알아보도록 하겠습니다.



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



출처: http://cranix.net/347



안드로이드 Authenticator Service 만들기 (2) - AbstractAccountAuthenticator 구현하기



이번에는 AuthenticationService 를 실제로 구현해 보도록 하겠습니다.

 

이전에 “Accounts & Sync” 메뉴에 내 계정을 등록시키는 작업을 했습니다.

그 다음에 원하는 것은 내가 만든 계정을 클릭했을 때 로그인 창을 띄우는 것 입니다.

 

그런데 여기서 문제는 실제로 로그인 을 요청하는 부분은 다른 Process 가 한다는 것이죠.

결국 이 프로세스가 우리가 만든 Service 와 통신하기 위해서는 바인더를 이용하여야 합니다.

 

안드로이드에서는 이를 위해서 AbstractAccountAuthenticator 라는 추상 클래스를 제공합니다.

우리는 이를 구현해서 iBinder 만 넘겨주면 됩니다.

 

이를 소스로 살펴보면 아래와 같습니다.

 

package net.cranix.android.cranixsyncsample.authenticator;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class AuthenticationService extends Service {
private Authenticator authenticator;

@Override
public void onCreate() {
authenticator = new Authenticator(this);
}

@Override
public IBinder onBind(Intent intent) {
return authenticator.getIBinder();
}
}



 

즉 Authenticator 는 AbstractAccountAuthenticator 를 구현한 클래스이고 이것을 우리가 만든 서비스에서 만들고 사용하는 모습입니다.

 

이제 로그인 및 기타 처리는 Authenticator 클래스에서 구현해주면 됩니다.

Authenticator 클래스는 아래와 같습니다.

 

package net.cranix.android.cranixsyncsample.authenticator;

import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.os.Bundle;


public class Authenticator extends AbstractAccountAuthenticator {

public Authenticator(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures, Bundle options)
throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response,
Account account, Bundle options) throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse response,
String accountType) {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response,
Account account, String authTokenType, Bundle options)
throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

@Override
public String getAuthTokenLabel(String authTokenType) {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response,
Account account, String[] features) throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response,
Account account, String authTokenType, Bundle options)
throws NetworkErrorException {
// TODO Auto-generated method stub
return null;
}

}



 

AbstractAccountAuthenticator 를 상속받은 Authenticator 클래스의 기본형은 위와 같습니다.

이제 각각의 메소드들은 시스템에서 계정관련 작업시 알아서 호출하게 됩니다.

 

여기서는 간단하게 계정추가를 만들어보도록 하겠습니다.

계정추가는 설정에 “Accounts & Sync” 에서 계정을 누를때 일어나며, 위의 메소드 중에서는 “addAccount” 를 호출하게 됩니다.

 

그럼 위 소스를 아래와 같이 수정합니다.

 

public class Authenticator extends AbstractAccountAuthenticator { 
private Context context;
public Authenticator(Context context) {
super(context);
this.context = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures, Bundle options)
throws NetworkErrorException {

AccountManager manager = AccountManager.get(context);

Account account = new Account("cranix",accountType);
manager.addAccountExplicitly(account, "passowrd", null);
return new Bundle();
}




}

 

여기서는 AccountManager.addAccountExplicitly 를 사용하여 계정을 추가하는데 이것을 사용하려면 권한이 필요합니다.

아래내용을 manifest 파일에 추가합니다.

 

<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"></uses-permission>

 

이제 설정 메뉴의 “Accounts & Sync” 에서 “Add account” 를 누른후에 자신이 추가한 계정을 클릭하면 아래와 같이 추가되는것을 볼 수 있습니다.


 

이번 포스트에서는 UI 없이 간단하게 추가되도록 만들었습니다.

안드로이드에서는 여기서 사용하는 UI 를 지원하기 위해서 AccountAuthenticatorActivity 라는 클래스를 제공합니다.

 

다음 에는 이 클래스를 이용해서 UI 를 붙혀서 로그인 하는법을 알아보도록 하겠습니다.




/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


출처: http://cranix.net/349


전 포스트에서 AbstractAccountAuthenticator 의 addAccount 를 구현해서 아이디를 추가하는것을 해 보았습니다.

그런데 실제로 서비스에서는 “Add account” 를 누르면 아이디 비밀번호를 묻는 UI 가 나와야 정상일 것입니다.

그래서 안드로이드는 이를위해 AccountAuthenticatorActivity 를 제공합니다.

 

여기서 쓰는 UI 는 아이디와 비밀번호를 입력받는 EditText 가 두개있고 login 과 cancel 버튼이 있는 레이아웃 입니다.

여기서 쓰는 xml 은 아래와 같습니다.

 

<?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">

<ScrollView android:layout_height="wrap_content"
android:id="@+id/ScrollView01"
android:layout_weight="1"
android:layout_width="fill_parent">

<LinearLayout android:id="@+id/LinearLayout01"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">

<EditText android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/EditText_id"
android:hint="아이디">
</EditText>
<EditText android:inputType="textPassword"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/EditText_password"
android:hint="비밀번호">
</EditText>
</LinearLayout>
</ScrollView>

<LinearLayout android:id="@+id/LinearLayout02"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:background="#c6c3c6">
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/Button_login"
android:text="Login">
</Button>
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/Button_cancel"
android:text="Cancel">
</Button>
</LinearLayout>
</LinearLayout>

 

매니페스트에는 아래와 같이 activity 를 추가합니다.

<activity android:name=".authenticator.AuthenticatorActivity" 
android:theme="@android:style/Theme.Dialog">
</activity>

 

이제 AuthenticatorActivity 소스를 보면 아래와 같습니다.

package net.cranix.android.cranixsyncsample.authenticator;

import net.cranix.android.cranixsyncsample.Constants;
import net.cranix.android.cranixsyncsample.R;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class AuthenticatorActivity extends AccountAuthenticatorActivity {
private EditText textId;
private EditText textPassword;

private AccountManager accountManager;

@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);

final Button btnLogin = (Button) findViewById(R.id.Button_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doLogin();
}
});

final Button btnCancel = (Button) findViewById(R.id.Button_cancel);
btnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doCancel();
}
});

textId = (EditText) findViewById(R.id.EditText_id);
textPassword = (EditText) findViewById(R.id.EditText_password);
accountManager = AccountManager.get(this);
}

private void doLogin() {
final String id = textId.getText().toString();
final String password = textPassword.getText().toString();
final Account account = new Account(id,Constants.ACCOUNT_TYPE);
if (accountManager.addAccountExplicitly(account, password, null)) {
final Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, id);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);

setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK,intent);
finish();
}
else {
doCancel();
}
}
private void doCancel() {
setResult(RESULT_CANCELED);
finish();
}
}

 

AccountAuthenticatorActivity 는 일반 Activity 와 다른점이 두가지 있습니다.

   1. 액티비티를 띄울때 intent 의 extra 로 AccountAuthenticatorResponse 객체를 담고 있어야 함.

   2. “setAccountAuthenticatorResult(bundle)” 메소드로 결과를 돌려줘야함.

 

위의 doLogin 함수에서 setAccountAuthenticatorResult 메소드를 쓰는것을 볼 수 있습니다.

이제 이 Activity 를 띄우기 위해서 지난번에 만들었던 Authenticator 클래스를 수정해 봅시다.

 

public class Authenticator extends AbstractAccountAuthenticator {
private Context context;
public Authenticator(Context context) {
super(context);
this.context = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures, Bundle options)
throws NetworkErrorException {

Intent intent = new Intent(context,AuthenticatorActivity.class);

intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}

...
...
}

 

여기서 intent 의 extra 로 response 를 넣어서 만든후에 bundle 에 담아서 넘겨주는것을 볼 수 있습니다.

이렇게 하면 자동으로 인텐트에 지정된 Activity 가 뜨게 되면서 처리를 위임하게 됩니다.

 

이제 “Add account” 누른후 자신의 계정을 클릭하면 아래와 같은 화면이 나오는 것을 볼 수 있습니다.


 

 

이제 껍데기는 다 만들었습니다.

이제 이 아이디를 이용해서 sync 를 구현하면 됩니다.

sync 구현 역시 새로운 서비스를 만들어야 하는데 이는 다음번에 알아보도록 하겠습니다.



/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


출처: http://cranix.net/350


안드로이드 Authenticator Service 만들기 (4) - SyncAdapter 구현



이전에 Authenticator Service 를 구현해 보았습니다.

이제 이것을 이용하는 SyncAdapter 를 구현해 보도록 하겠습니다.

 

SyncAdapter 는 구글 싱크가 일어날때 자동으로 싱크를 처리해주는 부분으로서

“Accounts & Sync” 셋팅메뉴에서 원하는 아이디를 클릭하면 SyncAdapter 의 리스트를 확인할 수 있습니다.

 

그럼 먼저 “Accounts & Sync” 메뉴에서 “Add account” 버튼을 눌러 이전에 만들었던 계정으로 로그인을 합니다.

 


 

위와 같이 로그인된 아이디가 저장이 될텐데 저 아이디를 클릭하면 이 계정에 등록되어진 SyncAdapter 를 확인할 수 있습니다.

현재에는 하나도 없는것을 볼 수 있는데 이번 포스트에서 여기에 추가해보도록 하겠습니다.

 

SyncAdapter 는 Service 로 이루어져 있습니다.

 

Service 를 추가하기전에 SyncAdapter 를 쓰기 위한 권한을 추가합니다.

아래 내용을 manifest 에 등록합니다.

 

<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />  
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />

이제 아래와 같은 형태의 서비스를 추가합니다.

<service android:name=".syncadapter.SyncService">
<intent-filter>
<action
android:name="android.content.SyncAdapter"
/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>

그다음 syncadapter.xml 파일을 추가합니다.

<?xml version="1.0" encoding="utf-8"?>

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.android.contacts"
android:accountType="net.cranix.android.cranixsyncsample"
android:supportsUploading="false"
/>

“android:contentAuthority” 는 sync 할 ContentProvider 를 지정합니다.

여기서 Sync 이름은 우리가 직접 지정하는 것이 아니고 이 ContentProvider 의 이름을 따라가기 때문에 유의해야 합니다.

 

“android:supportsUploading” 은 폰에서 이 SyncAdapter 의 데이터를 변경 가능한게 할지 말지를 결정하는 것 입니다.

이것을 false 로 하면 서버에서 싱크만 되고 폰에서는 수정이안되는 readonly 상태가 됩니다.

 

이제 마지막으로 SyncService 와 SyncAdapter 소스를 구현해 보도록 합니다.

먼저 SyncService 입니다.

package net.cranix.android.cranixsyncsample.syncadapter;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class SyncService extends Service {
private SyncAdapter syncAdapter;

@Override
public void onCreate() {
super.onCreate();
syncAdapter = new SyncAdapter(this,true);
}
@Override
public IBinder onBind(Intent arg0) {
return syncAdapter.getSyncAdapterBinder();
}

}


이 Service 는 SyncAdapter 에 모든 처리를 위임합니다.

SyncAdapter 를 보면 다음과 같습니다.

package net.cranix.android.cranixsyncsample.syncadapter;



import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;

public class SyncAdapter extends AbstractThreadedSyncAdapter {



public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}

@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {

// TODO : Sync 처리
}
}

 

이제 “Accounts & sync” 메뉴에서 아이디를 클릭해 보면 아래와 같이 추가된 SyncAdapter 가 보일 것입니다.


 

 

이제 위의 “Sync Contacts” 를 클릭하거나 시스템에서 sync 가 발생할때 위 SyncAdapter 의 onPerformSync 메소드가 호출되게 됩니다.

결국 실제 Sync 를 처리하는 구문은 onPerformSync 메소드만 구현해주면 됩니다.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


출처: http://softcream.tistory.com/91


이글은… 

안드로이드에서 제공하는 AccountManager사용에 대해서 잘 설명된 http://cranix.net/349 내용을 따라가다가 에러가 나서, 이를 해결(?)한 내용을 정리한 글입니다. 아래의 부가설명을 제외한 나머지 내용은 원본 페이지의 내용을 참고하시기 바랍니다.



참고한 내용에 있는대로 하면 아래에서 오류가 난다.


accountManager.addAccountExplicitly(account, password, null);


문제가 된건 accout에 있는 account type이 맞지 않아서 발생한 현상.

결국 해결한(?) 방법은 계정에 대한 속성(xml 에 정의한)값중 accountType값을 account 생성시에 넘겨 주면 된다.


계정에 대한 정보가 있는 xml 파일에는 다음과 같이 되어 있다.


<account-authenticator xmlns:android="“http://schemas.android.com/apk/res/android”" 

         android:accounttype="“ClipAndBooks" service”="" 

         android:icon="“@drawable/ic_launcher”" 

         android:smallicon="“@drawable/ic_launcher”" 

         android:label="“@string/set_service_account”">

</account-authenticator>



android:accountType=“ClipAndBooks Service” 로 되어 있으니, 코드상의 Account에서는 다음과 같이 한다.


final Account account = new Account(id, “ClipAndBooks Service”);


그런데.. 매번 이렇게 쓰는건 불편할테니.. 별도의 class에다가 써 두는게 낫다. (아.. 용어가 맞지 않을 수 있다.) 

실제 참고로 한 사이트에서도 별도 class로 만든것으로 보이는데, 이 부분에 대한 언급이 따로 없었다.


<Constants.java> 

package com.clipandbooks.test.android_account;


public class Constants { 

   final static String ACCOUNT_TYPE = “ClipAndBooks Service”; 

}


결국은 다음과 같이 한다.


final Account account = new Account(id, Constants.ACCOUNT_TYPE);


이후 나머지는 참고 사이트 내용과 동일하다.



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

반응형