Monday, December 19, 2016

Evernote를 사용을 중지하고 OneNote로 갈아탔다

사실 에버노트를 버리고 원노트로 가려고 한 시도가 예전에 몇번이나 있었지만, 이제는 연동할 디바이스가 늘어나니 더이상 로그인을 해서 연동 해제를 해야하는 번거로움이 너무 크다. 아니면 유료를 사용해야 하는데 아직까지는 그러기는 싫다.

원노트가 싫었던 이유는 다음과 같다.
1.동기화가 느리다.
2.원노트에서 텍스트로 복사할때 잘 깨진다.
3.커서가 아무데나 막 간다. 흡사 이건 예전에 Delphi나 C++Builder와 같다. Borland IDE의 것과 유사하다는 점이다.

그런데 단하나의 장점
1.연동 무제한, 완전 무료

이거 하나로 모든걸 커버한다.

Sunday, December 18, 2016

Android EUC-KR 텍스트 파일 읽기 및 인코딩

안드로이드에서 TXT파일을 처리하다보면 EUC-KR 파일을 처리할때가 있다. 그럴때는 다음과 같이 UniversalDetector를 쓰면 되는데 gradle에 먼저 다음과 같이 추가해 주어야 한다.
    //encoding
    compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'

그리고 다음과 같이 프로그래밍 하면 텍스트 파일 내용을 UTF-8 자바 문자열로 얻을 수 있다.
File file = new File(path);
FileInputStream is = new FileInputStream(file);

UniversalDetector detector = new UniversalDetector(null);
byte[] buffer = new byte[is.available()];
is.read(buffer);
is.close();
detector.handleData(buffer, 0, buffer.length);
detector.dataEnd();

String text = new String(buffer, detector.getDetectedCharset());

Thursday, December 15, 2016

마제스터치 닌자 레드 텐키레스 체리 청축 구입

마제스터치 닌자 레드 텐키레스 청축을 구매하였다.
오랫만에 기계식 키보드를 사용해보고 싶었고, 그리고 청축을 사용해 보고 싶어서 중고나라에서 중고로 7.5만원에 구매하였다.

텐키레스를 좋아하므로 텐키레스를 주문했다. 그런데 중고 답게 빨간색 도색이 군데군데 벗겨졌고, 먼지가 뭍어 있어서 더스트 블로어를 뿌렸더니 조금 액이 뭍어버렸다.

키감은 전부 청축 키감이 살아있었고 디자인도 너무 좋아서 마음에 드는 물건이다.
오랫만에 다시 팔지말고 잘 사용해 봐야겠다.



iOS Collection element of type 'double' is not an Objective-C object

http://stackoverflow.com/questions/22758048/collection-element-of-type-double-is-not-an-objective-c-object

위의 에러메세지는 컬렉션에는 double 타입이 들어가지 못한다는 에러 메세지이다.
그래서 NSNumber로 박싱을 해야 한다.

박싱은 간단하게 @(doubleTypeVariable) 또는 [NSNumber numberWithDouble:doubleTypeVariable] 이렇게 할수 있다.

Thursday, July 14, 2016

C언어 Quick Soft 예제

int data[7] = { 3,5,1,2,6,9,7 };

void swapInt(int &a, int &b)
{
int tmp = a;
a = b;
b = tmp;
}

