c언어 파일 이벤트 감시 (inotify)

프로그래밍/C, C++|2020. 1. 8. 15:03
반응형

맨 하단에 있는 Example code 를 확인합니다.

해당 코드는 특정 디렉토리 내의 하위 디렉토리 및 파일에 대한 이벤트를 감지하여 줍니다.

단일 파일에 대한 감시를 원할 경우 디렉토리를 만들어 그 안에 파일을 넣어두거나, 소스를 수정하여 사용하도록 합니다.

사용법은 test.c 파일로 내용을 저장하여 실행 파일로 만들면 됩니다.

# cc -o test test.c

# ./test &


==================================================================================

리눅스에서는 커널 2.6.13 부터 파일시스템 이벤트를 모니터링할 수 있는 메커니즘을 제공합니다. inotify와 dnotify인데요, 이 글에서는 inotify만 다룰 계획입니다. inotify가 dnotify를 대체하기 위한 녀석이기 때문이죠.


Inotify는 파일이나 디렉토리를 개별적으로 모니터링 할 수 있도록 해줍니다. 여기서 한가지 유의할 점은 디렉토리 모니터링 시 재귀적으로 모니터링 되진 않는다는 것입니다. 만약 하위 모든 디렉토리를 모니터링 하기 위해선 각 디렉토리에 대해 모니터링을 하도록 해야합니다. 


한가지 Inotify의 사용 예시를 생각해보죠. 여러분이 Daemon과 Daemon의 설정 파일을 제공한다고 가정했을 때, 누군가 설정파일을 변경 시 Daemon에서 이걸 알고 처리할 수 있도록 하는 기능 추가 시 유용할 수 있습니다.


그럼, inotify API와 사용법을 알아본 후 예제 코드를 살펴보시죠.


  •  Inotify API

[SYNOPSIS]

#include <sys/inotify.h>


int inotify_init(void);


return

초기화된 inotify instance를 가리키는 file descriptor


inotify instance를 초기화 하기 위한 함수입니다. 성공하면 파일 디스크립터를 리턴하고, 에러 발생 시 -1을 리턴하고, errno가 설정됩니다. 여기서 파일 디스크립터는 inotify instance를 참조할 때 사용되어집니다.



[SYNOPSIS]

#include <sys/inotify.h>


int inotify_add_watch(int fd, const char *pathname, uint32_t mask);


Parameter

fd : inotify_init() 에서 리턴받은 file descriptor

pathname : 모니터링 리스트에 생성되거나 수정되는 파일

mask : pathname에 모니터링할 이벤트 지정 마스크


return

추가 또는 수정된 inotify instance를 가리키는 file descriptor


file descriptor가 가리키는 inotify instance의 모니터링 리스트에 새로운 파일 또는 디렉토리를 추가하거나 기존에 추가되어 있던 항목을 수정하기 위해 사용됩니다. 기존에 추가 되어있다면 mask만 수정됩니다. mask의 종류는 매우 다양하며 몇 가지만 뒤에서 알아보겠습니다.



[SYNOPSIS]

#include <sys/inotify.h>


int inotify_rm_watch(int fd, int wd);


Parameter

fd : inotify_init() 에서 리턴받은 file descriptor

wd : inotify_add_watch()의 이전 호출이 리턴한 file descriptor.


return

수정된 inotify instance를 가리키는 file descriptor


File descriptor fd가 가리키는 inotify instance로부터 wd로 지정한 모니터링 리스트를 제거합니다.


  •  Inotify Event Mask

위에서 inotify_add_watch() API를 설명할 때 parameter로 이벤트 관련된 mask를 설명드렸었습니다. 이 parameter는 pathname에 대해 모니터링할 이벤트를 전달할 수 있는데요. 그 중 몇 가지를 알아보도록 하겠습니다.


아래 표 중 입력은 실제 mask로 지정할 수 있는 이벤트이며, 출력은 지정하지 않아도 모니터링 가능한 이벤트입니다.


 Mask

입력

출력

설명 

 IN_ACCESS

파일 접근 가능 ( read() )

IN_ATTRIB

파일 메타데이터 변경됨

 IN_CLOSE_WRITE

Write 위해 열린 파일이 닫힘

IN_CLOSE_NOWRITE

ReadOnly로 열린 파일이 닫힘

