안드로이드 파일공유 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~]

 

 

반응형

댓글()

CentOS 7 에서 111번 포트 중지하기

리눅스/OS 일반|2019. 9. 5. 14:42
반응형

[root@sysdocu ~]# systemctl stop rpcbind

[root@sysdocu ~]# systemctl disable rpcbind

[root@sysdocu ~]# systemctl mask rpcbind

[root@sysdocu ~]# systemctl stop rpcbind.socket

[root@sysdocu ~]# systemctl disable rpcbind.socket

 

 

반응형

댓글()

폴리라인 (polyline) 그리기

반응형

PolylineOptions polylineOptions = new PolylineOptions();

 

 

String output = out.toString();    // gpx 파일 내용이 output 에 있다고 가정하자.

    String[] split = output.split("\\n");  // 개행 단위로 배열에 넣고
    for(int i=0 ; i<split.length ; i++) {      // 라인 단위로 작업을 시작한다.

        // 정규표현식으로 좌표 값만 가져오기
        if(split[i].contains("lat=") && split[i].contains("lon=")) {

            add_lat = split[i].replaceAll(".*lat=\"", "");
            temp_lat = Double.parseDouble(add_lat.replaceAll("\".*", ""));
            add_lon = split[i].replaceAll(".*lon=\"", "");
            temp_lon = Double.parseDouble(add_lon.replaceAll("\".*", ""));

            polylineOptions.color(Color.RED);  // 폴리라인 옵션에 색상과
            polylineOptions.width(8);                   // 라인 굵기와
            polylineOptions.add(new LatLng(temp_lat, temp_lon)); // 좌표를 반복해서 추가한다.

 

        }

    }

 

 

제거하려는 경우 아래 코드 사용

polyline.remove();

 

 

나쁜 예)

아래와 같이 사용하면 동작은 하나 많은 라인 (테스트시 27,000 라인) 을 가져올때 굉장히 느리며, 앱이 종료되는 현상 발생

mMap.addPolyline(polylineOptions.add(new LatLng(temp_lat, temp_lon)));

 

 

반응형

댓글()

권한 여부 체크하기

반응형

int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR);

if (permissionCheck == PackageManager.PERMISSION_DENIED) {

    // 권한 없음
else {
    // 권한 있음
}

 

* 위 파란색 글씨 대신에 다른 권한 대입 가능하다.

 

반응형

댓글()

저장소 파일 불러올 때 읽기 권한 요청 설정

반응형

import android.Manifest;

import android.content.pm.PackageManager;

import android.support.v4.app.ActivityCompat;

import android.support.v4.content.ContextCompat;

 

 

onCreate 내부에 아래 내용 삽입

//읽기 권한 요청

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {

      if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.READ_EXTERNAL_STORAGE)) {

      } else {

            ActivityCompat.requestPermissions(this,

            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},

            1);

      }

}

 

AndroidManifest.xml 에 아래 내용 추가

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

 

반응형

댓글()

[MongoDB에러] server returned error on sasl authentication step: authentication failed.

리눅스/MongoDB|2019. 8. 30. 15:48
반응형

이런식으로 옵션을 추가하면 실행이 잘 된다.

 

$ mongorestore -u sysdocu -p 12345678 --authenticationDatabase admin --db log ./path/to/dump/log/

 

 

반응형

댓글()

MySQL 5.6 소스 설치

리눅스/MySQL|2019. 8. 29. 14:42
반응형

mysql 5.5 이상 버전 부터 cmake 를 이용하여 설치 하기 때문에 cmake를 먼저 설치 후 mysql 설치를 진행 합니다.

# cd  /usr/local/src

 

# groupadd mysql
# useradd -g mysql mysql

 

# wget https://src.fedoraproject.org/repo/pkgs/cmake/cmake-2.8.5.tar.gz/3c5d32cec0f4c2dc45f4c2e84f4a20c5/cmake-2.8.5.tar.gz
# tar zxvfp ./cmake-2.8.5.tar.gz
# cd  ./cmake-2.8.5
# ./bootstrap
# make
# make install

# cd ../

# wget https://distfiles.macports.org/mysql56/mysql-5.6.45.tar.gz
# tar xvzf mysql-5.6.45.tar.gz
# cd mysql-5.6.45
# cmake \
-DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
-DMYSQL_UNIX_ADDR=/tmp/mysql.sock \
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci \
-DWITH_EXTRA_CHARSETS=all \
-DMYSQL_DATADIR=/usr/local/mysql/data \
-DENABLED_LOCAL_INFILE=1 \
-DWITH_MYISAM_STORAGE_ENGINE=1 \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_ARCHIVE_STORAGE_ENGINE=1 \
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
-DWITH_MEMORY_STORAGE_ENGINE=1 \
-DWITH_READLINE=1 \
-DENABLED_LOCAL_INFILE=1 \
-DMYSQL_USER=mysql \
-DMYSQL_TCP_PORT=3306 \
-DENABLE_DOWNLOADS=1

 