void printArr(int arr[]) {
printf("arr=");
for (int i = 0; i < 7; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}

void quickSort(int arr[], int left, int right)
{
printf("left=%d right=%d\n", left, right);

int i = left;
int j = right;
printArr(arr);

int pivotIndex = (left + right) / 2;
int pivot = arr[pivotIndex];
printf("pivotIndex=%d pivot=%d\n", pivotIndex, pivot);

while (i <= j) {
// 피봇을 기준으로 left의 값이 pivot의 값보다 큰 것을 찾는다.(잘못 놓여진 것을 찾음)
while (arr[i] < pivot) {
printf("arr[%d]=%d i++\n", i, arr[i]);
i++;
}
// 피봇을 기준으로 right의 값이 pivot의 값보다 작은 것을 찾는다.(잘못 놓여진 것을 찾음)
while (arr[j] > pivot) {
printf("arr[%d]=%d j--\n", j, arr[j]);
j--;
}

// 인덱스가 정상적인 상황일때 스왑한다.
if (i <= j) {
printf("swap begin %d %d\n", i, j);
swapInt(arr[i], arr[j]);
printArr(arr);
i++;
j--;
printf("swap end %d %d\n", i, j);
}
}

// 나머지 좌우에 대해서 다시 퀵소트 한다.
if (left < j) {
quickSort(arr, left, j);
}

if (i < right) {
quickSort(arr, i, right);
}
}


int main()
{
quickSort(data, 0, 6);
printf("data=");
for (int i = 0; i < 7; i++) {
printf("%d ", data[i]);
}
printf("\n");
return 0;
}

Android Camera2 API 기능

간단히 Camera2 API가 할 수 있는 기능은

- Burst capture at full resolution at up to 30fps.
센서 풀 해상도로 초당 30프레임의 사진을 찍을 수 있습니다.

- RAW (dng) image capture (more on this later).
DNG 파일로 캡처할 수 있습니다.

- Full manual focus.
흉내만 낸 수동초점이 아닌 진짜 리얼 풀 매뉴얼 포커스가 가능합니다.

- Faster autofocus
좀더 빠른 자동초점을 잡습니다.

- A smoother viewfinder.
부드러운 미리보기를 볼 수 있습니다.

- Full resolution video.
센서 풀 해상도의 영상을 저장할 수 있습니다.

- No viewfinder swapping when switching between modes.
모드변경을 위해 미리보는 화면 변경되는 과정이 없습니다.

- Implement granular settings before capture.
세부적인 설정이 가능합니다.

- Request target frame rates.
프레임레이트를 정할 수 있습니다.

- Access to raw sensor data.
RAW 데이타에 접근할 수 있습니다. 이전에는 포스트프로세스 애프터프로세스를 통해 JPG로 저장되었죠

- Flash firing support
플래쉬 터뜨리는 방법을 조절할 수 있습니다. 상상이지만 모델링 플래쉬도 구현되겠죠?

- Video HDR
HDR 영상을 지원합니다.

- Focus stacking.
Burst로 한 사진에 여러 초점 사진을 찍을 수 있습니다.

- Exposure bracketing
Burst로 노출 브라켓팅 (여러 노출로 찍는 기법입니다)을 찍을 수 있습니다.

Wednesday, July 13, 2016

Android ANR 디버깅 방법

안드로이드에서 ANR 디버깅 방법
-Method profiling
-Thread monitor
-StrictMode 정의
Main Thread에서는 시간이 많이 소모될 수 있는, 동작을 규정하고 막을 수 있다.
그 규정은 안드로이드에서 제공하는 범위에서 개발자가 정한다.
그 규정 위반시 안드로이드에서 제공하는 범위에서 처리를 할 수 있다.
-Dropbox 사용(제조사, 에뮬레이터)

http://cluster1.cafe.daum.net/_c21_/bbs_search_read?grpid=1MWA2&fldid=aAfL&datanum=99&openArticle=true&docid=1MWA2%7CaAfL%7C99%7C20110712112022

동영상 프레임의 종류 I/P/B 프레임(i-frame, p-frame, b-frame)

I 프레임 - Infra Frame 의 약자로, 쉽게 말해 키 프레임 입니다. 이것은 JPEG 같은 방식으로 소스로부터 직접 압축되어 온 전체 그림이죠. 가장 화질도 좋지만 가장 용량도 큽니다.
P 프레임 - Previous 또는 Predicted Frame 이라 불리며, 이전에 나온 키 프레임의 정보를 바탕으로 구성된 프레임 입니다. 화질/용량 둘 다 중간급입니다.
B 프레임 - Bidirectional Frame 의 약자로, 전후의 I/P 프레임의 정보를 바탕으로 구성된 프레임 입니다. 화질/용량이 다 최하급입니다.

Java GC root

GC의 reachability를 판단할때는 다음과 같은 GC root 노드를 기준으로 판단한다. 그 GC root에 해당하는 것들은 다음과 같은 것이 있다.

The so-called GC (Garbage Collector) roots are objects special for garbage collector. Garbage collector collects those objects that are not GC roots and are not accessible by references from GC roots.

There are several kinds of GC roots. One object can belong to more than one kind of root. The root kinds are:

- Class - class loaded by system class loader. Such classes can never be unloaded. They can hold objects via static fields. Please note that classes loaded by custom class loaders are not roots, unless corresponding instances of java.lang.Class happen to be roots of other kind(s).
- Thread - live thread
- Stack Local - local variable or parameter of Java method
- JNI Local - local variable or parameter of JNI method
- JNI Global - global JNI reference
- Monitor Used - objects used as a monitor for synchronization
- Held by JVM - objects held from garbage collection by JVM for its purposes. Actually the list of such objects depends on JVM implementation. Possible known cases are: the system class loader, a few important exception classes which the JVM knows about, a few pre-allocated objects for exception handling, and custom class loaders when they are in the process of loading classes. Unfortunately, JVM provides absolutely no additional detail for such objects. Thus it is up to the analyst to decide to which case a certain "Held by JVM" belongs.

Tuesday, July 12, 2016

Android RenderScript mono filter 예제

// mono.rs
#pragma version(1)
#pragma rs java_package_name(com.example.renderscriptsample)

float3 gMonoMult = {0.2125, 0.7154, 0.0721};

void root(const uchar4 *v_in, uchar4 *v_out) {
    float4 f4 = rsUnpackColor8888(*v_in);
    float3 mono = dot(f4.rgb, gMonoMult);
    *v_out = rsPackColorTo8888(mono);
}

MainActivity.java
package com.example.renderscriptsample;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.renderscript.Allocation;
import android.renderscript.RenderScript;
import android.view.Menu;
import android.widget.ImageView;

public class MainActivity extends Activity {
 // RenderScript
 private RenderScript mRS;
 private Allocation mInAllocation;
 private Allocation mOutAllocation;
 private ScriptC_mono mScript;

 private Bitmap loadBitmap(int resource) {
  final BitmapFactory.Options options = new BitmapFactory.Options();
  options.inPreferredConfig = Bitmap.Config.ARGB_8888;
  return BitmapFactory.decodeResource(getResources(), resource, options);
 }

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // bitmap load
  final Bitmap bm = loadBitmap(R.drawable.data);
  final Bitmap bmOut = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());

  // renderscript init
  mRS = RenderScript.create(this);
  mInAllocation = Allocation.createFromBitmap(mRS, bm, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
  mOutAllocation = Allocation.createFromBitmap(mRS, bm, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
  mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);

  // renderscript run
  mScript.forEach_root(mInAllocation, mOutAllocation);
  mOutAllocation.copyTo(bmOut);
 
  // set output bitmap
  final ImageView iv = (ImageView) findViewById(R.id.imageView1);
  iv.setImageBitmap(bmOut);

 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

}

Android pixmaps 특징

EGL also supports rendering surfaces whose color buffers are stored in native pixmaps. Pixmaps differ from windows in that they are typically allocated in off- screen (non-visible) graphics or CPU memory. Pixmaps differ from pbuffers in that they do have an associated native pixmap and native pixmap type, and it may be possible to render to pixmaps using APIs other than client APIs .

EGL defines several types of drawing surfaces collectively referred to as EGLSurfaces. These includewindows, used for onscreen rendering; pbuffers, used for offscreen rendering; and pixmaps, used for offscreen rendering into buffers that may be accessed through native APIs. EGL windows and pixmaps are tied to native window system windows and pixmaps.

The main differences between pixmaps and windows are:
•An EGL implementation doesn't need to support pixmaps (the native windowing system might not have an equivalent concept)
•Pixmaps are typically single-buffered, whereas a window might be double- or triple-buffered.
•Pixmaps are offscreen surfaces (although offscreen windows might be possible as well)

Android RenderScript FilterScript 예제

FilterScript

  • Introduced in Android 4.2 (API Level 17), Filterscript defines a subset of Renderscript that focuses on image processing operations, such as those that you would typically write with an OpenGL ES fragment shader.
  • 현재 android developer의 renderscript 항목에는 기본적으로 filterscript를 사용하기를 설명하고 있다.
    • http://developer.android.com/guide/topics/renderscript/compute.html
Usage
  • Inputs and return values of root functions cannot contain pointers. The default root function signature contains pointers, so you must use the __attribute__((kernel)) attribute to declare a custom root function when using Filterscript.
  • Built-in types cannot exceed 32-bits.
  • Filterscript must always use relaxed floating point precision by using the rs_fp_relaxed pragma.
    • Most applications can use rs_fp_relaxed without any side effects. This may be very beneficial on some architectures due to additional optimizations only available with relaxed precision (such as SIMD CPU instructions).
  • Filterscript files must end with an .fs extension, instead of an .rs extension
    • Android-18에서는 fs대신에 rs를 써도 된다.

//mono.fs
#pragma version(1)
#pragma rs java_package_name(com.example.renderscriptsample)
#pragma rs_fp_relaxed
// for version 17
// can called by forEach_invert(in, out)
uchar4 __attribute__((kernel)) invert(uchar4 in, uint32_t x, uint32_t y) {
  uchar4 out = in;
  out.r = 255 - in.r;
  out.g = 255 - in.g;
  out.b = 255 - in.b;
  return out;
}


//MainActivity.java
// renderscript init
mRS = RenderScript.create(this);
mInAllocation = Allocation.createFromBitmap(mRS, bm, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mOutAllocation = Allocation.createFromBitmap(mRS, bm, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);
// renderscript run
mScript.forEach_invert(mInAllocation, mOutAllocation);

Saturday, July 9, 2016

Android compileReleaseNdk 에러 해결

NDK를 사용할때 JNI라는 폴더가 있으면 다음과 같이 최신의 experimental plugin을 사용하라고 에러메세지가 뜬다.

Error:Execution failed for task ':imagefilter:compileReleaseNdk'.
> Error: NDK integration is deprecated in the current plugin.  Consider trying the new experimental plugin.  For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental.  Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.

그럴때는 gradle에서 다음과 같이 선언하면 jni폴더를 무시하게 되고, 에러가 발생하지 않으며, 콘솔에서 ndk-build로 빌드할 수가 있다.

    sourceSets {
        main {
            // eclipse의 폴더명과 동일하게 사용함, 왜냐면 기본적으로 ndk-build의 결과는 libs안에 들어가게 된다.
            jniLibs.srcDirs = ["src/main/libs"]

            // android studio가 직접 컴파일하지 않게 함
            jni.srcDirs = []
        }
    }

Tuesday, July 5, 2016

Build 2016 요약 정리


  • 2.7억개 장치에서 윈도우10 가동
  • 펜 및 잉크 지원 향상
  • 윈도우10 Bash 지원
  • 코타나 향상 - XboxOne 지원
  • 통합 스토어 윈도우10
  • Edge 생체 인증 지원
  • 페이스북 광고 플랫폼이 유니버설 윈도우 플랫폼으로 들어옴
  • 일반 XboxOne을 개발기기로 사용가
  • 데스크탑앱을 유니버설 앱으로 컨버팅 지원 - 나중에 에이지오브엠파이어2 HD버전과 위처3로 데모할 예정
  • Visual Studio 2015 Update 2 예정됨
  • 모든 게임이 윈도우10과 Xbox One에 릴리즈 예정
  • 홀로렌즈 개발자킷이 공개됨
  • 주위의 사람들을 인식할수 있는 새로운 스마트폰 앱이 공개됨
  • CaptionBot이라는 사진인식 AI가 공개됨

iOS WWDC 2016 요약 정리


watchOS3
백그라운드 로딩속도 향상
글자인식
긴급상황+위치정보 전송
휠체어 운동량
미키마우스 인터페이스 변경


iOS10
손으로 들었을때 자동으로 켜짐
홈버튼 눌러서 잠금해제로 변경
잠금상태에서 3D터치로 메세지전송,사진촬영등 가능
제어센터 좌우 슬라이드
시리 써드파티 개방
사진 촬영장소, 얼굴인식 분류 가능
스마트홈앱
전화 스팸여부 확인, 써드파티 VoIP와 연결
메세지 업그레이드 - 손글씨, 전체화면, 이모티콘 변환
아이메세지 특수효과

Friday, July 1, 2016

iOS Swift 키보드 show/hide 시 UI 위치 보정 처리

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        
        registerForKeyboardNotifications()
    }
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        
        unregisterForKeyboardNotifications()
    }

    //MARK: Keyboard events
    func registerForKeyboardNotifications() {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillShow), name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillHide), name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterForKeyboardNotifications() {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }
    
    func keyboardWillShow(note: NSNotification) {
        // UI 키보드 위로 올려준다
        print("keyboardWillShow")

        let s = note.userInfo![UIKeyboardFrameEndUserInfoKey]
        let rect = s!.CGRectValue()
        
        // 입력창 위치를 올려줌
        var frame = textField.frame
        frame.origin.y -= rect.height
        textField.frame = frame

        let keyboardFrameEnd = view!.convertRect(rect, toView: nil)
        view.frame = CGRectMake(0, 0, keyboardFrameEnd.size.width, keyboardFrameEnd.origin.y)
        view.layoutIfNeeded()
    }
    
    func keyboardWillHide(note: NSNotification) {
        // UI 원위치 한다
        print("keyboardWillHide")

        let s = note.userInfo![UIKeyboardFrameBeginUserInfoKey]
        let rect = s!.CGRectValue()

        var frame = textField.frame
        frame.origin.y += rect.height
        textField.frame = frame
        
        frame = view.frame
        view.frame = CGRectMake(frame.origin.x, frame.origin.y, frame.width, frame.height + rect.height)
        view.layoutIfNeeded()
    }