IN_CLOSE


IN_CLOSE_WRITE | IN_CLOSE_NOWRITE 

IN_CREATE

모니터링 디렉토리 내의 파일 or 디렉토리 생성됨 

IN_DELETE

모니터링 디렉토리 내의 파일 or 디렉토리 삭제됨 

 IN_DELETE_SELF

모니터링 중인 디렉토리 or 파일이 삭제됨 

IN_MODIFY

파일이 수점됨 

 IN_MOVE_SELF

모니터링 중인 디렉토리 or 파일이 이동됨 

IN_MOVE_FROM

파일이 모니터링중인 디렉토리 밖으로 이동됨

IN_MOVE_TO

파일이 모니터링중인 디렉토리로 이동됨 

IN_OPEN

파일이 열림 

 IN_ALL_EVENTS

 

위의 모든 이벤트를 포함함. 

 IN_DONT_FOLLOW

 

Symbolic Link를 역참조하지 않음


위의 mask외에도 여러 mask가 있습니다. 상세한 내용은 man-page를 참고하시면 좋을 것 같습니다.


  •  Inotify_event

그럼 이제 모니터링한 파일 or 디렉토리에서 발생한 이벤트를 어떻게 읽어올 것인지 살펴보겠습니다. inotify_event를 이용해 이벤트를 읽어옵니다. 보통 read()를 사용하나, Blocking 되는 문제가 있어, select나 poll 같은 Multiflexing I/O들을 많이 사용합니다. 여기서는 read()를 이용한 설명을 드리겠습니다. 아래는 inotify_event 구조체를 나타낸 것입니다. 


1
2
3
4
5
6
7
8
struct inotify_event {
    int      wd;       /* Watch descriptor */
    uint32_t mask;     /* Mask describing event */
    uint32_t cookie;   /* Unique cookie associating related
                                     events (for rename(2)) */
    uint32_t len;      /* Size of name field */
    char     name[];   /* Optional null-terminated name */
};

cs


  • Example Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <poll.h>
#include <errno.h>
#include <unistd.h>
#include <sys/inotify.h>
 
#define INOTIFY_PATH "/tmp/"
 
static void __handle_inotify_event(const struct inotify_event *event)
{
    if (event->mask & IN_ACCESS)
        printf("IN_ACCESS ");
    if (event->mask & IN_ATTRIB)
        printf("IN_ATTRIB ");
    if (event->mask & IN_CLOSE_NOWRITE)
        printf("IN_CLOSE_NOWRITE ");
    if (event->mask & IN_CLOSE_WRITE)
        printf("IN_CLOSE_WRITE ");
    if (event->mask & IN_CREATE)
        printf("IN_CREATE ");
    if (event->mask & IN_DELETE)
        printf("IN_DELETE ");
    if (event->mask & IN_ISDIR)
        printf("IN_ISDIR ");
    if (event->mask & IN_MODIFY)
        printf("IN_MODIFY ");
    if (event->mask & IN_MOVE_SELF)
        printf("IN_MOVE_SELF ");
    if (event->mask & IN_MOVED_FROM)
        printf("IN_MOVED_FROM ");
    if (event->mask & IN_MOVED_TO)
        printf("IN_MOVED_TO ");
    if (event->mask & IN_OPEN)
        printf("IN_OPEN ");
    if (event->len > 0)
        printf(": name = %s\n", event->name);
}
 
int main(int argc, char *argv[])
{
    int ret;
    int fd;
    int wd;
    char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
    char *ptr;
    ssize_t size;
    const struct inotify_event *event;
 
    fd = inotify_init();
    if (fd < 0) {
        perror("inotify_init");
        exit(EXIT_FAILURE);
    }
 
    wd = inotify_add_watch(fd, INOTIFY_PATH, IN_MODIFY | IN_CREATE | IN_DELETE);
    if (wd < 0) {
        fprintf(stderr, "Failed to add watch [%s] [%s]", INOTIFY_PATH, strerror(errno));
        perror("inotify_add_watch");
        exit(EXIT_FAILURE);
    }
 
    while (1) {
        size = read(fd, buf, sizeof(buf));
        if (size == -1 && errno != EAGAIN) {
            perror("read");
            fprintf(stderr, "read : %s", strerror(errno));
            exit(EXIT_FAILURE);
        }
 
        if (size <= 0)
            break;
 
        for (ptr = buf; ptr < buf + size; ptr += sizeof(struct inotify_event) + event->len) {
            event = (struct inotify_event *)ptr;
            __handle_inotify_event(event);
        }
    }
 
    ret = inotify_rm_watch(fd, wd);
    if (ret < 0) {
        fprintf(stderr, "Failed to rm watch [fd : %d] [wd : %d] [%s]", fd, wd, strerror(errno));
        perror("inotify_rm_watch");
        exit(EXIT_FAILURE);
    }
 
    return 0;
}
 
