안드로이드 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를 통해 영상 스트리밍이 가능합니다.

아래에서는 HamoniKR 7 환경에서 작성하였습니다.

 

 

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 > 아래로 한참 내려서 XIAO_ESP32S3를 선택합니다.

 

USB 에 기기 연결 후, 상단 셀렉트 옵션에서 해당 보드를 선택합니다.

 

 

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

 

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

권한이 없을 경우, 아래와 같은 에러 메세지가 출력됩니다.

[Errno 13] could not open port /dev/ttyACM0: [Errno 13] Permission denied: '/dev/ttyACM0'

 

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

$ sudo usermod -a -G dialout $USER

 

시리얼 포트에 직접 권한을 부여 합니다.

$ sudo chmod 666 /dev/ttyACM0

 

두번째 명령은 임시적인 해결책으로, 재부팅 후에는 다시 권한을 설정해야 합니다.

 

 

3. 예제 입력 및 컴파일

 

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

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

 

Arduino IDE 실행 > 파일 > 예제 > ESP32 > Camera > CameraWebServer를 선택합니다.

 

열려진 예제 코드에서 아래 두군데를 수정합니다.

 

1) 카메라 모델 변경

기본 카메라 모델 define CAMERA_MODEL_ESP_EYE 을 주석 처리하고,

#define CAMERA_MODEL_XIAO_ESP32S3을 주석 해제 합니다. (사용 한다는 뜻)

 

2) WiFi 설정 변경

연결 가능한 Wifi의 SSID값과 패스워드를 정확히 입력합니다.
const char* ssid = "CDH";                   // Wi-Fi SSID
const char* password = "12345678";  // Wi-Fi 비밀번호

 

그리고 메뉴 : 파일 > 도구 > PSRAM: disabled 를 "OPI PSRAM"으로 변경합니다.


업로드를 실행 합니다.

 

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

 

[출력]

스케치는 프로그램 저장 공간 990114 바이트(29%)를 사용. 최대 3342336 바이트.
전역 변수는 동적 메모리 62996바이트(19%)를 사용, 264684바이트의 지역변수가 남음.  최대는 327680 바이트.
...

(생략)

...
riting at 0x000f8f09... (97 %)
Writing at 0x000fef8b... (100 %)
Wrote 990256 bytes (646409 compressed) at 0x00010000 in 6.7 seconds (effective 1174.6 kbit/s)...
Hash of data verified.

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

 

 

4. 실시간 영상 확인

 

Wifi 또는 핫스팟 기기에서 할당받은 ESP32S3 sense 웹서버IP 주소를 확인을 위해 '시리얼 모니터' 를 열면 되는데,

/dev/ttyACM0 에 연결할 수 없다고 할 것입니다.

(업로드 하면 자동으로 기기가 동작되어 업로드 상태가 해제됩니다)

 

그래서, 이상태 그대로 위 '2번' 항목의 명령을 다시 실행하면, 시리얼 모니터에 기기 IP 주소가 출력됩니다.

$ sudo usermod -a -G dialout $USER

$ sudo chmod 666 /dev/ttyACM0

 

그 주소로 접속하여 실시간 영상을 확인할 수 있습니다.

 

 

5. 참고 : 에러 모음

 

[ 에러 ]

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

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

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

 

[ 해결 ]

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

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

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

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

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

 

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

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

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

 

[ 에러 ]

업로드시 아래와 같은 에러메세지가 출력된다면, 현재 사용자에게 장치 접근 권한을 추가해야 합니다.

 

스케치는 프로그램 저장 공간 1000446 바이트(76%)를 사용. 최대 1310720 바이트. 전역 변수는 동적 메모리 62908바이트(19%)를 사용, 264772바이트의 지역변수가 남음. 최대는 327680 바이트. esptool.py v4.8.1 Serial port /dev/ttyACM0 A fatal error occurred: Could not open /dev/ttyACM0, the port is busy or doesn't exist. ([Errno 13] could not open port /dev/ttyACM0: [Errno 13] Permission denied: '/dev/ttyACM0') Hint: Try to add user into dialout or uucp group. Failed uploading: uploading error: exit status 2

 

[ 해결 ]

# sudo usermod -a -G dialout $USER