Monday, June 27, 2016

iOS Swift NSThread 생성하기

selector의 구문이 바뀌었다.
self 클래스에 runLoop()가 있어야 한다.

            let thread = NSThread(target:self, selector:#selector(runLoop), object: nil)
            thread.start()
  
func runLoop() {
        while(true) {
            print("runLoop")
        }
    }

Friday, June 17, 2016

Android 소프트키 상태바 반투명 만들기

전체화면 애플리케이션을 만들대 소프트키와 상태바를 반투명으로 만들어서 보이기/감추기를 해야될때가 있다.
그럴때는 다음과 같이 한다.

안드로이드 4.4부터 지원이 되므로 values-v19 폴더를 만든다.
다음과 같이 테마에서 translucent값을 2개를 true로 설정한다.
    <style name="PlayerTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:windowTranslucentNavigation">true</item>
        <item name="android:windowTranslucentStatus">true</item>
    </style>
액티비티에서 해당 테마를 적용한다.
android:theme="@style/PlayerTheme"

Android 소프트키 유무 체크 및 높이 구하기

안드로이드 하단바 소프트키 네비게이션바 높이를 구할때는 다음과 같이 구한다.
단위는 pixel이다.

private boolean hasSoftMenu() {
        //메뉴버튼 유무
        boolean hasMenuKey = ViewConfiguration.get(getApplicationContext()).hasPermanentMenuKey();

        //뒤로가기 버튼 유무
        boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);

        if (!hasMenuKey && !hasBackKey) { // lg폰 소프트키일 경우
            return true;
        } else { // 삼성폰 등.. 메뉴 버튼, 뒤로가기 버튼 존재
            return false;
        }
    }

    private int getSoftMenuHeight() {
        Resources resources = this.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        int deviceHeight = 0;

        if (resourceId > 0) {
            deviceHeight = resources.getDimensionPixelSize(resourceId);
        }
        return deviceHeight;
    }

Friday, June 10, 2016

Android N Preview 요약

안드로이드 N Preview
-멀티윈도우 서포트
-노티피케이션 커스텀(다이렉트 리플라이, 번들드)
-JIT/AOT 향상
-업데이트 설치 간소화
-Doze 향상
-백그라운드 최적화
-데이터 세이버
-OpenGL ES와 같은 Vulkan
-퀵 세팅 타일
-넘버 블로킹
-콜 스크리닝
-멀티 로케일
-새로운 이모티콘
-ICU4J API 유니코드
-OpenGL ES 3.2
-Android TV 레코딩
-Android For Work
-VR 지원
-Sign v2
-가상파일
-Scoped 디렉토리
-네트웍 시큐리티

Thursday, June 9, 2016

Android FFmpeg MacOS에서 빌드하기

인터넷에 안드로이드 FFmpeg 빌드하기 내용을 찾아봤보았다.
거의다 Linux거나 Windows의 cygwin으로 빌드하는 방법이 나온다.

그런데 MacOSX에서도 직접 빌드가 가능하다.
그리고 예전의 버전들에 비해서 빌드가 한결 간편해 졌다.
사용하는 버전은 다음과 같다.

Android NDK: r11c
ffmpeg: 3.0.2
MacOSX: El Capitan  10.11.5

먼저 다음 디렉토리에 ffmpeg 소스를 받아서 압축을 푼다.
~/android-ndk-r11c/sources/ffmpeg-3.0.2$

그리고 다음과 같이 config.sh를 만들어서 실행한다.
여기서 중요한 것은 다른 예제에서는 --target-os=linux로 설명하는데 안드로이드가 리눅스라도 그냥 빌드하게되면 .so파일 뒤에 .so.5.24 이런식으로 major, minor 버전으로 symlink가 걸리게 된다.
그러므로 반드시 --target-os=android로 설정하자.

#!/bin/bash
NDK=/Users/duongame/android-ndk-r11c
SYSROOT=$NDK/platforms/android-9/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
function build_one
{
./configure \
    --disable-symver \
    --prefix=$PREFIX \
    --enable-shared \
    --disable-static \
    --disable-doc \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --disable-avdevice \
    --disable-doc \
    --disable-symver \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --target-os=android \
    --arch=arm \
    --enable-cross-compile \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
}
CPU=arm
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
build_one

빌드가 끝나면 다음 디렉토리에 include, lib가 생기는데 이것을 복사해서 ndk빌드할때 사용하던지, 패스를 설정하던지 하면된다.
~/android-ndk-r11c/sources/ffmpeg-3.0.2/android/arm

Thursday, June 2, 2016

MacOSX Visual Studio Code PATH 추가

Visual Studio Code를 열고 Shift+Command+P를 입력 후
"shell command"를 입력

Tuesday, May 31, 2016

MacOSX 엘케피탄 트랙패드 세손가락으로 드래그하기

엘케피탄은 한영키도 그렇지만 기본적으로 트랙패드 세손가락으로 드래그하기가 설정이 안되어 있다.

그래서 다음과 같이 설정한다. 손쉬운 사용 > 트랙패드 옵션...에 들어가서 '드래그 활성화'를 한다음에 '세 손가락으로 드래그'하기를 선택한다.

MacOSX 엘케피탄 한영전환 설정하기 Command+Space

MacOSX 엘케피탄으로 오면서 예전버전에서 사용하던 Command+Space로 한영전환이 되지 않고 Control+Space로 한영전환이 되어서 정말 불편하게 되었다.

설정 > 키보드 > 단축키 > 입력소스에서 다시 Command+Space로 설정한다.
그런데 그냥 이렇게 설정하게 되면 Spotlight와 키가 겹치게 되므로 Spotlight도 키를 다음과 같이 바꾸어 주어야 한다.

언리얼엔진4 설치

언리얼엔진4가 요즘 최신 유행이라서 개발 맥북프로에 설치를 해보았다.
아직 샘플프로그램은 하나도 돌려보지 않은 상태이다.
그래도 정말 멋진 게임 엔진이 될것 같다는 생각이 든다.

삼성전자 연봉 및 근속

