RockyLinux 9 에서 root 원격접속 허용하기

리눅스/OS 일반|2025. 3. 14. 09:29
반응형

일반계정이 아닌 root 로 바로 접근을 허용하려는 경우 아래와 같은 SSH 설정 옵션으로 셋팅 후 데몬을 재시작 해 줍니다.


# vi /etc/ssh/sshd_config

...
PermitRootLogin yes
PubkeyAuthentication yes
PasswordAuthentication yes
...

 

# systemctl restart sshd

 

반응형

댓글()

Ubuntu 24.04 APT 저장소 및 키 초기화 하기

리눅스/OS 일반|2025. 2. 19. 10:31
반응형

모든 추가 저장소 삭제
# rm -f /etc/apt/sources.list.d/*.list

모든 GPG 키 삭제
# rm -f /etc/apt/keyrings/*.gpg

기본 APT 저장소 복구 (Ubuntu 24.04 Noble 기준)
# tee /etc/apt/sources.list <<EOF
deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu noble-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu noble-security main restricted universe multiverse
EOF

APT 캐시 및 설정 초기화
# apt clean
# apt autoclean
# apt -y autoremove --purge
r# m -rf /var/lib/apt/lists/*

저장소 업데이트 및 확인
# apt -y update && apt -y upgrade

 

반응형

댓글()

RockyLinux 9 기본 방화벽 변경하기 (firewalld -> iptables)

리눅스/Security|2025. 1. 10. 14:35
반응형

Rocky Linux 9는 기본적으로 firewalld를 사용하지만, iptables를 설치하고 사용할 수도 있습니다.

아래는 iptables 설치 및 활성화 방법입니다.

 

1. firewalld 비활성화

우선 사용하지 않을 firewalld 를 비활성화 합니다.

# systemctl disable --now firewalld

 

2. iptables 설치

iptables 패키지를 설치합니다.

# dnf -y install iptables iptables-services

 

3. iptables 룰 작성

방화벽을 가동하기 전에 룰셋을 정합니다.

# vi /etc/sysconfig/iptables

*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type any -j ACCEPT

-A INPUT -s 192.168.10.2/32 -p tcp --dport 22 -j ACCEPT
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

COMMIT

 

 

4. iptables 방화벽 가동 

# systemctl enable --now iptables

 

5. iptables 상태 확인

# systemctl status iptables

# iptables -nL

 

반응형

댓글()

Nginx 에서 Proxy 구성시 Arguments 사용하기

리눅스/APACHE|2024. 12. 12. 12:35
반응형

Nginx 를 프록시 서버로 만들건데, Backend 서버를 내가 원하는 IP 로 접속하는 예제 입니다.

Client 에서는 Backend 서버에 직접 접속이 안되기 때문에, 프록시 서버를 이용하는 것입니다.

 

- 접속예 : http://www.sysdocu.kr/?url=192.168.10.2

- 192.168.10.2 사설 IP 를 가진 서버에 www.sysdocu.kr  이라는 프록시 서버를 이용해 접근합니다. (통신, 트래픽 흐름)

 

nginx.conf 파일에서 서버 구성내용을 아래와 같이 수정합니다.

아래 예시에서는 upstream 없이 구성하였습니다.

url 값은 arg_url 로 받아야 합니다.

 

# vi /etcc/nginx/nginx.conf

...

    ###################################
    server {
        listen 80;
        server_name www.sysdocu.kr;

        # 404 에러 페이지 (선택, 404 출력 파일의경로와 파일명 입력)

        location = /custom_404.html {

            root /etc/nginx;

            internal;

        }

 

        location / {
            set $backend "http://$arg_url";
            proxy_pass $backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Connection "";
        }
    }
    ###################################

...

 

# systemctl restart nginx

 

반응형

댓글()

안드로이드 java 에서 웹페이지 출력 내용을 String 에 넣기

프로그래밍/Android (Java)|2024. 11. 30. 22:39
반응형

(onCreate 안에서)

 

        // 웹페이지의 내용을 가져오기
        new Thread(() -> {
            // AtomicReference 사용
            AtomicReference<String> webContent = new AtomicReference<>("");

            // fetchWebPage 결과를 AtomicReference에 저장
            webContent.set(fetchWebPage("https://sysdocu.tistory.com/list.html"));

            runOnUiThread(() -> {
                // UI 스레드에서 Toast 실행
                Toast.makeText(MainActivity.this, webContent.get(), Toast.LENGTH_SHORT).show();
            });
        }).start();

 

 

