eunja511005 / AutoCoding

0 stars 0 forks source link

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

Open eunja511005 opened 10 months ago

eunja511005 commented 10 months ago

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

1. File > New > New Project...

image


image


image


image


image

eunja511005 commented 10 months 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 10 months 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 10 months 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 10 months 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 10 months 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 10 months ago

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

image

eunja511005 commented 10 months ago

image

eunja511005 commented 10 months ago

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

eunja511005 commented 10 months ago

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

image


image

eunja511005 commented 10 months ago

9. 모바일로 배포

image

eunja511005 commented 10 months ago

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

android:usesCleartextTraffic="true"

image

eunja511005 commented 10 months ago

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

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

image

eunja511005 commented 10 months 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 10 months 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);
    }