직급 기간 승진율 연봉(기본급/세전)
=======================================================================
사원 입사후 2년 대졸초임 약 3800만원정도
주임 3년차 ~ 5년차 95%(거의 자동빵) 평균 4천대 중반
대리 6년차 ~ 9년차. 75%정도 평균 5천대 중반
과장 10년차 ~ 14년차 50~60%정도 평균 6천대 중후반
차장 15년차 ~ 20년차 40%정도 평균 7천대 중후반
부장 20년 이상 30%정도 평균 8천대 중후반

대개 이런식으로 되어 있습니다. 승급연한은 기본적으론 저 정도 되고 개인의 능력에 따라 속도의 차이가 있습니다. 승진율이라고 쓴건 진급대상자중 진급되는 사람의 비율입니다.

연봉은 기본급을 적은거구 저기에 PI와 PS가 추가 되는데 PI는 생산성장려금인데 개인 인센티브라고 보시면 되는데 대개 고과에 따라 차등 지급 되고 연봉의 평균 15%수준이라고 보면 됩니다.
제가 다닐때는 일년에 두번 나눠서 줬습니다.

그리고 중요한게 PS인데 이건 초과수익에 대한 인센티브인데 사업부마다 차등 지급됩니다.
최고가 연봉의 50%로 2월에 일시 불로 지급하는데 이게 짭잘합니다만 무선사업부(스마트폰)이런데는 50%를 다 받지만 백색가전사업부 이런데는 한푼도 못 받습니다.
여기 가끔 보면 삼전 신입사원이 연봉 6000이다 이런글을 보는데 무선사업부 신입사원이라면 이것저석 다 합치면 세전으로 그정도 될 듯 합니다만 4000만원대 받는 사람이 더 많습니다.

잘 모르시는 분들은 삼전에 입사할려면 대단한 스펙이 필요할 거라고 생각하시는데 사실 상대적으로 다른 대기업보다 학벌을 가장 안 따집니다. 숫자로 봐도 그렇고 비율을 봐도 그렇고 지방대 비율이 가장 높은 곳이 삼전입니다. 아 물론 주로 공대에 해당하는 얘기입니다. 상대도 있긴 있는데 그 비율이 낮습니다.

삼전이 지방대를 많이 뽑는 이유중에 하나가 지방사업장이 많아서 그렇습니다. 경북대 출신들은 주로 구미로 많이가고 전남대출신들은 주로 광주로 많이 보냅니다. 구미 광주 말고도 지방사업장이 많은데 서울 출신을 보내면 퇴사로 이어지는 경우가 많아 그지역 출신을 주로 보내게 됩니다.

흔히 대기업은 경쟁이 빡세서 오래다니기 힘들다고 하는데 사실은 그렇지 않습니다.

밑에 댓글을 보니 삼성전자 평균근속이 6년이라고 하시는데 그건 통계상의 오류가 있는 겁니다.

첫째, 생산직 여자근로자의 경우 결혼하면 퇴사하는 경우가 엄청나게 많습니다. 교대근무를 해야하는데 살림이나 육아를 병행하기가 쉽지가 않습니다. 생산직의 경우 여자의 비율이 훨씬 높습니다.

둘째, 자발적 퇴사자의 경우 대개 조기 퇴사자가 많습니다.

이 두가지경우가 포함되다 보니 근속이 짧게 나오는 거지 맘만 먹으면 정년까지 다닐 수 있습니다.

대졸사원의 경우 100명이 입사동기가 있다면 대개 아래와 같다고 보시면 됩니다.

입사 1년차에 퇴사 : 대개 맘에 안드는 사업부로 발령나거나 지방사업장으로 발령 나는 경우에 해당합니다.
또는 억지로 취직을 하긴 했으나 원래의 꿈을 찾아 공부를 게속 하거나 자격증 시험을 위해 퇴사하는 경우도 있습니다. 여기에 해당하는 경우가 적게는 20% 많게는 30%가 넘는 경우도 봤습니다.

입사 3년차에 퇴사 : 아경우 조직생활에 적응을 못하는 경우에 해당됩니다. 참으면서 2~3년 직장생활을 했는데 결국 적응하지 못하고 조금 널럴한 직장으로 이직하게 됩니다. 이런 경우가 10% 넘습니다.

이미 입사 3년차쯤 되면 동기들의 1/3이상이 다른 곳으로 떠난 상태입니다.

입 사 7년차 전후 : 이시기가 각종 스카웃제의가 가장 많을 때입니다. 직급으로는 대리말년이난 과장초임인데 어느 정도만 되도 다른 기업이나 헤드헌터들에게 스카웃 제의가 많이 들어옵니다. 이때쯤 되면 대충 감이 옵니다. 내가 삼성에서 승승장구할 정도는 아니라고 판단되는 사람들이 다른 직장으로 옮기게 됩니다.

그래서 과장쯤 되면 절반이상의 동기들이 옆에 없습니다.

입 사 15년차 : 이시기에 접어들면 처지는 사람이 나옵니다. 승진에 누락되고 한직으로 돌기만 하고 이때에는 회사의 명퇴프로그램이 있으면 유혹을 받게 됩니다. 명퇴금 챙기고 대개는 한급 아래인 중견기업이나 삼전과 거래관계인 1차 납품업체로 주로 옮깁니다. 그래도 이때까진 옮길만 합니다.

입사 20년차 : 동기들은 다들 부장이고 빠른 친구들은 임원 단 사람도 나옵니다.
근 데 여기서 밀린 사람들은 엄청난 눈치와 스트레스를 받아야 합니다. 이걸 무시하고 다니면 다니는 건데 간단한 문제가 아닙니다. 그래서 그 스트레스 때문에 퇴사를 하게 됩니다. 대개 취직은 어려우니 개인사업을 하게 됩니다. 물론 끝까지 버티고 남는 사람도 제법 있긴 합니다.

이때쯤 되면 동기들의 10%정도만 회사에 남아 있습니다.

http://i.orbi.kr/bbs/board.php?bo_table=united&wr_id=3579828

이공계 대기업 과장 연봉

삼성전자의 경우 박사든 사원부터 시작했든 책임 1년차이면 차이가 나는건 박사 수당과 연봉 고과 딜해서 좋은 고과를 1-2년 정도 보장 받아서 차이가 나는 연봉 밖에 없습니다. 어차피 사원으로 시작한 사람도 올라가면서 고과를 잘 받았다면 박사 받고 들어오는 사람보다 연봉이 높을 수 있고요. 연봉 테이블이 정해져 있기 때문에 특수 인재라고 연봉을 더 높게 받는게 아니라 직급을 조정하거나 보너스를 더 많이 받고 오거나 고과를 좀 더 오랫동안 좋은 고과를 보장해 줍니다.


원글님이 물은 대강의 연봉에 대해 제가 아는 바를 한국 나이로 말씀드리면, 남자 병역필: 27세 시작 - 대리(31세) - 과장(35세) - 차장(39세) - 부장(43세) - 상무(51세) 여자 (혹은 면제): 24세 시작 ... 똑같음 삼성: 대리=선임, 과장=책임 (senior engineer), 부장=수석 (principal engineer), 차장 없음. LG: 과장=선임, 차장=책임, 부장=수석. 회사마다 과장 달기 까지 7년이 걸리는 곳도 있고 7년이었다가 8년이 된 곳도 있고 그렇습니다.


일반적인 남자의 경우 35세에 과장을 처음 달게 됩니다. 그리고 43세에 부장이 되죠. 다만 여기서 예외가 되는게 여자와 면제자 그리고 석사 후에 병특으로 입사한 경우입니다. 이 경우 3살을 빼서 32살에 과장, 40세 부장이 평균이 되죠. 박사의 경우는 석사와 박사를 따로 했다면 비교적 짧게 걸리는 전공의 경우 도합 6-7년 정도 걸리고, CS 처럼 오래 걸리는 경우 7-9년이 걸립니다. 그리고 경력 없이 박사만 했다면 과장 1년차를 받습니다.


