eunja511005 / AutoCoding

0 stars 0 forks source link

안드로이드 스튜디오에서 유저 등록 앱 만들기 #132

Open eunja511005 opened 1 year ago

eunja511005 commented 1 year ago

1. 안드로이드 스튜디오에서 신규 프로젝트 생성

1. File > New > New Project...

image


image


image


image


image

eunja511005 commented 1 year ago

2. res/layout/activity_main.xml 파일 수정

image


image

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/selectedImage"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:src="@drawable/ic_launcher_foreground" />

    <EditText
        android:id="@+id/nameEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/selectedImage"
        android:hint="Enter Name"
        android:padding="16dp" />

    <Button
        android:id="@+id/selectImageButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/nameEditText"
        android:layout_centerHorizontal="true"
        android:text="Select Image" />

    <Button
        android:id="@+id/uploadButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/selectImageButton"
        android:layout_centerHorizontal="true"
        android:text="Upload" />

</RelativeLayout>
eunja511005 commented 1 year ago

3. java/com/example/mysendimageapplication/MainActivity.java 수정

MainActivity.java 파일을 열고, 다음과 같이 selectImageButton 버튼 클릭 이벤트를 처리합니다.

 

public class MainActivity extends AppCompatActivity {

    private static final int PICK_IMAGE_REQUEST = 1;
    private ImageView selectedImage;
    private EditText nameEditText;
    private Uri selectedImageUri;

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

        selectedImage = findViewById(R.id.selectedImage);
        nameEditText = findViewById(R.id.nameEditText);

        Button selectImageButton = findViewById(R.id.selectImageButton);
        selectImageButton.setOnClickListener(view -> openGallery());
    }

    private void openGallery() {
        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, PICK_IMAGE_REQUEST);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
            selectedImageUri = data.getData();
            selectedImage.setImageURI(selectedImageUri);
        }
    }
}
eunja511005 commented 1 year ago

4. Retrofit 라이브러리를 사용하기 위해 build.gradle 파일에 아래 의존성을 추가

    // Retrofit2
    implementation 'com.squareup.retrofit2:retrofit:2.7.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.7.1'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.3.1'

image

eunja511005 commented 1 year ago

5. Retrofit 서비스 인터페이스를 정의합니다. ApiService.java 파일을 생성하고 다음과 같이 작성합니다.

import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;

public interface ApiService {
    @Multipart
    @POST("save")
    Call<ApiResponse> saveUserManage(
        @Part MultipartBody.Part file,
        @Part("name") RequestBody name
    );
}

image

eunja511005 commented 1 year ago

6. AndroidManifest.xml 권한 추가

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

image

eunja511005 commented 1 year ago

7. 실행을 위한 모바일 장치 생성

image

eunja511005 commented 1 year ago

image

eunja511005 commented 1 year ago

중요 : 안드로이드 스튜디오는 꼭 관리자 권한으로 실행해야 설치할 파일들이 제대로 설치된다.

eunja511005 commented 1 year ago

8. 모바일 개발자 모드 켜기

image


image

eunja511005 commented 1 year ago

9. 모바일로 배포

image

eunja511005 commented 1 year ago

10. HTTPS가 아니라 HTTP로 전송해도 오류 안나게 하려면 아래 내용 추가해야 함

android:usesCleartextTraffic="true"

image

eunja511005 commented 1 year ago

[중요] 아래 오류 발생시 해결 방법

com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $

image

eunja511005 commented 1 year ago

최종 MainActivity 소스