(onCreate 밖에 같은 레벨에서)


    // 웹페이지 json 코드 가져오기
    private String fetchWebPage(String urlString) {
        String result = "";
        OkHttpClient client = new OkHttpClient();
        // 요청 객체 생성
        Request request = new Request.Builder()
                .url(urlString)
                .build();
        try {
            // 요청 실행 및 응답 받기
            Response response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                result = response.body().string(); // 응답 내용을 String으로 변환
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

반응형

댓글()

[Arduino-IDE] Seeed Studio XIAO ESP32S3 Sense 기본 예제

프로그래밍/C, C++|2024. 11. 1. 18:48
반응형

Seeed Studio XIAO ESP32S3 Sense는 카메라(OV2640)와 디지털 마이크가 내장된 ESP32-S3 기반 개발 보드로, Wi-Fi를 통해 영상 스트리밍이 가능합니다.

 

 

1. Arduino IDE 기본 세팅

 

Arduino IDE 실행 > 파일 > 기본 설정 > (중간) Language 에 한국어 선택 후 확인.

 

Arduino IDE 실행 > 파일 > 기본 설정 > (맨아래) 추가 보드 관리자 URL 에 아래 내용입력 후 확인.

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

 

Arduino IDE 실행 > 도구 > 보드 > 보드매니저 실행해서 아래 두개 설치

- Arduino ESP32 Boards

- esp32

 

Arduino IDE 실행 > 도구 > 보드 > esp32 > ESP32S3 Dev Module Octal (WROOM2) 선택

 

USB 에 기기 연결 후, 상단 셀렉트 옵션에서 기기 선택해 놓습니다.

 

 

2. 시리얼 포트 접근 권한 추가

 

리눅스 시스템에서 시리얼 포트에 대한 접근 권한을 주어야 합니다.

다음과 같이 사용자를 dialout 그룹에 추가합니다.

명령을 실행한 후 시스템에서 로그아웃했다가 다시 로그인해야 변경사항이 적용됩니다. (생략 가능)

$ sudo usermod -a -G dialout $USER

 

시리얼 포트에 직접 권한 부여하는 방법도 있습니다.

$ sudo chmod 666 /dev/ttyACM0

하지만 이 방법은 임시적인 해결책으로, 재부팅 후에는 다시 권한을 설정해야 합니다.

 

 

3. 예제 입력 및 컴파일

 

Arduino IDE에는 여러가지 예제가 준비되어 있습니다.

ESP32S3 카메라를 올바르게 사용하기 위해 기본 제공되는 예제를 사용해 봅니다.

 

Arduino IDE 실행 > 파일 > 예제 > ESP32 > Camera > CameraWebServer 선택

 

열려진 예제 코드에서 WiFi 설정만 수정합니다.
const char* ssid = "CDH";                   // Wi-Fi SSID
const char* password = "12345678";  // Wi-Fi 비밀번호

보드 선택이 "ESP32S3 Dev Module" 또는 "ESP32S3 Sense"로 설정되어 있는지 확인합니다.
업로드를 실행 합니다.

 

아래와 같은 결과가 출력되면 성공한 것입니다.

 

[출력]

스케치는 프로그램 저장 공간 1000446 바이트(76%)를 사용. 최대 1310720 바이트.
전역 변수는 동적 메모리 62908바이트(19%)를 사용, 264772바이트의 지역변수가 남음.  최대는 327680 바이트.
esptool.py v4.8.1
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 (QFN56) (revision v0.2)
Features: WiFi, BLE, Embedded PSRAM 8MB (AP_3v3)
Crystal is 40MHz
MAC: d8:3b:da:45:d2:7c
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Flash will be erased from 0x00000000 to 0x00004fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x0000e000 to 0x0000ffff...
Flash will be erased from 0x00010000 to 0x00104fff...
Compressed 18976 bytes to 12282...
Writing at 0x00000000... (100 %)
Wrote 18976 bytes (12282 compressed) at 0x00000000 in 0.3 seconds (effective 499.2 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 134...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (134 compressed) at 0x00008000 in 0.0 seconds (effective 568.3 kbit/s)...
Hash of data verified.
Compressed 8192 bytes to 47...
Writing at 0x0000e000... (100 %)
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 756.9 kbit/s)...
Hash of data verified.
Compressed 1000592 bytes to 653189...
Writing at 0x00010000... (2 %)
Writing at 0x0001c4b8... (5 %)
Writing at 0x000253f6... (7 %)
Writing at 0x0002926d... (10 %)
Writing at 0x00030be0... (12 %)
Writing at 0x0003b4c8... (15 %)
Writing at 0x000449a9... (17 %)
Writing at 0x0004a26a... (20 %)
Writing at 0x0004f8be... (22 %)
Writing at 0x0005531c... (25 %)
Writing at 0x0005aabf... (27 %)
Writing at 0x0005fd0b... (30 %)
Writing at 0x00065104... (32 %)
Writing at 0x0006a55c... (35 %)
Writing at 0x0006fc7e... (37 %)
Writing at 0x00075228... (40 %)
Writing at 0x0007a3d1... (42 %)
Writing at 0x0007f636... (45 %)
Writing at 0x00084d7b... (47 %)
Writing at 0x0008aafd... (50 %)
Writing at 0x0009111e... (52 %)
Writing at 0x0009695f... (55 %)
Writing at 0x0009be10... (57 %)
Writing at 0x000a13d3... (60 %)
Writing at 0x000a6810... (62 %)
Writing at 0x000ab6c9... (65 %)
Writing at 0x000b0853... (67 %)
Writing at 0x000b5c6b... (70 %)
Writing at 0x000baf83... (72 %)
Writing at 0x000bffbe... (75 %)
Writing at 0x000c5188... (77 %)
Writing at 0x000cac39... (80 %)
Writing at 0x000d07d9... (82 %)
Writing at 0x000d5cc7... (85 %)
Writing at 0x000e0b33... (87 %)
Writing at 0x000e64ab... (90 %)
Writing at 0x000eb3f0... (92 %)
Writing at 0x000f35f3... (95 %)
Writing at 0x000f92d7... (97 %)
Writing at 0x000fee95... (100 %)
Wrote 1000592 bytes (653189 compressed) at 0x00010000 in 6.9 seconds (effective 1154.1 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting with RTC WDT...

 

[ 에러조치 ]

/dev/ttyACM0 가 보였다 안보였다 하는 증상은 Arduino IDE 에서 포트가 보였다 안보였다 하는 증상과 같습니다.

이는 업로드를 이미 했거나, 하는중 오류가 발생하였거나 기타 등등의 문제로 인해 생기는데,

해결책은 아래와 같습니다.

 

데이터 케이블과 ESP32S3 Sense (기기) 와 분리합니다.

C-Type 머리가 보이게, 그리고 위로 올린 방향으로 두면 C-Type 오른쪽으로 보이는 버튼이 BOOT 버튼입니다.

BOOT 버튼을 누른채 데이터 케이블을 연결하면 불이 깜빡이며, 멈추었을때 손을 떼면 초기화 됩니다.

초기화 된 상태에서 다시 케이블을 분리하면 원래대로 돌아갑니다.

그러므로 연결된 상태 그대로 소스코드를 수정하고 업로드 해야 합니다.

 

※ BootLoader 모드란?
- 공장 초기화가 아닌, 새로운 펌웨어를 강제로 업로드할 수 있도록 하는 모드 

- 기존의 프로그램이 손상되었거나 업로드가 실패할 때, 이 모드를 통해 새로운 펌웨어를 강제로 설치 가능 

- 일반적인 운영 중에는 사용되지 않으며, 포트 인식 오류나 업로드 문제 해결 시 사용

 

반응형

댓글()

Ubuntu 24.04 에서 Arduino IDE 2.3.5 설치하기

리눅스/OS 일반|2024. 11. 1. 18:48
반응형

다운로드 및 실행은 일반 계정으로 해야 합니다.


공식 사이트 또는 깃허브에서 압축파일을 다운로드 합니다.

- https://github.com/arduino/arduino-ide/releases

 

$ wget https://downloads.arduino.cc/arduino-ide/arduino-ide_2.3.5_Linux_64bit.zip
$ unzip arduino-ide_2.3.5_Linux_64bit.zip 

$ cd arduino-ide_2.3.5_Linux_64bit

$ ./arduino-ide

 

반응형

댓글()

Notification 알림으로 인한 Activity 중복 호출 방지

반응형

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Activity 중복 생성 방지
        if (!isTaskRoot) {
            finish()
            return
        }

}

 

반응형

댓글()

[Android Kotlin] 포그라운드 서비스 (Foreground Service)

반응형

[출처] https://iamjm29.tistory.com/13

 

자세한 설명은 출처를 확인하도록 합니다.

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
http://schemas.android.com/apk/res/android"</linearlayout xmlns:android="
    xmlns:app="
http://schemas.android.com/apk/res-auto"
    xmlns:tools="
http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context=".MainActivity">

    <Button
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:text="Start Service"
        android:id="@+id/btn_start"/>

    <Button
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:text="Stop Service"
        android:id="@+id/btn_stop"/>

</LinearLayout>

 

 

AndroidManifest.xml

아래 전체를 복사하지 말고, 색칠한 곳만 참고하도록 합니다.

생성된 패키지명이 다르면 테마 이름도 다르기 때문입니다.

<?xml version="1.0" encoding="utf-8"?>
http://schemas.android.com/apk/res/android"</manifest xmlns:android="
    xmlns:tools="
http://schemas.android.com/tools">

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

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Test"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true" />

    </application>

</manifest>

 

 

MainActivity.kt

package kr.sysdocu.test

import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    //Button
    var btn_start: Button? = null
    var btn_stop: Button? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btn_start = findViewById(R.id.btn_start)
        btn_stop = findViewById(R.id.btn_stop)

        btn_start!!.setOnClickListener(View.OnClickListener {
            val serviceIntent = Intent(this@MainActivity, MyService::class.java)
            startService(serviceIntent)
            Toast.makeText(this@MainActivity, "Service start", Toast.LENGTH_SHORT).show()
        })

        btn_stop!!.setOnClickListener(View.OnClickListener {
            val serviceIntent = Intent(this@MainActivity, MyService::class.java)
            stopService(serviceIntent)
            Toast.makeText(this@MainActivity, "Service stop", Toast.LENGTH_SHORT).show()
        })
    }
}

 