따라서 군대를 갔다온 경우 학부 후 바로 박사를 시작했다면 위에서 1년정도 빼면 됩니다. 여자분이 학부 졸후 바로 박사를 시작해서 6년만에 받았다면, 30살에 과장으로 시작하게 되어 일반 사원으로 시작하거나 석박사 합쳐 8년 걸린 사람보다 5년이 진급이 빠르게 됩니다. 여기에 보통 잘나가는 사업부의 경우 좋은 고과가 많이 나오므로 보통 진급을 1-2년 빨리 하죠. 그러면 40대 초반에 임원을 달게 됩니다. 중소 기업이나 기업의 규모가 작을 수록 승진에 걸리는 기한이 짧게 됩니다. 따라서 젊은 나이에 임원을 달고 있는 건 흔한 일이지만 연봉은 대기업 과장보다 낮은 경우가 수두룩 합니다. 여기서 하나 짚고 넘어갈 것은, 대기업에 40대 초반에 임원으로 승진한 분이 많다고 해서 그때에 임원으로 승진해야 한다고 생각하는 겁니다.


삼성전자 같은 경우도 15년 전에는 이렇게 큰 기업이 아니었습니다. 따라서 승진이 더 빨랐고요. 원래 승진 가속도는 회사가 커지는 속도에 비례하게 되어 있습니다. 예전에 우리나라 기업들이 계속 고성장할 때에는 30대에 임원 달고 사장 다는 일이 많았던 것도 이와 비례합니다. 이런 점에서 보면 반드시 정점에 올라 있는 대기업 만이 최고가 아니라 계속해서 성장해 나가는 회사에 있는 것의 장점도 기업 선택시에 생각해 봐야죠.


연봉은 과장 1년차이면 삼전은 윗분들 얘기하신대로 모든 보너스가 최대치로 나올 경우 1억 근처가 됩니다. 연구소는 이보다 5백-1천 정도 낮다고 보면 되고, 잘 안되는 사업부는 7-8천정도라 봐야죠. 현대차는 7천 정도, LG 전자는 5천 초반, KT는 5천, 포스코는 5천, 일반 중소기업 3천-5천, 은행 등 금융권과 SK 텔레콤은은 7천-1억, 탑 3 컨설팅은 1억2천-3천 정도, 가 과장 1년차에 해당하는 일반적인 군대 갔다온 남자 35살, 여자 32살의 연봉이라고 보면 됩니다.


중소 기업 중에도 귀한 엔지니어들은 6-7천 받기도 하고요. 공무원과 군인, 학교 선생님은 3-5천. 남자는 레지던트를 막 마칠 나인 다음(여자는 3년 더+) 이므로 큰 병원에 월급장이로 취업하면 연봉 5-7천 정도. 지방으로 갈 경우 연봉 7천-1억 2천. 교수가 될 나이이기도 하므로 교수는 6천-9천. 파일럿 1억. 변리사 8천. 회계사 6천-1억 정도라고 봐야 겠네요. 물론 주로 열거된 직업군들은 고연봉에 고학벌 등이 많으므로 사실상 평균은 이와 관련이 없다고 봐야 겠네요. 괜찮은 대학 나온 평범한 회사원이라면 남자 35살에 연봉 4-6천이라고 봐야 할 듯 합니다.

학사출신 삼성전자 무선,반도체 기준 연봉

학사출신 삼성전자 무선,반도체 기준

초봉 6000
대리 7000
과장 9000
차장 1억
부장 1.2억
상무 2~3억
전무 4~6억

성과급 포함, 세전기준
부사장 이상급은 연봉협상 천차만별
박사 마치고 입사할시에는 과장급으로 입사

Parallels 최신버전 무료 어플라이언스

오랫만에 맥북프로를 포맷하고 새로 깔면서 Parallels도 새로 설치했다.
윈도우를 새로 설치하려고 보니 아래와 같은 무료 어플라이언스가 있었다.
이제는 ISO이미지가 없어도 OS를 자동으로 설치를 할수가 있다.
대단한 발전이다. 클라우드 서비스를 따라가는 것 같다.
 

Sunday, May 29, 2016

Android 디바이스 화면 크기 구하기

DisplayMetrics dm = getApplicationContext().getResources().getDisplayMetrics();
int width = dm.widthPixels;
int height = dm.heightPixels;

Thursday, May 26, 2016

iOS 리젝 사유 17.1 Apps cannot transmit data...

변경된 사항이 거의 없는데도 불구하고 아래의 사유로 iOS 앱이 리젝 되었다.
이 문제의 원인은 사용자 정보를 서버로 전송하는데 유저의 동의를 구하지 않았고, 개인정보 취급방침을 지정하지 않았다는 것이다.
예전에 스토어 심사를 할때는 유저의 동의를 구하는 절차는 구현하였는데, 개인정보 취급방침이 없었던게 문제이다.
이번에 웹브라우저로 링크를 걸어서 개인정보 취급방침을 띄우는 것을 구현하고 재심사를 신청했다.

17.1 - Apps cannot transmit data about a user without obtaining the user's prior permission and providing the user with access to information about how and where the data will be used

17.1 Details

We noticed that your app does not obtain the user’s consent prior to uploading users’ scores to a global leaderboard.

To collect personal data with your app, you must make it clear to the user that their personal data will be uploaded to your server.

Next Steps

Please revise your app to include a privacy policy URL in the App Information page on iTunesConnect and ensure that the URL you provide directs users to your privacy policy.

Android Studio gradle export jar

안드로이드 스튜디오에서 라이브러리 프로젝트를 jar 파일로 만드는 방법을 알아보자.

//기존의 jar파일을 지우는 task
task deleteOldJar(type: Delete) {
    delete 'release/daumapi.jar'
}

//라이브러리 프로젝트의 output인 classes.jar를 daumapi.jar로 이름 변경
task exportJar(type: Copy) {
// classes.jar 패스는 여기에 떨어짐
    from('build/intermediates/bundles/release/')
    into('release/')
    include('classes.jar')
    rename('classes.jar', 'daumapi.jar')
}

exportJar.dependsOn(deleteOldJar, build)

Wednesday, May 25, 2016

Node.js console exit

process.exit(0); // 0은 리턴 값

또는

.exit


PHP getallheaders() 구현 소스

if (!function_exists('getallheaders')) {
    /**
     * Get all HTTP header key/values as an associative array for the current request.
     *
     * @return string[string] The HTTP header key/value pairs.
     */
    function getallheaders()
    {
        $headers = array();
        $copy_server = array(
            'CONTENT_TYPE'   => 'Content-Type',
            'CONTENT_LENGTH' => 'Content-Length',
            'CONTENT_MD5'    => 'Content-Md5',
        );
        foreach ($_SERVER as $key => $value) {
            if (substr($key, 0, 5) === 'HTTP_') {
                $key = substr($key, 5);
                if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) {
                    $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
                    $headers[$key] = $value;
                }
            } elseif (isset($copy_server[$key])) {
                $headers[$copy_server[$key]] = $value;
            }
        }
        if (!isset($headers['Authorization'])) {
            if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
                $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
            } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
                $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
                $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pa
ss);
            } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
                $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
            }
        }
        return $headers;
    }
}

Tuesday, May 24, 2016

Github를 만들었습니다

프로그램 소스를 Google 드라이브에만 올리다가 드디어 github를 만들었습니다.

원래는 소스공개를 하지 않는 개인 프로젝트는 github보다는 bitbucket에 올려두었는데 공개용 소스는 앞으로 아래의 링크에 올리겠습니다.

감사합니다.
두덕리온라인

https://github.com/duongame

Friday, May 20, 2016

Android PHP 인앱 구매 영수증 서버 검증

이전에 iOS와 PHP로도 인앱 구매 영수증 서버 검증을 진행했는데 이번에는 Android에도 적용해 보자.

일단 플레이스토어에 가면 다음과 같이 [서비스 및 API] > [라이센스 및 인앱 결제] 항목에 Base64 인코딩된 RSA 공개키가 있다.


이것을 .pem형식으로 다음과 같이 저장한다.
물론 PHP의 chunk_split함수를 이용해도 된다.
단, 설명문에 RSA 공개키라고 하더라도 반드시 그냥 PUBLIC KEY라고 입력해야 한다. 

