WebStorm : express 프로젝트 생성
Node.js 8.x 설치 설치
출처
Node.js 8.x 설치
$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - $ sudo apt-get install -y nodejs
Node.js 버전 확인
$ node --version v8.15.0
Express 설치
$ sudo npm install -g express + express@4.16.4 added 48 packages from 36 contributors in 16.445s
Express 애플리케이션 생성기 설치
$ sudo npm install express-generator -g /usr/bin/express -> /usr/lib/node_modules/express-generator/bin/express-cli.js + express-generator@4.16.0 added 10 packages from 13 contributors in 5.69s
Express 애플리케이션 만들기
$ express myapp
warning: the default view engine will not be jade in future releases
warning: use `--view=jade' or `--help' for additional options
create : myapp/
create : myapp/public/
create : myapp/public/javascripts/
create : myapp/public/images/
create : myapp/public/stylesheets/
create : myapp/public/stylesheets/style.css
create : myapp/routes/
create : myapp/routes/index.js
create : myapp/routes/users.js
create : myapp/views/
create : myapp/views/error.jade
create : myapp/views/index.jade
create : myapp/views/layout.jade
create : myapp/app.js
create : myapp/package.json
create : myapp/bin/
create : myapp/bin/www
change directory:
$ cd myapp
install dependencies:
$ npm install
run the app:
$ DEBUG=myapp:* npm start
Express 애플리케이션 초기화
$ cd myapp $ sudo npm install audited 194 packages in 9.077s found 2 low severity vulnerabilities run `npm audit fix` to fix them, or `npm audit` for details
Express 애플리케이션 실행
$ DEBUG=myapp:* npm start > myapp@0.0.0 start /home/pi/workspace/myapp > node ./bin/www myapp:server Listening on port 3000 +0ms GET / 200 2337.812 ms - 170 GET /stylesheets/style.css 200 28.972 ms - 111 GET /favicon.ico 404 165.796 ms - 1062
브라우져로 확인
안드로이드 NDK (Native Development Kit) r16b 설치
출처
- NDK 10 - Android Rebuilds
- Google Developers Korea Blog: Google Developers Korea Blog: 안드로이드 NDK (Native Development Kit) r14 를 소개합니다.
Python 설치
$ sudo apt install python
Android NDK(C/C++) 다운로드, 압축 해제
$ wget https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip $ sudo mv android-ndk-r16b-linux-x86_64.zip /opt/ $ cd /opt/ $ sudo unzip android-ndk-r16b-linux-x86_64.zip
NDK 경로 path에 추가
$ echo 'export ANDROID_NDK_ROOT=/opt/android-ndk-r16b' | tee -a ~/.bashrc $ echo 'export PATH=$PATH:$ANDROID_NDK_ROOT' | tee -a ~/.bashrc
.profile-ndk 파일 작성
$ vi ~/.profile_ndk
.profile-ndk 내용
export ANDROID_NDK_ROOT=/opt/android-ndk-r16b export NDK_TOOLCHAIN_VERSION=4.7 export TOOLCHAIN=/opt/android-14-toolchain export PATH=$TOOLCHAIN/bin:$PATH
.profile-ndk 실행 (NDK 빌드시 사용)
$ chmod a+x ~/.profile_ndk $ source ~/.profile_ndk
toolchain 설치
$ sudo $ANDROID_NDK_ROOT/build/tools/make-standalone-toolchain.sh \ --verbose \ --toolchain=arm-linux-androideabi-4.7 \ --install-dir=$TOOLCHAIN \ --platform=android-14 HOST_OS=linux HOST_EXE= HOST_ARCH=x86_64 HOST_TAG=linux-x86_64 HOST_NUM_CPUS=8 BUILD_NUM_CPUS=16 Auto-config: --arch=arm ## COMMAND: python /opt/android-ndk-r16b/build/tools/make_standalone_toolchain.py --arch arm --api 14 --stl gnustl --install-dir=/opt/android-14-toolchain Toolchain installed to /opt/android-14-toolchain.
toolchain 환경설정
$ echo 'export TOOLCHAIN=/opt/android-14-toolchain' | tee -a ~/.bashrc $ echo 'export PATH=$TOOLCHAIN/bin:$PATH' | tee -a ~/.bashrc $ echo 'export CC=arm-linux-androideabi-gcc' | tee -a ~/.bashrc
예제
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY)
hello-jni.c
#include <string.h>
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a/NEON (hard-float)"
#else
#define ABI "armeabi-v7a/NEON"
#endif
#else
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a (hard-float)"
#else
#define ABI "armeabi-v7a"
#endif
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__x86_64__)
#define ABI "x86_64"
#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "arm64-v8a"
#else
#define ABI "unknown"
#endif
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");
}
컴파일
$ ndk-build NDK_PROJECT_PATH=`pwd` APP_BUILD_SCRIPT=Android.mk Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-14. [arm64-v8a] Compile : hello-jni <= hello-jni.c [arm64-v8a] SharedLibrary : libhello-jni.so [arm64-v8a] Install : libhello-jni.so => libs/arm64-v8a/libhello-jni.so [armeabi-v7a] Compile thumb : hello-jni <= hello-jni.c [armeabi-v7a] SharedLibrary : libhello-jni.so [armeabi-v7a] Install : libhello-jni.so => libs/armeabi-v7a/libhello-jni.so [x86] Compile : hello-jni <= hello-jni.c [x86] SharedLibrary : libhello-jni.so [x86] Install : libhello-jni.so => libs/x86/libhello-jni.so [x86_64] Compile : hello-jni <= hello-jni.c [x86_64] SharedLibrary : libhello-jni.so [x86_64] Install : libhello-jni.so => libs/x86_64/libhello-jni.so
Java 사용 예제
package com.example.hellojni;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class HelloJni extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_jni);
TextView tv = (TextView)findViewById(R.id.hello_textview);
tv.setText( stringFromJNI() );
}
public native String stringFromJNI();
public native String unimplementedStringFromJNI();
static {
System.loadLibrary("hello-jni");
}
}sqlite
출처
DBManager.java
package com.bluexmas.common;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBManager extends SQLiteOpenHelper {
private static String TAG = DBManager.class.getName();
// Database 버전
public static final int DB_VERSION = 12;
// Database 이름 (SQLite 파일명)
public static final String DB_NAME = "bluexmas.db";
// DBManager 객체 - 한번만 객체가 생성되도록 static 으로 변수 선언 : singleton 패턴
private static DBManager instance = null;
private Context context;
// private 생성자 : singleton 패턴
private DBManager(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.context = context;
}
// DBManager 객체 반환 : singleton 패턴
public static DBManager getInstance(Context context) {
if (instance == null)
instance = new DBManager(context);
return instance;
}
public Context getContext() {
return this.context;
}
@Override
public void onCreate(SQLiteDatabase db) {
// 테이블 생성 관련 SQL 스크립트 실행
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// DB_VERSION 버전이 올라 가면 변경될 SQL 스크립트 실행
}
}
VideoEntity.java
package com.bluexmas.common;
public class VideoEntity {
public static final String TABLE_NAME = "video_info";
public static final String COLUMN_NAME_MEDIA_ID = "v_id";
public static final String COLUMN_NAME_TYPECODE = "type_code";
public static final String COLUMN_NAME_FILEPATH = "path";
public static final String COLUMN_NAME_REGDATE = "regdate";
public String getTableName() {
return TABLE_NAME;
}
public String genCreateSQL() {
return "CREATE TABLE "+this.getTableName()+"( "
+ COLUMN_NAME_MEDIA_ID+" INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_NAME_TYPECODE+" CHAR(2) NOT NULL, "
+ COLUMN_NAME_FILEPATH+" TEXT NOT NULL, "
+ COLUMN_NAME_REGDATE+" TEXT NOT NULL "
+ ");";
}
}
DBManager.java
package com.bluexmas.common;
public class DBManager extends SQLiteOpenHelper {
//
private static String TAG = DBManager.class.getName();
// Database 버전
public static final int DB_VERSION = 12;
// Database 이름 (SQLite 파일명)
public static final String DB_NAME = "bluexmas.db";
// DBManager 객체 - 한번만 객체가 생성되도록 static 으로 변수 선언 : singleton 패턴
private static DBManager instance = null;
private Context context;
private VideoEntity videoEntity = null;
// private 생성자 : singleton 패턴
private DBManager(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.context = context;
// VideoEntity 생체 생성
this.videoEntity = new VideoEntity();
}
// DBManager 객체 반환 : singleton 패턴
public static DBManager getInstance(Context context) {
if (instance == null)
instance = new DBManager(context);
return instance;
}
public Context getContext() {
return context;
}
@Override
public void onCreate(SQLiteDatabase db) {
// 테이블 생성 관련 SQL 스크립트 실행
// VideoEntity 테이블 생성
db.execSQL(this.videoEntity.genCreateSQL());
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// DB_VERSION 버전이 올라 가면 변경될 SQL 스크립트 실행
}
// 비디오 정보 추가 (레코드 추가)
public int addVideo(String type_code, String path) {
if (path == null)
return;
// DB 객체 가져오기
SQLiteDatabase db = this.getWritableDatabase();
//
ContentValues values = new ContentValues();
values.put(this.videoEntity.COLUMN_NAME_TYPECODE, type_code);
values.put(this.videoEntity.COLUMN_NAME_FILEPATH, path);
values.put(this.videoEntity.COLUMN_NAME_REGDATE, "-");
// 데이터 삽입
long newRowId = db.insert(this.videoEntity.getTableName(), null, values);
return getLastID(this.videoEntity.getTableName());
}
/**
* 마지막 추가한 ID 구하기
* @param table_name 테이블 이름
* @return ID
*/
public int getLastID(String table_name) {
// DB 객체 가져오기
SQLiteDatabase db = this.getWritableDatabase();
final String query = "SELECT last_insert_rowid() FROM "+ table_name;
Cursor cur = db.rawQuery(query, null);
cur.moveToFirst();
int last_id = cur.getInt(0);
cur.close();
return last_id;
}
}
-
안드로이드 부팅 이벤트 받기, 백그라운드 실행 유지, 서비스 강제 종료 후 다시 서비스 실행
출처
- [Android]단말 부팅 시 App 실행하기(BOOT_COMPLETED) - kanzler의 세상 이야기
- Android에서 Service 로 Background 에서 음악 플레이 하기 : Android Developer88
- Create services never stop in Android – Razia Sandhu – Medium
- [Android] 사용자가 강제로 서비스를 종료하더라도 자동으로 서비스를 다시 시작하는 방법은 무엇입니까? Service Alarm | CODE Q&A 해결 된 문제 [한국어]
- android - clear Recent apps wipe the apps memory and my receiver stopped working - Stack Overflow
- broadcastreceiver - Trying to start a service on boot on Android - Stack Overflow
- Android 8.0: java.lang.IllegalStateException: Not allowed to start service Intent - Stack Overflow
- 장범석님의 개발일지: [안드로이드] Task의 종료시점 확인하기
안드로이드 부팅 이벤트 받기
BootReceiver,java - 부팅 이벤트를 받은 BroadcastReceiver를 상속받은 클래스 생성
package com.bluexmas.common;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
public class BootReceiver extends BroadcastReceiver {
private final static String TAG = BootReceiver.class.getSimpleName();
// BroadcastReceiver를 상속하여 처리 해줍니다.
@Override
public void onReceive(final Context context, Intent intent) {
// TODO Auto-generated method stub
// 전달 받은 Broadcast의 값을 가져오기
// androidmanifest.xml에 정의한 인텐트 필터를 받아 올 수 있습니다.
String action = intent.getAction();
// 전달된 값이 '부팅완료' 인 경우에만 동작 하도록 조건문을 설정 해줍니다.
if (action.equals("android.intent.action.BOOT_COMPLETED")) {
// TODO
// 부팅 이후 처리해야 코드 작성
// Ex.서비스 호출, 특정 액티비티 호출등등
Log.d(TAG, "action = " + action);
//
new Handler().postDelayed(new Runnable() {
// 3초 후에 실행
@Override public void run() {
//Toast.makeText(context, "-- BootReceiver.onReceive", Toast.LENGTH_LONG).show();
// BackgroundService
Intent serviceLauncher = new Intent(context, BackgroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceLauncher);
} else {
context.startService(serviceLauncher);
}
}
}, 3000);
}
}
public static boolean isServiceRunning(Context context, Class serviceClass) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i (TAG,"ServiceRunning? = "+true);
return true;
}
}
Log.i(TAG,"ServiceRunning? = "+ false);
return false;
}
}
AndroidManifest.xml 내용추가
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bluexmas.main" >
<!-- 안드로이드 부팅 이벤트 받기 권한 추가 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name="com.bluexmas.main.BluexmasApplication"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:largeHeap="true">
<!-- 안드로이드 부팅 이벤트 받기 : BroadcastReceiver -->
<receiver
android:name="com.bluexmas.common.BootReceiver"
android:enabled="true"
android:exported="false"
android:label="BOOTReceiver">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
앱이 종료 이후 백그라운드 서비스 실행 유지
서비스 생성
package com.bluexmas.common;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
public class BackgroundService extends Service {
private final static String TAG = BackgroundService.class.getSimpleName();
private Context context = null;
public int counter=0;
// 생성자1 : 반듯이 필요
public BackgroundService() {
}
// 생성자2
public BackgroundService(Context applicationContext) {
super();
context = applicationContext;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
// 서비스에서 가장 먼저 호출됨(최초에 한번만)
Log.d(TAG, "BackgroundService.onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// 서비스가 호출될 때마다 실행
Log.d(TAG, "BackgroundService.onStartCommand");
//
startTimer();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
// 서비스가 종료될 때 실행
Log.d(TAG, "BackgroundService.onDestroy");
//
Intent broadcastIntent = new Intent("com.bluexmas.common.RestartService");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.i(TAG, "in timer ++++ "+ (counter++));
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
}
AndroidManifest.xml 내용추가 - 서비스와 종료 이벤트를 받은 receiver 등록
<!-- BackgroundService -->
<service
android:name="com.bluexmas.common.BackgroundService"
android:enabled="true" >
</service>
<receiver
android:name="com.bluexmas.common.RestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped">
<intent-filter>
<action android:name="com.bluexmas.common.RestartService" />
</intent-filter>
</receiver>
</application>
종료 이벤트를 받은 receiver 클래스 생성
package com.bluexmas.common;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class RestarterBroadcastReceiver extends BroadcastReceiver {
private final static String TAG = RestarterBroadcastReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "RestarterBroadcastReceiver.onReceive");
context.startService(new Intent(context, BackgroundService.class));
}
}
MainActivity - 서비스가 실행하고 있지 않는 경우 서비스 실행
// BackgroundService
private Intent mBackgroundServiceIntent;
private BackgroundService mBackgroundService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ... 생략 ...
// BackgroundService
mBackgroundService = new BackgroundService(getApplicationContext());
mBackgroundServiceIntent = new Intent(getApplicationContext(), mBackgroundService.getClass());
// 서비스가 실행 중인지 확인
if (!BootReceiver.isServiceRunning(this, mBackgroundService.getClass())) {
// 서비스가 실행하고 있지 않는 경우 서비스 실행
startService(mBackgroundServiceIntent);
}
}
사용자가 강제로 서비스를 종료하더라도 자동으로 서비스를 다시 시작하기
AndroidManifest.xml 내용추가 - android:stopWithTask="false"로 설정해야 onTaskRemoved 메소드가 호출됨
<!-- BackgroundService -->
<service
android:name="com.bluexmas.common.BackgroundService"
android:enabled="true"
android:stopWithTask="false">
</service>
기존 BackgroundService 클래스에 onTaskRemoved 오버라이드 메소드 구현
// 사용자가 강제로 서비스를 종료하더라도 자동으로 서비스를 다시 시작하는 방법은 무엇입니까?
@Override
public void onTaskRemoved(Intent rootIntent) {
Log.d(TAG, "BackgroundService.onTaskRemoved");
//create an intent that you want to start again.
Intent intent = new Intent(getApplicationContext(), BackgroundService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime() + 5000, pendingIntent);
super.onTaskRemoved(rootIntent);
}Swift4 tip
출처
- Convert Dictionary to JSON in Swift - Stack Overflow
- 때로는 까칠하게.. :: iOS 10에서 Privacy 설정 - 까칠코더 - 티스토리
- file io - CFBundleDocumentTypes & UIFileSharingEnabled issues - Stack Overflow
- Swift 화면 전환 기법: 뷰 컨트롤러, 내비게이션 컨트롤러, 세그웨이 : 네이버 블로그
- Swift 3.0에서 소스코드상에서 화면 전환 방법 - Swifter {Swift Developer} - iOS 앱 개발 프리랜서 (3가지 방법 정리)
- GitHub - parthibanios/custom-Video-Recording-Swift-3.0: Record video using AVCapture AVFoundation framework
- Swift로 파일 다루기 :
Eth Developer's Lab
- 오토 레이아웃으로 iOS 앱 쉽게 개발하기 - Realm Academy
- [iOS / Swift4] Alamofire 사용하기 | ry4nkim
- 상상 너머 그 무언가... :: iPhone App 개발할때 iTunes에서 파일공유 가능하게 하는방법 ( UIFileSharingEnabled )
- Segue를 통한 뷰 컨트롤러 전환과 데이터 교환 방법 · Wireframe
- ios - How to parse JSON response from Alamofire API in Swift? - Stack Overflow
- swift3 - upload image to server using Alamofire - Stack Overflow
Dictionary to JSON
출처 : Convert Dictionary to JSON in Swift - Stack Overflow
let jsonData = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions.prettyPrinted)
let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String
print(jsonString)
화면전환, 파라미터 전달
출처 : Swift 화면 전환 기법: 뷰 컨트롤러, 내비게이션 컨트롤러, 세그웨이 : 네이버 블로그
func native_video_recording(_ data : NSDictionary) {
let vcRecord = self.storyboard!.instantiateViewController(withIdentifier: "vcRecord") as! RecordController // 1
vcRecord.setParamData(data) // 파라미터 전달
vcRecord.modalTransitionStyle = UIModalTransitionStyle.crossDissolve // 2
self.present(vcRecord, animated: true) // 3
}
카메라 리소스 구하기
//MARK:- Setup Camera
func setupSession() -> Bool {
//captureSession.sessionPreset = AVCaptureSessionPresetHigh
//1080
captureSession.sessionPreset = AVCaptureSession.Preset.hd1920x1080
// Setup Camera
let camera = AVCaptureDevice.default(for: .video)
do {
let input = try AVCaptureDeviceInput(device: camera!)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
activeInput = input
}
} catch {
print("Error setting device video input: \(error)")
return false
}
// Setup Microphone
let microphone = AVCaptureDevice.default(for: .audio)
do {
let micInput = try AVCaptureDeviceInput(device: microphone!)
if captureSession.canAddInput(micInput) {
captureSession.addInput(micInput)//addInput(micInput)
}
} catch {
print("Error setting device audio input: \(error)")
return false
}
movieOutput.maxRecordedDuration = CMTimeMake(9, 1)
// Movie output
if captureSession.canAddOutput(movieOutput) {
captureSession.addOutput(movieOutput)
}
return true
}
도큐먼트 디렉토리 구하기 - UIFileSharingEnabled 설정의 Root 경로
Eth Developer's Lab
//EDIT 1: I FORGOT THIS AT FIRST
func getMp4PathURL() -> URL? {
/*
let directory = NSTemporaryDirectory() as NSString
if directory != "" {
let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
return URL(fileURLWithPath: path)
}
return nil
*/
// 도큐먼트 디렉토리 구하기 - UIFileSharingEnabled의 Root 경로
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
let fileName = NSUUID().uuidString + ".mp4"
let fileURL = documentsDirectory.appendingPathComponent(fileName)
/*
print(imageURL)
do {
let imageData = try Data(contentsOf: imageURL)
return UIImage(data: imageData)
} catch let err as NSError {
print("이미지 로딩 에러 : \(err)")
}
*/
return fileURL
}
Swift4 : Alamofire 프로젝트에 추가하기
출처 : [iOS / Swift4] Alamofire 사용하기 | ry4nkim
Podfile 파일 편집
# Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'project' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! project 'project.xcodeproj' # Pods for project pod 'Firebase/Core' pod 'Firebase/Messaging' pod 'Alamofire', '~> 4.7' end
pod install
$ pod install Analyzing dependencies Downloading dependencies Installing Alamofire (4.7.3) Installing Firebase 5.4.0 Installing FirebaseAnalytics (5.0.1) Using FirebaseCore (5.0.5) Using FirebaseInstanceID (3.1.1) Using FirebaseMessaging (3.0.3) Using GoogleToolboxForMac (2.1.4) Using Protobuf (3.6.0) Installing nanopb (0.3.8) Generating Pods project Integrating client project Sending stats Pod installation complete! There are 3 dependencies from the Podfile and 9 total pods installed. [!] Automatically assigning platform `ios` with version `9.3` on target `project` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
Alamofire 파일 업로드, 결과 JSON 파싱
출력 : swift3 - upload image to server using Alamofire - Stack Overflow, ios - How to parse JSON response from Alamofire API in Swift? - Stack Overflow
Alamofire.upload(multipartFormData: { (multipartFormData) in
/*
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
*/
multipartFormData.append("\(jsonString)".data(using: String.Encoding.utf8)!, withName: "json_str")
/*
if let data = imageData{
multipartFormData.append(data, withName: "image", fileName: "image.png", mimeType: "image/png")
}
*/
multipartFormData.append(mp4FileUrl, withName: "test.mp4")
}, usingThreshold: UInt64.init(), to: self.upload_url, method: .post, headers: headers) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
print("Succesfully uploaded")
if let err = response.error {
print(err)
onCompletion?(nil, err)
return
}
if let result = response.result.value {
let JSON = result as! NSDictionary
print(JSON)
onCompletion?(JSON, nil)
}
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
onCompletion?(nil, error)
}
}
Timer, String format
출처 : ios - Swift - Update/Refresh Label that Displays Time - Stack Overflow
let movieOutput = AVCaptureMovieFileOutput()
var durationTimer: Timer?
//MARK:- Setup Camera
func setupSession() -> Bool {
//captureSession.sessionPreset = AVCaptureSessionPresetHigh
//1080
captureSession.sessionPreset = AVCaptureSession.Preset.hd1920x1080
// Setup Camera
// 생략
// Setup Microphone
// 생략
// 녹화 시간 제한
movieOutput.maxRecordedDuration = CMTimeMake(300, 1)
return true
}
@IBAction func startRecording() {
if movieOutput.isRecording == false {
// ... 생략 ...
self.seconds = 0
self.durationTimer = Timer(timeInterval: 1.0, target: self, selector: #selector(self.refreshDurationLabel), userInfo: nil, repeats: true)
RunLoop.current.add(self.durationTimer!, forMode: RunLoopMode.commonModes)
self.durationTimer?.fire()
} else {
self.durationTimer?.invalidate()
self.durationTimer = nil
self.seconds = 0
//self.durationTxt.text = secondsToFormatTimeFull(second: 0)
stopRecording()
}
}
func secondsToFormatTimeFull(second: Int)->String {
let sec : Int = second % 60
let min : Int = second / 60
return String(format : "%02d:%02d", min, sec)
}
@objc func refreshDurationLabel() {
self.durationTxt.text = secondsToFormatTimeFull(second: self.seconds)
seconds = seconds + 1
}
연락처 조회, 선택, WebView 자바스크립트 호출(evaluateJavaScript)
출처 : ContactsUISample/ViewController.swift at master · koogawa/ContactsUISample · GitHub
[SWIFT] 주소록에 저장된 데이터 불러오기 - Things take time - 티스토리
import Contacts
import ContactsUI
class WebViewBase : UIViewController, CNContactPickerDelegate {
func native_show_contacts() {
let pickerViewController = CNContactPickerViewController()
pickerViewController.delegate = self
// Display only a person's phone, email, and postal address
let displayedItems = [CNContactPhoneNumbersKey, CNContactEmailAddressesKey, CNContactPostalAddressesKey]
pickerViewController.displayedPropertyKeys = displayedItems
// Show the picker
self.present(pickerViewController, animated: true, completion: nil)
}
// MARK: CNContactPickerDelegate methods
// Called when a property of the contact has been selected by the user.
func contactPicker(picker: CNContactPickerViewController, didSelectContactProperty contactProperty: CNContactProperty) {
//
}
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
// You can fetch selected name and number in the following way
// user name
let userName:String = contact.givenName
let name = contact.familyName + contact.givenName
//print(name)
var primaryPhoneNumberStr:String = ""
if (!contact.phoneNumbers.isEmpty) {
// user phone number
let userPhoneNumbers:[CNLabeledValue<cnphonenumber>] = contact.phoneNumbers
let firstPhoneNumber:CNPhoneNumber = userPhoneNumbers[0].value
// user phone number string
primaryPhoneNumberStr = firstPhoneNumber.stringValue
}
//print("phonenum = \(primaryPhoneNumberStr)")
webView.evaluateJavaScript("setContract('\(name)', '\(primaryPhoneNumberStr)')")
}
// Called when the user taps Cancel.
func contactPickerDidCancel(picker: CNContactPickerViewController) {
//
}
}jni
출처
- JNI 헤더 만들기 :: CheatSheet
- How to pass C structs back and forth to Java code in JNI? - Stack Overflow
- JNI Types and Data Structures - Oracle Docs
- double 데이터형 값 주고 받기 | JNI Tutorial - skyfe79
- JNI(Java Native Interface) 이용 방법 (2/6) - 3.1~3.2 - Daum 블로그
- 강좌와 팁 - JNI programming - (2) 기본 변수형 다루기 - 임베디드 리눅스 시스템 포럼 embedded linux system
- Hwp 수식을 latex로(hml Equation Parser) – Wonno's Note – Daily Study Note
- GitHub - johnhuang-cn/jpserve: Calling Python from JAVA
py_pwm.h
typedef struct
{
//PyObject_HEAD
unsigned int gpio;
float freq;
float dutycycle;
} PWMObject;
int PWM_init(PWMObject *self, int channel, float frequency);
void PWM_start(PWMObject *self, float dutycycle);
int PWM_ChangeDutyCycle(PWMObject *self, float dutycycle);
PWMObject.java
package com.pi4j.wiringpi;
public class PWMObject {
public int gpio;
public float freq;
public float dutycycle;
public PWMObject() {
}
public int getGpio() {
return gpio;
}
public void setGpio(int gpio) {
this.gpio = gpio;
}
public float getFreq() {
return freq;
}
public void setFreq(float freq) {
this.freq = freq;
}
public float getDutycycle() {
return dutycycle;
}
public void setDutycycle(float dutycycle) {
this.dutycycle = dutycycle;
}
}
com_pi4j_wiringpi_ASUSGpio.h
#include <jni.h>
/* Header for class com_pi4j_wiringpi_ASUSGpio */
#ifndef _Included_com_pi4j_wiringpi_ASUSGpio
#define _Included_com_pi4j_wiringpi_ASUSGpio
#ifdef __cplusplus
extern "C" {
#endif
#undef com_pi4j_wiringpi_ASUSGpio_ASUS
#define com_pi4j_wiringpi_ASUSGpio_ASUS 13L
#undef com_pi4j_wiringpi_ASUSGpio_OUTPUT
#define com_pi4j_wiringpi_ASUSGpio_OUTPUT 1L
JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pySetmode
(JNIEnv *, jclass, jint);
JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pySetupChannel
(JNIEnv *, jclass, jint, jint);
JNIEXPORT jobject JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmInit
(JNIEnv *, jclass, jint, jfloat);
JNIEXPORT void JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmStart
(JNIEnv *, jclass, jobject, jfloat);
JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmChangeDutyCycle
(JNIEnv *, jclass, jobject, jfloat);
#ifdef __cplusplus
}
#endif
#endif
com_pi4j_wiringpi_ASUSGpio.c
#include <stdio.h>
#include <jni.h>
#include <wiringPi.h>
#include <RKIO.h>
#include <wiringTB.h>
#include <constants.h>
#include <common.h>
#include <c_gpio.h>
#include <py_gpio.h>
#include <py_pwm.h>
#include "com_pi4j_wiringpi_ASUSGpio.h"
void setPwmObject(JNIEnv *env, jclass targetClass, jobject newObject, PWMObject pwm) {
jfieldID fid;
// JniObject 객체의 intField 필드값 설정
fid = (*env)->GetFieldID(env, targetClass, "gpio", "I");
(*env)->SetIntField(env, newObject, fid, pwm.gpio);
// JniObject 객체의 intField 필드값 설정
fid = (*env)->GetFieldID(env, targetClass, "freq", "F");
(*env)->SetFloatField(env, newObject, fid, pwm.freq);
// JniObject 객체의 intField 필드값 설정
fid = (*env)->GetFieldID(env, targetClass, "dutycycle", "F");
(*env)->SetFloatField(env, newObject, fid, pwm.dutycycle);
}
void getPwmObject(JNIEnv *env, jclass targetClass, jobject newObject, PWMObject *pwm) {
jfieldID fid;
// JniObject 객체의 intField 필드값 설정
fid = (*env)->GetFieldID(env, targetClass, "gpio", "I");
pwm->gpio = (*env)->GetIntField(env, newObject, fid);
// JniObject 객체의 intField 필드값 설정
fid = (*env)->GetFieldID(env, targetClass, "freq", "F");
pwm->freq = (*env)->GetFloatField(env, newObject, fid);
// JniObject 객체의 intField 필드값 설정
fid = (*env)->GetFieldID(env, targetClass, "dutycycle", "F");
pwm->dutycycle = (*env)->GetFloatField(env, newObject, fid);
}
JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pySetmode
(JNIEnv *env, jclass obj, jint new_mode)
{
return py_setmode(new_mode);
}
JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pySetupChannel
(JNIEnv *env, jclass obj, jint channel, jint direction)
{
return py_setup_channel(channel, direction);
}
JNIEXPORT jobject JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmInit
(JNIEnv *env, jclass obj, jint channel, jfloat frequency)
{
//pwm = GPIO.PWM(myservo,50) # 50hz yani 20mslik periyod
PWMObject pwm;
PWM_init(&pwm, channel, frequency);
//printf("pwm.gpio = %d\n", pwm.gpio);
//
jclass targetClass = (*env)->FindClass(env, "com/pi4j/wiringpi/PWMObject");
// 생성자 찾기
jmethodID mid = (*env)->GetMethodID(env, targetClass, "<init>", "()V");
// 객체 생성(객체 레퍼런스 반환)
jobject newObject = (*env)->NewObject(env, targetClass, mid, "()V");
setPwmObject(env, targetClass, newObject, pwm);
return newObject;
}
JNIEXPORT void JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmStart
(JNIEnv *env, jclass obj, jobject thiz, jfloat dutycycle)
{
PWMObject pwm;
//
jclass targetClass = (*env)->FindClass(env, "com/pi4j/wiringpi/PWMObject");
getPwmObject(env, targetClass, thiz, &pwm);
PWM_start(&pwm, dutycycle);
setPwmObject(env, targetClass, thiz, pwm);
}
JNIEXPORT jint JNICALL Java_com_pi4j_wiringpi_ASUSGpio_pwmChangeDutyCycle
(JNIEnv *env, jclass obj, jobject thiz, jfloat dutycycle)
{
PWMObject pwm;
//
jclass targetClass = (*env)->FindClass(env, "com/pi4j/wiringpi/PWMObject");
getPwmObject(env, targetClass, thiz, &pwm);
int result = PWM_ChangeDutyCycle(&pwm, dutycycle);
setPwmObject(env, targetClass, thiz, pwm);
return result;
}
ASUSGpio.java
package com.pi4j.wiringpi;
import com.pi4j.util.NativeLibraryLoader;
public class ASUSGpio {
// private constructor
private ASUSGpio() {
// forbid object construction
}
public static final int ASUS = 13;
public static final int OUTPUT = 1;
static {
// Load the platform library
NativeLibraryLoader.load("libpi4j.so");
}
public static native int pySetmode(int new_mode);
public static native int pySetupChannel(int channel, int direction);
public static native PWMObject pwmInit(int channel, float frequency);
public static native void pwmStart(PWMObject self, float dutycycle);
public static native int pwmChangeDutyCycle(PWMObject self, float dutycycle);
}
빌드
$ export SimulatedPlatform="TinkerBoard GPIO Provider" $ mvn clean install
-----------------
hml-equation-parser 패키지를 설치
$ sudo apt-get install pandoc $ sudo pip3 install typing $ sudo pip3 install hml_equation_parser
jpserve 패키지를 설치
$ sudo pip install jpserve
jython 설치
$ java -jar jython-installer-2.7.0.jar
Font awesome 가운데 정렬
출처
css 파일 참조
<link rel="stylesheet" href="<c:url value="/commons/fontawesome/css/all.css"/>">
css
.fa {
vertical-align: middle;
}
html
<i class="fa fa-plus-circle fa-2x" aria-hidden="true" style="margin-left: 5px;"></i> <i class="fa fa-minus-circle fa-2x" aria-hidden="true"></i>
Programming
- WebStorm : express 프로젝트 생성 2019.02.16
- Node.js 8.x 설치 설치 2019.01.26 1
- 안드로이드 NDK (Native Development Kit) r16b 설치 2019.01.26
- sqlite 2018.09.29
- 안드로이드 부팅 이벤트 받기, 백그라운드 실행 유지, 서비스 강제 종료 후 다시 서비스 실행 2018.09.24 1
- Swift4 tip 2018.08.27
- jni 2018.08.25
- Font awesome 가운데 정렬 2018.08.13