cs



출처: https://sonseungha.tistory.com/436 [Developer's Delight]

반응형

댓글()

레이어가 겹칠때 하단 레이어는 터치 비활성화 시키기

프로그래밍/Android (Java)|2019. 12. 24. 08:33
반응형

레이어가 겹치게 되면 경우에 따라 상단 또는 하단 레이어가 터치 됩니다.

하단에 가려진 레이어를 터치되지 않도록 하려면 아래와 같이 하는 방법이 있습니다.

 

아래와 같이 layout 을 꾸미게 되면 3번 레이어가 최상단에 올라옵니다.

 

1번 레이어

2번 레이어

3번 레이어

 

이때 3번 레이어에 setClickable(true);  를 설정 합니다.

 

 

반응형

댓글()

어플에 애드몹 광고 넣기 예제

프로그래밍/Android (Java)|2019. 12. 20. 14:23
반응형

예제 소스 배포

https://github.com/googleads/googleads-mobile-android-examples/tree/master/java/admob

 

예제 소스 다운로드

https://github.com/googleads/googleads-mobile-android-examples/archive/master.zip

 

압축 파일 다운로드 후 원하는 광고 방식의 소스를 활용하면 됩니다.

 

----------

 

안드로이드에서 설명하는 예제 (쉬움)

https://developers.google.com/admob/android/quick-start

 

----------

 

반응형

댓글()

쉘스크립트에서 환경 변수 입력, 적용, 스크립트 종료 후에도 사용하기

프로그래밍/BASH SHELL|2019. 11. 29. 17:28
반응형

#!/bin/bash

echo "alias c='clear'" >> ~/.bashrc    # 환경 변수 입력

. ~/.bashrc                                                    # 적용 (스크립트 종료시까지 유효)

exec bash                                                      # 쉘스크립트 종료 후에도 적용 유지 (본 코드는 스크립트 맨 아래에 사용해야 한다. 이 아래에 코드를 넣으면 실행되지 않기 때문이다.)





반응형

댓글()

php 코드로 json 형태로 출력하기

프로그래밍/PHP|2019. 10. 30. 08:48
반응형

php 로 json 형태의 문자열을 echo 출력 했음에도 불구하고 json 인식이 되지 않는 경우는

php 소스 최상단에 아래 코드를 입력하도록 합니다.

이경우 html 코드가 (예 : <br>) 텍스트로 출력되어 버리니 필요 없는 태그는 미리 삭제 하도록 합니다.



1. 소스 코드 추가


<?

header('Content-Type: application/json');

...

?>



2. 출력 예시


{

    "status": "true",

    "message": "Data fetched successfully!",

    "data": [

    {

        "id": "1",

        "title": "제목",

        "body": "내용을 씁니다.",

    },

    {

        "id": "2",

        "title": "인사말",

        "body": "안녕하세요. 반갑습니다.",

    }

    ]

}


반응형

댓글()

커스텀 스피너 (Custom Spinner)

프로그래밍/Android (Java)|2019. 10. 29. 15:19
반응형

1. 레이아웃 추가


<Spinner

    android:id="@+id/simpleSpinner"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_centerHorizontal="true" /> 



2. custom_spinner_items.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="wrap_content"

    android:orientation="horizontal">


    <ImageView

        android:id="@+id/imageView"

        android:layout_width="50dp"

        android:layout_height="50dp"

        android:padding="5dp"

        android:src="@drawable/ic_launcher" />


    <TextView

        android:id="@+id/textView"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:layout_gravity="center"

        android:text="Demo"

        android:textColor="#000" />

</LinearLayout> 



3. 스피너 추가할 화면의 java 파일