그런 다음 로그아웃, 로그인없이 적용하기 위해서 현재 터미널에서 새 그룹 권한을 바로 적용합니다.

# newgrp dialout

 

이제 android-ide 를 실행하면  /dev/ttyACM0 장치에 대한 접근권한이 생깁니다.

 

반응형

댓글()

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

    }
}

 

 

 

 

 

 

 

 

반응형

댓글()

VideoJS 로 웹페이지에서 동영상 플레이어 사용하기

반응형

소스코드는 굉장히 단순합니다.

아래 옵션중 vjs-big-play-centered 는 플레이 버튼을 중앙에 두며, 미설정시 좌측 상단에 버튼이 위치합니다.

스마트폰에서 재생 버튼이 느리게 동작되어 영상을 다운로드 후 재생하는 듯 보인다면 웹서버에 H.264 모듈을 설치하여 스트리밍이 가능합니다.

 

<head>
        <meta charset="UTF-8">
        <link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.10.2/video-js.min.css" rel="stylesheet" />

        <script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.10.2/video.min.js"></script>

</head>


<body>
<video id="my-video" 

class="video-js vjs-big-play-centered

controls 

preload="auto" 

width="320

height="180

data-setup='{
"autoplay": true
}'>
        <source src="The.mkv" type='video/mp4'>

        <track kind="subtitles" src="The.vtt" srclang="ko" label="Korean" default />

</video>

</body>

 

 

* 참고 (SRT -> VTT 자막 변경 명령어)

아래 3가지만 고치면 되지만, 번거로우므로 명령어를 사용합니다.

- 시간 정보에 , (콤마) 대신 . (점)

- 자막 순번 제거

- 맨 위에 WEBVTT 라고 명시

# ffmpeg -i The.srt The.vtt

반응형

댓글()

[C] 자신과 동일한 프로세스 이름이 가동중인지 확인하는 코드 (중복 실행 차단)

프로그래밍/C, C++|2024. 8. 27. 16:28
반응형

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>

int is_another_instance_running(const char *process_name, pid_t current_pid) {
    DIR *dir;
    struct dirent *entry;
    FILE *fp;
    char path[256], cmdline[256];

    dir = opendir("/proc");
    if (dir == NULL) {
        perror("opendir");
        return 0;
    }

    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_DIR && atoi(entry->d_name) > 0) {
            snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name);
            fp = fopen(path, "r");
            if (fp != NULL) {
                if (fgets(cmdline, sizeof(cmdline), fp) != NULL) {
                    // 프로세스 이름이 같고 현재 프로세스가 아닌 경우
                    if (strstr(cmdline, process_name) != NULL && atoi(entry->d_name) != current_pid) {
                        fclose(fp);
                        closedir(dir);
                        return 1;  // 다른 인스턴스가 실행 중임
                    }
                }
                fclose(fp);
            }
        }
    }

    closedir(dir);
    return 0;  // 다른 인스턴스가 실행 중이지 않음
}

int main(int argc, char *argv[]) {
    pid_t current_pid = getpid();  // 현재 프로세스의 PID 가져오기
    const char *process_name = argv[0];  // 자신의 실행 파일 이름

    // 실행 파일 이름에서 경로 제거 (예: ./myprogram -> myprogram)
    const char *base_name = strrchr(process_name, '/');
    if (base_name) {
        base_name++;  // '/' 이후의 문자열을 사용
    } else {
        base_name = process_name;  // 경로가 없는 경우 원래 이름 사용
    }

    if (is_another_instance_running(base_name, current_pid)) {
        printf("이미 가동 중입니다. 프로그램을 종료합니다.\n");
    } else {
        printf("미가동 상태입니다. 프로그램을 시작합니다.\n");
        // 여기에 프로그램의 메인 코드를 작성
        while (1) {
            sleep(10);  // 예시로 무한 대기
        }
    }

    return 0;
}

반응형

댓글()

[Shell Script] 텍스트 좌우 정렬, 가운데 공백 채우기

프로그래밍/BASH SHELL|2024. 7. 5. 11:39
반응형

결과를 출력해야 하는 파일에서 아래 예제를 적용해 사용하면 됩니다.

한글은 원래 3 bytes 이지만 한글 한 글자가 영문 두 글자를 차지하므로, 2bytes 로 체크 해야 하는 것이 포인트 입니다.

