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

[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 를 붙혀서 로그인 하는법을 알아보도록 하겠습니다.

 

 

 

 

 

반응형

 

 

728x90

 

 


 

 

 

출처: 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);

 

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

 

 


 

 

반응형