Category Archives: 안드로이드

Lock Timer를 소개합니다.

이름       : 잠금 타이머(Lock Timer)
분류       : 도구
가격       : 무료
언어       : 한국어, 영어 지원
지원버전 : 2.2 이상
스토어    : 안드로이드 마켓

애플리케이션 설명

이 어플리케이션은 정해진 시간에 여러분의 휴대폰을 잠그는 어플입니다.  여러분의 휴대폰을 다른 사람이나 여러분의 아이들이 잠시 사용하다가 언제 되돌려 주어야 하는지 알려줍니다. 

휴대폰을 주면 시간 가는 줄 모르고 사용하는 우리 아이들. 지나친 휴대폰 사용은 건강에 해롭습니다. 잠금 타이머로 우리 아이들을 지나친 휴대폰 사용으로부터 지켜주세요~

이 어플리케이션은 장치 관리자 권한이 필요하기 때문에, 반드시 장치 관리자 권한을 확인해 주시기 바랍니다.  삭제 하시리면, 동일하게 장치 관리자 권한을 먼저 해제한 후 삭제하셔야 합니다.

안드로이드 마켓 다운로드
https://play.google.com/store/apps/details?id=com.someboyj.locktimer

구글의 새로운 안드로이드 마스코드 젤리빈(Jelly Bean)

구글 젤리빈 마스코트를 구글 캠퍼스에 새우면서, 새로운 안드로이드 버전인 젤리빈(Jelly Bean)을 공식화했습니다.  어떻게 생겼는지 아래 사진을 참고하시구요.  왠지 안드로보의 팔을 돌리면 젤리빈이 아래로 나올 것 같네요. ^^  새로운 안드로이드 버전에서는 과연 어떤 신기술을 탑재했을지 기대됩니다.  참, 새로운 안드로이드 버전인 Jelly Bean의 버전은 5.0이 아니라, 4.1이라고 합니다.  버전을 보면 아주 큰 변화보다는 소소한 기능 업데이트로 보여지긴 합니다.  미국시간으로 수요일(6월 27일)에 구글 I/O에서 자세한 내용은 발표할 것으로 보입니다.  이번 구글 I/O는 동영상으로 생중계 해준다고 하니, 관심있는 분들은 보셔도 좋을 것 같습니다.  생중계를 보실 분들은 아래 링크로 가시면 됩니다.

https://developers.google.com/events/io/

[번역] Google Play 사용자 리뷰에 답글 달기

이 포스트는 구글 안드로이드 개발자 블로그의 포스트를 번역한 내용입니다. 원문과 표현상의 차이는 있을 수 있으나 전체적인 맥락은 동일함을 미리 말씀드리며, 한국 안드로이드 개발자들에게 도움이 되었으면 좋겠습니다.

Google Play에 있는 사용자 리뷰는 사용자들이 좋은 앱을 발견하고 개발자와 앱 사용 예정자들에게 피드백을 전달하기에 좋은 방법입니다.  하지만, 개발자들이 사용자들에게 피드백을 전달하고 싶을 때는 어떻게 할까요?  가끔은 사용자들에게 약간의 도움이 필요할 수도 있고, 개발자가 새롭게 추가된 기능을 공유하고 싶을 때도 있습니다.

이런 이유 때문에 Google Play 개발자들이 Google Play Android Developer Console에서 사용자 리뷰에 답장을 할 수 있는 기능을 추가하게 되었습니다.  개발자들은 추가적인 정보를 수집하고, 앱 사용법을 안내하고, (가장 중요하게) 사용자가 요청한 기능이 추가되었음을 알릴 수 있습니다.

또한, 개발자가 리뷰에 답장을 하면 사용자의 이메일로 전달될 것입니다.  만약 추가적인 지원이 필요하거나 리뷰를 업데이트 할 경우, 사용자는 개발자에게 직접 연락할 수도 있습니다.

이 기능을 오늘  Top Developer badge 개발자들에게만 제공합니다.  사용자와 개발자의 피드백을 기초로 추 후 추가적인 Google Play 개발자들에게도 제공될 예정입니다.