public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener{



    String[] countryNames={"India","China","Australia","Portugle","America","New Zealand"};

    int flags[] = {R.drawable.india, R.drawable.china, R.drawable.australia, R.drawable.portugle, R.drawable.america, R.drawable.new_zealand};


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);


        Spinner spin = (Spinner) findViewById(R.id.simpleSpinner);

        spin.setOnItemSelectedListener(this);


        CustomAdapter customAdapter=new CustomAdapter(getApplicationContext(),flags,countryNames);

        spin.setAdapter(customAdapter);

    }



    @Override

    public void onItemSelected(AdapterView<?> arg0, View arg1, int position,long id) {

        Toast.makeText(getApplicationContext(), countryNames[position], Toast.LENGTH_LONG).show();

    }


    @Override

    public void onNothingSelected(AdapterView<?> arg0) {

        // TODO Auto-generated method stub

    }

} 



4. CustomAdapter.java 생성


import android.content.Context;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.BaseAdapter;

import android.widget.ImageView;

import android.widget.TextView;


public class CustomAdapter extends BaseAdapter {

    Context context;

    int flags[];

    String[] countryNames;

    LayoutInflater inflter;


    public CustomAdapter(Context applicationContext, int[] flags, String[] countryNames) {

        this.context = applicationContext;

        this.flags = flags;

        this.countryNames = countryNames;

        inflter = (LayoutInflater.from(applicationContext));

    }


    @Override

    public int getCount() {

        return flags.length;

    }


    @Override

    public Object getItem(int i) {

        return null;

    }


    @Override

    public long getItemId(int i) {

        return 0;

    }


    @Override

    public View getView(int i, View view, ViewGroup viewGroup) {

        view = inflter.inflate(R.layout.custom_spinner_items, null);

        ImageView icon = (ImageView) view.findViewById(R.id.imageView);

        TextView names = (TextView) view.findViewById(R.id.textView);

        icon.setImageResource(flags[i]);

        names.setText(countryNames[i]);

        return view;

    }

} 



[출처] https://abhiandroid.com/ui/custom-spinner-examples.html

반응형

댓글()

ImageView 모서리 둥글게 하기

프로그래밍/Android (Java)|2019. 10. 29. 14:30
반응형

이미지뷰 모서리를 둥글게 하는 방법으러 java 에서 처리하는 방법도 있지만 간단히 레이아웃에서 처리하는 방법도 있어 방법을 설명합니다.



1. app 단의 build.gradle 수정


implementation 'androidx.cardview:cardview:1.0.0'    // androidx 의 경우

또는

implementation 'com.android.support:cardview-v7:28.0.0' 


수정시 우측 상단에 뜨는 Sync 버튼 클릭



2. layout 수정


ImageView 를 감싸는 CardView 를 만들어 줍니다.


<androidx.cardview.widget.CardView    // androidx 의 경우

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        app:cardCornerRadius="5dp">


    <ImageView

        android:id="@+id/iv"

        android:src="@drawable/sysdocu_img"

        android:layout_width="50dp"

        android:layout_height="50dp"

        android:scaleType="fitXY"/>


</androidx.cardview.widget.CardView> 


* androidx 가 아닌 경우 아래 것으로 사용합니다.

<android.support.v7.widget.CardView

    ....>

    <ImageView

        ..../>

</android.support.v7.widget.CardView>



반응형

댓글()

웹 브라우저 링크로 앱 실행 방법 (유사 딥링크, 동적링크)

프로그래밍/Android (Java)|2019. 9. 30. 11:18
반응형

안드로이드 자체 브라우저로 특정 앱을 실행 하는 것이 가능하다.

이것은 안드로이드 OS가 가진 intent의 특징으로 가능 한것 같다.

Intent 의 자세한 내용은 안드로이드 API Guide에 있는  Intents and Intent Filters 를 보면 알수 있다.


1. 먼저 실행 하고자 하는 앱의 AndroidManifest.xml 파일에서 실행하고자 하는 Activity아래에 Intent-filter를 선언해준다.


<activity android:name="SearchActionActivity">
             <intent-filter>  
                <action android:name="android.intent.action.VIEW"/>  
                <category android:name="android.intent.category.DEFAULT"/>  
                <category android:name="android.intent.category.BROWSABLE"/>  
                <data android:scheme="callMyApp" android:host="search"/>  
            </intent-filter>