* euckr 으로 설치를 원할 경우 위 파란 부분을 각각 euckr 과 euckr_korean_ci 로 변경


# make
# make install

# cp -arp /usr/local/mysql/support-files/my-default.cnf /etc/my.cnf
# /usr/local/mysql/scripts/mysql_install_db --defaults-file=/etc/my.cnf --user=mysql --basedir=/usr/local/mysql/ --datadir=/usr/local/mysql/data/
# cp -arpf /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
 

소유권 그룹 권한등을 변경 하여 줍니다.

# chown -R mysql:mysql /usr/local/mysql/
# chmod 700 /etc/init.d/mysqld
# chmod 711 /usr/local/mysql
# chmod 700 /usr/local/mysql/data
# chmod 751 /usr/local/mysql/bin
# chmod 750 /usr/local/mysql/bin/*
# chmod 755 /usr/local/mysql/bin/mysql
# chmod 755 /usr/local/mysql/bin/mysqldump
 

mysql 명령어를 사용할 수 있게 환경변수를 등록하여 줍니다.

# vi /etc/profile
PATH="$PATH:/usr/local/mysql/bin"

# source /etc/profile

# chkconfig mysqld --add
# chkconfig mysqld on
# /etc/init.d/mysqld start

 

 

[출처] https://idchowto.com/?p=26547

반응형

댓글()

Fragment 에서 상위 Activity 재시작 하기

프로그래밍/Android (Java)|2019. 8. 28. 17:19
반응형

                         Intent intent = getActivity().getIntent();
                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
                            | Intent.FLAG_ACTIVITY_NO_ANIMATION);
                    getActivity().overridePendingTransition(0, 0);
                    getActivity().finish();
                    getActivity().overridePendingTransition(0, 0);
                    startActivity(intent);

 

 

[출처] https://gist.github.com/chrisjenx/3176258

반응형

댓글()

디렉토리내 파일명 가져올때 정렬하기

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

파일명을 가져올 때 따로 정렬하는 API는 없지만 Arrays 클래스의 sort 메소드를 사용하면 된다.

File[] filesList = mDirectory.listFiles();

Arrays.sort(filesList); // 가져온 파일을 파일명으로 정렬  

 

 

[출처] https://seojune.tistory.com/m/105

반응형

댓글()

파일 브라우저 만들기

프로그래밍/Android (Java)|2019. 8. 28. 13:54
반응형

디렉토리 읽기권한 필수

https://sysdocu.tistory.com/1429

 

 

파일 입출력 메소드는 파일 안에 저장된 데이터를 관리하는데 비해 파일 관리 메소드는 파일 그 자체가 관리 대상이다.

 

File 클래스의 다음 메소드는 파일의 목록을 구한다.

 

    String[] list([FilenameFilter filter])

    File[] listFiles([FilenameFilter filter])

 

파일의 경로 목록을 구할 수도 있고 File 객체의 목록을 구할 수도 있다.

 

filter를 지정하면 특정 조건에 맞는 파일의 목록만 조사하며 생략하면 모든 파일이 조사된다. 디렉터리도 같이 조사되지만 현재 디렉터리인 .과 부모 디렉터리인 ..은 제외된다.

만약 파일이 하나도 없으면 null이 리턴된다.

 

다음 메소드는 파일의 이름을 변경하거나 삭제한다.

 

    boolean renameTo(File newPath)

    boolean delete()

    void deleteOnExit()

 

둘 다 패키지 디렉터리 아래의 파일만 관리할 수 있으며 경로는 역시 사용할 수 없다. deleteOnExit는 가상 머신이 종료될 때 삭제하도록 예약하는 것이다.

 

다음 메소드는 파일에 대한 정보를 조사한다.

 

    boolean exists()

    boolean isFile()

    boolean isDirectory()    

    long length()

    boolean isHidden()

    long lastModified()

    boolean canRead()

    boolean canWrite()

 

디렉터리를 생성할 때는 다음 메소드를 사용한다.

    boolean mkdir()

    boolean mkdirs()

 

mkdir은 부모 폴더까지 생성하지 않는데 비해 mkdirs는 부모 폴더까지 한꺼번에 생성한다는 점이 다르다.

 

다음 예제는 SD카드의 파일과 디렉터리 목록을 보여준다.

 

 

SD카드를 사용하기 때문에 반드시 매니페스트 파일에 퍼미션을 추가해야한다.

 

AndroidManifest.xml

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

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.ch25_fileexplorer"

android:versionCode="1"

android:versionName="1.0" >

 

<uses-sdk

android:minSdkVersion="19"

android:targetSdkVersion="19" />

 

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name=".FileExplorer"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

 

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

</intent-filter>

</activity>

</application>

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

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

</manifest>

 

 

fileexplorer.xml 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