-----BEGIN PUBLIC KEY-----
한 줄에 64바이트씩 잘라서 입력
-----END PUBLIC KEY-----

다음은 안드로이드에서 구매후 $signature와 $data를 보내줘야 한다.
주의할 사항은 $data는 original json을 그대로 보내면 된다.
        params.put("signature", info.getSignature());
        params.put("data", info.getOriginalJson());

검증 PHP는 다음과 같이 구성한다.
openssl_verify가 1을 리턴하면 검증이 성공한 것이다.

function verify_android($signature) {
    // key 파일을 읽음
    $public_key_path="./play.pem";
    $public_key = file_get_contents($public_key_path);
    $public_key_id = openssl_get_publickey ($public_key);
    $decoded_signature = base64_decode($signature);
    $data="";
    if(isset($_POST['data'])) {
        $data = $_POST['data'];       
    }
    $result = (int)openssl_verify($data, $decoded_signature, $public_key_id);
    if($result == 1) {
        return true;
    }
    else {
        return false;
    }   
}

PHP Visual Studio Code에서 패스설정하기

새로나온 에디터인 Visual Studio Code에서 다음과 같이 PHP파일을 편집하다 보면 에러 메세지가 나올 때가 있다. 

Cannot validate the php file. The php program was not found. Use the 'php.validate.executablePath' setting to configure the location of 'php'

그럴 때는 파일 > 기본설정 > 사용자 설정 에서 다음과 같이 PHP 실행파일 패스를 입력한다.



PHP json_encode 후에 앞에 이상한 문자가 추가되는 현상(strange character)

PHP로 json_encode하여서 클라이언트에서 응답을 받았는데, android studio 화면상에 보이는 문자와 실제의 문자열의 길이가 달랐다. 그리고 JSONObject의 parse가 자꾸 실패를 하게 되었다.

이렇게 되는 원인을 찾았는데 PHP 소스파일의 인코딩이 UTF-8 with BOM으로 되어 있었다. 이것을 UTF-8로 변경해주면 문제 없이 동작한다.

최근에 atom, vscode등 여러가지 에디터를 변경하면서 생긴 문제이다. 항상 기존의 코드와인코딩이 맞는지를 항상 확인하자.

Wednesday, May 18, 2016

Node.js 최신버전 업데이트

sudo npm cache clean -f
sudo npm install -g n
sudo n stable

sudo npm update npm -g

Monday, May 16, 2016

iOS PHP 인앱 구매 영수증 서버 검증

//iOS -(void)serverVerfication:(SKPaymentTransaction*)transaction andRestore:(BOOL)isRestore
{
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    NSString *user_id = [userDefault objectForKey:@"uid"];
    NSString *item_id = transaction.payment.productIdentifier;
   
    // 추가된 order_id(구글과 맞추기 위해서 용어를 변경하였다.)
    NSString *order_id;
    int restore = 0;
    if(isRestore == YES) {
        order_id = transaction.originalTransaction.transactionIdentifier;
        restore = 1;
    }
    else {
        order_id = transaction.transactionIdentifier;
    }

    // Load the receipt from the app bundle.
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
   
    // Create the JSON object that describes the request
    NSError *error;
    NSDictionary *requestContents = @{
                                      @"receipt-data": [receipt base64EncodedStringWithOptions:0],
                                      @"platform": @"ios",
                                      @"item_id": item_id,
                                      @"order_id": order_id,
                                      @"user_id": user_id,
                                      @"restore": [NSNumber numberWithInt:restore],
                                      @"sandbox": [NSNumber numberWithInt:0]
                                      };
    NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                          options:0
                                                            error:&error];
   
    // Create a POST request with the receipt data.
//    NSURL *storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];


    //검증하려고 하는 자체 서버
    NSURL *storeURL = [NSURL URLWithString:[URLManager getPaymentLog]];
    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:requestData];
   
    // Make a connection to the iTunes Store on a background queue.
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                               if (connectionError) {
                                   /* ... Handle error ... */
                               } else {
                                   NSError *error;
                                   NSString *jsonData = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

                                   NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:[jsonData dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&error];
                                   if (!jsonResponse) { /* ... Handle error ...*/
                                   }
                                   /* ... Send a response back to the device ... */
                                   NSNumber *result = [jsonResponse objectForKey:@"result"];
                                   if(result == nil) {
                                       NSLog(@"구매 인증 실패");
                                       handleSendBuyEvent(-1, 0);// M_E_ERROR = -1
                                   }
                                   else {
                                       if([result longValue] == 0) {
                                           NSLog(@"구매 인증 성공");
                                           [self provideContent: transaction.payment.productIdentifier];
                                       }
                                       else {
                                           NSLog(@"구매 인증 실패 %d", [result longValue]);
                                           handleSendBuyEvent(-1, 0);// M_E_ERROR = -1
                                       }
                                   }
                               }
                           }];
   
}

 
//PHP
//JSON데이터를 $_POST에 넣어준다
$postdata = file_get_contents("php://input");
$_POST = json_decode($postdata, true);

function verify($sandbox, $receipt) {
    // Environment sandbox인지 체크
    // 영수증을 애플 서버에 보내자
    $endpoint = "";
   
    // 샌드박스일 경우
    if($sandbox == 1) {
         $endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt';     
    }
    else {// 실결제일 경우
        $endpoint = 'https://buy.itunes.apple.com/verifyReceipt';
    }
    error_log("endpoint=$endpoint");

    $postData = json_encode(array('receipt-data' => $receipt)); 
   
    // curl로 요청하자
    $ch = curl_init($endpoint); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION , true); 
    curl_setopt($ch, CURLOPT_POST, true); 
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
   
    $response = curl_exec($ch); 
    $errno    = curl_errno($ch); 
    $errmsg   = curl_error($ch); 
    curl_close($ch);  

    error_log("response=$response");
    if($errno)
        error_log("errno=$errno");
    if($errmsg)
        error_log("errmsg=$errmsg");
    error_log("response ok");
   
    $data = json_decode($response);
    error_log("data=".print_r($data, true));
   
    if(!is_object($data)) {
        return false;
    }
   
    if(!isset($data->status) || $data->status != 0) {
        return false;
    }
    return true;
}

    // 2. 애플 영수증 검증
    // 실결제를 먼저하고, 샌드박스 결제를 검증해야 한다.
    // 왜냐하면 애플 심사과정에서는 샌드박스로 테스트를 하기 때문이다.
    if(strlen($receipt) > 0) {
        if(!verify(0, $receipt)) {// 실결제 테스트
            if(!verify(1, $receipt)) {// 샌드박스 테스트
                // 실결제, 샌드박스 둘다 실패하면 에러다
                $ret = array("result" => 5, "error" => "receipt error");
                echo json_encode($ret);
                exit;
            }
        }
    }
    // 문제없이 exit가 안되었다면 검증이 성공한 것이다.

Thursday, May 5, 2016

Java 하위 폴더 모든 소스 컴파일 빌드

다음과 같이 build.sh를 만들면 src폴더 하위에 있는 모든 java파일을 컴파일 할수 있다.
java파일과 같은 폴더내에 class파일이 만들어진다.

cd src
javac -cp . -d . $(find . -name *.java)

Sunday, April 24, 2016

iOS 디바이스 모델 정보 얻기

iOS 최신 버전에서는 [[UIDevice currentDevice]model]을 하여도 "iPhone"이라는 문자열 밖에 얻을 수 없다.
따라서 아래와 같이 구현하면 최신 iOS에서도 디바이스 모델 정보를 얻어올 수 있다.
다만 시뮬레이터에서 실행하면 x86_64가 얻어진다.(64비트 아이폰의 경우)

#import <sys/utsname.h> // import it in your header or implementation file.

NSString* deviceName()
{
    struct utsname systemInfo;
    uname(&systemInfo);

    return [NSString stringWithCString:systemInfo.machine
                              encoding:NSUTF8StringEncoding];
}