</activity>


2. 웹 브라우저 상에서 링크 설정 방법
- 웹에서 특정 URL형태로 설정을 해줘야 해당 앱이 호출되어 실행 되어 진다.

<a href="callMyApp://search"> 나의 앱 검색 실행 </a>

위와같이 웹페이지에서 링크를 설정해 두면 해당 앱이 설치되어 있으면 해당 SearchActionActivity가 바로 실행 되어지는 걸 알수 있다.

따라서, 다른 액션의 Activity를 실행하고 싶다면 다른 Activity에 intent-filter만 추가해주면 가능하다.

<activity android:name="TakePhotoActionActivity">
             <intent-filter>  
                <action android:name="android.intent.action.VIEW"/>  
                <category android:name="android.intent.category.DEFAULT"/>  
                <category android:name="android.intent.category.BROWSABLE"/>  
                <data android:scheme="callMyApp" android:host="takePhoto"/>  
            </intent-filter>
</activity>

웹에서 호출 방법은

<a href="callMyApp://takePhoto"> 나의 앱 사진 찍기 실행 </a>


3. 필요에 따라서 앱이 설치 되어 있으면 실행하고 설치 되어 있지 않으면 구글 플레이 마켓으로 이동 하고 싶다고 한다면 intent 전달 방식으로 호출 하면 된다.(Android Only)

기본 형식은 아래와 같다.

Intent://[host명]?파라미터=파리미터값
#Intent;scheme=callMyApp;action=android.intent.action.VIEW;category=android.intent.category.BROWSABLE;package=com.test.myapp;end


간단한 호출 예
<a href="Intent://takePhoto#Intent;scheme=callMyApp;package=com.test.myapp;end">
나의 앱 사진 찍기 실행 </a>




3. 앱에서 파라메터 값 받아서 처리 하기
- 앱에서 호출되어진 Activity 에서 Intent를 통해서 들어온 데이터에서 파라메터 값을 받아서 처리 할수도 있다.

Uri uriData = getIntent().getData();
String photoNumber = uriData.getQueryParameter("photoNumber ");

웹에서 호출
<a href="callMyApp://takePhoto?photoNumber=1"> 나의 앱 1번 사진 보기 </a>


이상으로 웹에서 링크로 안드로이드 앱의 호출을 알아보았습니다.

이것이 되는 것은 Android OS상에서 기본적으로 Intent 호출 방식을 지원하기 때문에 가능하지 않나 생각됩니다.

 



참고 사이트
http://developer.android.com/guide/components/intents-filters.html
http://developer.naver.com/wiki/pages/UrlScheme

 

원본글 URL

 http://gwons.blogspot.kr/2014/11/android.html



출처: https://lkmstar.tistory.com/4 [천지파멸의 이야기]

반응형

댓글()

android 에서 기기에 저장된 파일 이름 변경하기

프로그래밍/Android (Java)|2019. 9. 20. 08:45
반응형

안드로이드 내 파일 이름은 아래 코드로 변경이 가능합니다.


File old_filename = new File(Environment.getExternalStorageDirectory() + "/Download/변경전 파일이름.txt");

File new_filename = new File(Environment.getExternalStorageDirectory() + "/Download/변경후 파일이름.txt");


old_filename.renameTo(new_filename);


결과를 보기 위한 다른 방법은 if 문으로 처리 여부를 확인하는 방법입니다.


if (old_filename.renameTo(new_filename)) {

    Toast.makeText(this, "변경 성공", Toast.LENGTH_SHORT).show();

} else {

    Toast.makeText(this, "변경 실패", Toast.LENGTH_SHORT).show();

}


반응형

댓글()

안드로이드 파일공유 PROVIDER

프로그래밍/Android (Java)|2019. 9. 16. 10:28
반응형
안드로이드에서 파일 공유시 권한 문제로 인해 오류가 발생 된다.

해결 방법


/res/xml/file_provider.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path path="Android/data/${applicationId}/" name="files_root" />
<root-path name="root" path="/" />

</paths> 

