태그 보관물: 안드로이드

Java에서 Javascript의 encodeURIComponent 구현하기

자바에서는 URLEncoder를 사용해서 인코딩을 할 수 있는데 자바스크립트에서 사용하는 encodeURIComponent와 살짝 다르다.

Java의 URLEncoder:

  • literal characters (regex representation): [-a-zA-Z0-9._*]
  • the space character " " is converted into a plus sign "+".

JavaScript의 encodeURIComponent():

  • literal characters (regex representation): [-a-zA-Z0-9._*~'()!]

즉, 자바에서는 빈칸을 ‘+‘로 바꾸고 자바스크립트는 빈칸을 ‘%20‘으로 변환,
그리고 자바스크립트에서는 ~'()!을 그대로 두지만 자바에서는 각각 UTF-8 인코딩시킨다.

결국 빈칸과 ~'()!을 변환시키면 URLEncoder의 결과를 encodeURIComponent의 결과와 동일하게 만들 수 있다.

 

public static String encodeURIComponent(String s)
  {
    String result = null;
 
    try
    {
      result = URLEncoder.encode(s, "UTF-8")
                         .replaceAll("\\+", "%20")
                         .replaceAll("\\%21", "!")
                         .replaceAll("\\%27", "'")
                         .replaceAll("\\%28", "(")
                         .replaceAll("\\%29", ")")
                         .replaceAll("\\%7E", "~");
    }
 
    // This exception should never occur.
    catch (UnsupportedEncodingException e)
    {
      result = s;
    }
 
    return result;
  }

안드로이드에서 개발한 앱과 서버간의 통신이 제대로 안되어 헤맸었는데 빈칸이 다르게 변환되는 것이 문제였다.

Java equivalent to JavaScript’s encodeURIComponent that produces identical output?

안드로이드 장치에서 이메일 주소 읽어오기

안드로이드 앱에서 사용자의 이메일 주소를 읽어오는 방법입니다.

회원가입시에 미리 입력 시켜둬서 사용자 편의성을 높이는데 사용할 수 있으며 악용하진 말아야할 것 입니다.

 

1. AccountManager 사용하기 (API 5 이상)

AccountManager를 이용하면  기기에 등록된 모든 계정이름을 불러올 수 있고 계정이름이 이메일인 경우가 많기 때문에 활용할 수 있습니다.

Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
    if (emailPattern.matcher(account.name).matches()) {
        String possibleEmail = account.name;
        ...
    }
}

AccountManager를 사용하려면 GET_ACCOUNTS 권한이 필요하니 AndroidManifest.xml에 권한을 추가해둬야 합니다.

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

 

2. ContactsContract.Profile 이용하기 (API 14 이상)

ICS부터는 사용자의 프로필에 접근하여 이메일을 가져올 수 있습니다.

public class ExampleActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getLoaderManager().initLoader(0, null, this);
    }
 
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle arguments) {
        return new CursorLoader(this,
                // Retrieve data rows for the device user's 'profile' contact.
                Uri.withAppendedPath(
                        ContactsContract.Profile.CONTENT_URI,
                        ContactsContract.Contacts.Data.CONTENT_DIRECTORY),
                ProfileQuery.PROJECTION,
 
                // Select only email addresses.
                ContactsContract.Contacts.Data.MIMETYPE + " = ?",
                new String[]{ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE},
 
                // Show primary email addresses first. Note that there won't be
                // a primary email address if the user hasn't specified one.
                ContactsContract.Contacts.Data.IS_PRIMARY + " DESC");
    }
 
    @Override
    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
        List<String> emails = new ArrayList<String>();
        cursor.moveToFirst();
        while (!cursor.isAfterLast()) {
            emails.add(cursor.getString(ProfileQuery.ADDRESS));
            // Potentially filter on ProfileQuery.IS_PRIMARY
            cursor.moveToNext();
        }
 
        ...
    }
 
    @Override
    public void onLoaderReset(Loader<Cursor> cursorLoader) {
    }
 
    private interface ProfileQuery {
        String[] PROJECTION = {
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
        };
 
        int ADDRESS = 0;
        int IS_PRIMARY = 1;
    }
}

