public boolean hasSoftKeys() {
boolean hasSoftwareKeys = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
final Display d = getWindowManager().getDefaultDisplay();
final DisplayMetrics realDisplayMetrics = new DisplayMetrics();
d.getRealMetrics(realDisplayMetrics);
final int realHeight = realDisplayMetrics.heightPixels;
final int realWidth = realDisplayMetrics.widthPixels;
final DisplayMetrics displayMetrics = new DisplayMetrics();
d.getMetrics(displayMetrics);
final int displayHeight = displayMetrics.heightPixels;
final int displayWidth = displayMetrics.widthPixels;
hasSoftwareKeys = (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;
} else {
boolean hasMenuKey = ViewConfiguration.get(this).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
hasSoftwareKeys = !hasMenuKey && !hasBackKey;
}
return hasSoftwareKeys;
}
Thursday, December 31, 2015
Sunday, December 27, 2015
Android Volley와 Gson 예제
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
build.gradle
compile 'com.google.code.gson:gson:2.5'
compile 'com.github.bumptech.glide:volley-integration:1.3.1'
mt_rand.php
<?php
$ret = array('value' => mt_rand()+1000000000);
echo json_encode($ret);
?>
<uses-permission android:name="android.permission.INTERNET"/>
build.gradle
compile 'com.google.code.gson:gson:2.5'
compile 'com.github.bumptech.glide:volley-integration:1.3.1'
mt_rand.php
<?php
$ret = array('value' => mt_rand()+1000000000);
echo json_encode($ret);
?>
public class Uid {
private String value;
public String getValue() {
return value;
}
}
private RequestQueue mRequestQueue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String url ="http://192.168.29.188/mt_rand.php";
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d("MainActivity", response.toString());
Uid uid = new Gson().fromJson(response.toString(), Uid.class);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("MainActivity", error.toString());
}
}
);
mRequestQueue = Volley.newRequestQueue(this);
mRequestQueue.add(request);
}
Tuesday, December 22, 2015
MySQL C API에서 /var/lib/mysql/mysql.sock으로 접속이 안될때
MySQL Server를 설치한다.
yum -y install mysql mysql-server (or apt-get install mysql mysql-server)
Enable the MySQL service:
/sbin/chkconfig mysqld on
Start the MySQL server:
/sbin/service mysqld start
vi /etc/my.cnf
아래와 같이 클라이언트를 추가한다.
이미 있다면 수정하여 준다.
[client]
#password = your_password
host = 127.0.0.1
port = 3306
socket = /var/run/mysql/mysql.sock
socket을 /var/lib/mysql/mysql.sock로 수정해주고
host를 127.0.0.1 또는 localhost로 수정해준다.
host는 여기에 입력한 대로 mysql_connect에서 호출해야 한다.(localhost로 입력했으면 127.0.0.1로는 접속안됨)
yum -y install mysql mysql-server (or apt-get install mysql mysql-server)
Enable the MySQL service:
/sbin/chkconfig mysqld on
Start the MySQL server:
/sbin/service mysqld start
vi /etc/my.cnf
아래와 같이 클라이언트를 추가한다.
이미 있다면 수정하여 준다.
[client]
#password = your_password
host = 127.0.0.1
port = 3306
socket = /var/run/mysql/mysql.sock
socket을 /var/lib/mysql/mysql.sock로 수정해주고
host를 127.0.0.1 또는 localhost로 수정해준다.
host는 여기에 입력한 대로 mysql_connect에서 호출해야 한다.(localhost로 입력했으면 127.0.0.1로는 접속안됨)
Monday, December 21, 2015
iOS 디바이스 UUID 획득하기
//처음에 UUID를 KeyChain에서 불러오는데 nil이라면 UUID를 생성해서 KeyChain에 저장한다.
//저장 후에 다시 함수를 호출 하면 저장된 값을 리턴한다.
NSString* getUUID()
{
// initialize keychaing item for saving UUID.
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"UUID" accessGroup:nil];
NSString *uuid = [wrapper objectForKey:(__bridge id)(kSecAttrAccount)];
if( uuid == nil || uuid.length == 0)
{
// if there is not UUID in keychain, make UUID and save it.
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
uuid = [NSString stringWithString:(__bridge NSString *) uuidStringRef];
CFRelease(uuidStringRef);
// save UUID in keychain
[wrapper setObject:uuid forKey:(__bridge id)(kSecAttrAccount)];
}
return uuid;
}
//저장 후에 다시 함수를 호출 하면 저장된 값을 리턴한다.
NSString* getUUID()
{
// initialize keychaing item for saving UUID.
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"UUID" accessGroup:nil];
NSString *uuid = [wrapper objectForKey:(__bridge id)(kSecAttrAccount)];
if( uuid == nil || uuid.length == 0)
{
// if there is not UUID in keychain, make UUID and save it.
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
uuid = [NSString stringWithString:(__bridge NSString *) uuidStringRef];
CFRelease(uuidStringRef);
// save UUID in keychain
[wrapper setObject:uuid forKey:(__bridge id)(kSecAttrAccount)];
}
return uuid;
}
Android 디바이스 UUID 획득하기
public static String getDeviceUUID(final Context context) {
// preference에서 저장된 것이 있는지 확인해봄
final SharedPreferences prefs = context.getSharedPreferences("UUIDTEST", MODE_PRIVATE);
final String id = prefs.getString("UUID", null);
UUID uuid = null;
if (id != null) {
uuid = UUID.fromString(id);
} else {
final String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
try {
// 예전에 아래와 같은 고정값이 리턴되는 문제가 있었음
if (!"9774d56d682e549c".equals(androidId)) {
uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
} else {
final String deviceId = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
uuid = deviceId != null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")) : UUID.randomUUID();
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
final SharedPreferences.Editor editor = prefs.edit();
editor.putString("UUID", uuid.toString());
editor.commit();
}
return uuid.toString();
}
// preference에서 저장된 것이 있는지 확인해봄
final SharedPreferences prefs = context.getSharedPreferences("UUIDTEST", MODE_PRIVATE);
final String id = prefs.getString("UUID", null);
UUID uuid = null;
if (id != null) {
uuid = UUID.fromString(id);
} else {
final String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
try {
// 예전에 아래와 같은 고정값이 리턴되는 문제가 있었음
if (!"9774d56d682e549c".equals(androidId)) {
uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
} else {
final String deviceId = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
uuid = deviceId != null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")) : UUID.randomUUID();
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
final SharedPreferences.Editor editor = prefs.edit();
editor.putString("UUID", uuid.toString());
editor.commit();
}
return uuid.toString();
}
Friday, December 18, 2015
Android 플레이스토어 앱설치시 '콘텐츠 제공자의 권한이 중복됩니다' 및-505 에러 유저 대응 방법
플레이스토어 앱설치시 '콘텐츠 제공자의 권한이 중복됩니다' 및 -505 에러가 난 유저에게는 다음과 같이 안내하면 된다.
1. 설정 > 앱(LG) 또는 애플리케이션 관리자(삼성) > 전체로 이동
2. Google Play 스토어 > 캐쉬 삭제 > 데이터 삭제
3. Google Play 서비스 > 캐쉬 삭제 > 데이터 삭제
4. 설정 > 계정 > Google > 기존에 사용하는 구글 계정 삭제
5. 디바이스 재부팅
6. 설정 > 계정 > Google > 다시 기존에 사용하는 구글 계정 추가
7. Play 스토어에서 앱 다시 다운로드
1. 설정 > 앱(LG) 또는 애플리케이션 관리자(삼성) > 전체로 이동
2. Google Play 스토어 > 캐쉬 삭제 > 데이터 삭제
3. Google Play 서비스 > 캐쉬 삭제 > 데이터 삭제
4. 설정 > 계정 > Google > 기존에 사용하는 구글 계정 삭제
5. 디바이스 재부팅
6. 설정 > 계정 > Google > 다시 기존에 사용하는 구글 계정 추가
7. Play 스토어에서 앱 다시 다운로드
Thursday, December 17, 2015
Tuesday, December 15, 2015
Mac 기본 웹서버 apache root 폴더
/Library/WebServer/Documents
/etc/apache2/httpd.conf
sudo /usr/sbin/apachectl restart
/etc/apache2/httpd.conf
sudo /usr/sbin/apachectl restart
Monday, December 14, 2015
iOS 리뷰 유도를 위해서 앱스토어로 이동하기
#define URL_APPSTORE @"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=xxxxxxxx&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"
// 앱스토어로 이동
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:URL_APPSTORE]];
// 앱스토어로 이동
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:URL_APPSTORE]];
Android 리뷰 유도를 위해서 플레이스토어로 이동하기
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + Config.getPackageName()));
Sunday, December 13, 2015
Ubuntu The mcrypt extension is missing. PHP
php5-fpm과 nginx환경에서 phpmyadmin을 실행과정에서 다음과 같은 에러가 발생했다.
The mcrypt extension is missing. Please check your PHP configuration.
해결방법은 다음과 같다.
sudo apt-get install php5-mcrypt
sudo php5enmod mcrypt
sudo service php5-fpm restart
sudo service nginx restart
The mcrypt extension is missing. Please check your PHP configuration.
해결방법은 다음과 같다.
sudo apt-get install php5-mcrypt
sudo php5enmod mcrypt
sudo service php5-fpm restart
sudo service nginx restart
CentOS nginx phpMyAdmin 설치
# 확장 패키지(EPEL)가 있는지 확인
rpm -qa epel-release
# 확장 패키지 설치
yum install epel-release
# phpMyAdmin 설치
yum install phpMyAdmin
# 설치폴더
/usr/share/phpMyAdmin
# 폴더 심볼릭 링크 생성
cd /usr/share/nginx/html
ln -s /usr/share/phpMyAdmin phpMyAdmin
CentOS nginx + php에서 file not found 에러
Nginx와 php연동시 File not found 에러가 나는 경우가 있다.
이때는 nginx설정에서 fastcgi_param에서 다음과 같이 html폴더를 절대경로로 명시해 주면된다.
vi /etc/nginx/conf.d/default.conf
이때는 nginx설정에서 fastcgi_param에서 다음과 같이 html폴더를 절대경로로 명시해 주면된다.
vi /etc/nginx/conf.d/default.conf
location ~ \.php$ {
#root /usr/share/nginx/html;
#fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/var/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;
include fastcgi_params;
}
그리고 nginx restart하자.
service nginx restart
CentOS nginx iptables 설정
CentOS에서 nginx를 설치하고 나면 80번 포트가 막혀있다.
그래서 다음과 같이 iptables를 편집해서 80번 포트를 열어야 한다.
sudo vi /etc/sysconfig/iptables
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
service iptables restart
Visual C++ 11자리 정수형 유니크 ID 만들기
정수형으로 유저ID로 사용할 유니크ID를 만들어 보았다.
32비트 UINT_MAX는 0xFFFFFFFF로 4,294,967,295가되며 약 42억9천만이 된다.
그리고 rand()를 사용하면 리턴값은 int지만 사실상 short인 SHRT_MAT 32767이 최대값이다.
따라서 다음과 같이 만들면 long형인 11자리 값을 만들수가 있다.
srand(time(NULL));
unsigned hirand = rand() << 16;
unsigned lorand = rand();
//앞에 1또는 다른 값을 붙여 써도 됨
printf("1%010u\n", hirand | lorand);
32비트 UINT_MAX는 0xFFFFFFFF로 4,294,967,295가되며 약 42억9천만이 된다.
그리고 rand()를 사용하면 리턴값은 int지만 사실상 short인 SHRT_MAT 32767이 최대값이다.
따라서 다음과 같이 만들면 long형인 11자리 값을 만들수가 있다.
srand(time(NULL));
unsigned hirand = rand() << 16;
unsigned lorand = rand();
//앞에 1또는 다른 값을 붙여 써도 됨
printf("1%010u\n", hirand | lorand);
Saturday, December 12, 2015
Android 네이버 앱스토어에서 결제 실패로 반려되는 경우
네이버 앱스토어는 자사의 결제를 이용하지 않은 구글 플레이스토어 결재만을 탑재한 앱의 APK를 올리는 것을 허용하고 있다.
이때 유의할 것이, 플레이스토어의 앱 업데이트가 먼저 배포된 다음에 네이버 앱스토어의 심사를 진행해야 한다는 것이다. 왜냐면 아래에 언급한 문제가 있기 때문이다.
Android 앱에서 플레이스토어 결제가 되지 않는 경우
그리고 일반적으로 구글의 심사 속도보다 네이버의 심사속도가 느리다. 그러므로 항상 구글의 심사가 완벽히 끝난 다음에 네이버 앱스토어의 심사를 진행하자.
이때 유의할 것이, 플레이스토어의 앱 업데이트가 먼저 배포된 다음에 네이버 앱스토어의 심사를 진행해야 한다는 것이다. 왜냐면 아래에 언급한 문제가 있기 때문이다.
Android 앱에서 플레이스토어 결제가 되지 않는 경우
그리고 일반적으로 구글의 심사 속도보다 네이버의 심사속도가 느리다. 그러므로 항상 구글의 심사가 완벽히 끝난 다음에 네이버 앱스토어의 심사를 진행하자.
Android 앱에서 플레이스토어 결제가 되지 않는 경우
플레이스토어에 올라간 앱의 경우, 스토어의 최신 버전보다 높은 version number를 가진 앱의 경우에는 결제가 실패한다.
업데이트 대기중인 앱의 결제 테스트가 실패하는게 그 원인이다. 따라서 앱의 결제 테스트를 할때는 스토어의 최신버전으로 맞춰놓고 결제 테스트를 하고, 실제 테스트는 플레이스토어 배포를 한후에 테스트를 하자.
Ubuntu ssh 설치
우분투 desktop은 외부에서 접속하는 ssh가 기본적으로 설치되어 있지 않다.
그래서 다음과 같이 설치하면 22번으로 ssh가 설치되고 자동으로 서비스가 시작된다.
sudo apt-get install openssh-server
그래서 다음과 같이 설치하면 22번으로 ssh가 설치되고 자동으로 서비스가 시작된다.
sudo apt-get install openssh-server
Thursday, December 10, 2015
Mac 패러랠즈 10에서 우분투 15.10 설치기
오랫만에 리눅스가 필요해서 패러랠즈 10에다가 우분투 15.10을 설치였더니 설치가 잘되었다.
하지만 parallels tools가 설치가 되지 않아서 14.04로 다운그레이드 했다. 가상 시디를 넣으면 한자가 깨지는 것 같은 파일만 보이게 되기 때문이다.
하지만 parallels tools가 설치가 되지 않아서 14.04로 다운그레이드 했다. 가상 시디를 넣으면 한자가 깨지는 것 같은 파일만 보이게 되기 때문이다.
패러랠즈를 11로 업데이트를 해야되는 시점이 온것 같다.
Tuesday, December 8, 2015
Sunday, December 6, 2015
Android 플레이스토어 앱설치시 '콘텐츠 제공자의 권한이 중복됩니다'
구글 플레이스토어에서 앱을 설치시 '콘텐츠 제공자의 권한이 중복됩니다' 에러 메세지가 나타날 경우가 있다.
이 문제는 앱의 Facebook SDK를 버전업 하였을때 provider의 이름 설정과 export 설정이 잘못되어서 다른 앱과 충돌할때 나타난다.
아래의 exported를 반드시 false로 해야지 앱자체의 provider로만 동작한다. 왜냐면 exported를 true로 하면 provider name이 다른 Facebook SDK를 사용한 앱의 provider와 같기 때문이다.
<provider android:authorities="com.facebook.app.FacebookContentProvider0123456789..."
android:name="com.facebook.FacebookContentProvider0123456789..."
android:exported="false" />
이 문제는 앱의 Facebook SDK를 버전업 하였을때 provider의 이름 설정과 export 설정이 잘못되어서 다른 앱과 충돌할때 나타난다.
아래의 exported를 반드시 false로 해야지 앱자체의 provider로만 동작한다. 왜냐면 exported를 true로 하면 provider name이 다른 Facebook SDK를 사용한 앱의 provider와 같기 때문이다.
<provider android:authorities="com.facebook.app.FacebookContentProvider0123456789..."
android:name="com.facebook.FacebookContentProvider0123456789..."
android:exported="false" />
Android 인증 SMS 자동인증
<uses-permission android:name="android.permission.RECEIVE_SMS" />
// 폰번호를 인식하여 문자를 보내달라고 요청
runOnUiThread(new Runnable() {
@Override
public void run() {
final TelephonyManager tm = (TelephonyManager) getActivity()
.getSystemService(Context.TELEPHONY_SERVICE);
if(tm == null)
return;
// 내 폰번호를 받음
String myNumber = tm.getLine1Number();
if(StringUtils.isEmpty(StringUtils.trim(myNumber)))
return;
// KT일경우 폰번호 변환
if(myNumber.startsWith("+82")) {
myNumber = myNumber.replace("+82", "0");
}
_phone.setText(myNumber);
_useSmsReceiver = true;
sendVerification();
}
});
private void sendVerification() {
// 인증번호란에 바로 입력할수 있도록 포커스를 줌
_code.requestFocus();
// SMS 인증문자를 바로 수신해서 결과를 입력해줌
if(_useSmsReceiver)
registerReceiver();
}
private void registerReceiver() {
unregisterReceiver();
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SMSReceiver.ACTION);
_receiver = new SMSReceiver();
registerReceiver(_receiver, intentFilter);
}
private void unregisterReceiver() {
if(_receiver != null) {
unregisterReceiver(_receiver);
_receiver = null;
}
}
private class SMSReceiver extends BroadcastReceiver {
private static final String TAG="SMSReceiver";
private static final String ACTION = "android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(Context context, Intent intent) {
// 문자만 수신받음
if(intent.getAction().equals(ACTION)) {
final Bundle bundle = intent.getExtras();
if(bundle == null)
return;
final Object[] pdusObj = (Object[])bundle.get("pdus");
if(pdusObj == null)
return;
final SmsMessage[] smsMessages = new SmsMessage[pdusObj.length];
for(int i=0; i<smsMessages.length; i++) {
smsMessages[i] = SmsMessage.createFromPdu((byte[])pdusObj[i]);
final String address = smsMessages[i].getOriginatingAddress();
final String message = smsMessages[i].getDisplayMessageBody();
if(address.equals(CS_NUMBER)) {
String adjustMessage = message.replace("[Web발신]", "");
int begin = adjustMessage.indexOf('[');
int end = adjustMessage.indexOf(']');
// 인증번호를 찾았다
final String codeMessage = adjustMessage.substring(begin+1, end);
runOnUiThread(new Runnable() {
@Override
public void run() {
_code.setText(codeMessage);
confirmVerification();
}
});
unregisterReceiver();
}
}
}
}
}
// 폰번호를 인식하여 문자를 보내달라고 요청
runOnUiThread(new Runnable() {
@Override
public void run() {
final TelephonyManager tm = (TelephonyManager) getActivity()
.getSystemService(Context.TELEPHONY_SERVICE);
if(tm == null)
return;
// 내 폰번호를 받음
String myNumber = tm.getLine1Number();
if(StringUtils.isEmpty(StringUtils.trim(myNumber)))
return;
// KT일경우 폰번호 변환
if(myNumber.startsWith("+82")) {
myNumber = myNumber.replace("+82", "0");
}
_phone.setText(myNumber);
_useSmsReceiver = true;
sendVerification();
}
});
private void sendVerification() {
// 인증번호란에 바로 입력할수 있도록 포커스를 줌
_code.requestFocus();
// SMS 인증문자를 바로 수신해서 결과를 입력해줌
if(_useSmsReceiver)
registerReceiver();
}
private void registerReceiver() {
unregisterReceiver();
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SMSReceiver.ACTION);
_receiver = new SMSReceiver();
registerReceiver(_receiver, intentFilter);
}
private void unregisterReceiver() {
if(_receiver != null) {
unregisterReceiver(_receiver);
_receiver = null;
}
}
private class SMSReceiver extends BroadcastReceiver {
private static final String TAG="SMSReceiver";
private static final String ACTION = "android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(Context context, Intent intent) {
// 문자만 수신받음
if(intent.getAction().equals(ACTION)) {
final Bundle bundle = intent.getExtras();
if(bundle == null)
return;
final Object[] pdusObj = (Object[])bundle.get("pdus");
if(pdusObj == null)
return;
final SmsMessage[] smsMessages = new SmsMessage[pdusObj.length];
for(int i=0; i<smsMessages.length; i++) {
smsMessages[i] = SmsMessage.createFromPdu((byte[])pdusObj[i]);
final String address = smsMessages[i].getOriginatingAddress();
final String message = smsMessages[i].getDisplayMessageBody();
if(address.equals(CS_NUMBER)) {
String adjustMessage = message.replace("[Web발신]", "");
int begin = adjustMessage.indexOf('[');
int end = adjustMessage.indexOf(']');
// 인증번호를 찾았다
final String codeMessage = adjustMessage.substring(begin+1, end);
runOnUiThread(new Runnable() {
@Override
public void run() {
_code.setText(codeMessage);
confirmVerification();
}
});
unregisterReceiver();
}
}
}
}
}
Android 채팅 EditText 자동 크기 확장 및 멀티라인
<EditText
android:id="@+id/message_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="3dp"
android:hint="@string/chat_room_message_hint"
android:paddingLeft="5dp"
android:textSize="13sp"
android:inputType="textMultiLine"
android:scrollbars="vertical"
android:singleLine="false"
android:minLines="2"
android:maxLines="5"
/>
android:id="@+id/message_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="3dp"
android:hint="@string/chat_room_message_hint"
android:paddingLeft="5dp"
android:textSize="13sp"
android:inputType="textMultiLine"
android:scrollbars="vertical"
android:singleLine="false"
android:minLines="2"
android:maxLines="5"
/>
Android Toast 폰트 변경 및 single line 만들기
// 토스트 폰트 통일
final LinearLayout toastLayout = (LinearLayout) toast.getView();
final TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTypeface(Typeface.createFromAsset(getAssetManager(), "NanumGothic.otf"));
if(singleLine) {
toastTV.setSingleLine(true);
toastTV.setEllipsize(TextUtils.TruncateAt.END);
}
toast.show();
final LinearLayout toastLayout = (LinearLayout) toast.getView();
final TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTypeface(Typeface.createFromAsset(getAssetManager(), "NanumGothic.otf"));
if(singleLine) {
toastTV.setSingleLine(true);
toastTV.setEllipsize(TextUtils.TruncateAt.END);
}
toast.show();
Visual C++ 파일 열기 다이얼로그 API
char szFile[256];
//API file dialog
OPENFILENAMEA ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = "All\0*.*\0Map\0*.map\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
BOOL ret = GetOpenFileNameA(&ofn);
if (ret) {
// load map and enter training mode
}
//API file dialog
OPENFILENAMEA ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = "All\0*.*\0Map\0*.map\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
BOOL ret = GetOpenFileNameA(&ofn);
if (ret) {
// load map and enter training mode
}
Wednesday, December 2, 2015
Linux epoll example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#define MAXEVENTS 64
static int
make_socket_non_blocking(int sfd) {
int flags, s;
flags = fcntl(sfd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl");
return -1;
}
flags |= O_NONBLOCK;
s = fcntl(sfd, F_SETFL, flags);
if (s == -1) {
perror("fcntl");
return -1;
}
return 0;
}
static int
create_and_bind(char *port) {
struct addrinfo hints;
struct addrinfo *result, *rp;
int s, sfd;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */
hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */
hints.ai_flags = AI_PASSIVE; /* All interfaces */
s = getaddrinfo(NULL, port, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
return -1;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
s = bind(sfd, rp->ai_addr, rp->ai_addrlen);
if (s == 0) {
/* We managed to bind successfully! */
break;
}
close(sfd);
}
if (rp == NULL) {
fprintf(stderr, "Could not bind\n");
return -1;
}
freeaddrinfo(result);
return sfd;
}
int
main(int argc, char *argv[]) {
int sfd, s;
int efd;
struct epoll_event event;
struct epoll_event *events;
if (argc != 2) {
fprintf(stderr, "Usage: %s [port]\n", argv[0]);
exit(EXIT_FAILURE);
}
sfd = create_and_bind(argv[1]);
if (sfd == -1)
abort();
s = make_socket_non_blocking(sfd);
if (s == -1)
abort();
s = listen(sfd, SOMAXCONN);
if (s == -1) {
perror("listen");
abort();
}
efd = epoll_create1(0);
if (efd == -1) {
perror("epoll_create");
abort();
}
event.data.fd = sfd;
event.events = EPOLLIN | EPOLLET;
s = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
if (s == -1) {
perror("epoll_ctl");
abort();
}
/* Buffer where events are returned */
events = calloc(MAXEVENTS, sizeof event);
/* The event loop */
while (1) {
int n, i;
n = epoll_wait(efd, events, MAXEVENTS, -1);
for (i = 0; i < n; i++) {
if ((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN))) {
/* An error has occured on this fd, or the socket is not
ready for reading (why were we notified then?) */
fprintf(stderr, "epoll error\n");
close(events[i].data.fd);
continue;
}
else if (sfd == events[i].data.fd) {
/* We have a notification on the listening socket, which
means one or more incoming connections. */
while (1) {
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept(sfd, &in_addr, &in_len);
if (infd == -1) {
if ((errno == EAGAIN) ||
(errno == EWOULDBLOCK)) {
/* We have processed all incoming
connections. */
break;
}
else {
perror("accept");
break;
}
}
s = getnameinfo(&in_addr, in_len,
hbuf, sizeof hbuf,
sbuf, sizeof sbuf,
NI_NUMERICHOST | NI_NUMERICSERV);
if (s == 0) {
printf("Accepted connection on descriptor %d "
"(host=%s, port=%s)\n", infd, hbuf, sbuf);
}
/* Make the incoming socket non-blocking and add it to the
list of fds to monitor. */
s = make_socket_non_blocking(infd);
if (s == -1)
abort();
event.data.fd = infd;
event.events = EPOLLIN | EPOLLET;
s = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
if (s == -1) {
perror("epoll_ctl");
abort();
}
}
continue;
}
else {
/* We have data on the fd waiting to be read. Read and
display it. We must read whatever data is available
completely, as we are running in edge-triggered mode
and won't get a notification again for the same
data. */
int done = 0;
while (1) {
ssize_t count;
char buf[512];
count = read(events[i].data.fd, buf, sizeof buf);
if (count == -1) {
/* If errno == EAGAIN, that means we have read all
data. So go back to the main loop. */
if (errno != EAGAIN) {
perror("read");
done = 1;
}
break;
}
else if (count == 0) {
/* End of file. The remote has closed the
connection. */
done = 1;
break;
}
/* Write the buffer to standard output */
s = write(1, buf, count);
if (s == -1) {
perror("write");
abort();
}
}
if (done) {
printf("Closed connection on descriptor %d\n",
events[i].data.fd);
/* Closing the descriptor will make epoll remove it
from the set of descriptors which are monitored. */
close(events[i].data.fd);
}
}
}
}
free(events);
close(sfd);
return EXIT_SUCCESS;
}
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#define MAXEVENTS 64
static int
make_socket_non_blocking(int sfd) {
int flags, s;
flags = fcntl(sfd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl");
return -1;
}
flags |= O_NONBLOCK;
s = fcntl(sfd, F_SETFL, flags);
if (s == -1) {
perror("fcntl");
return -1;
}
return 0;
}
static int
create_and_bind(char *port) {
struct addrinfo hints;
struct addrinfo *result, *rp;
int s, sfd;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */
hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */
hints.ai_flags = AI_PASSIVE; /* All interfaces */
s = getaddrinfo(NULL, port, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
return -1;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
s = bind(sfd, rp->ai_addr, rp->ai_addrlen);
if (s == 0) {
/* We managed to bind successfully! */
break;
}
close(sfd);
}
if (rp == NULL) {
fprintf(stderr, "Could not bind\n");
return -1;
}
freeaddrinfo(result);
return sfd;
}
int
main(int argc, char *argv[]) {
int sfd, s;
int efd;
struct epoll_event event;
struct epoll_event *events;
if (argc != 2) {
fprintf(stderr, "Usage: %s [port]\n", argv[0]);
exit(EXIT_FAILURE);
}
sfd = create_and_bind(argv[1]);
if (sfd == -1)
abort();
s = make_socket_non_blocking(sfd);
if (s == -1)
abort();
s = listen(sfd, SOMAXCONN);
if (s == -1) {
perror("listen");
abort();
}
efd = epoll_create1(0);
if (efd == -1) {
perror("epoll_create");
abort();
}
event.data.fd = sfd;
event.events = EPOLLIN | EPOLLET;
s = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
if (s == -1) {
perror("epoll_ctl");
abort();
}
/* Buffer where events are returned */
events = calloc(MAXEVENTS, sizeof event);
/* The event loop */
while (1) {
int n, i;
n = epoll_wait(efd, events, MAXEVENTS, -1);
for (i = 0; i < n; i++) {
if ((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN))) {
/* An error has occured on this fd, or the socket is not
ready for reading (why were we notified then?) */
fprintf(stderr, "epoll error\n");
close(events[i].data.fd);
continue;
}
else if (sfd == events[i].data.fd) {
/* We have a notification on the listening socket, which
means one or more incoming connections. */
while (1) {
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept(sfd, &in_addr, &in_len);
if (infd == -1) {
if ((errno == EAGAIN) ||
(errno == EWOULDBLOCK)) {
/* We have processed all incoming
connections. */
break;
}
else {
perror("accept");
break;
}
}
s = getnameinfo(&in_addr, in_len,
hbuf, sizeof hbuf,
sbuf, sizeof sbuf,
NI_NUMERICHOST | NI_NUMERICSERV);
if (s == 0) {
printf("Accepted connection on descriptor %d "
"(host=%s, port=%s)\n", infd, hbuf, sbuf);
}
/* Make the incoming socket non-blocking and add it to the
list of fds to monitor. */
s = make_socket_non_blocking(infd);
if (s == -1)
abort();
event.data.fd = infd;
event.events = EPOLLIN | EPOLLET;
s = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
if (s == -1) {
perror("epoll_ctl");
abort();
}
}
continue;
}
else {
/* We have data on the fd waiting to be read. Read and
display it. We must read whatever data is available
completely, as we are running in edge-triggered mode
and won't get a notification again for the same
data. */
int done = 0;
while (1) {
ssize_t count;
char buf[512];
count = read(events[i].data.fd, buf, sizeof buf);
if (count == -1) {
/* If errno == EAGAIN, that means we have read all
data. So go back to the main loop. */
if (errno != EAGAIN) {
perror("read");
done = 1;
}
break;
}
else if (count == 0) {
/* End of file. The remote has closed the
connection. */
done = 1;
break;
}
/* Write the buffer to standard output */
s = write(1, buf, count);
if (s == -1) {
perror("write");
abort();
}
}
if (done) {
printf("Closed connection on descriptor %d\n",
events[i].data.fd);
/* Closing the descriptor will make epoll remove it
from the set of descriptors which are monitored. */
close(events[i].data.fd);
}
}
}
}
free(events);
close(sfd);
return EXIT_SUCCESS;
}
Tuesday, December 1, 2015
PHP CDKEY 쿠폰번호 생성 소스
<?php
//지정된 자릿수의 랜덤한 숫자를 반환합니다. 최대 10까지 가능합니다. 4 이면 1000 에서 9999 사이의 랜덤 숫자
function get_rand_number($len=4) {
$len = abs((int)$len);
if ($len < 1) $len = 1;
else if ($len > 10) $len = 10;
return rand(pow(10, $len - 1), (pow(10, $len) - 1));
}
//넘어온 세자리수를 36진수로 변환해서 반환합니다. preg_match_callback 을 통해서만 사용됩니다.
function get_simple_36($m){
$str = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$div = floor($m[0] / 36);
$rest = $m[0] % 36;
return $str[$div] . $str[$rest];
}
//지정된 자리수에 존재하는 소수 전체를 배열로 반환합니다. max len = 5
function get_simple_prime_number($len=5){
$len = abs((int)$len);
if ($len < 1) $len = 1;
else if ($len > 5) $len = 5;
$prime_1 = Array(1, 2, 3, 5, 7);
if ($len == 1) return $prime_1;
$start = pow(10, ($len - 1)) + 1;//101
$end = pow(10, $len) - 1;//999
$prime = $prime_1;
unset($prime[0]);//1제거
unset($prime[1]);//2제거
$array = Array();
for($i = 11; $i <= $end; $i+=2){//10보다 큰 소수에는 짝수가 없다.
$max = floor(sqrt($i));
foreach($prime as $j) {
if ($j > $max) break;
if ($i % $j == 0) continue 2;
}
$prime[] = $i;
if ($i >= $start) $array[] = $i;
}
return $array;
}
//지정된 자릿수의 숫자로된 시리얼을 반환합니다. - 를 포함하고 싶지 않을때는 $cut 이 $len 보다 크거나 같으면 됩니다. max len = 36
function get_serial($len=16, $cut=4, $hipen='-'){
$len = abs((int)$len);
if ($len < 1) $len = 16;
else if ($len > 36) $len = 36;
$cut = abs((int)$cut);
if ($cut < 1) $cut = 4;
else if ($cut > $len) $cut = $len;
list($usec, $sec) = explode(' ', microtime());
$base_number = (string)$sec . str_replace('0.', '', (string)$usec);
$base_number .= (string)get_rand_number(10) . (string)get_rand_number(8);//36자리 유니크한 숫자 문자열
$prime = get_simple_prime_number(5);//5자리 소수 배열
shuffle($prime);
$prime = $prime[0];//랜덤한 5자리 소수
$serial = bcmul(substr($base_number, 0, $len), $prime);
$serial_length = strlen($serial);
$sub = $len - $serial_length;
if ($sub > 0) $serial .= (string)get_rand_number($sub);
else if ($sub < 0) $serial = substr($serial, 0, $len);
return preg_replace("`(.{" . $cut . "})`", "$1" . $hipen, $serial, floor(($len-1) / $cut));
}
//지정된 자릿수의 숫자와 영문으로된 시리얼을 반환합니다. - 를 포함하고 싶지 않을때는 $cut 이 $len 보다 크거나 같으면 됩니다. max len = 24
function get_serial_mix($len=16, $cut=4, $hipen='-'){
$len = abs((int)$len);
if ($len < 1) $len = 16;
else if ($len > 24) $len = 24;
$cut = abs((int)$cut);
if ($cut < 1) $cut = 4;
else if ($cut > $len) $cut = $len;
$len2 = (int)($len * 3 / 2);
if ($len2 % 2 == 1) $len2 += 1;
$serial = get_serial($len2, $len2, $hipen);
$serial = substr(preg_replace_callback("`.{3}`", "get_simple_36", $serial), 0, $len);
return preg_replace("`(.{" . $cut . "})`", "$1" . $hipen, $serial, floor(($len-1) / $cut));
}
echo get_serial_mix(36, 4, '-');
?>
//지정된 자릿수의 랜덤한 숫자를 반환합니다. 최대 10까지 가능합니다. 4 이면 1000 에서 9999 사이의 랜덤 숫자
function get_rand_number($len=4) {
$len = abs((int)$len);
if ($len < 1) $len = 1;
else if ($len > 10) $len = 10;
return rand(pow(10, $len - 1), (pow(10, $len) - 1));
}
//넘어온 세자리수를 36진수로 변환해서 반환합니다. preg_match_callback 을 통해서만 사용됩니다.
function get_simple_36($m){
$str = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$div = floor($m[0] / 36);
$rest = $m[0] % 36;
return $str[$div] . $str[$rest];
}
//지정된 자리수에 존재하는 소수 전체를 배열로 반환합니다. max len = 5
function get_simple_prime_number($len=5){
$len = abs((int)$len);
if ($len < 1) $len = 1;
else if ($len > 5) $len = 5;
$prime_1 = Array(1, 2, 3, 5, 7);
if ($len == 1) return $prime_1;
$start = pow(10, ($len - 1)) + 1;//101
$end = pow(10, $len) - 1;//999
$prime = $prime_1;
unset($prime[0]);//1제거
unset($prime[1]);//2제거
$array = Array();
for($i = 11; $i <= $end; $i+=2){//10보다 큰 소수에는 짝수가 없다.
$max = floor(sqrt($i));
foreach($prime as $j) {
if ($j > $max) break;
if ($i % $j == 0) continue 2;
}
$prime[] = $i;
if ($i >= $start) $array[] = $i;
}
return $array;
}
//지정된 자릿수의 숫자로된 시리얼을 반환합니다. - 를 포함하고 싶지 않을때는 $cut 이 $len 보다 크거나 같으면 됩니다. max len = 36
function get_serial($len=16, $cut=4, $hipen='-'){
$len = abs((int)$len);
if ($len < 1) $len = 16;
else if ($len > 36) $len = 36;
$cut = abs((int)$cut);
if ($cut < 1) $cut = 4;
else if ($cut > $len) $cut = $len;
list($usec, $sec) = explode(' ', microtime());
$base_number = (string)$sec . str_replace('0.', '', (string)$usec);
$base_number .= (string)get_rand_number(10) . (string)get_rand_number(8);//36자리 유니크한 숫자 문자열
$prime = get_simple_prime_number(5);//5자리 소수 배열
shuffle($prime);
$prime = $prime[0];//랜덤한 5자리 소수
$serial = bcmul(substr($base_number, 0, $len), $prime);
$serial_length = strlen($serial);
$sub = $len - $serial_length;
if ($sub > 0) $serial .= (string)get_rand_number($sub);
else if ($sub < 0) $serial = substr($serial, 0, $len);
return preg_replace("`(.{" . $cut . "})`", "$1" . $hipen, $serial, floor(($len-1) / $cut));
}
//지정된 자릿수의 숫자와 영문으로된 시리얼을 반환합니다. - 를 포함하고 싶지 않을때는 $cut 이 $len 보다 크거나 같으면 됩니다. max len = 24
function get_serial_mix($len=16, $cut=4, $hipen='-'){
$len = abs((int)$len);
if ($len < 1) $len = 16;
else if ($len > 24) $len = 24;
$cut = abs((int)$cut);
if ($cut < 1) $cut = 4;
else if ($cut > $len) $cut = $len;
$len2 = (int)($len * 3 / 2);
if ($len2 % 2 == 1) $len2 += 1;
$serial = get_serial($len2, $len2, $hipen);
$serial = substr(preg_replace_callback("`.{3}`", "get_simple_36", $serial), 0, $len);
return preg_replace("`(.{" . $cut . "})`", "$1" . $hipen, $serial, floor(($len-1) / $cut));
}
echo get_serial_mix(36, 4, '-');
?>
Sunday, November 29, 2015
Android Facebook SDK hash key
public void printHashKey() {
try {
PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
String hashKey = new String(Base64.encode(md.digest(), 0));
Log.i(TAG, "printHashKey() Hash Key: " + hashKey);
}
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "printHashKey()", e);
} catch (Exception e) {
Log.e(TAG, "printHashKey()", e);
}
}
try {
PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
String hashKey = new String(Base64.encode(md.digest(), 0));
Log.i(TAG, "printHashKey() Hash Key: " + hashKey);
}
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "printHashKey()", e);
} catch (Exception e) {
Log.e(TAG, "printHashKey()", e);
}
}
원격제어 VNC, RDP 차이
요즘 원격 접속 제어 등에 관심이 생겨서 그러는데,
VNC와 RDP 차이가 어떤 점일까요...?
기술적으로 어떤게 다른지 알면 더 좋을 것 같네요.
예를 들면, VNC는 다중 접속이 가능하고 RDP는 그렇지 않다던가...
VNC는 화면 캡쳐 기반이고 RDP는 가상 채널 기반이라던가...
(실제로 맞는지 모릅니다. 그냥 대충 적어놓은 거에요. ㅠㅠ_
확실히 서로 다른 솔루션이고 장단점이 있을 것 같은데 정확히는 잘 모르겠네요.
RDP는 윈도우용이고 VNC는 서버 프로그램을 설치하면 윈도우, 맥, 리눅스 등 다 사용가능하다던지...
혹시 잘 아시는 분 계세요..?
RDP는 마이크로소프트에서 만든 것으로 서버로는 윈도우만 가능하고 클라이언트는 윈도우, 맥, 리눅스 모두 가능합니다. 속도나 성능, 기능적인 측면에서 좋은 편입니다. 서버 버전이 아닌 윈도우에서는 다중 접속이 안 되는 단점이 있으나 약간의 패치로 수정이 가능하기도 합니다.
VNC는 멀티플랫폼이 된다는 게 가장 큰 장점입니다.
RDP를 제외한 나머지류는 화면 전체를 캡쳐해서 인코딩하여 보내는 방식인지라 다양한 운영체제에서 사용이 가능한 반면에, 역으로 좀 느립니다. MS RDP는 MS의존적인 반면에 단순 압축이 아니라 경우에 따라 간소화된 프로토콜을 이용하여 좀 더 빠릅니다. 다만 MS RDP가 항상 MS OS에서만 사용 가능한 것은 아니고, 어디까지나 단순 이미지를 전송하는 방식도 있기 때문에 Virtualbox같은 경우에는 모든 OS에 대하여 접속이 가능하도록 설정할 수 있습니다.
그리고 또 VNC는 원격 접속류이기 때문에 해당 대상 컴퓨터의 콘솔 세션에만 접근이 가능한 반면, RDP는 비 콘솔 세션에 접근할 수 있지요.
VNC와 RDP 차이가 어떤 점일까요...?
기술적으로 어떤게 다른지 알면 더 좋을 것 같네요.
예를 들면, VNC는 다중 접속이 가능하고 RDP는 그렇지 않다던가...
VNC는 화면 캡쳐 기반이고 RDP는 가상 채널 기반이라던가...
(실제로 맞는지 모릅니다. 그냥 대충 적어놓은 거에요. ㅠㅠ_
확실히 서로 다른 솔루션이고 장단점이 있을 것 같은데 정확히는 잘 모르겠네요.
RDP는 윈도우용이고 VNC는 서버 프로그램을 설치하면 윈도우, 맥, 리눅스 등 다 사용가능하다던지...
혹시 잘 아시는 분 계세요..?
RDP는 마이크로소프트에서 만든 것으로 서버로는 윈도우만 가능하고 클라이언트는 윈도우, 맥, 리눅스 모두 가능합니다. 속도나 성능, 기능적인 측면에서 좋은 편입니다. 서버 버전이 아닌 윈도우에서는 다중 접속이 안 되는 단점이 있으나 약간의 패치로 수정이 가능하기도 합니다.
VNC는 멀티플랫폼이 된다는 게 가장 큰 장점입니다.
RDP를 제외한 나머지류는 화면 전체를 캡쳐해서 인코딩하여 보내는 방식인지라 다양한 운영체제에서 사용이 가능한 반면에, 역으로 좀 느립니다. MS RDP는 MS의존적인 반면에 단순 압축이 아니라 경우에 따라 간소화된 프로토콜을 이용하여 좀 더 빠릅니다. 다만 MS RDP가 항상 MS OS에서만 사용 가능한 것은 아니고, 어디까지나 단순 이미지를 전송하는 방식도 있기 때문에 Virtualbox같은 경우에는 모든 OS에 대하여 접속이 가능하도록 설정할 수 있습니다.
그리고 또 VNC는 원격 접속류이기 때문에 해당 대상 컴퓨터의 콘솔 세션에만 접근이 가능한 반면, RDP는 비 콘솔 세션에 접근할 수 있지요.
Thursday, November 26, 2015
Android immersive fullscreen toggle
/**
* Detects and toggles immersive mode (also known as "hidey bar" mode).
*/
public void toggleHideyBar() {
// BEGIN_INCLUDE (get_current_ui_flags)
// The UI options currently enabled are represented by a bitfield.
// getSystemUiVisibility() gives us that bitfield.
int uiOptions = getActivity().getWindow().getDecorView().getSystemUiVisibility();
int newUiOptions = uiOptions;
// END_INCLUDE (get_current_ui_flags)
// BEGIN_INCLUDE (toggle_ui_flags)
boolean isImmersiveModeEnabled =
((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
if (isImmersiveModeEnabled) {
Log.i(TAG, "Turning immersive mode mode off. ");
} else {
Log.i(TAG, "Turning immersive mode mode on.");
}
// Navigation bar hiding: Backwards compatible to ICS.
if (Build.VERSION.SDK_INT >= 14) {
newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
// Status bar hiding: Backwards compatible to Jellybean
if (Build.VERSION.SDK_INT >= 16) {
newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
// Immersive mode: Backward compatible to KitKat.
// Note that this flag doesn't do anything by itself, it only augments the behavior
// of HIDE_NAVIGATION and FLAG_FULLSCREEN. For the purposes of this sample
// all three flags are being toggled together.
// Note that there are two immersive mode UI flags, one of which is referred to as "sticky".
// Sticky immersive mode differs in that it makes the navigation and status bars
// semi-transparent, and the UI flag does not get cleared when the user interacts with
// the screen.
if (Build.VERSION.SDK_INT >= 18) {
newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
getActivity().getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
//END_INCLUDE (set_ui_flags)
}
* Detects and toggles immersive mode (also known as "hidey bar" mode).
*/
public void toggleHideyBar() {
// BEGIN_INCLUDE (get_current_ui_flags)
// The UI options currently enabled are represented by a bitfield.
// getSystemUiVisibility() gives us that bitfield.
int uiOptions = getActivity().getWindow().getDecorView().getSystemUiVisibility();
int newUiOptions = uiOptions;
// END_INCLUDE (get_current_ui_flags)
// BEGIN_INCLUDE (toggle_ui_flags)
boolean isImmersiveModeEnabled =
((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
if (isImmersiveModeEnabled) {
Log.i(TAG, "Turning immersive mode mode off. ");
} else {
Log.i(TAG, "Turning immersive mode mode on.");
}
// Navigation bar hiding: Backwards compatible to ICS.
if (Build.VERSION.SDK_INT >= 14) {
newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
// Status bar hiding: Backwards compatible to Jellybean
if (Build.VERSION.SDK_INT >= 16) {
newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
// Immersive mode: Backward compatible to KitKat.
// Note that this flag doesn't do anything by itself, it only augments the behavior
// of HIDE_NAVIGATION and FLAG_FULLSCREEN. For the purposes of this sample
// all three flags are being toggled together.
// Note that there are two immersive mode UI flags, one of which is referred to as "sticky".
// Sticky immersive mode differs in that it makes the navigation and status bars
// semi-transparent, and the UI flag does not get cleared when the user interacts with
// the screen.
if (Build.VERSION.SDK_INT >= 18) {
newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
getActivity().getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
//END_INCLUDE (set_ui_flags)
}
CLion에서 사용하는 CMakeLists.txt 간단 강좌
리눅스에서 프로그래밍할때 IntelliJ 기반의 CLion이라는 툴을 사용해보았다. CLion은 Makefile이 아닌 범용 maketool인 CMake를 사용한다. 그래서 Makefile 대신에 CMakeLists.txt를 만들어줘야 빌드가 된다.
1. 아래는 long string에 대한 에러가 날때 사용한다. CFLAGS옵션 이다.
Makefile
FLAGS = -g -O3 -fpermissive -w
CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
2. 아래는 include directory에 대해서 설정이다. I옵션이다.
Makefile
INCS = -I/usr/include -I/usr/local/mysql/include -I/usr/local/include
CMakeLists.txt
include_directories(/usr/include)
include_directories(/usr/local/mysql/include)
include_directories(/usr/local/include)
1. 아래는 long string에 대한 에러가 날때 사용한다. CFLAGS옵션 이다.
Makefile
FLAGS = -g -O3 -fpermissive -w
CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
2. 아래는 include directory에 대해서 설정이다. I옵션이다.
Makefile
INCS = -I/usr/include -I/usr/local/mysql/include -I/usr/local/include
CMakeLists.txt
include_directories(/usr/include)
include_directories(/usr/local/mysql/include)
include_directories(/usr/local/include)
3. 아래는 AnsiC에 대한 옵션이다.
set(CMAKE_C_FLAGS "-std=c99")
4. 아래는 define 정의이다. FLAGS+= -D와 같다.
add_definitions(-DREGION_KR=1)
add_definitions(-DMODE_REAL=1)
add_definitions(-DDB_REAL=1)
5. 아래는 static library에 대한 옵션이다.
Makefile
LIBS = -lc -lssl -lpthread -L'/usr/local/mysql/lib/mysql' -lmysqlclient -lz -lcrypt -lnsl -lm -ldl -lrt
CMakeLists.txt
add_executable(dark_server ${SOURCE_FILES})
#link must be after add_executable
target_link_libraries(dark_server c)
target_link_libraries(dark_server ssl)
target_link_libraries(dark_server pthread)
target_link_libraries(dark_server mysqlclient)
Wednesday, November 18, 2015
C# 서버개발 관련 마영전 정리
마영전 서버가 C#으로 개발되었습니다.
마영전 서버 세션 정리
http://www.scribd.com/doc/57921356/ndc2011-%EB%A7%88-%EB%B9%84-%EB%85%B8-%EA%B8%B0-%EC%98%81-%EC%9B%85-%EC%A0%84-%EC%84%9C-%EB%B2%84-%EA%B4%80-%EB%A0%A8-%EC%84%B8-%EC%85%98-%EC%A0%95-%EB%A6%AC
C#으로 게임 서버를 개발 할 경우 성능에 대한 글
http://rosagigantea.tistory.com/408
NDC 참관기: 마비노기 영웅전 자이언트 서버의 비밀
https://rein.kr/blog/archives/2671
게임코디 쓰레드
http://www.gamecodi.com/board/zboard.php?id=GAMECODI_Talkdev&no=1792
마영전 서버 세션 정리
http://www.scribd.com/doc/57921356/ndc2011-%EB%A7%88-%EB%B9%84-%EB%85%B8-%EA%B8%B0-%EC%98%81-%EC%9B%85-%EC%A0%84-%EC%84%9C-%EB%B2%84-%EA%B4%80-%EB%A0%A8-%EC%84%B8-%EC%85%98-%EC%A0%95-%EB%A6%AC
C#으로 게임 서버를 개발 할 경우 성능에 대한 글
http://rosagigantea.tistory.com/408
NDC 참관기: 마비노기 영웅전 자이언트 서버의 비밀
https://rein.kr/blog/archives/2671
게임코디 쓰레드
http://www.gamecodi.com/board/zboard.php?id=GAMECODI_Talkdev&no=1792
Tuesday, November 17, 2015
Linux binary executable bit check
실행파일 바이너리의 비트수를 체크하는 명령은 다음과 같다.
file <filename>
root@3bd49b29-5448-41da-a3e1-581280f439e0:~/dark_server/src# file mgs_dark
mgs_dark: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x1b8c7bd92afcb4134444103f026ddb336cff19c4, not stripped
root@3bd49b29-5448-41da-a3e1-581280f439e0:~/dark_server/src#
file <filename>
root@3bd49b29-5448-41da-a3e1-581280f439e0:~/dark_server/src# file mgs_dark
mgs_dark: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x1b8c7bd92afcb4134444103f026ddb336cff19c4, not stripped
root@3bd49b29-5448-41da-a3e1-581280f439e0:~/dark_server/src#
Monday, November 9, 2015
iOS Please verify that your device’s clock is properly set, and that your signing certificate is not expired.
Please verify that your device’s clock is properly set, and that your signing certificate is not expired. (0xE8008018).
Xcode -> Preferences -> Accounts -> View Details -> Download All
Xcode -> Preferences -> Accounts -> View Details -> Download All
Tuesday, November 3, 2015
Android 5.0 WebView image not shown
// Android 5.0에서는 그림이 안보이니 이걸 해주어야 한다.
if(Build.VERSION.SDK_INT >= 21) {
mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
if(Build.VERSION.SDK_INT >= 21) {
mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
Sunday, November 1, 2015
gem eventmachine openssl/ssl.h not found
bundle config build.eventmachine --with-cppflags=-I$(brew --prefix openssl)/include
bundle install
bundle install
Mac openssl 설치
./config (32bit)
./Configure darwin64-x86_64-cc (64bit)
make
sudo make install
Thursday, October 22, 2015
iOS 개발자 인증서 내보내기
iOS 개발자 인증서를 여러 맥북에서 공유하기 위해서 내보내기를 해보았다.
1. 먼저 key파일을 내보내야 하므로 '키체인 접근'을 실행시키자.
여기서 내보낼 키를 Development, Distribution 두개를 선택하고 '2개 항목 보내기'를 선택하여 .pem 파일로 저장한다.
1. 먼저 key파일을 내보내야 하므로 '키체인 접근'을 실행시키자.
여기서 내보낼 키를 Development, Distribution 두개를 선택하고 '2개 항목 보내기'를 선택하여 .pem 파일로 저장한다.
2. 두번째는 개발자 프로필을 내보내야 하므로 Xcode를 실행시켜서 Preference에서 Accounts를 선택하여 내보낼 Apple ID를 선택하고 하단에 있는 설정모양 아이콘을 눌러서 Export Developer Accounts를 선택하여 내보내자.
이 두가지 파일을 다른 맥북에 옮겨서 두개를 더블 클릭하면 동일하게 다른 맥북에서도 iOS 개발을 할수 있다.
iOS -ObjC 옵션에서 SDL 2.0 라이브러리가 duplicate가 날때
iOS 프로젝트에서 SDL 2.0 라이브러리를 사용할때가 있다. 그런데 여기서 SDL_main을 사용하지 않고 라이브러리로만 사용할때 -ObjC를 사용하게 되면 duplicate symbol for architecture x86_64라는 에러메세지가 뜬다.
-ObjC는 duplicate를 허용하지 않는 옵션이기 때문에 메인 프로젝트에 UIApplication을 띄우는 main함수가 있고 그리고 SDL 라이브러리 안에도 있기 때문이다.
따라서 다음과 같이 SDL_uikitappdelegate.m에서 main을 주석처리하면 해결된다.
-ObjC는 duplicate를 허용하지 않는 옵션이기 때문에 메인 프로젝트에 UIApplication을 띄우는 main함수가 있고 그리고 SDL 라이브러리 안에도 있기 때문이다.
따라서 다음과 같이 SDL_uikitappdelegate.m에서 main을 주석처리하면 해결된다.
iOS 라이브러리 추가시에는 항상 'Add Files to (ProjectName)'을 사용하자
소스단위로 되어 있는 iOS 라이브러리를 프로젝트에 폴더단위로 드래그앤 드롭을 하게 되면 안에 있는 .a 라이브러리 파일들이 Build Settings에 Linked Frameworks and Libraries에 자동으로 추가가 되지 않는다.
따라서 항상 소스단위의 iOS 라이브러리를 프로젝트에 추가할때는 프로젝트의 폴더에서 오른쪽 버튼을 누른후 'Add Files to (ProjectName)'을 사용해야 한다.
따라서 항상 소스단위의 iOS 라이브러리를 프로젝트에 추가할때는 프로젝트의 폴더에서 오른쪽 버튼을 누른후 'Add Files to (ProjectName)'을 사용해야 한다.
HTML 모바일웹 사이즈
일반웹으로 모바일웹으로 바꾸려면 다음과 같이 head에 viewport를 설정해 주어야 한다. 이것은 기본적으로 화면 사이즈에 맞게 표시해주고, 화면 확대를 방지하는 코드이다.
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width">
</head>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width">
</head>
Ruby 다중라인 주석달기
Ruby는 파이썬과 비슷해서 #는 한라인 주석달기이다.
그런데 다중라인 주석달기는 =begin ~ =end인데 특이한점이 무조건 그 라인에서 첫번째 글자부터 시작해야 된다.
=앞에 스페이스나 탭이 들어가면 안된다.
그런데 다중라인 주석달기는 =begin ~ =end인데 특이한점이 무조건 그 라인에서 첫번째 글자부터 시작해야 된다.
=앞에 스페이스나 탭이 들어가면 안된다.
Android Studio 쓰다보니 좋은 점들
장점
1. 소스 디컴파일러가 내장되어 있어서 소스가 없는것도 소스를 쉽게 볼수 있고, 안드로이드 소스도 바로 연결이 된다.
2. gradle 빌드 시스템으로 라이브러리의 최신버전을 repository에서 받아온다.
3. 탭간 이동이 비쥬얼 스튜디오와 동일하게 ctrl + tab이다. 이게 가장 편하다.
4. dracular 테마가 있어서 좋다.
5. 프로젝트 관리가 이클립스의 workspace가 아니라서 git에 넣었을 경우에 비쥬얼 스튜디오 처럼 프로젝트를 로딩하면 모든 셋팅이 같이 로딩된다.
6. UI 에디터도 쓸만하게 프리뷰를 할수 있다.
1. 소스 디컴파일러가 내장되어 있어서 소스가 없는것도 소스를 쉽게 볼수 있고, 안드로이드 소스도 바로 연결이 된다.
2. gradle 빌드 시스템으로 라이브러리의 최신버전을 repository에서 받아온다.
3. 탭간 이동이 비쥬얼 스튜디오와 동일하게 ctrl + tab이다. 이게 가장 편하다.
4. dracular 테마가 있어서 좋다.
5. 프로젝트 관리가 이클립스의 workspace가 아니라서 git에 넣었을 경우에 비쥬얼 스튜디오 처럼 프로젝트를 로딩하면 모든 셋팅이 같이 로딩된다.
6. UI 에디터도 쓸만하게 프리뷰를 할수 있다.
Sunday, October 18, 2015
Android Fullscreen PopupWindow 예제
팝업 윈도우를 타이틀바(액션바)를 포함하여 풀스크린으로 띄우고 싶을 때는 다음과 같은 코드를 이용한다.
new를 할때와 showAtLocation할때 동일한 view가 들어가는 것에 유의하자.
final View view = getLayoutInflater().inflate(R.layout.rotate_popup, null);
rotatePopup.setFocusable(true);
rotatePopup.showAtLocation(view, Gravity.CENTER, 0, 0);
new를 할때와 showAtLocation할때 동일한 view가 들어가는 것에 유의하자.
final View view = getLayoutInflater().inflate(R.layout.rotate_popup, null);
private PopupWindow rotatePopup;
rotatePopup = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);rotatePopup.setFocusable(true);
rotatePopup.showAtLocation(view, Gravity.CENTER, 0, 0);
Thursday, October 15, 2015
Tuesday, October 13, 2015
Ruby On Rails에서 로그 출력 하는 방법
콘솔용 Ruby에서는 print와 puts를 사용하면 화면에 문자열을 찍을 수 있는데 Ruby On Rails는 웹 프레임워크라서 print/puts를 사용하면 문자열이 HTML로 출력이 된다.
그래서 다음과 같이 출력하면 로그 파일에 안전하게 로그가 출력이 된다.
Rails.logger.debug "(문자열)"
문자열은 log/development.log, production.log에 출력이 된다.
debug대신에 info, warn등을 사용할수 있는데 이것은 다음과 같은 환경설정을 따른다.
config/environments/development.rb, production.rb에서 다음과 같이 지정한다.
config.log_level = :debug
그래서 다음과 같이 출력하면 로그 파일에 안전하게 로그가 출력이 된다.
Rails.logger.debug "(문자열)"
문자열은 log/development.log, production.log에 출력이 된다.
debug대신에 info, warn등을 사용할수 있는데 이것은 다음과 같은 환경설정을 따른다.
config/environments/development.rb, production.rb에서 다음과 같이 지정한다.
config.log_level = :debug
Git 윈도우10에서 SourceTree가 다운될때
윈도우10에서는 Git클라이언트인 SourceTree 최신버전이 다운된다.
그래서 아래 링크에 있는 SourceTree 1.5.2를 받아서 설치하면 해결된다.
하지만 SourceTree 1.5.2는 파일을 add/commit할때 폴더별로 정렬이 안되어서 불편하다.
아직은 윈도우8.1이 가장 편리한 윈도우인것 같다.(최신 지원 기능 및 적은 업데이트)
다운로드
그래서 아래 링크에 있는 SourceTree 1.5.2를 받아서 설치하면 해결된다.
하지만 SourceTree 1.5.2는 파일을 add/commit할때 폴더별로 정렬이 안되어서 불편하다.
아직은 윈도우8.1이 가장 편리한 윈도우인것 같다.(최신 지원 기능 및 적은 업데이트)
다운로드
Monday, October 12, 2015
Redis 설치(Mac)
brew redis install
redis-server
redis-server
Saturday, October 10, 2015
Ruby cannot load such file -- 2.2/pg_ext (LoadError) 에러 해결 방법
윈도우에 Ruby on Rails를 설치하여 빌드하여 실행 하려고 하는데(rails s) 다음과 같은 에러가 발생했다.
C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/pg-0.17.1-x64-mingw32/lib/pg.rb:10:in `require': cannot load such file -- 2.2/pg_ext (LoadError)
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/pg-0.17.1-x64-mingw32/lib/pg.rb:10:in `rescue in <top (required)>'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/pg-0.17.1-x64-mingw32/lib/pg.rb:3:in `<top (required)>'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:76:in `require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:76:in `block (2 levels) in require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:72:in `each'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:72:in `block in require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:61:in `each'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:61:in `require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler.rb:134:in `require'
from C:/git/amanda-api/config/application.rb:7:in `<top (required)>'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:79:in `require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:79:in `block in server'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:76:in `tap'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:76:in `server'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands.rb:17:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
이를 해결하는 방법은 다음과 같다.
gem instasll pg --pre
gem list pg
확인결과 최신버전은 0.18.3이다.
그럼 Gemfile, Gemfile.lock에서 pg의 버전을 0.18.3으로 변경하면 해결 된다.
C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/pg-0.17.1-x64-mingw32/lib/pg.rb:10:in `require': cannot load such file -- 2.2/pg_ext (LoadError)
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/pg-0.17.1-x64-mingw32/lib/pg.rb:10:in `rescue in <top (required)>'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/pg-0.17.1-x64-mingw32/lib/pg.rb:3:in `<top (required)>'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:76:in `require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:76:in `block (2 levels) in require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:72:in `each'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:72:in `block in require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:61:in `each'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/runtime.rb:61:in `require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler.rb:134:in `require'
from C:/git/amanda-api/config/application.rb:7:in `<top (required)>'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:79:in `require'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:79:in `block in server'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:76:in `tap'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:76:in `server'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/railties-4.1.13/lib/rails/commands.rb:17:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
이를 해결하는 방법은 다음과 같다.
gem instasll pg --pre
gem list pg
확인결과 최신버전은 0.18.3이다.
그럼 Gemfile, Gemfile.lock에서 pg의 버전을 0.18.3으로 변경하면 해결 된다.
Monday, October 5, 2015
Android keyboard show/hide event
public static void hideKeyBoard(View v) {
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getApplicationWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
public static void showKeyBoard(View v) {
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(INPUT_METHOD_SERVICE);
imm.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT);
}
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseFragmentActivity.this);
if(heightDiff <= contentViewTop){
onHideKeyboard();
Intent intent = new Intent("KeyboardWillHide");
broadcastManager.sendBroadcast(intent);
} else {
int keyboardHeight = heightDiff - contentViewTop;
onShowKeyboard(keyboardHeight);
Intent intent = new Intent("KeyboardWillShow");
intent.putExtra("KeyboardHeight", keyboardHeight);
broadcastManager.sendBroadcast(intent);
}
}
};
private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;
protected void onShowKeyboard(int keyboardHeight) {}
protected void onHideKeyboard() {}
protected void attachKeyboardListeners(View contentView) {
if (keyboardListenersAttached) {
return;
}
rootLayout = (ViewGroup)contentView;
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
keyboardListenersAttached = true;
}
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getApplicationWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
public static void showKeyBoard(View v) {
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(INPUT_METHOD_SERVICE);
imm.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT);
}
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseFragmentActivity.this);
if(heightDiff <= contentViewTop){
onHideKeyboard();
Intent intent = new Intent("KeyboardWillHide");
broadcastManager.sendBroadcast(intent);
} else {
int keyboardHeight = heightDiff - contentViewTop;
onShowKeyboard(keyboardHeight);
Intent intent = new Intent("KeyboardWillShow");
intent.putExtra("KeyboardHeight", keyboardHeight);
broadcastManager.sendBroadcast(intent);
}
}
};
private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;
protected void onShowKeyboard(int keyboardHeight) {}
protected void onHideKeyboard() {}
protected void attachKeyboardListeners(View contentView) {
if (keyboardListenersAttached) {
return;
}
rootLayout = (ViewGroup)contentView;
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
keyboardListenersAttached = true;
}
RubyMine 7 스타트업 회사에서 싸게 구매하는 법(Startup discount)
https://www.jetbrains.com/shop/eform/startup
최근에 Ruby On Rails로 된 서버를 개발하다가 개발툴인 RubyMine을 구매하려고 하다가 보니 다음과 같은 공식 사이트에서 스타트업을 위한 할인 캠페인을 발견했다.
다음과 같이 3년이하, 10인이하 사업장에서 할인을 받을수 있다. 할인 폼에 정보를 입력하면 2-3일정도의 심사 과정을 거친후에 메일로 그 결과가 발송된다.
최근에 Ruby On Rails로 된 서버를 개발하다가 개발툴인 RubyMine을 구매하려고 하다가 보니 다음과 같은 공식 사이트에서 스타트업을 위한 할인 캠페인을 발견했다.
다음과 같이 3년이하, 10인이하 사업장에서 할인을 받을수 있다. 할인 폼에 정보를 입력하면 2-3일정도의 심사 과정을 거친후에 메일로 그 결과가 발송된다.
This discount is available to any company that meets all the following requirements:
- Is privately held,
- Has been in business for no more than 3 years,
- Has no more than 10 employees,
- Is engaged in development of a software-based product or service, and
- Is an established business with a website and/or existing public references on the Internet.
If your company meets these criteria, apply for your discount now.
After your application is approved, your startup discount will be available for one or more purchases for 12 months
Monday, September 28, 2015
Android Studio logcat 색깔 변경
이클립스와 달리 Android Studio는 디버깅시 logcat의 색깔이 하이라이팅 지정되지 않아서 여간 불편한게 아니었다.
하지만 다음과 같이 설정하면 logcat의 색깔을 지정할 수 있게 된다. 이클립스와 똑같은 환경을 만들자.
하지만 다음과 같이 설정하면 logcat의 색깔을 지정할 수 있게 된다. 이클립스와 똑같은 환경을 만들자.
Tuesday, September 22, 2015
MySQL 초기 root 패스워드 설정하기
mysql> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> update user set password=password('password') where user='root';
Query OK, 4 rows affected (0.07 sec)
Rows matched: 4 Changed: 4 Warnings: 0
mysql> flush privileges;
Query OK, 0 rows affected (0.21 sec)
mysql> select host, user, password from user;
+----------------------+------------------+-------------------------------------------+
| host | user | password |
+----------------------+------------------+-------------------------------------------+
| localhost | root | *78D22DFB85972182BE20A883D8B5021FD8B5FCCA |
| yegam400.vps.phps.kr | root | *78D22DFB85972182BE20A883D8B5021FD8B5FCCA |
| 127.0.0.1 | root | *78D22DFB85972182BE20A883D8B5021FD8B5FCCA |
| ::1 | root | *78D22DFB85972182BE20A883D8B5021FD8B5FCCA |
| localhost | debian-sys-maint | *E54187CB3AF30C421C5063781FEE02F787E6842F |
+----------------------+------------------+-------------------------------------------+
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> update user set password=password('password') where user='root';
Query OK, 4 rows affected (0.07 sec)
Rows matched: 4 Changed: 4 Warnings: 0
mysql> flush privileges;
Query OK, 0 rows affected (0.21 sec)
mysql> select host, user, password from user;
+----------------------+------------------+-------------------------------------------+
| host | user | password |
+----------------------+------------------+-------------------------------------------+
| localhost | root | *78D22DFB85972182BE20A883D8B5021FD8B5FCCA |
| yegam400.vps.phps.kr | root | *78D22DFB85972182BE20A883D8B5021FD8B5FCCA |
| 127.0.0.1 | root | *78D22DFB85972182BE20A883D8B5021FD8B5FCCA |
| ::1 | root | *78D22DFB85972182BE20A883D8B5021FD8B5FCCA |
| localhost | debian-sys-maint | *E54187CB3AF30C421C5063781FEE02F787E6842F |
+----------------------+------------------+-------------------------------------------+
Wednesday, September 16, 2015
Android Studio auto import 설정하기
Eclipse에서는 Cmd+Shift+M을 하면 auto import가 되었는데 Android Studio에서는 찾기가 쉽지 않았다. 그런데 드디어 찾아내서 방법을 공유한다.
Preferences - Editor - Auto Import에 가서 가운데에 있는 3가지 항목을 전부 checked한다.
Preferences - Editor - Auto Import에 가서 가운데에 있는 3가지 항목을 전부 checked한다.
Wednesday, September 9, 2015
iOS Failed to locate or generate maching signing assets
https://developer.apple.com/library/ios/recipes/xcode_help-accounts_preferences/articles/import_signing_assets.html#//apple_ref/doc/uid/TP40013306-CH9-SW1
Sunday, September 6, 2015
Android Studio ViewPager PageTabStrip titles are initialy not shown problem
Eclipse에서 Android Studio로 마이그레이션 했는데, ViewPager에서 PageTabStrip의 타이틀이 초기로딩시에 렌더링이 되지 않았다. ViewPager가 다른 페이지로 한번 페이징 된 후에는 PageTabStrip의 타이틀이 정상적으로 나왔다.
이 문제에 대해서 하루종일 고민하다가 결론을 얻었다. Android Studio에서는 appcompat v4라이브러리를 직접적으로 링크하면 안되고, 대신에 design library를 링크해야한다는 것이다.
dependencies {
...
compile 'com.android.support:design:22.2.0'
...
}
이 문제에 대해서 하루종일 고민하다가 결론을 얻었다. Android Studio에서는 appcompat v4라이브러리를 직접적으로 링크하면 안되고, 대신에 design library를 링크해야한다는 것이다.
dependencies {
...
compile 'com.android.support:design:22.2.0'
...
}
Thursday, September 3, 2015
iOS 개발시 스토리보드 제거하기
iOS 프로젝트에서 스토리보드를 제거하는 방법을 알아보자. 스토리보드가 있으면, 대형 프로젝트에서 여러사람이 협업시에 불편한점이 있고 수동으로 뷰를 컨트롤 하는게 명확하므로 제거하는 것이 좋다.
1. main.plist에서 "Main storyboard base file name"을 삭제해준다.
2. 기존 뷰 컨트롤러를 삭제해 준다. 예제에서는 ViewController.h/m이다.
3. 뷰 컨트롤러를 새로 생성한다. 생성하는 방법은 New file - iOS - Source - Cocoa Touch Class이다.
그리고 반드시 XIB를 생성하는 옵션을 선택하여야 한다.
4. AppDelegate.m에 다음과 같은 코드를 추가 한다.
1. main.plist에서 "Main storyboard base file name"을 삭제해준다.
2. 기존 뷰 컨트롤러를 삭제해 준다. 예제에서는 ViewController.h/m이다.
3. 뷰 컨트롤러를 새로 생성한다. 생성하는 방법은 New file - iOS - Source - Cocoa Touch Class이다.
그리고 반드시 XIB를 생성하는 옵션을 선택하여야 한다.
4. AppDelegate.m에 다음과 같은 코드를 추가 한다.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 스토리보드 없이 사용하는 방법
self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
ViewController *viewController = [[ViewController alloc]initWithNibName:@"ViewController" bundle:nil];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
return YES;
}
iOS ARC 프로젝트에서 non-ARC 소스 컴파일하기(-fno-objc-arc)
iOS 프로그램을 개발하다보면 ARC(Auto Reference Counting) 프로젝트에서 non-ARC 소스를 컴파일 해야 할 때가 있다.
그럴때는 다음과 같이 Build Phases에서 해당하는 .m(ObjC) 파일의 옵션에다가 '-fno-objc-arc'를 하게 되면 해당 .m 파일만 non-ARC 방식으로 컴파일할 수 있다.
최신 프로젝트에 구형 SDK를 연동할때 많이 쓰는 방식이다. 나는 이걸 몰라서 C#에 Native C++을 연동할때 Managed C++ 프로젝트를 만드는 것처럼 프로젝트를 ARC/non-ARC 분리를 했는데 그렇게 하면 더 많은 문제가 생긴다. iOS에서는 이 방식을 추천한다.
그럴때는 다음과 같이 Build Phases에서 해당하는 .m(ObjC) 파일의 옵션에다가 '-fno-objc-arc'를 하게 되면 해당 .m 파일만 non-ARC 방식으로 컴파일할 수 있다.
최신 프로젝트에 구형 SDK를 연동할때 많이 쓰는 방식이다. 나는 이걸 몰라서 C#에 Native C++을 연동할때 Managed C++ 프로젝트를 만드는 것처럼 프로젝트를 ARC/non-ARC 분리를 했는데 그렇게 하면 더 많은 문제가 생긴다. iOS에서는 이 방식을 추천한다.
Wednesday, September 2, 2015
HTML5 아이폰 채팅 말풍선
<style>
body {
font-family: "Helvetica Neue";
font-size: 20px;
font-weight: normal;
}
section {
max-width: 450px;
margin: 50px auto;
}
section div {
max-width: 255px;
word-wrap: break-word;
margin-bottom: 20px;
line-height: 24px;
}
.clear {
clear: both;
}
.from-me {
position: relative;
padding: 10px 20px;
color: white;
background: #0B93F6;
border-radius: 25px;
float: right;
}
.from-me:before {
content: "";
position: absolute;
z-index: -1;
bottom: -2px;
right: -7px;
height: 20px;
border-right: 20px solid #0B93F6;
border-bottom-left-radius: 16px 14px;
-webkit-transform: translate(0, -2px);
}
.from-me:after {
content: "";
position: absolute;
z-index: 1;
bottom: -2px;
right: -56px;
width: 26px;
height: 20px;
background: white;
border-bottom-left-radius: 10px;
-webkit-transform: translate(-30px, -2px);
}
.from-them {
position: relative;
padding: 10px 20px;
background: #E5E5EA;
border-radius: 25px;
color: black;
float: left;
}
.from-them:before {
content: "";
position: absolute;
z-index: 2;
bottom: -2px;
left: -7px;
height: 20px;
border-left: 20px solid #E5E5EA;
border-bottom-right-radius: 16px 14px;
-webkit-transform: translate(0, -2px);
}
.from-them:after {
content: "";
position: absolute;
z-index: 3;
bottom: -2px;
left: 4px;
width: 26px;
height: 20px;
background: white;
border-bottom-right-radius: 10px;
-webkit-transform: translate(-30px, -2px);
}
</style>
<section>
<div class="from-me">
<p>Hey there! What's up?</p>
</div>
<div class="clear"></div>
<div class="from-them">
<p>Checking out iOS7 you know..</p>
</div>
<div class="clear"></div>
<div class="from-me">
<p>Check out this bubble!</p>
</div>
<div class="clear"></div>
<div class="from-them">
<p>It's pretty cool!</p>
</div>
<div class="clear"></div>
<div class="from-me">
<p>Yeah it's pure CSS & HTML</p>
</div>
<div class="clear"></div>
<div class="from-them">
<p>Wow that's impressive. But what's even more impressive is that this bubble is really high.</p>
</div>
</section>
Friday, August 28, 2015
Android SQLite 예제
package com.example.xxx;
import java.util.ArrayList;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class BookmarkDB extends SQLiteOpenHelper {
private final static String TAG="BookmarkDB";
public BookmarkDB(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("CREATE TABLE bookmark (id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT, page INTEGER);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
public void querySQL(String sql) {
Log.d(TAG,"querySQL sql="+sql);
SQLiteDatabase db = getWritableDatabase();
db.execSQL(sql);
db.close();
}
ArrayList<Integer> getPages(String path) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT page FROM bookmark WHERE path='"
+ path + "'", null);
ArrayList<Integer> pageList = new ArrayList<Integer>();
while (cursor.moveToNext()) {
int page = cursor.getInt(0);
Log.d(TAG, "getPages page="+page);
pageList.add(page);
}
db.close();
return pageList;
}
}
import java.util.ArrayList;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class BookmarkDB extends SQLiteOpenHelper {
private final static String TAG="BookmarkDB";
public BookmarkDB(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("CREATE TABLE bookmark (id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT, page INTEGER);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
public void querySQL(String sql) {
Log.d(TAG,"querySQL sql="+sql);
SQLiteDatabase db = getWritableDatabase();
db.execSQL(sql);
db.close();
}
ArrayList<Integer> getPages(String path) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT page FROM bookmark WHERE path='"
+ path + "'", null);
ArrayList<Integer> pageList = new ArrayList<Integer>();
while (cursor.moveToNext()) {
int page = cursor.getInt(0);
Log.d(TAG, "getPages page="+page);
pageList.add(page);
}
db.close();
return pageList;
}
}
Tuesday, August 25, 2015
Android 키보드 입력시 액티비티 윈도우 리사이즈 방지하기
안드로이드에서 키보드 입력시 액티비티의 전체윈도우가 리사이즈 되는 경우가 있다.
그럴 경우 다음과 같으 AndroidManifest.xml에서 다음과 같이 정의하면 키보드가 나오더라도 리사이즈 되지 않는다.
<application
...
android:windowSoftInputMode="adjustPan" >
그럴 경우 다음과 같으 AndroidManifest.xml에서 다음과 같이 정의하면 키보드가 나오더라도 리사이즈 되지 않는다.
<application
...
android:windowSoftInputMode="adjustPan" >
Monday, August 24, 2015
Android ViewPager + PagerTabStrip 예제
안드로이드에서 메인화면에 탭을 구현하는 방법은 여러가지가 있다.
첫번째는 ActionBar에 NAVIGATION_TABS로 모드를 설정하는 방법이고,
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
두번째는 컨텐츠뷰에 PageTabStrip이라는 UI를 사용하는 방법인데, 이 방법은 무조건 ViewPager가 있어야 한다.
일단 이전글의 ViewPager 예제를 완성해놓고, 여기다가 조금만 추가하면 PageTabStrip을 만들수 있다.
http://duongame.blogspot.kr/2015/08/android-viewpager.html
activity_main.xml
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<android.support.v4.view.PagerTabStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:textColor="#000000" /> </android.support.v4.view.ViewPager>
private class PagerAdapter extends FragmentStatePagerAdapter {
// Tab Titles
private String tabtitles[] = new String[] { "Tab1", "Tab2", "Tab3", "Tab4", "Tab5" };
...
// PageTabStrip
@Override
public CharSequence getPageTitle(int position) {
return tabtitles[position];
}
}
첫번째는 ActionBar에 NAVIGATION_TABS로 모드를 설정하는 방법이고,
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
두번째는 컨텐츠뷰에 PageTabStrip이라는 UI를 사용하는 방법인데, 이 방법은 무조건 ViewPager가 있어야 한다.
일단 이전글의 ViewPager 예제를 완성해놓고, 여기다가 조금만 추가하면 PageTabStrip을 만들수 있다.
http://duongame.blogspot.kr/2015/08/android-viewpager.html
activity_main.xml
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<android.support.v4.view.PagerTabStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:textColor="#000000" /> </android.support.v4.view.ViewPager>
private class PagerAdapter extends FragmentStatePagerAdapter {
// Tab Titles
private String tabtitles[] = new String[] { "Tab1", "Tab2", "Tab3", "Tab4", "Tab5" };
...
// PageTabStrip
@Override
public CharSequence getPageTitle(int position) {
return tabtitles[position];
}
}
Android ViewPager 예제 (FragmentStatePagerAdapter 사용)
Android에서 많이 사용하는 ViewPager의 예제이다. ViewPager를 사용하려면 필수적으로 FragmentActivity를 사용해야하는데, ActionBarActivity를 사용하고 있으면 무시하고 ActionBarActivity를 그대로 사용해도 된다.(FragmentActivity를 상속받고 있기 때문이다.)
public class MainActivity extends ActionBarActivity {
private ViewPager mViewPager;
private PagerAdapter mPagerAdapter;
private class PagerAdapter extends FragmentStatePagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
// 해당하는 page의 Fragment를 생성합니다.
return PageFragment.create(position);
}
@Override
public int getCount() {
return 5; // 총 5개의 page를 보여줍니다.
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mPagerAdapter);
}
@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;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
public class PageFragment extends Fragment {
private int mPageNumber;
public static PageFragment create(int pageNumber) {
PageFragment fragment = new PageFragment();
Bundle args = new Bundle();
args.putInt("page", pageNumber);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt("page");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_page, container, false);
((TextView) rootView.findViewById(R.id.number)).setText(mPageNumber + "");
return rootView;
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.viewpagerexample.MainActivity" >
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</android.support.v4.view.ViewPager>
</RelativeLayout>
fragment_page.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
</RelativeLayout>
https://github.com/duongame/ViewPagerExample1
public class MainActivity extends ActionBarActivity {
private ViewPager mViewPager;
private PagerAdapter mPagerAdapter;
private class PagerAdapter extends FragmentStatePagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
// 해당하는 page의 Fragment를 생성합니다.
return PageFragment.create(position);
}
@Override
public int getCount() {
return 5; // 총 5개의 page를 보여줍니다.
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mPagerAdapter);
}
@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;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
public class PageFragment extends Fragment {
private int mPageNumber;
public static PageFragment create(int pageNumber) {
PageFragment fragment = new PageFragment();
Bundle args = new Bundle();
args.putInt("page", pageNumber);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt("page");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_page, container, false);
((TextView) rootView.findViewById(R.id.number)).setText(mPageNumber + "");
return rootView;
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.viewpagerexample.MainActivity" >
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</android.support.v4.view.ViewPager>
</RelativeLayout>
fragment_page.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
</RelativeLayout>
https://github.com/duongame/ViewPagerExample1
Subscribe to:
Posts (Atom)