package com.example.mysendimageapplication;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.File;
import java.io.IOException;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

    private static final String BASE_URL = "http://192.168.219.106:8080/";
    private ImageView selectedImage;
    private EditText nameEditText;
    private Uri selectedImageUri;
    private String token;

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

        selectedImage = findViewById(R.id.selectedImage);
        nameEditText = findViewById(R.id.nameEditText);

        Button selectImageButton = findViewById(R.id.selectImageButton);
        selectImageButton.setOnClickListener(view -> openGallery());

        Button uploadButton = findViewById(R.id.uploadButton);
        uploadButton.setOnClickListener(view -> getToken());

    }

    private void openGallery() {
        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        imagePickerLauncher.launch(intent);
    }

    private ActivityResultLauncher<Intent> imagePickerLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                    selectedImageUri = result.getData().getData();
                    selectedImage.setImageURI(selectedImageUri);
                }
            }
    );

    private ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(
            new ActivityResultContracts.RequestPermission(), isGranted -> {
                if (isGranted) {
                    // 권한이 허용된 경우
                    uploadData();
                } else {
                    // 권한이 거부된 경우
                    // 사용자에게 권한 필요 안내 등을 표시할 수 있음
                }
            });

    private void uploadData() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) {

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            ApiService apiService = retrofit.create(ApiService.class);

            if (token != null) {

                // 추가할 Authorization 헤더
                String authorizationHeader = "Bearer " + token;

                String name = nameEditText.getText().toString();

                RequestBody requestFile = RequestBody.create(MediaType.parse("image/*"), new File(getRealPathFromURI(selectedImageUri)));
                MultipartBody.Part photoPart = MultipartBody.Part.createFormData("file", "image.jpg", requestFile);

                RequestBody nameBody = RequestBody.create(MediaType.parse("text/plain"), name);

                Call<ApiResponse> call = apiService.saveUserManage(authorizationHeader, photoPart, nameBody);
                call.enqueue(new Callback<ApiResponse>() {
                    @Override
                    public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) {
                        if (response.isSuccessful()) {
                            // 서버 응답 처리
                            ApiResponse apiResponse = response.body();
                            if (apiResponse != null) {
                                // 성공한 경우
                                Log.d("API_SUCCESS", apiResponse.getMessage());
                            }
                        } else {
                            // 서버 응답은 받았으나 성공하지 않은 경우
                            Log.e("API_ERROR", "Server responded with error code: " + response.code());
                        }
                    }

                    @Override
                    public void onFailure(Call<ApiResponse> call, Throwable t) {
                        // 요청 실패
                        Log.e("API_FAILURE", "API request failed", t);
                    }
                });
            }
        } else {
            // 권한이 허용되지 않은 경우
            requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE);
        }
    }

    private String getRealPathFromURI(Uri contentUri) {
        String[] projection = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(contentUri, projection, null, null, null);
        if (cursor == null) return null;
        int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        String path = cursor.getString(columnIndex);
        cursor.close();
        return path;
    }

    private void getToken() {
        Gson gson = new GsonBuilder()
                .setLenient()
                .create();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL) // 실제 서버 주소로 변경해야 함
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

        ApiService apiService = retrofit.create(ApiService.class);

        UserInfoDTO userInfoDTO = new UserInfoDTO();
        userInfoDTO.setUsername("apiUser");
        userInfoDTO.setPassword("jw0713!@");

        Call<String> call = apiService.getToken(userInfoDTO);
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                if (response.isSuccessful()) {
                    token = response.body();
                    uploadData();
                } else {
                    // 서버 응답은 받았으나 인증 실패 또는 에러인 경우
                    Log.e("API_ERROR", "Server responded with error code: " + response.code());
                    try {
                        String errorBody = response.errorBody().string();
                        Log.e("API_ERROR_BODY", "Error body: " + errorBody);
                    } catch (IOException e) {
                        Log.e("API_ERROR", "Error while parsing error body", e);
                    }
                }
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                // 요청 실패
                Log.e("API_FAILURE", "API request failed", t);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK && data != null) {
            selectedImageUri = data.getData();
            selectedImage.setImageURI(selectedImageUri);
        }
    }
}
eunja511005 commented 1 year ago

파일을 받기 위한 서버 소스

    @PostMapping("/mobile/image/save")
    public @ResponseBody ApiResponse saveUserManage(@RequestParam("file") MultipartFile file, UserManageDTO userManageDTO) throws IOException {
        userManageDTO.setPicture(fileUtil.saveImage(file, "openImg/mobile"));
        //userManageService.mergeUser(userManageDTO);
        return new ApiResponse<>(true, "Success save", null);
    }