또는

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--Context.getCacheDir() 내부 저장소-->
<cache-path
name="cache"
path="." />
<!--Context.getFilesDir() 내부 저장소-->
<files-path
name="files"
path="." />
<!-- Environment.getExternalStorageDirectory() 외부 저장소-->
<external-path
name="external"
path="." />
<!-- Context.getExternalCacheDir() 외부 저장소-->
<external-cache-path
name="external-cache"
path="." />
<!-- Context.getExternalFilesDir() 외부 저장소-->
<external-files-path
name="external-files"
path="." />
</paths>

 



 <?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mrsohn.sample"
android:installLocation="auto">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:hardwareAccelerated="true">
<activity
android:name=".SplashActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".MainActivity"
android:screenOrientation="portrait" />


<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.
fileprovider"

android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_path" />
</provider>


</application>

</manifest>



URI얻어오는 방법 

Uri contentUri = FileProvider.getUriForFile(getContext(),

                    getApplicationContext().getPackageName() + ".fileprovider", 파일경로);



content://패키지명.fileprovider/root/data/data/파일경로 형태로 uri가 생성 된다.


 /**

     * 멀티 파일 공유 

     * @param shareFiles

     */

    public void shareMultiFIles(File[] shareFiles) {

        final Intent intent = new Intent();

        intent.setAction(Intent.ACTION_SEND_MULTIPLE);  // 멀티파일 보내기 

//        intent.setPackage("com.google.android.gm");   // 지메일로 보내기 

        // 파일형태에 맞는 type설정

//        intent.setType("plain/text"); // text 형태로 전달

        intent.setType("*/*");        // 모든 공유 형태 전달

        intent.putExtra(Intent.EXTRA_SUBJECT, "공유 제목");  // 제목

        intent.putExtra(Intent.EXTRA_TEXT, "공유 내용");     // 내용

        if (shareFiles != null && shareFiles.length > 0) {

            ArrayList<Uri> uris = new ArrayList<Uri>();

            for (File file : shareFiles) {

                Uri contentUri = FileProvider.getUriForFile(this,

                        getApplicationContext().getPackageName() + ".fileprovider", file); // manifest의  ${applicationId}.fileprovider

                uris.add(contentUri);

                Log.i(TAG, "contentUri="+contentUri.toString());

            }

            intent.putExtra(Intent.EXTRA_STREAM, uris); // 멀티 파일 전송

        }

        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);     // 공유 앱에 권한 주기 

        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);    // 공유 앱에 권한 주기 

        startActivity(Intent.createChooser(intent, "공유 타이틀"));

    } 



    /**

     * 단일 파일 공유 

     * @param shareFile

     */

    public void shareFIle(File shareFile) {

        final Intent intent = new Intent();

        intent.setAction(Intent.ACTION_SEND);           // 단일파일 보내기 

//        intent.setPackage("com.google.android.gm");   // 지메일로 보내기 

        // 파일형태에 맞는 type설정

        MimeTypeMap type = MimeTypeMap.getSingleton();

        intent.setType(type.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(shareFile.getPath())));

//        intent.setType("plain/text"); // text 형태로 전달

//        intent.setType("*/*");        // 모든 공유 형태 전달 

        intent.putExtra(Intent.EXTRA_SUBJECT, "공유 제목");  // 제목

        intent.putExtra(Intent.EXTRA_TEXT, "공유 내용");     // 내용

        Log.i(TAG, "test.file.getpath="+shareFile.getPath());

        if (shareFile != null) {

            Uri contentUri = FileProvider.getUriForFile(this,

                    getApplicationContext().getPackageName() + ".fileprovider", shareFile); // manifest의  ${applicationId}.fileprovider

     

            intent.putExtra(Intent.EXTRA_STREAM, contentUri); // 단일 파일 전송

        }

        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);     // 공유 앱에 권한 주기 

        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);    // 공유 앱에 권한 주기 

        startActivity(Intent.createChooser(intent, "공유 타이틀"));

    } 


[출처] https://mrsohn.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%ED%8C%8C%EC%9D%BC%EA%B3%B5%EC%9C%A0-provider-1


반응형

댓글()

지오코딩 GeoCoding (주소,지명 ↔ 위도,경도 변환) 예제

반응형

안드로이드: 지오코딩 GeoCoding (주소,지명 ↔ 위도,경도 변환)

 

 

