728x90

출처

Node.js 8.x 설치

X
user@localhost:~

[user@localhost]$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
[user@localhost]$ sudo apt-get install -y nodejs

Node.js 버전 확인

X
user@localhost:~

[user@localhost]$ node --version
v8.15.0

Express 설치

X
user@localhost:~

[user@localhost]$ sudo npm install -g express
+ express@4.16.4
added 48 packages from 36 contributors in 16.445s

Express 애플리케이션 생성기 설치

X
user@localhost:~

[user@localhost]$ 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 애플리케이션 만들기

X
user@localhost:~

[user@localhost]$ 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 애플리케이션 초기화

X
user@localhost:~

[user@localhost]$ cd myapp
[user@localhost]$ 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 애플리케이션 실행

X
user@localhost:~

[user@localhost]$ 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

브라우져로 확인

728x90
728x90

출처

Python 설치

X
user@localhost:~

[user@localhost]$ sudo apt install python

Android NDK(C/C++) 다운로드, 압축 해제

X
user@localhost:~

[user@localhost]$ wget https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip
[user@localhost]$ sudo mv android-ndk-r16b-linux-x86_64.zip /opt/
[user@localhost]$ cd /opt/
[user@localhost]$ sudo unzip android-ndk-r16b-linux-x86_64.zip

NDK 경로 path에 추가

X
user@localhost:~

[user@localhost]$ echo 'export ANDROID_NDK_ROOT=/opt/android-ndk-r16b' | tee -a ~/.bashrc
[user@localhost]$ echo 'export PATH=$PATH:$ANDROID_NDK_ROOT' | tee -a ~/.bashrc

.profile-ndk 파일 작성

X
user@localhost:~

[user@localhost]$ 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 빌드시 사용)

X
user@localhost:~

[user@localhost]$ chmod a+x ~/.profile_ndk
[user@localhost]$ source ~/.profile_ndk

toolchain 설치

X
user@localhost:~

[user@localhost]$ 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 환경설정

X
user@localhost:~

[user@localhost]$ echo 'export TOOLCHAIN=/opt/android-14-toolchain' | tee -a ~/.bashrc
[user@localhost]$ echo 'export PATH=$TOOLCHAIN/bin:$PATH' | tee -a ~/.bashrc
[user@localhost]$ 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 ".");
}

컴파일

X
user@localhost:~

[user@localhost]$ 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");
    }
}
728x90
728x90

출처

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;
    }
}



-

728x90
728x90

출처

안드로이드 부팅 이벤트 받기

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);
    }
728x90
728x90

출처

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
    }

카메라 리소스 구하기

출처 : GitHub - parthibanios/custom-Video-Recording-Swift-3.0: Record video using AVCapture AVFoundation framework

    //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 경로

출처 : Swift로 파일 다루기 :

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

X
user@localhost:~

[user@localhost]$ 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) {
        //
    }
}
728x90
728x90

출처

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);
}

빌드

X
user@localhost:~

[user@localhost]$ export SimulatedPlatform="TinkerBoard GPIO Provider"
[user@localhost]$ mvn clean install

-----------------

hml-equation-parser 패키지를 설치

X
user@localhost:~

[user@localhost]$ sudo apt-get install pandoc
[user@localhost]$ sudo pip3 install typing
[user@localhost]$ sudo pip3 install hml_equation_parser

jpserve 패키지를 설치

X
user@localhost:~

[user@localhost]$ sudo pip install jpserve

jython 설치

X
user@localhost:~

[user@localhost]$ java -jar jython-installer-2.7.0.jar

728x90
728x90

출처

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>
728x90
728x90

출처

Maven 다운로드

X
user@localhost:~

[user@localhost]$ wget http://mirror.navercorp.com/apache/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz

Maven 압축 해제

X
user@localhost:~

[user@localhost]$ tar xvf apache-maven-3.5.4-bin.tar.gz

Maven /opt 디렉토리 밑으로 이동

X
user@localhost:~

[user@localhost]$ sudo mv apache-maven-3.5.4 /opt

Maven /opt/maven 링크 설정

X
user@localhost:~

[user@localhost]$ sudo ln -s /opt/apache-maven-3.5.4 /opt/maven

Maven 환경설정

X
user@localhost:~

[user@localhost]$ echo "export M2_HOME=/opt/maven" | sudo tee -a /etc/profile
[user@localhost]$ source /etc/profile
[user@localhost]$ echo "export PATH=$PATH:$M2_HOME/bin" | sudo tee -a /etc/profile
[user@localhost]$ source /etc/profile

Maven 설치확인

X
user@localhost:~

[user@localhost]$ mvn -version
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T02:33:14+08:00)
Maven home: /opt/maven
Java version: 1.8.0_171, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-8-openjdk-armhf/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.4.39-bpi-m2p-kernel", arch: "arm", family: "unix"

728x90

+ Recent posts