MyService.kt

package kr.sysdocu.test

import android.app.*
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat

class MyService : Service() {
    private var mThread: Thread? = null

    companion object {
        private const val TAG = "MyService"
        private const val NOTI_ID = 1
    }

    private fun createNotification() {
        val builder = NotificationCompat.Builder(this, "default")
        builder.setSmallIcon(R.mipmap.ic_launcher)
        builder.setContentTitle("Foreground Service")
        builder.setContentText("포그라운드 서비스")
        builder.color = Color.RED

        val notificationIntent = Intent(this, MainActivity::class.java)
        notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
        val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
        builder.setContentIntent(pendingIntent) // 알림 클릭 시 이동

        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationManager.createNotificationChannel(
                NotificationChannel(
                    "default",
                    "기본 채널",
                    NotificationManager.IMPORTANCE_DEFAULT
                )
            )
        }
        val notification = builder.build()
        startForeground(NOTI_ID, notification)
    }

    override fun onCreate() {
        super.onCreate()
        createNotification()
        mThread = object : Thread("My Thread") {
            override fun run() {
                super.run()
                for (i in 0..99) {
                    Log.d(TAG, "count : $i")
                    try {
                        sleep(1000)
                    } catch (e: InterruptedException) {
                        currentThread().interrupt()
                        break
                    }
                }
            }
        }
        mThread!!.start()
        Log.d(TAG, "onCreate")
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand")
        return START_NOT_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        if (mThread != null) {
            mThread!!.interrupt()
            mThread = null
        }
        Log.d(TAG, "onDestroy")
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

 

 

 

 

반응형

댓글()

AndroidStudio 에서 JAVA 로 GPS 현재 위치 확인하기

프로그래밍/Android (Java)|2024. 11. 1. 18:47
반응형

[출처] https://mainia.tistory.com/1153

출처에서 activity_main.xml 파일 내용만 추가하여 테스트하였고 정상 동작 확인하였습니다.

 

 

1. AndroidManifest.xml

...
    </uses-permission android:name="android.permission.internet" >
    </uses-permission android:name="android.permission.access_fine_location" >
    </uses-permission android:name="android.permission.access_coarse_location">
...

 

 

2. MainActivity.java

package kr.sysdocu.position;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

/** GPS 샘플 */
public class MainActivity extends Activity {

    private Button btnShowLocation;
    private TextView txtLat;
    private TextView txtLon;
    private final int PERMISSIONS_ACCESS_FINE_LOCATION = 1000;
    private final int PERMISSIONS_ACCESS_COARSE_LOCATION = 1001;
    private boolean isAccessFineLocation = false;
    private boolean isAccessCoarseLocation = false;
    private boolean isPermission = false;

    // GPSTracker class
    private GpsInfo gps;

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

        btnShowLocation = (Button) findViewById(R.id.btn_start);
        txtLat = (TextView) findViewById(R.id.tv_latitude);
        txtLon = (TextView) findViewById(R.id.tv_longitude);

        // GPS 정보를 보여주기 위한 이벤트 클래스 등록
        btnShowLocation.setOnClickListener(new View.OnClickListener() {
            public void onClick(View arg0) {
                // 권한 요청을 해야 함
                if (!isPermission) {
                    callPermission();
                    return;
                }

                gps = new GpsInfo(MainActivity.this);
                // GPS 사용유무 가져오기
                if (gps.isGetLocation()) {

                    double latitude = gps.getLatitude();
                    double longitude = gps.getLongitude();

                    txtLat.setText(String.valueOf(latitude));
                    txtLon.setText(String.valueOf(longitude));

                    Toast.makeText(
                            getApplicationContext(),
                            "당신의 위치 - \n위도: " + latitude + "\n경도: " + longitude,
                            Toast.LENGTH_LONG).show();
                } else {
                    // GPS 를 사용할수 없으므로
                    gps.showSettingsAlert();
                }
            }
        });

        callPermission();  // 권한 요청을 해야 함
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                           int[] grantResults) {
        if (requestCode == PERMISSIONS_ACCESS_FINE_LOCATION
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            isAccessFineLocation = true;

        } else if (requestCode == PERMISSIONS_ACCESS_COARSE_LOCATION
                && grantResults[0] == PackageManager.PERMISSION_GRANTED){

            isAccessCoarseLocation = true;
        }

        if (isAccessFineLocation && isAccessCoarseLocation) {
            isPermission = true;
        }
    }

    // 전화번호 권한 요청
    private void callPermission() {
        // Check the SDK version and whether the permission is already granted or not.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            requestPermissions(
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_ACCESS_FINE_LOCATION);

        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED){

            requestPermissions(
                    new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                    PERMISSIONS_ACCESS_COARSE_LOCATION);
        } else {
            isPermission = true;
        }
    }
}

 

 

3. activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <!-- 시작 버튼 -->
    <Button
        android:id="@+id/btn_start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="위치 가져오기" />

    <!-- 위도 표시 -->
    <TextView
        android:id="@+id/tv_latitude"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="위도: "
        android:textSize="18sp"
        android:paddingTop="20dp" />

    <!-- 경도 표시 -->
    <TextView
        android:id="@+id/tv_longitude"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="경도: "
        android:textSize="18sp"
        android:paddingTop="10dp" />

</LinearLayout>

 

 

4. GpsInfo.java

package kr.sysdocu.test;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import androidx.core.content.ContextCompat;

public class GpsInfo extends Service implements LocationListener {

    private final Context mContext;

    // 현재 GPS 사용유무
    boolean isGPSEnabled = false;

    // 네트워크 사용유무
    boolean isNetworkEnabled = false;

    // GPS 상태값
    boolean isGetLocation = false;

    Location location;
    double lat; // 위도
    double lon; // 경도

    // 최소 GPS 정보 업데이트 거리 10미터
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10;

    // 최소 GPS 정보 업데이트 시간 밀리세컨이므로 1분
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1;

    protected LocationManager locationManager;

    public GpsInfo(Context context) {
        this.mContext = context;
        getLocation();
    }

    @TargetApi(23)
    public Location getLocation() {
        if ( Build.VERSION.SDK_INT >= 23 &&
                ContextCompat.checkSelfPermission(
                        mContext, android.Manifest.permission.ACCESS_FINE_LOCATION )
                        != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(
                        mContext, android.Manifest.permission.ACCESS_COARSE_LOCATION)
                        != PackageManager.PERMISSION_GRANTED) {

            return null;
        }

        try {
            locationManager = (LocationManager) mContext
                    .getSystemService(LOCATION_SERVICE);

            // GPS 정보 가져오기
            isGPSEnabled = locationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);

            // 현재 네트워크 상태 값 알아오기
            isNetworkEnabled = locationManager
                    .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnabled && !isNetworkEnabled) {
                // GPS 와 네트워크사용이 가능하지 않을때 소스 구현
            } else {
                this.isGetLocation = true;
                // 네트워크 정보로 부터 위치값 가져오기
                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(
                            LocationManager.NETWORK_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                    if (locationManager != null) {
                        location = locationManager
                                .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (location != null) {
                            // 위도 경도 저장
                            lat = location.getLatitude();
                            lon = location.getLongitude();
                        }
                    }
                }

                if (isGPSEnabled) {
                    if (location == null) {
                        locationManager.requestLocationUpdates(
                                LocationManager.GPS_PROVIDER,
                                MIN_TIME_BW_UPDATES,
                                MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                        if (locationManager != null) {
                            location = locationManager
                                    .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                lat = location.getLatitude();
                                lon = location.getLongitude();
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return location;
    }

    /**
     * GPS 종료
     * */
    public void stopUsingGPS(){
        if(locationManager != null){
            locationManager.removeUpdates(GpsInfo.this);
        }
    }

    /**
     * 위도값을 가져옵니다.
     * */
    public double getLatitude(){
        if(location != null){
            lat = location.getLatitude();
        }
        return lat;
    }

    /**
     * 경도값을 가져옵니다.
     * */
    public double getLongitude(){
        if(location != null){
            lon = location.getLongitude();
        }
        return lon;
    }

    /**
     * GPS 나 wife 정보가 켜져있는지 확인합니다.
     * */
    public boolean isGetLocation() {
        return this.isGetLocation;
    }

    /**
     * GPS 정보를 가져오지 못했을때
     * 설정값으로 갈지 물어보는 alert 창
     * */
    public void showSettingsAlert(){
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

        alertDialog.setTitle("GPS 사용유무셋팅");
        alertDialog.setMessage("GPS 셋팅이 되지 않았을수도 있습니다. \n 설정창으로 가시겠습니까?");

        // OK 를 누르게 되면 설정창으로 이동합니다.
        alertDialog.setPositiveButton("Settings",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog,int which) {
                        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        mContext.startActivity(intent);
                    }
                });
        // Cancle 하면 종료 합니다.
        alertDialog.setNegativeButton("Cancel",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });

        alertDialog.show();
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    public void onLocationChanged(Location location) {
        // TODO Auto-generated method stub

    }

    public void onStatusChanged(String provider, int status, Bundle extras) {
        // TODO Auto-generated method stub

    }

    public void onProviderEnabled(String provider) {
        // TODO Auto-generated method stub

    }

    public void onProviderDisabled(String provider) {
        // TODO Auto-generated method stub

    }
}

 

 

 

 

 

 

 

 

반응형

댓글()

ffmpeg 명령어로 포맷 변환 예제 (mkv to mp4, mp4 to mp3)

리눅스/OS 일반|2024. 11. 1. 18:47
반응형

ffmpeg 명령으로 할 수 있는것은 다양하지만, 필자의 필요에 의해서 아래 내용만 기록해 둡니다. 

 

 

1. mkv to mp4

 

# ffmpeg -i example.mkv -c:v copy -c:a copy example.mp4

 

입력 파일 : example.mkv

출력 파일 : example.mp4

 

위 옵션 사용시 비디오 및 오디오 스트림이 원본 품질로 유지됩니다.

이 방식은 매우 빠르고 효율적이며, 데이터 손실이 없습니다.

 

 

2. mp4 to mp3

 

영상에서 오디오만 추출이 가능합니다.

 

# ffmpeg -i input.mp4 -q:a 0 -map a output.mp3

 

-i input.mp4 : 입력 MP4 파일

-q:a 0 : 오디오 품질을 최상으로 설정 (-q:a 0 대신에 -b:a 128k 와 같이 비트레이트 지정 가능)

-map a : 오디오 스트림만 추출

output.mp3 : 출력 MP3 파일 이름

 

특정 시간대의 오디오만 추출하려면 -ss (시작 시간) 와 -t (지속 시간) 를 추가합니다.

 

# ffmpeg -ss 00:01:00 -i input.mp4 -to 00:00:30 -q:a 0 -map a output.mp3

 

-ss 00:01:00 : 추출 시작 시간 (1분 0초)

-t 00:00:30 : 추출 지속 시간 (0분 30초)

-q:a 0 : 오디오 품질을 최상으로 설정 (-q:a 0 대신에 -b:a 128k 와 같이 비트레이트 지정 가능)

빠른 처리를 위해 -ss 옵션을 입력 파일 앞에 배치 하였습니다.

  이 경우, ffmpeg는 해당 시간대부터 직접 처리하므로 더욱 빠릅니다.

- mp4 말고 오디오파일 mp3 에서 부분 추출도 가능합니다.

 

반응형

댓글()