대화는 양방향이어야 하며, 개발자와 사용자 사이의 토론은 궁극적으로 모두에게 이득이 되는 더 나은 앱을 만들어 낼 것입니다.

관련 포스트: http://android-developers.blogspot.kr/2012/06/replying-to-user-reviews-on-google-play.html

자명종 설명서

자명종 이용 매뉴얼입니다.  필요하신 분들은 다운로드 받아가시길 바랍니다.

어플 소개
자명종은 매 정시간을 알림으로(다양한 음성지원) 들을 수 있는 어플리케이션 입니다.
상단 상태바에서 바로 끌어서 설정가능하며, 토스트 팝업으로 매 정시간 팝업메시지를 띄울 수도 있습니다. 매 시간 알림기능이 작동하지만 설정을 통해 원하는 시간에만 작동하게끔 설정이 가능하며 요일별 및 15분 단위 설정 또한 가능합니다.
바쁜 생활 가운데 시간 가는줄 모르신다구요? 이젠 자명종과 함께 어떻게 시간가는지 확인해 보세요~!

다운로드 -> 자명종_이용매뉴얼



[번역] Using DialogFragments

이 포스트는 구글 안드로이드 개발자 블로그의 포스트를 번역한 내용입니다. 원문과 표현상의 차이는 있을 수 있으나 전체적인 맥락은 동일함을 미리 말씀드리며, 한국 안드로이드 개발자들에게 도움이 되었으면 좋겠습니다.

허니컴은 앱에서 여러 액티비티 사이에서 재사용 할 수 있는 UI와 로직을 지원하기 위해 Fragments라는 것을 소개했습니다. 동시에 액티비티의 showDialog와 dismissDialog 메서드 대신 DialogFragments를 사용하길 권장하고 있습니다.

이 포스트에서는 v4 지원 라이브러리 (허니콤 이전 버전 호환을 위한)를 이용해 DialogFragments를 어떻게 사용하는지 간단한 대화창을 통해 보여드릴 것입니다. Dialogs와 관련한 디자인 가이드라인은 Android Design 사이트를 참고하시기 바랍니다.

 

레이아웃

여기에 fragment_edit_name.xml라는 이름을 가진 대화창의 레이아웃이 있습니다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/edit_name"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:layout_gravity="center" android:orientation="vertical"  >

    <TextView
        android:id="@+id/lbl_your_name" android:text="Your name"
        android:layout_width="wrap_content" android:layout_height="wrap_content" />

    <EditText
        android:id="@+id/txt_your_name"
        android:layout_width="match_parent"  android:layout_height="wrap_content"
        android:inputType=”text”
        android:imeOptions="actionDone" />
</LinearLayout>

두 개의 선택적인 속성을 주의해 보시기 바랍니다.  android:inputType=”text” 속성과 함께 설정된 android:imeOptions=”actionDone” 속성은 소프트 키보드가 엔터키 대신 완료키를 보이도록 설정합니다.

대화창 코드

대화창은 DialogFragment를 상속하며, 하위버전 호환성을 위해 v4 지원 라이브러리를 참조할 것입니다. (Eclipse 프로젝트에 라이브러리를 추가하려면, 프로젝트에서 오른쪽 마우스 버튼을 누르고 Android Tools | Add Support Library를 선택하면 됩니다.)

import android.support.v4.app.DialogFragment;
// ...

public class EditNameDialog extends DialogFragment {

    private EditText mEditText;

    public EditNameDialog() {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_edit_name, container);
        mEditText = (EditText) view.findViewById(R.id.txt_your_name);
        getDialog().setTitle("Hello");

        return view;
    }
}

대화창은 DialogFragment를 상속하고 반드시 필요한 빈 constructor를 포함합니다.  Fragments는 onCreateView() 메서드가 제공된 LayoutInflater를 이용해 실제로 뷰를 로딩하도록 구현합니다.

 

대화창 보이기

이제 Activity에서 대화창을 보여주는 코드가 필요합니다. 여기에 사용자의 이름을 입력할 EditNameDialog를 즉시 보여주는 간단한 예제가 있습니다. 작업 완료 후, 입력된 텍스트를 Toast로 보여줍니다.

import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
// ...