READ_PROFILEREAD_CONTACTS의 두 가지 권한이 필요합니다.

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

참조 : How to get the Android device’s primary e-mail address

x86 안드로이드 시뮬레이터에서 구글 지도 API 사용하기

  1. Android Virtual Device Manager에서 Target을 Google API를 포함한 “Google APIs (Google Inc.) – API Level 16” 같은 걸로 선택하여 AVD 생성
  2. 생성한 AVD 실행. emulator -avd name_of_avd
  3. 터미널에서 adb pull /system/etc/permissions/com.google.android.maps.xml
  4. adb pull /system/framework/com.google.android.maps.jar
  5. Android Virtual Device Manager에서 CPU/ABI를 Intel Atom (x86)으로 하여 AVD 생성
  6. 생성한 AVD 실행. emulator -avd name_of_avd
  7. adb remount rw
  8. adb push com.google.android.maps.xml /system/etc/permissions
  9. adb push com.google.android.maps.jar /system/framework
  10. mkfs.yaffs2.x86 다운로드
  11. adb push mkfs.yaffs2.x86 /data
  12. adb shell
  13. cd /data
  14. chmod 777 mkfs.yaffs2.x86
  15. ./mkfs.yaffs2.x86 /system system.img
  16. exit
  17. adb pull /data/system.img (...be patient)
  18. system.img을 avd 디렉토리안에 복사
  19. emulator 다시시작

How to use Google Maps API in Android emulator SDK version 17

Titanium android에선 테두리가 검은 이미지를 9-patch로 인식

Titanium의 안드로이드 지원은 꽤나 후진편인데,

검은색 이미지를 사용할 경우 이런 에러가 나오는 것을 볼 수 있다.(2.0.2 GA 기준)

06-07 16:56:04.507: E/TiApplication(2738): (KrollRuntimeThread) [438,440] APP PROXY: [email protected]
06-07 16:56:14.105: E/TiApplication(2738): (main) [1156,8379] Sending event: exception on thread: main msg:java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0; Titanium 2.0.2,2012/05/30 10:21,2ff31a3
06-07 16:56:14.105: E/TiApplication(2738): java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
06-07 16:56:14.105: E/TiApplication(2738): 	at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
06-07 16:56:14.105: E/TiApplication(2738): 	at java.util.ArrayList.get(ArrayList.java:304)
06-07 16:56:14.105: E/TiApplication(2738): 	at org.appcelerator.titanium.util.TiNinePatchHelper.createChunk(TiNinePatchHelper.java:189)
06-07 16:56:14.105: E/TiApplication(2738): 	at org.appcelerator.titanium.util.TiNinePatchHelper.process(TiNinePatchHelper.java:52)
06-07 16:56:14.105: E/TiApplication(2738): 	at org.appcelerator.titanium.util.TiFileHelper.loadDrawable(TiFileHelper.java:308)
06-07 16:56:14.105: E/TiApplication(2738): 	at org.appcelerator.titanium.util.TiUIHelper.buildBackgroundDrawable(TiUIHelper.java:493)
06-07 16:56:14.105: E/TiApplication(2738): 	at org.appcelerator.titanium.view.TiUIView.handleBackgroundImage(TiUIView.java:783)
06-07 16:56:14.105: E/TiApplication(2738): 	at org.appcelerator.titanium.view.TiUIView.processProperties(TiUIView.java:562)
06-07 16:56:14.105: E/TiApplication(2738): 	at ti.modules.titanium.ui.widget.TiView.processProperties(TiView.java:39)
...

로컬 이미지 리소스를 불러올때에 9-patch 이미지인지 확인을 하면서

이미지 테두리가 투명과 검은색으로만 이루어져있으면 9-patch로 인식을 하도록 하는 병맛스러운 코드가 있다.

테두리가 모두 투명하면 9-patch로 인식하지 않도록 하는 방어코드(?)가 있으니 피하려면 테두리를 모두 투명하게 하거나 검은색에서 약간 투명한 색을 사용하면 될듯하다;

그런데 애초에 .9.png가 아니면 9-patch로 인식을 할 필요가 있을까?