지오코딩(GeoCoding) 이란  '주소나 지명' 을 '좌표 (위도, 경도)' 로 변환시키는 작업이다.   혹은 반대로 '좌표(위도, 경도)' 를 '주소나 지명' 으로 변환하는 작업 (역 지오코딩: Reverse GeoCoding) 이라 한다.

 

예를 들면 '서울 시청' 을 지오코딩 하면   좌표가   ( 37.56647, 126.977963 ) 이 튀어 나오는 겁니다.   
사실 이런 서비스는 이미 구글맵 등에서도 제공되었죠.  구글맵에서 특정 지점 위에서 마우스우클릭 - '이곳이 궁금한가요?'  실행하면 위도, 경도 가 나왔죠.

 

 

 

 

 

 

 

 

 

 

지오코딩 서비스는 역시 안드로이드 플랫폼을 만든 구글 답게 구글에서 제공해주는 거고, 안드로이드 API 에 Geocoder 객체 + Address 객체 를 통해 서비스 사용이 가능합니다.

 

 

 

 

안드로이드에서 지오코딩을 구현하는 주요 Step 은 다음과 같습니다.

 

Step1 : 메니페스트 파일에 인터넷(INTERNET) 과 위치정보 서비스(ACCESS_FINE_LOCATION) 권한을 받아옵니다

 

Step2 : Geocoder 객체 생성

 

Step3 : '좌표(위도, 경도)' 를 '주소나 지명' 으로 변환하는 경우
          getFromLocation() 메소드 사용.  결과는 List<Address> 형태

 

        : '주소나 지명' 을 '좌표(위도, 경도)' 으로 변환하는 경우

          getFromLocationName() 메소드 사용.  결과는 List<Address> 형태

 

 

Step4 : Address 객체의 toString(), getCountryName(), getLatitude(), getLongitude(), getPostalCode(), getPhone() 메소드 등을 통해 세부정보 추출 가능

 

 

 

 

 

 

아래 에제에서는 

1)  '좌표(위도, 경도)' 를 '주소나 지명' 변환 

2)  '주소나 지명' 을 '좌표(위도, 경도)' 으로 변환

     두가지를 해볼거고, 각각에 대해 변환된 문자열 출력과,  변환된 위도,경도 좌표를 통해 지도를 보여주는 것을 구현해봅니다.  이때 지도는 묵시적 인텐트 (implicit intent) 로 호출합니다.   (묵시적 인텐트 에 대해서는 지난 포스팅 참조 → 클릭)   

 

 

 

 

 

[AndroidManifest.xml]

 

인터넷(INTERNET) 과 위치정보 서비스(ACCESS_FINE_LOCATION) 권한을 받아옵니다

 

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

 

 

 

 

[activity_main.xml]

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="위도"
android:textAppearance="?android:attr/textAppearanceLarge" />

<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:ems="10"
android:inputType="numberDecimal" />

<EditText
android:id="@+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editText1"
android:layout_alignTop="@+id/textView2"
android:ems="10"
android:inputType="numberDecimal" />

<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/editText2"
android:layout_marginTop="26dp"
android:text="주소"
android:textAppearance="?android:attr/textAppearanceLarge" />

<EditText
android:id="@+id/editText3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editText2"
android:layout_alignTop="@+id/textView3"
android:ems="10" >

<requestFocus />
</EditText>

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/editText1"
android:layout_marginTop="22dp"
android:text="경도"
android:textAppearance="?android:attr/textAppearanceLarge" />

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/editText2"
android:layout_alignBottom="@+id/editText2"
android:layout_alignParentRight="true"
android:text="변환1" />

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/textView3"
android:layout_alignBottom="@+id/textView3"
android:layout_alignParentRight="true"
android:text="변환2" />

<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/button2"
android:layout_marginTop="28dp"
android:text="조회한 주소정보 출력"
android:textAppearance="?android:attr/textAppearanceMedium" />

<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/editText2"
android:layout_alignRight="@+id/editText2"
android:text="지도1" />

<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/editText3"
android:layout_alignBottom="@+id/editText3"
android:layout_alignRight="@+id/editText3"
android:text="지도2" />

</RelativeLayout>

 

 

 

 

 

 

 

 

 

[MainActiviy.java]

 

 

 

 