public class FragmentDialogDemo extends FragmentActivity implements EditNameDialogListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        showEditDialog();
    }

    private void showEditDialog() {
        FragmentManager fm = getSupportFragmentManager();
        EditNameDialog editNameDialog = new EditNameDialog();
        editNameDialog.show(fm, "fragment_edit_name");
    }

    @Override
    public void onFinishEditDialog(String inputText) {
        Toast.makeText(this, "Hi, " + inputText, Toast.LENGTH_SHORT).show();
    }
}

여기에 주의깊에 봐야 할 부분이 몇 가지 더 있습니다.  먼저, Fragment API와 하위 버전과의 호환성을 위해 지원 라이브러리를 사용하고 있기 때문에, 샘플 코드의 Activity는 지원 라이브러리의 FragmentActivity를 상속합니다.  때문에, getFragmentManager() 대신 getSupportFragmentManager()를 호출합니다.

초기 뷰를 로딩한 후, 액티비티는 곧바로 show() 메서드를 호출하여 EditNameDialog를 보여줍니다.  이를 통해 DialogFragment가 Dialog에 어떤 일이 일어나고 있는지 알 수 있게 하며, Fragment 상태도 일관성을 유지하게 됩니다.  기본적으로 뒤로가기 버튼은 추가적인 코딩이 없어도 대화창을 사라지게 합니다.

 

대화창 사용하기

다음으로 EditNameDialog가 결과 문자열을 Activity로 리턴하도록 수정해 봅시다.

import android.support.v4.app.DialogFragment;
// ...
public class EditNameDialog extends DialogFragment implements OnEditorActionListener {

    public interface EditNameDialogListener {
        void onFinishEditDialog(String inputText);
    }

    private EditText mEditText;

    public EditNameDialog() {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_edit_name, container);
        mEditText = (EditText) view.findViewById(R.id.txt_your_name);
        getDialog().setTitle("Hello");

        // Show soft keyboard automatically
        mEditText.requestFocus();
        getDialog().getWindow().setSoftInputMode(
                LayoutParams.SOFT_INPUT_STATE_VISIBLE);
        mEditText.setOnEditorActionListener(this);

        return view;
    }

    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (EditorInfo.IME_ACTION_DONE == actionId) {
            // Return input text to activity
            EditNameDialogListener activity = (EditNameDialogListener) getActivity();
            activity.onFinishEditDialog(mEditText.getText().toString());
            this.dismiss();
            return true;
        }
        return false;
    }
}

사용자 편의를 위해 mEditText.requestFocus()를 사용해 프로그램적으로 EditText로 포커스를 이동합니다.  다른 방법으로는 레이아웃 XML에 <requestFocus/> 태그를 사용할 수도 있습니다. 하지만, 경우에 따라서는 프로그램적으로 포커스를 호출하는 것이 더 좋을 수도 있습니다.  예를 들어, 레이아웃 XML에서 포커스를 호출할 경우, Fragment의 onCreateView() 메서드에 추가된 OnFocusChangeListener가 호출되지 않습니다.

사용자가 EditText에 포커스를 두면, 소프트 키보드가 자동으로 나타납니다.  프로그램적인 포커스로 동일한 이벤트를 발생시키려면, callgetDialog().getWindow().setSoftInputMode()를 호출합니다.  이전에 Dialog에서 사용하던 Window 관련 작업들도 동일하게 DialogFragment에서 사용할 수 있지만, getWindow() 대신에 getDialog().getWindow()를 호출해야만 합니다.  결과 대화창은 휴대폰과 태블릿에 동일하여 보여집니다:

onEditorAction() 메서드는 사용자가 완료키를 누를 때, 콜백을 처리하는데, 이는 EditText에 OnEditorActionListener를 설정했기 때문입니다. 입력된 텍스트를 전달하기 위해 Activity로 호출됩니다. 이를 위해 EditNameDialog에서 Activity에 의해 구현된 EditNameDialogListener 인터페이스를 선언합니다.  이것은 대화창이 많은 액티비티에서 재사용될 수 있게 해줍니다. onFinishEditDialog()에서 콜백 메서드를 발생시키기 위해 getActivity()를 호출하여 대화창을 실행하는 Activity의 참조키를 획득합니다.  이것은 MVCarchitecture에서 뷰가 컨테이너와 통신할 수 있게 하는 일반적인 패턴입니다.