>

 

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical"

>

 

<TextView

android:id="@+id/current"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="current"

/>

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content">

<Button

android:id="@+id/btnroot"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="Root"/>

<Button

android:id="@+id/btnup"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="mOnClick"

android:text="Up"/> 

 

</LinearLayout> 

</LinearLayout>

    <ListView

     android:id="@+id/filelist"

     android:layout_width="match_parent"

     android:layout_height="match_parent"

     />

 

</LinearLayout>

 

 

 

FileExplorer.java

package com.example.ch25_fileexplorer;

 

import java.io.File;

import java.util.ArrayList;

 

import android.app.Activity;

import android.os.Bundle;

import android.os.Environment;

import android.view.View;

import android.widget.AdapterView;

import android.widget.ArrayAdapter;

import android.widget.ListView;

import android.widget.TextView;

import android.widget.Toast;

 

 

public class FileExplorer extends Activity {

    String mCurrent;

    String mRoot;

    TextView mCurrentTxt;

    ListView mFileList;

    ArrayAdapter<String> mAdapter;

    ArrayList<String> arFiles;

      

    

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.fileexplorer);

      

        mCurrentTxt = (TextView)findViewById(R.id.current);

        mFileList = (ListView)findViewById(R.id.filelist);

        

        arFiles = new ArrayList<String>();

        //SD카드 루트 가져옴

        mRoot = Environment.getExternalStorageDirectory().getAbsolutePath();

        mCurrent = mRoot;

        

        //어댑터를 생성하고 연결해줌

        mAdapter = new ArrayAdapter<String>(this,

                android.R.layout.simple_list_item_1, arFiles);

        mFileList.setAdapter(mAdapter);//리스트뷰에 어댑터 연결

        mFileList.setOnItemClickListener(mItemClickListener);//리스너 연결

        

        refreshFiles();

        

    }

    

    //리스트뷰 클릭 리스너

    AdapterView.OnItemClickListener mItemClickListener =

            new AdapterView.OnItemClickListener() {

 

                @Override

                public void onItemClick(AdapterView<?> parent, View view,

                        int position, long id) {

                    // TODO Auto-generated method stub

                    String Name = arFiles.get(position);//클릭된 위치의 값을 가져옴

                    

                    //디렉토리이면 

                    if(Name.startsWith("[") && Name.endsWith("]")){

                        Name = Name.substring(1, Name.length() - 1);//[]부분을 제거해줌

                    }

                    //들어가기 위해 / 터치한 파일 명을 붙여줌

                    String Path = mCurrent + "/" + Name;

                    File f = new File(Path);//File 클래스 생성

                    if(f.isDirectory()){//디렉토리면?

                        mCurrent = Path;//현재를 Path 바꿔줌

                        refreshFiles();//리프레쉬

                    }else{//디렉토리가 아니면 토스트 메세지를 뿌림

                    Toast.makeText(FileExplorer.this, arFiles.get(position), 0).show();

                }

            }

    };

    

    //버튼 2 클릭시

    public void mOnClick(View v){

        switch(v.getId()){

        case R.id.btnroot://루트로 가기

            if(mCurrent.compareTo(mRoot) != 0){//루트가 아니면 루트로 가기

                mCurrent = mRoot;

                refreshFiles();//리프레쉬

            }

            break;

        case R.id.btnup:

            if(mCurrent.compareTo(mRoot) != 0){//루트가 아니면

                int end = mCurrent.lastIndexOf("/");/// 나오는 마지막 인덱스를 찾고

                String uppath = mCurrent.substring(0, end);//그부분을 짤라버림  위로가게됨

                mCurrent = uppath;

                refreshFiles();//리프레쉬

            }

            break;

        }

    }

      

    

    void refreshFiles(){

        mCurrentTxt.setText(mCurrent);//현재 PATH 가져옴

        arFiles.clear();//배열리스트를 지움

        File current = new File(mCurrent);//현재 경로로 File클래스를 만듬

        String[] files = current.list();//현재 경로의 파일과 폴더 이름을 문자열 배열로 리턴

        

        //파일이 있다면?

        if(files != null){

            //여기서 출력을 해줌

            for(int i = 0; i < files.length;i++){

                String Path = mCurrent + "/" + files[i];

                String Name = "";

                

                File f = new File(Path);

                if(f.isDirectory()){

                    Name = "[" + files[i] + "]";//디렉토리면 [] 붙여주고

                }else{

                    Name = files[i];//파일이면 그냥 출력

                }

                

                arFiles.add(Name);//배열리스트에 추가해줌

            }

        }

        //다끝나면 리스트뷰를 갱신시킴

        mAdapter.notifyDataSetChanged();

    }

}

 

 

 

 

출력 화면

 



출처: https://gakari.tistory.com/entry/안드로이드-파일-탐색기-만들기 [가카리의 공부방]

 

 

 

 

 

반응형

댓글()