@"i386"      on 32-bit Simulator
@"x86_64"    on 64-bit Simulator
@"iPod1,1"   on iPod Touch
@"iPod2,1"   on iPod Touch Second Generation
@"iPod3,1"   on iPod Touch Third Generation
@"iPod4,1"   on iPod Touch Fourth Generation
@"iPod7,1"   on iPod Touch 6th Generation
@"iPhone1,1" on iPhone
@"iPhone1,2" on iPhone 3G
@"iPhone2,1" on iPhone 3GS
@"iPad1,1"   on iPad
@"iPad2,1"   on iPad 2
@"iPad3,1"   on 3rd Generation iPad
@"iPhone3,1" on iPhone 4 (GSM)
@"iPhone3,3" on iPhone 4 (CDMA/Verizon/Sprint)
@"iPhone4,1" on iPhone 4S
@"iPhone5,1" on iPhone 5 (model A1428, AT&T/Canada)
@"iPhone5,2" on iPhone 5 (model A1429, everything else)
@"iPad3,4" on 4th Generation iPad
@"iPad2,5" on iPad Mini
@"iPhone5,3" on iPhone 5c (model A1456, A1532 | GSM)
@"iPhone5,4" on iPhone 5c (model A1507, A1516, A1526 (China), A1529 | Global)
@"iPhone6,1" on iPhone 5s (model A1433, A1533 | GSM)
@"iPhone6,2" on iPhone 5s (model A1457, A1518, A1528 (China), A1530 | Global)
@"iPad4,1" on 5th Generation iPad (iPad Air) - Wifi
@"iPad4,2" on 5th Generation iPad (iPad Air) - Cellular
@"iPad4,4" on 2nd Generation iPad Mini - Wifi
@"iPad4,5" on 2nd Generation iPad Mini - Cellular
@"iPad4,7" on 3rd Generation iPad Mini - Wifi (model A1599)
@"iPhone7,1" on iPhone 6 Plus
@"iPhone7,2" on iPhone 6
@"iPhone8,1" on iPhone 6S
@"iPhone8,2" on iPhone 6S Plus
@"iPhone8,4" on iPhone SE

Thursday, April 21, 2016

[티맥스OS] 윈도우 호환OS 만드는 방법

이번에 티맥스OS의 발표를 보고 너무나 안타까워서 오랫동안 윈도우 프로그래머로서의 경험을 살려서 윈도우 호환OS 만드는 방법을 생각해 봤다.

내가 윈도우 호환OS를 만든다면 MySQL 호환 DBMS인 MariaDB와 같은 방식으로 만들었을 것 같다. MariaDB는 이름만 MariaDB이지만 터미널에서 mysql 명령어 실행파일로 동작하며, MySQL C API의 이름도 심지어 똑같다. 함수명을 예를 들면 mysql_connect() 등. 사실상 쓰는 사람의 입장에서는 MySQL인지 MarisDB인지 모른다. 태생이 MySQL 소스코드에서 출발해서 그렇지만...

티맥스의 가장 큰 고객은 대한민국 정부이다. 그리고 아래아한글(HWP) 처럼 공공기관의 MS워드를 대체하는 것처럼, 윈도우를 대체하는 것이 목표이다.

요구사항
1. 인터넷 익스플로러가 구동 되거나 인터넷 익스플로러 호환 브라우저가 있을 것
예전 웹기반 IT 시스템은 IE 6.0을 타겟으로 만들어진 것이 많다. 그래서 IE버전을 7.0 이상 올려도 화면이 깨지는 웹시스템이 많다.

2. ActiveX가 돌아갈 것
말안해도 알겠지만 전자정부의 공인인증서, 인터넷뱅킹 및 증명서 발급은 ActiveX 기반의 보안 시스템 및 리포트 툴(문서 프린트 소프트웨어)을 사용한다.

3. 윈도우의 드라이버가 그대로 설치될 것
수많은 하드웨어 드라이버를 새로 만들 수는 없다. 그래서 기존의 드라이버를 그대로 돌아가게 해야한다.

4. 윈도우의 실행파일 및 소프트웨어가 그대로 설치 및 실행될 것
가장 기본적으로 오피스와 개발도구인 Visual Studio가 돌아가야 한다. 최신버전이 안돌아 가더라도 최소한 오피스2003이나 Visual Studio 2008 정도는 실행되어야 한다.

구현방법
1. Boot Loader의 구현
이거는 모든 OS의 공통적인 사항이다. 목표가 윈도우98이면 IO.SYS, MSDOS.SYS를 구현하고, 윈도우XP이면 NTLDR, 윈도우7이상이면 BOOTMGR를 똑같이 구현하던가 나름대로 구현해야한다.
뭐 리눅스 방식으로 구현해도 상관은 없다. 부팅만 되면 되니까.

2. PE 파일의 구동
리눅스의 실행파일은 ELF(Executable and Linkable Format), 윈도우의 실행파일은 PE(Portable Executable)라고 한다. PE는 가장 많이 보는 EXE파일이다.
윈도우 호환 OS라면 이 EXE가 실행이 되야 한다. 말 그대로 윈도우 EXE로 되어 있는 프로그램이 설치가 되고 구동이 되어야 한다는 거다.

3. Win32 API의 구현
GDI32.DLL
USER32.DLL
KERNEL32.DLL
EXE파일을 실행시키려면 최소한의 DLL 3개가 필요하다. Visual C++ 해본 프로그래머들은 알거다. 아무것도 안해도 Win32 EXE로 컴파일해도 위 3개는 필요하다는거.
그리고 기존에 간단한 윈도우 프로그램을 만든 걸 돌리려고 해도 기본적인 API가 호환이 되어야 한다.

4. COM(Component Object Model)의 구현
이건 MS에서 사용하는 바이너리 인터페이스 모델이다. DLL, OCX에서 VB, VC++,Delphi 프로그램 상호간 통신하고 오피스와 ActiveX에서 사용하는 기술인데 이게 되어야 ActiveX가 된다. 그리고 복잡한 프로그램은 이걸 지원해야 한다.
그리고 이게 되어야 DirectX가 된다. 게임이 된다는 소리다. OpenGL 게임은 뭐 상관없지만 대부분의 게임은 DirectX로 개발되어 있다.

5. WDM(Windows Driver Model) 구현
OS를 만들어도 드라이버가 없으면 어떻게 쓰냐? 그래서 기존에 윈도우용 드라이버를 호환 시켜야 한다.

이번에 티맥스OS가 목표를 어떻게 잡았는지 모르겠는데 내가 윈도우 호환 OS를 만든다면 윈도우2000이나 윈도우XP에서 32비트만 목표로 잡고 호환시킨후에 추후에 64비트 호환 그리고 FreeBSD기반인 OSX나 Linux의 ELF바이너리를 실행할수 있게 호환을 시켰을거다. 그러면 거의 무적의 OS가 될 수 있다. 모바일계의 Xamarin처럼

P.S.#1
운영체제 개발에 관심있는 사람은 이책을 추천한다. 국산 OS 개발관련 책 중에서 제일 괜찮은것 같다. 내가 저자는 아니므로 광고는 아님
http://www.yes24.com/24/goods/2502876?scode=032&OzSrank=8

P.S.#2
안철수가 만든 안랩이 알려진 바로는 연봉도 적고 무시 받는 직장일 수 있는데 한국에서는 안랩 프로그래머가 윈도우 시스템 프로그래밍은 제일 잘하는 듯 하다.

윈도우 디버깅, 커널모드, 드라이버 관련 저술은 여기 엔지니어들이 제일 많이 내는 듯. 요즘은 석박사들도 윈도우는 연구를 안하고 죄다 리눅스를 하니깐 그런지 몰라도.

Tuesday, April 19, 2016

Android 런쳐 뱃지 카운트 설정