대화창을 두 가지 방법 중 한 가지로 없앨 수 있습니다.  여기서는 Dialog 클래스 자체에 있는 dismiss()를 호출하고 있습니다.  이것은 show() 메서드처럼 Activity에서 호출 될 수도 있습니다.

이 글이 Dialogs와 관련이 있는 Fragments에 대한 이해하는데 어느 정도 도움이 되었으면 합니다.  이 글에 사용된 샘플 코드는 Google Code에서 찾으실 수 있습니다.

Fragments에 대한 추가적인 자료:

구글 블로그 원문: http://android-developers.blogspot.com/2012/05/using-dialogfragments.html

Activity와 Service 혹은 두 개의 프로세스에서 SharedPreferences 값 공유하기

Gingerbread (Android 2.3)  이전 버전에서는 SharedPreferences에 저장된 값을 여러 프로세스나 Activity와 Service 사이에서 사용할 수 있었습니다.  예를 들어 PreferenceActivity에서 변경한 값을 바로 Service 프로세스에서 가져와 사용할 수가 있었죠.  하지만 최근 안드로이드 버전 (Gingerbread 이 후)에서는 명시적으로 설정하지 않는 한 같은 방식으로 사용할 수가 없습니다.   하나의 프로세스에서 SharedPreferences의 값을 변경해도 계속 이전 값만을 불러오게 됩니다.

이 문제를 해결하려면, 변경된 값을 불러오는 프로세스에서 SharedPreferences를 새로 로딩할 때 아래와 같이 설정해줘야 합니다.  이 문제를 해결하는데 며칠을 구글링했네요….  ㅠㅜ  동일한 문제를 겪고 계신 개발자분들에게 도움이 되었으면 좋겠습니다.

SharedPreferences myPrefs 
  = context.getSharedPreferences("myPrefs", Context.MODE_MULTI_PROCESS);

PreferenceActivity에서 사용자 지정 SharedPreferences 사용하기

안드로이드 개발자라면 PreferenceActivity가 얼마나 편리한지 다들 알고 계시리라 생각합니다.  PreferenceActivity (HONEYCOMB 이상은 PreferenceFragment) 에는 컨텍스트의 기본 SharedPreferences가 제공되는데, 가끔은 다른 이름이나 다른 모드로 사용해야 할 때가 있습니다.

이럴 경우 사용하는 방법이 있습니다.  PreferenceActivity에 직접적으로 특정 SharedPreferences를 지정할 수 있는 방법이 없기 때문에, 아래와 같은 코드를 사용하면 됩니다:

public class MyPreferencesActivity extends PreferenceActivity {
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    PreferenceManager prefMgr = getPreferenceManager();
    prefMgr.setSharedPreferencesName("my_preferences");
    prefMgr.setSharedPreferencesMode(MODE_WORLD_READABLE);

    addPreferencesFromResource(R.xml.preferences);
  }
}

DDMS File Explorer를 사용해서 SharedPrefereces 이름을 확인해 보시기 바랍니다.

갤럭시노트 아이스크림샌드위치 업데이트 소식

갤럭시노트가 아이스크림샌드위치로 업데이트된다는 소식입니다.  갤럭시S2는 이미 업데이트되었죠.  머니투데이 기사를 보니,

