일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 2022 플러터 설치
- 스택 자바 코드
- 자바 다형성
- 플러터 설치 2022
- 서비스 vs 쓰레드
- 안드로이드 레트로핏 crud
- 안드로이드 레트로핏 사용법
- android retrofit login
- 큐 자바 코드
- 스택 큐 차이
- jvm 작동 원리
- 클래스
- rxjava cold observable
- Rxjava Observable
- rxjava hot observable
- rxjava disposable
- 안드로이드 라이선스 종류
- 2022 플러터 안드로이드 스튜디오
- 객체
- android ar 개발
- 멤버변수
- ar vr 차이
- jvm이란
- ANR이란
- 안드로이드 유닛 테스트
- 안드로이드 os 구조
- 안드로이드 유닛 테스트 예시
- 서비스 쓰레드 차이
- 안드로이드 라이선스
- 안드로이드 유닛테스트란
- Today
- Total
나만을 위한 블로그
[Android] 구글 드라이브 API 사용법 본문
먼저 구글 API 콘솔에서 구글 드라이브 API를 쓰는 프로젝트를 만들고, 구글 드라이브 API를 사용중으로 바꿔야 한다.
이 때 OAuth라는 것을 설정해야 한다. 그냥 구글 드라이브 API를 적용할 패키지 이름(매니페스트에서 확인 가능), SHA-1 인증서 디지털 지문만 써주고 확인 누르면 끝난다.
SHA-1을 얻는 법은 예전에 써뒀다. 모른다면 참고하자
https://onlyfor-me-blog.tistory.com/44
OAuth를 설정하면 Client ID라는 문자열을 받게 되는데, 여기선 쓰이지 않지만 혹시 모르니 메모장 같은 곳에 복붙해둬서 금방 찾을 수 있는 곳에 저장해두자.
그 다음 매니페스트에 권한들을 추가해준다.
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
그리고 앱 수준 gradle에 아래의 의존성들을 추가한다.
implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation 'com.google.http-client:google-http-client-gson:1.26.0'
implementation('com.google.api-client:google-api-client-android:1.26.0') {
exclude group: 'org.apache.httpcomponents'
}
implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0') {
exclude group: 'org.apache.httpcomponents'
}
그리고 DriveServiceHelper.java라는 헬퍼 클래스를 하나 만든다.
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.util.Pair;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* A utility for performing read/write operations on Drive files via the REST API and opening a
* file picker UI via Storage Access Framework.
*/
public class DriveServiceHelper {
private final Executor mExecutor = Executors.newSingleThreadExecutor();
private final Drive mDriveService;
public DriveServiceHelper(Drive driveService) {
mDriveService = driveService;
}
/**
* Creates a text file in the user's My Drive folder and returns its file ID.
*/
public Task<String> createFile() {
return Tasks.call(mExecutor, () -> {
File metadata = new File()
.setParents(Collections.singletonList("root"))
.setMimeType("text/plain")
.setName("Untitled file");
File googleFile = mDriveService.files().create(metadata).execute();
if (googleFile == null) {
throw new IOException("Null result when requesting file creation.");
}
return googleFile.getId();
});
}
/**
* Opens the file identified by {@code fileId} and returns a {@link Pair} of its name and
* contents.
*/
public Task<Pair<String, String>> readFile(String fileId) {
return Tasks.call(mExecutor, () -> {
// Retrieve the metadata as a File object.
File metadata = mDriveService.files().get(fileId).execute();
String name = metadata.getName();
// Stream the file contents to a String.
try (InputStream is = mDriveService.files().get(fileId).executeMediaAsInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
String contents = stringBuilder.toString();
return Pair.create(name, contents);
}
});
}
/**
* Updates the file identified by {@code fileId} with the given {@code name} and {@code
* content}.
*/
public Task<Void> saveFile(String fileId, String name, String content) {
return Tasks.call(mExecutor, () -> {
// Create a File containing any metadata changes.
File metadata = new File().setName(name);
// Convert content to an AbstractInputStreamContent instance.
ByteArrayContent contentStream = ByteArrayContent.fromString("text/plain", content);
// Update the metadata and contents.
mDriveService.files().update(fileId, metadata, contentStream).execute();
return null;
});
}
/**
* Returns a {@link FileList} containing all the visible files in the user's My Drive.
*
* <p>The returned list will only contain files visible to this app, i.e. those which were
* created by this app. To perform operations on files not created by the app, the project must
* request Drive Full Scope in the <a href="https://play.google.com/apps/publish">Google
* Developer's Console</a> and be submitted to Google for verification.</p>
*/
public Task<FileList> queryFiles() {
return Tasks.call(mExecutor, new Callable<FileList>() {
@Override
public FileList call() throws Exception {
return mDriveService.files().list().setSpaces("drive").execute();
}
});
}
/**
* Returns an {@link Intent} for opening the Storage Access Framework file picker.
*/
public Intent createFilePickerIntent() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/plain");
return intent;
}
/**
* Opens the file at the {@code uri} returned by a Storage Access Framework {@link Intent}
* created by {@link #createFilePickerIntent()} using the given {@code contentResolver}.
*/
public Task<Pair<String, String>> openFileUsingStorageAccessFramework(
ContentResolver contentResolver, Uri uri) {
return Tasks.call(mExecutor, () -> {
// Retrieve the document's display name from its metadata.
String name;
try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
name = cursor.getString(nameIndex);
} else {
throw new IOException("Empty cursor returned for file.");
}
}
// Read the document's contents as a String.
String content;
try (InputStream is = contentResolver.openInputStream(uri);
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
content = stringBuilder.toString();
}
return Pair.create(name, content);
});
}
}
다음은 MainActivity.java 파일과 xml의 코드 구성이다.
테스트 프로젝트가 아닌 본 앱에 그대로 붙이겠다면, 다른 액티비티를 하나 만들어서 TAG 안의 MainActivity 문자열만 자신이 만든 액티비티명으로 바꿔주면 된다.
새 프로젝트를 파서 MainActivity에서 테스트해보겠다면 그냥 복붙하면 된다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/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"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
tools:context=".MainActivity">
<EditText
android:id="@+id/file_title_edittext"
android:layout_height="48dp"
android:layout_width="match_parent"
android:layout_alignParentTop="true" />
<EditText
android:id="@+id/doc_content_edittext"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_below="@id/file_title_edittext"
android:layout_above="@id/btn_layout" />
<LinearLayout
android:id="@+id/btn_layout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal"
android:layout_alignParentBottom="true">
<Button
android:id="@+id/open_btn"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:text="Open"
style="?attr/buttonBarButtonStyle" />
<Button
android:id="@+id/create_btn"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:text="Create"
style="?attr/buttonBarButtonStyle" />
<Button
android:id="@+id/save_btn"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:text="Save"
style="?attr/buttonBarButtonStyle" />
<Button
android:id="@+id/query_btn"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:text="Query"
style="?attr/buttonBarButtonStyle" />
</LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.api.Scope;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import android.widget.EditText;
import java.util.Collections;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int REQUEST_CODE_SIGN_IN = 1;
private static final int REQUEST_CODE_OPEN_DOCUMENT = 2;
private DriveServiceHelper mDriveServiceHelper;
private String mOpenFileId;
private EditText mFileTitleEditText;
private EditText mDocContentEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Store the EditText boxes to be updated when files are opened/created/modified.
mFileTitleEditText = findViewById(R.id.file_title_edittext);
mDocContentEditText = findViewById(R.id.doc_content_edittext);
// Set the onClick listeners for the button bar.
findViewById(R.id.open_btn).setOnClickListener(view -> openFilePicker());
findViewById(R.id.create_btn).setOnClickListener(view -> createFile());
findViewById(R.id.save_btn).setOnClickListener(view -> saveFile());
findViewById(R.id.query_btn).setOnClickListener(view -> query());
// Authenticate the user. For most apps, this should be done when the user performs an
// action that requires Drive access rather than in onCreate.
requestSignIn();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
switch (requestCode) {
case REQUEST_CODE_SIGN_IN:
if (resultCode == Activity.RESULT_OK && resultData != null) {
handleSignInResult(resultData);
}
break;
case REQUEST_CODE_OPEN_DOCUMENT:
if (resultCode == Activity.RESULT_OK && resultData != null) {
Uri uri = resultData.getData();
if (uri != null) {
openFileFromFilePicker(uri);
}
}
break;
}
super.onActivityResult(requestCode, resultCode, resultData);
}
/**
* Starts a sign-in activity using {@link #REQUEST_CODE_SIGN_IN}.
*/
private void requestSignIn() {
Log.d(TAG, "Requesting sign-in");
GoogleSignInOptions signInOptions =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestScopes(new Scope(DriveScopes.DRIVE_FILE))
.build();
GoogleSignInClient client = GoogleSignIn.getClient(this, signInOptions);
// The result of the sign-in Intent is handled in onActivityResult.
startActivityForResult(client.getSignInIntent(), REQUEST_CODE_SIGN_IN);
}
/**
* Handles the {@code result} of a completed sign-in activity initiated from {@link
* #requestSignIn()}.
*/
private void handleSignInResult(Intent result) {
GoogleSignIn.getSignedInAccountFromIntent(result)
.addOnSuccessListener(googleAccount -> {
Log.d(TAG, "Signed in as " + googleAccount.getEmail());
// Use the authenticated account to sign in to the Drive service.
GoogleAccountCredential credential =
GoogleAccountCredential.usingOAuth2(
this, Collections.singleton(DriveScopes.DRIVE_FILE));
credential.setSelectedAccount(googleAccount.getAccount());
Drive googleDriveService =
new Drive.Builder(
AndroidHttp.newCompatibleTransport(),
new GsonFactory(),
credential)
.setApplicationName("Drive API Migration")
.build();
// The DriveServiceHelper encapsulates all REST API and SAF functionality.
// Its instantiation is required before handling any onClick actions.
mDriveServiceHelper = new DriveServiceHelper(googleDriveService);
})
.addOnFailureListener(exception -> Log.e(TAG, "Unable to sign in.", exception));
}
/**
* Opens the Storage Access Framework file picker using {@link #REQUEST_CODE_OPEN_DOCUMENT}.
*/
private void openFilePicker() {
if (mDriveServiceHelper != null) {
Log.d(TAG, "Opening file picker.");
Intent pickerIntent = mDriveServiceHelper.createFilePickerIntent();
// The result of the SAF Intent is handled in onActivityResult.
startActivityForResult(pickerIntent, REQUEST_CODE_OPEN_DOCUMENT);
}
}
/**
* Opens a file from its {@code uri} returned from the Storage Access Framework file picker
* initiated by {@link #openFilePicker()}.
*/
private void openFileFromFilePicker(Uri uri) {
if (mDriveServiceHelper != null) {
Log.d(TAG, "Opening " + uri.getPath());
mDriveServiceHelper.openFileUsingStorageAccessFramework(getContentResolver(), uri)
.addOnSuccessListener(nameAndContent -> {
String name = nameAndContent.first;
String content = nameAndContent.second;
mFileTitleEditText.setText(name);
mDocContentEditText.setText(content);
// Files opened through SAF cannot be modified.
setReadOnlyMode();
})
.addOnFailureListener(exception ->
Log.e(TAG, "Unable to open file from picker.", exception));
}
}
/**
* Creates a new file via the Drive REST API.
*/
private void createFile() {
if (mDriveServiceHelper != null) {
Log.d(TAG, "Creating a file.");
mDriveServiceHelper.createFile()
.addOnSuccessListener(fileId -> readFile(fileId))
.addOnFailureListener(exception ->
Log.e(TAG, "Couldn't create file.", exception));
}
}
/**
* Retrieves the title and content of a file identified by {@code fileId} and populates the UI.
*/
private void readFile(String fileId) {
if (mDriveServiceHelper != null) {
Log.d(TAG, "Reading file " + fileId);
mDriveServiceHelper.readFile(fileId)
.addOnSuccessListener(nameAndContent -> {
String name = nameAndContent.first;
String content = nameAndContent.second;
mFileTitleEditText.setText(name);
mDocContentEditText.setText(content);
setReadWriteMode(fileId);
})
.addOnFailureListener(exception ->
Log.e(TAG, "Couldn't read file.", exception));
}
}
/**
* Saves the currently opened file created via {@link #createFile()} if one exists.
*/
private void saveFile() {
if (mDriveServiceHelper != null && mOpenFileId != null) {
Log.d(TAG, "Saving " + mOpenFileId);
String fileName = mFileTitleEditText.getText().toString();
String fileContent = mDocContentEditText.getText().toString();
mDriveServiceHelper.saveFile(mOpenFileId, fileName, fileContent)
.addOnFailureListener(exception ->
Log.e(TAG, "Unable to save file via REST.", exception));
}
}
/**
* Queries the Drive REST API for files visible to this app and lists them in the content view.
*/
private void query() {
if (mDriveServiceHelper != null) {
Log.d(TAG, "Querying for files.");
mDriveServiceHelper.queryFiles()
.addOnSuccessListener(fileList -> {
StringBuilder builder = new StringBuilder();
for (File file : fileList.getFiles()) {
builder.append(file.getName()).append("\n");
}
String fileNames = builder.toString();
mFileTitleEditText.setText("File List");
mDocContentEditText.setText(fileNames);
setReadOnlyMode();
})
.addOnFailureListener(exception -> Log.e(TAG, "Unable to query files.", exception));
}
}
/**
* Updates the UI to read-only mode.
*/
private void setReadOnlyMode() {
mFileTitleEditText.setEnabled(false);
mDocContentEditText.setEnabled(false);
mOpenFileId = null;
}
/**
* Updates the UI to read/write mode on the document identified by {@code fileId}.
*/
private void setReadWriteMode(String fileId) {
mFileTitleEditText.setEnabled(true);
mDocContentEditText.setEnabled(true);
mOpenFileId = fileId;
}
}
이렇게 한 후 구글 드라이브 화면을 켜놓은 다음 앱을 빌드하자. 이 액티비티로 들어가면 처음에 구글 계정을 선택하는 작은 다이얼로그가 나온다.
거기서 사용할 구글 계정을 선택한 후, CREATE 버튼을 누른 다음 상단부에 저장할 파일의 제목을 적고, 그 밑에 내용을 적는다.
그 후 SAVE 버튼을 누르고 컴퓨터에 켜진 구글 드라이브 화면을 새로고침하면 내가 입력한 제목과 내용의 파일이 구글 드라이브에 저장돼 있는 걸 볼 수 있다.
주의할 것은 OPEN으로 구글 드라이브에 저장된 파일을 열 때는 읽기 모드로 열려져서 제목과 내용을 수정할 수 없다.
'Android' 카테고리의 다른 글
[Android] getApplicationContext() vs XXXActvity.this의 차이 (0) | 2020.09.21 |
---|---|
[Android] android.os.FileUriExposedException: exposed beyond app through Intent.getData() 에러 해결 (0) | 2020.09.18 |
[Android] 버튼 하나로 레이아웃 나타나게 하거나 사라지게 하는 법 (0) | 2020.07.13 |
[Android] 리사이클러뷰 아이템의 position 값을 액티비티에서 알아내고 인텐트로 넘기는 방법 (0) | 2020.07.10 |
[Android] 엑셀 모듈 설치 후 삭제하며 했던 행동들 (0) | 2020.07.09 |