public class MainActivity extends AppCompatActivity {

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

// 지오코딩(GeoCoding) : 주소,지명 => 위도,경도 좌표로 변환
// 위치정보를 얻기위한 권한을 획득, AndroidManifest.xml
// ACCESS_FINE_LOCATION : 현재 나의 위치를 얻기 위해서 필요함
// INTERNET : 구글서버에 접근하기위해서 필요함

final TextView tv = (TextView) findViewById(R.id.textView4); // 결과창
Button b1 = (Button)findViewById(R.id.button1);
Button b2 = (Button)findViewById(R.id.button2);
Button b3 = (Button)findViewById(R.id.button3);
Button b4 = (Button)findViewById(R.id.button4);

final EditText et1 = (EditText)findViewById(R.id.editText1);
final EditText et2 = (EditText)findViewById(R.id.editText2);
final EditText et3 = (EditText)findViewById(R.id.editText3);

final Geocoder geocoder = new Geocoder(this);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 위도,경도 입력 후 변환 버튼 클릭
List<Address> list = null;
try {
double d1 = Double.parseDouble(et1.getText().toString());
double d2 = Double.parseDouble(et2.getText().toString());

list = geocoder.getFromLocation(
d1, // 위도
d2, // 경도
10); // 얻어올 값의 개수
} catch (IOException e) {
e.printStackTrace();
Log.e("test", "입출력 오류 - 서버에서 주소변환시 에러발생");
}
if (list != null) {
if (list.size()==0) {
tv.setText("해당되는 주소 정보는 없습니다");
} else {
tv.setText(list.get(0).toString());
}
}
}
});

b2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<Address> list = null;


String str = et3.getText().toString();
try {
list = geocoder.getFromLocationName(
str, // 지역 이름
10); // 읽을 개수
} catch (IOException e) {
e.printStackTrace();
Log.e("test","입출력 오류 - 서버에서 주소변환시 에러발생");
}

if (list != null) {
if (list.size() == 0) {
tv.setText("해당되는 주소 정보는 없습니다");
} else {
tv.setText(list.get(0).toString());
// list.get(0).getCountryName(); // 국가명
// list.get(0).getLatitude(); // 위도
// list.get(0).getLongitude(); // 경도
}
}
}
});

b3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 위도,경도 입력 후 지도 버튼 클릭 => 지도화면으로 인텐트 날리기
double d1 = Double.parseDouble(et1.getText().toString());
double d2 = Double.parseDouble(et2.getText().toString());

Intent intent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("geo:" + d1 + "," + d2));
startActivity(intent);
}
});

b4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 주소입력후 지도2버튼 클릭시 해당 위도경도값의 지도화면으로 이동
List<Address> list = null;

String str = et3.getText().toString();
try {
list = geocoder.getFromLocationName
(str, // 지역 이름
10); // 읽을 개수
} catch (IOException e) {
e.printStackTrace();
Log.e("test","입출력 오류 - 서버에서 주소변환시 에러발생");
}

if (list != null) {
if (list.size() == 0) {
tv.setText("해당되는 주소 정보는 없습니다");
} else {
// 해당되는 주소로 인텐트 날리기
Address addr = list.get(0);
double lat = addr.getLatitude();
double lon = addr.getLongitude();

String sss = String.format("geo:%f,%f", lat, lon);

Intent intent = new Intent(
Intent.ACTION_VIEW,
Uri.parse(sss));
startActivity(intent);
}
}
}
});

} // end of onCreate
} // end of class

 

 

 

 

 

[실행화면]

 

 

 

 

 

 

 

 

위 시작화면에서 적절한 주소, 혹은 지명 등을 입력하고 [변환2] 버튼을 누르면

아래와 같이 지오코딩 된 전체정보를 볼수 있습니다.  toString() 결과값

 

 

 

 

 

 

한변 [지도2] 버튼을 누르면, 아래와 같이 지도 앱으로 묵시적인텐트를 날려서

해당 지도앱이 넘겨받은 위도,경도 값으로 지도 표시를 합니다.

 

 

 

 

반면 상단의 위도, 경도 값을 직접 입력하고 [변환1] 을 누르면 이또한 지오코딩이 된 결과값이 나옵니다.

 

 


출처: https://bitsoul.tistory.com/135 [Happy Programmer~]

 

 

반응형

댓글()