한글 문자를 2 bytes 로 간주하고 문자열의 자릿수를 계산하려면, 각 문자의 유니코드 포인트를 확인하여 한글 문자인지 판단하고, 바이트 수를 계산해야 합니다.

 

# vi result.sh

#!/bin/bash

CALCULATE_LENGTH() {
    local input="$1"
    local length=$(echo -n "$input" | perl -C -Mutf8 -lpe '$_=length(join("",map{/[\x{AC00}-\x{D7AF}]/ ? "xx" : $_} split //))')
    echo "$length"
}

PRINT_ALIGNED() {
        left="$1"
        right="$2"
        total_length=60
        left_length=$(CALCULATE_LENGTH "$1")
        right_length=$(CALCULATE_LENGTH "$2")
        space_length=$((total_length - left_length - right_length))
        printf "%s%*s%s\n" "$left" "$space_length" "" "$right"
}

# 결과 파일 초기화
> a.txt

# 출력 내용 작성
PRINT_ALIGNED "티스토리 홈페이지" "100점" >> a.txt
PRINT_ALIGNED "sysdocu" "90점" >> a.txt
PRINT_ALIGNED "ID 는 sysdocu, 이름은 개발왕자" "100점" >> a.txt

cat a.txt

 

# sh result.sh

 

반응형

댓글()

script 를 이용한 안내 창 (Alert) 커스텀하여 출력하기

반응형

아래 소스를 적당히 수정하여 사용합니다.

 

<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom Alert</title>
    <style>
        /* Custom alert box styling */
        .alert-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9999; /* High z-index to ensure it is on top */
        }
        .alert-box {
            background: white;
            padding: 20px;
            border-radius: 5px;
            text-align: center;
            width: 400px;
            height: 250px;
            display: flex;
            flex-direction: column;
            justify-content: center;
        }
        .alert-box h1 {
            margin-top: 0;
        }
        .alert-box p {
            margin: 20px 0;
            font-size: 13px;
            color: #111111;
        }
        .alert-box button {
            width: 100px; /* Set fixed width */
            margin: 0px auto 0px; /* Center horizontally */
            padding: 10px;
            border: none;
            background: #007BFF;
            color: white;
            border-radius: 5px;
            cursor: pointer;
        }
        .alert-box button:hover {
            background: #0056b3;
        }
    </style>
</head>
<body>
    <div id="alertOverlay" class="alert-overlay" style="display: none;">
        <div class="alert-box">
            <h1>데모 페이지 로그인 정보</h1>
            <p>계정 : <font size="3" color="blue">sysdocu</font><br>
비밀번호 : <font size="3" color="blue">12345678</font><br><br>
* 다른 사용자와의 중복 테스트로 오작동이 일어날 수 있습니다.<br>
* 테스트를 마친 후 데이터를 삭제해 주시기 바랍니다.</p>
            <button onclick="closeAlert()">확인</button>
        </div>
    </div>
    <script>
        function showAlert() {
            document.getElementById('alertOverlay').style.display = 'flex';
        }

        function closeAlert() {
            document.getElementById('alertOverlay').style.display = 'none';
        }

        // Show the alert when the page loads
        window.onload = showAlert;
    </script>
</body>
</html>

 

반응형

댓글()

[php] PHPMailer 로 외부 SMTP 활용하여 메일 보내기

프로그래밍/PHP|2024. 2. 8. 08:25
보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

[C++] 파일쓰기 예제

프로그래밍/C, C++|2023. 12. 11. 17:02
반응형

#include<fstream>
#include<iostream>
#include<string>
 
using namespace std;
 
int main() {
    fstream my_file;
    my_file.open("a.txt", ios::out);    // 이 파일이 생성됩니다.
    my_file << "test" << endl;           // test 라는 내용이 들어가며
    my_file.write("12345", 5);           // 이렇게도 추가할 수 있습니다.
    my_file.close();
}

 

 

* 참고로 위 코드는 반복 실행시 계속 새로운 파일로 쓰이게 되며,

파일이 있을때 내용을 추가하고자 할경우 아래와 같이 ios::app (append) 플래그를 사용하면 됩니다.

my_file.open("a.txt", ios::out | ios::app);

 

반응형

댓글()