내 앱의 뱃지 알림의 갯수를 런쳐쪽에 전달을 해주어야 런쳐에서 내 앱의 뱃지 갯수를 그려준다. 그래서 다음과 같이 앱에서 설정해 주어야 한다.

    public static String getLauncherClassName() {
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);

        PackageManager pm = AmandaApp.getAppContext().getPackageManager();
        List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
        for (ResolveInfo resolveInfo : resolveInfos) {
            String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
            if (pkgName.equalsIgnoreCase(getPackageName())) {
                return resolveInfo.activityInfo.name;
            }
        }
        return null;
    }

        String launcherClassName = getLauncherClassName();
        if (launcherClassName == null) {
            return;
        }
        Intent response = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
        response.putExtra("badge_count", launcherCount);
        response.putExtra("badge_count_package_name", getPackageName());// 내 앱의 패키지명
        response.putExtra("badge_count_class_name", launcherClassName);// 런쳐의 클래스명
        AmandaApp.getAppContext().sendBroadcast(response);

Sunday, April 3, 2016

Visual C++ Win32 타이머 CreateTimerQueueTimer 예제

#include <stdio.h>
#include <windows.h>
VOID CALLBACK TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
{
 printf("TimerCallback\n");
}
int main() {
 // 타이머 큐를 만든다.
 HANDLE timerQueue = CreateTimerQueue();
 // 타이머를 만든다.
 HANDLE timer;
// 처음 시작할때 0.5초 지연, 주기 0.5초마다 호출되게
 CreateTimerQueueTimer(&timer, timerQueue, TimerCallback, NULL, 500, 500, 0);
 while (1) {
  Sleep(100);
 }
 return 0;
}

Thursday, March 31, 2016

Linux 게임 서버 개발 FAQ

좋은 글이 있어서 퍼왔다.
소켓에서 리눅스에서는 4000이하일때 select도 쓸만하다는게 맞는말 같다.

http://nomoreid.egloos.com/3137280

뭐 잘알려진 사실들 이지만 그냥 개인적인 정리입니다. 이상한 부분이나 태클 환영합니다.

1. 라이센스 문제를 피하고 싶다면 cent os를 추천한다. (http://www.centos.org/). 뭐 다들 아는 이야기 겠지만 리눅스에도 상용 배포판이 따로 있다. 하지만 상용 서비스라고 해서 상용 OS를 써야 하는건 아니다. cent os는 재미있게도 상용 배포판인 Red Hat Enterprise Linux(보통 줄여서 RHEL이라고 부른다) 의 클론에서 출발한 프로젝트이다. 바이너리 레벨에서 호환된다. 또 거의 모든 셋팅이 동일하다. 그리고 라이센스 부분도 문제가 없다. 현재 버전 넘버도 RHEL과 동일하다. (안정버전 4.XX 테스트 버전 5.XX)

2. 서버로 사용한다면 GUI인 X Windows는 안까는게 좋다. 200 - 300 메가 내외로 OS설치가 가능하다. 

3. telnet 이나 ftp는 구시대의 유물이므로 사용하지 않는것을 권장. (ssh , scp를 쓰라.)

4. ssh의 port를 바꿔라. 무작위 접속시도가 들어온다. 물론 root의 암호는 단순한것으로 하지 말아라. 

5. 커널은 2.6 이상을 반드시 사용하라. 물론 cent os의 3.XX이후는 다 커널 2.6기반이다.  윈도우가 NT에서 2000으로 넘어오면서 서버용 OS로서 엄청난 발전이 있었듯이 리눅스도 2.4 에서 2.6으로 넘어가면서 절대적인 성능의 개선이 일어났다. 커널 2.6의 개선사항에 대해선 여기를 참고 (http://www.zdnet.co.kr/builder/platform/nix/0,39031679,39129093,00.htm)

6. 리눅스에선 윈도우와는 달리 4000명 이하일때는 select도 상당히 쓸만하다. 단 OS의 기본 셋팅이 1024명을 기준으로 셋팅 되었기 때문에 몇군대 수정할 부분이 있다. (http://nomoreid.egloos.com/1379567)

7. 일반적으로 gcc도 상당히 빠르지만 더 빠른 속도를 원한다면 Intel c++ compiler for linux를 쓰면 된다. (물론 서버가 AMD라면 AMD홈페이지의 linux gcc 튜닝 가이드를 꼭 읽어 보길 바란다. 물론 intel에서도 gcc를 공식 지원해서 gcc 4.0 의 경우 인텔 컴파일러에 쓰인 기술들이 많이 들어갔다고 한다.)

8. linux 라고 해서 꼭 DB를 mysql을 써야하는건 아니다.  MS SQL 서버 , oracle 등도 리눅스에서 사용가능하다. (http://kldp.org/node/2368)

9. gdb사용법에 익숙해져라. gdb와 여러 콘솔기반 어플리케이션에 익숙해져라. 

10. screen 사용법을 익혀라. 데몬으로 만들지 않아도 서비스가 가능하다.

11. 서비스 초반에는 디버깅 모드로 돌릴 수 있으니 디버그 모드에서도 어느정도 성능이 나올 수 있게 짜야 한다.

12. 경험적으로 봤을때 리눅스는 윈도우에 비해 크래쉬시 생성되는 코어 파일이 더 많이 깨져있는거 같다. 물론 메모리 버그일때 이야기다. 물론 어떤 경우라도 메모리를 깨면 사형이다.

13. mysql로 서비스를 할때는 구매가 필요 없다. mysql 은 gpl 라이센스 버전과 상용 버전 두가지 라이센스가 있는데, GPL은 소프트웨어를 배포하거나 소스 수정 할때 소스 공개의 의무가 있다. 따라서 소스를 직접 수정하지 않고 단순히 서비스에 이용하는건 라이센스 문제가 없다. (http://www.mysql.com/company/legal/licensing/ Enterprise IT 항목)

대신 해외에 팔때 mysql을 직접적으로 깔아주면 패키지에 포함한 형태가 되서 라이센스 위반.
국내 퍼블리셔에 서비스시 서버 패키지에 mysql이 포함되어 있으면 라이센스 위반. 

퍼블리셔가 직접 mysql을 깔고 게임 서버를 돌릴 때는 라이센스 위반이 아님. (배포를 한게 아니므로.)

국내 Web Server hosting 업체들도 이방법으로 라이센스 부분을 피해가고 있습니다. 직접적으로 mysql을 깔아주는게 아닌 고객이 mysql을 깔도록 해서 라이센스를 비켜가는것이죠. (세연군 태클로 수정)

14. mysql 4.0 이후 버전은 clinet lib에 GPL라이센스가 걸려 있다. (3.XX 대는 상관 없음)
mysql clinet lib를 사용한 서버를 퍼블리셔에 배포하는 경우 라이센스 위반. (mysql client lib도 GPL)
ODBC를 이용해서 서버를 짜는 경우 라이센스 위반이 아님. 물론 odbc쪽 라이센스가 걸려있긴 하지만 
라이센스 문제 없는 것으로 잘 찾아서 쓰면 됨.
(예 : mysql server + iodbc http://www.iodbc.org/index.php?page=mysql2odbc/index BSD 라이센스.
         제가 써보진 않아서 성능에 대한 보장은 할 수 없습니다. )

15. 보통은 퍼블리셔에서 DB관련 된 부분은 제공해줘서. -_-;; 개발시는 그냥 신경쓰지 않고 일단 개발하다가 퍼블리셔의 DB에 맞춰서 커스터마이즈 해주는게..

16. cubrid DB를 사용하는걸 한번 고려해 보라!. mysql보다 2배 이상 빠르고 한글로 지원을 받을 수 있다.

.. 계속 정리중.

Wednesday, March 30, 2016

윈도우용 SVN 서버 VisualSVN Server

오래된 SVN Repository의 소스 정리를 하다가 SVNManager가 윈도우 10에서 동작하지 않아서 VisualSVN Server를 설치하였다.

설치해본 소감은 왜 내가 그동안 SVNManager를 고집했는지 후회 스러운 순간이었다. 윈도우 NT서버의 IIS와 같은 GUI와 함께 너무나도 쓰기 편리하게 되어 있었다.

감동의 순간을 스크린샷으로 남겨본다.