삼성전자(대표 최지성)가 한국에서 출시된 갤럭시 노트에 안드로이드 4.0(아이스크림 샌드위치, ICS)과 S펜 기능 등을 얹은 ‘프리미엄 스위트’ 업그레이드를 시작한다. 삼성전자는 22일부터 최신 안드로이드 운영체제인 ICS와 함께 ‘S노트’ 등 다양한 특화 기능을 추가로 제공하는 프리미엄 스위트 업그레이드를 시작한다고 밝혔다.  (출처: http://news.zum.com/articles/2547186)

라고 하고 있네요.

국내에 갤럭시노트 사용자가 꽤 되는 것으로 알고 있는데, 국내의 안드로이드 버전도 전체적으로 올라가겠군요.  현재 갤럭시S2를 쓰고 있어서 갤럭시노트를 구매할 것 같진 않지만, 큰 화면이 좀 부럽기는 합니다.  예전에 3인치대 기기는 어떻게 썼었는지… 점점 노안이 오나봅니다.  ㅜㅠ

업그레이드된 갤럭시노트의 기능이 아니라 안드로이드의 아이스크림샌드위치에 새로 포함된 기능인 페이스언락이라는 기능이 있습니다.  사용자가 자신의 얼굴을 미리 등록하고, 핸드폰을 언락할 때 자신의 얼굴을 인식시켜서 여는 방법입니다.  저도 몇 번 해봤는데, 카메라 위치설정을 잘 해야 성공할 수 있습니다.  재미로 사용해 볼만은 하나 기본 언락기능으로 쓰기에는 너무 불편할 것 같습니다.  오히려 지문인식이 더 편할 것 같네요.

아무튼, 새로운 업데이트를 통해 사용자들이 갤럭시노트에 대해 어떤 반응들이 있을지 지켜봐야 할 것 같습니다.  갤럭시S3 소식에 좀 관심이 덜 할 것 같긴 합니다.

 

[번역] 안전하게 데이터 저장하기

생강빵의 출시와 함께 개발자들이 고려해야 할 안드로이드 2.3에 대한 글들을 연재할 예정입니다.  개발자들이 가장 중요하게 고려해야 할 것 중 하나는 데이터가 유실되지 않는 것입니다.  생강빵이 출시되면서 이 규칙이 살짝 변하고 있기 때문에 이 주제가 좋은 시작점이 될 것 같습니다.  이 글을 제가 쓴 것이 아니라 안드로이드 엔지니어인 Brad Fitzpatrick, Dianne Hackborn, Brian Swetland, Chris Tate의 이메일 내용에서 발췌한 것들입니다.

어떻게 당신의 데이터가 확실하게 비휘발성 저장소에 저장할 수 있을까요?  해답은 fsync()라는 로우레벨 시스템 호출과 관련되어 있습니다.  저와 같은 옛날의 C 프로그래머들은 옛날에 아주 어려운 방법으로  이것을 배웠습니다.  제가 2008년 OSCON에서 참여한 Stewart Smith의 Eat My Data: How Everybody Gets File IO Wrong 세션은 아주 즐거웠습니다.

이 부분을 안드로이드 개발자들이 고려해야 하는 이유는 2.3부터 넥서스S를 포함한 점점 증가하는 디바이스들이 YAFFS에서 버퍼링을 훨씬 많이 하는 ext4 파일시스템으로 옮겨갈 것이기 때문입니다. 따라서, 여러분이 원할 때 데이터가 영구적인 저장소로 저장되는지 더욱 적극적으로 확인해야 합니다.


여러분이 SharedPreferencesSQLite를 사용하고 있다면, 안심하셔도 됩니다.  저희가 버퍼링을 제대로 하고 있는지 검증했기 때문이죠.  하지만, 여러분이 자체적으로 디스크에 저장하는 형식을 사용하고 있다면, write()나 파일을 닫기 위해 close()를 호출할 때, 데이터가 항상 플래시 칩에 도달하는 것은 아니라는 것을 기억해야 합니다.  여러분과 하드웨어 사이에는 여러 개의 레이어가 존재합니다!  그리고, ext4 버퍼링 정책때문에 여러분이 가지고 있다고 생각했던(그러나, 없었던) POSIX 보증이 없습니다.

일부 안드로이드 디바이스는 이미 비YAFFS 파일시스템에서 실행되고 있지만, 넥서스S에서 작업하면서 프레임워크 소스에서 실제로 여러번 버퍼링 문제를 발견했습니다.  생강빵 소스가 배포되면, 파일 I/O가 어떻게 이루어 져야 하는지에 대한 예제들을 많이 찾을 수 있을 것입니다.

일단 로우 데이터에서 작업하려면 적절한 방법으로 fsync() 호출을 담당하는 java.io.RandomAccessFile의 동기화 모드 중 하나를 사용합니다.  여러분이 못하시면, 아마 아래와 같은 Java 코드를 원하실 겁니다.

     public static boolean sync(FileOutputStream stream) {
         try {
             if (stream != null) {
                 stream.getFD().sync();
             }
             return true;
         } catch (IOException e) {
         }
         return false;

일부 어플리케이션에서 여러분은 close() 호출의 리턴상태까지 체크하길 원하실 수도 있습니다.

물론 이 이야기에는 두 가지 면이 있습니다. 여러분이 fsync()를 호출하고 데이터를 강제로 저장하면, 속도가 느려질 수 있습니다; 최악의 경우 언제 끝날지 예상할 수 없을 정도로 느릴 수도 있습니다. 따라서 필요한 경우에 호출하되 불필요하게 사용하지 않도록 주의해야 합니다.


원문링크

[번역] 안드로이드 브라우져 User-Agent 문제

이 글은 대형 메인보드를 사용하는 다양한 모바일 기기에서 웹사이트를 볼 때 생기는 문제에 대한 내용입니다.  이 글은 OEM (디바이스에 User Agent 값을 어떻게 설정해야 하는지) 업체나 웹사이트 디자이너/관리자들(모바일 버전, 데스크탑 버전, 혹은 대형 메인보드를 사용하는 터치 디바이스에서 어떻게 사이트를 제공할 것인지)에게 관심이 가는 주제일 것 입니다.


세부내용

대형 메인보드를 가진 안드로이드 디바이스의 출현으로 인해, 고객들이 가지고 있는 다양한 안드로이드 디바이스에 적절한 UI를 제공하는 최선의 방법을 모색하게 되었습니다.  대형화면을 가진 디바이스를 사용하는 고객들로부터 “모바일” 버전의 사이트보다 “전체”나 “데스크탑” 버전의 사이트를 더 선호한다는 피드백을 받았습니다.  대부분의 웹사이트는 전체화면 사이트를 제공할지 모바일 버전을 제공할지를 HTTP User-Agent 헤더 필드의 값을 기준으로 판단합니다.

대형화면을 가진 안드로이드 디바이스가 HTTP 헤더에 데스크탑 User Agent 값을 넣기 위해 “User Agent Spoofing”을 사용할 수 있으나, 바람직한 방법이 아닙니다.  안드로이드 디바이스에 필요한 사이트 변경(예를 들어 마우스 오버가 사용되는 방식의 변경과 같은 것)이 필요할 수 있는데, User Agent가 안드로이드 디바이스인 것을 나타내지 않으면 이런 변경사항을 제공할 수 없습니다.

현재 안드로이드 디바이스는 User-Agent에 다음과 같은 정보(표준 정보 외에)를 제공합니다: “Android”, 버전 번호, 디바이스 이름, 빌드, 웹키트 버전 정보, “Mobile”. 예를 들면, 넥서스원의 프로요는 다음과 같은 User Agent를 가집니다:


Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; Nexus One Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1


User Agent에 있는 “Mobile”이란 문자열은 이 디바이스는 가능하다면 모바일(소형 메인보드 디바이스)에 최적화된 웹사이트 버전을 선호한다는 것을 나타냅니다.

우리는 대형 메인보드 디바이스(사용자가 모바일 최적화 버전보다는 표준 웹사이트를 선호하는) 생산업체가 User Agent에서 “Mobile” 문자열을 지우는 것(User Agent에 현재 구현된 나머지는 유지)을 추천합니다.  그러면, 이제 웹사이트는 어떤 UI를 디바이스에 표현해야 할지 결정하기 위해 User Agent의 “Mobile” 키값을 사용할 필요가 없습니다.  따라서, 프로요가 설치된 대형화면을 가진 디바이스의 User Agent는 다음과 비슷합니다:

<code>
</code>

Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; device Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Safari/533.1


“device” 문자열은 신규 디바이스의 실제 이름으로 대체됩니다.  사이트는 계속해서 안드로이드 전용 기능에 최적화하기 위해 User Agent의 “Android”값을 사용함과 동시에 어떤 UI를 제공할지 결정하는 “Mobile”값을 비활성화 할 수 있습니다.


원문 링크