๋ค์ ๊ฒ์๋ฌผ์ ์ฐธ๊ณ ํด, ๋ฉ์ธ์กํฐ๋นํฐ์ ํ์๊ฐ์
๊ณผ ๋ก๊ทธ์ธ ํ๋ฉด๊ณผ Activity๋ฅผ ๋ง๋ค๊ณ , config, string์ ์์ฑํด์ค๋ค.
https://codebunny99.tistory.com/170
ํ์๊ฐ์ , ๋ก๊ทธ์ธ, ๋ก๊ทธ์์ ๊ธฐ๋ฅ ์ฑ(app)์ Retrofit ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ง๋ค๊ธฐ
Restful API(๋ฉ๋ชจ API)๋ฅผ ๋ง๋ค๊ณ , MySQL๊ณผ ์ฐ๋ํ์ฌ Postman์ผ๋ก ๊ฐ๋ฐํ ํ, ์งํํ์๋ค. ์กํฐ๋นํฐ ์ธ ๊ฐ ๋ง๋ค๊ธฐ ํ๊ฒฝ๋ณ์ ์ค์ ํ๊ธฐ Retrofit ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์นํ๊ธฐ build.gradle.kts(:app)์์impleme
codebunny99.tistory.com
config์ ๋๋ฉ์ธ ์ฃผ์๋,
ํฌ์คํ API๋ฅผ ๋ง๋ vscode๋ฅผ sls deploy ํด์ค ํ, ๋์จ endpoint ์ฃผ์๋ก ํ์๋ค
https://codebunny99.tistory.com/117
instargram ์ฌ์ง ํฌ์คํ API (1) ๊ธฐ๋ณธ ์ธํ (Setting) ํ๊ธฐ
API ๋ง๋ค๊ธฐ ์ ์, ๊ธฐ๋ณธ ์ ํ ํ๊ธฐ (1). serverless๋ก aws-posting-server๋ฅผ ๋ง๋ค๊ณ vscode๋ก ์ด์ด์ฃผ์๋ค. https://codebunny99.tistory.com/105 RestFul API๋ฅผ Serverless Framework๋ก ์ฐ๊ฒฐํ๊ธฐ*** ์๋์ฝ๋ค ํ๋กฌํํธ์์ ๊ฐ์
codebunny99.tistory.com
ํฌ์คํธ๋งจ์์๋ ๋๋ฉ์ธ ์ฃผ์๋ก ์ ์คํ๋๋์ง ์ ๊ฒํ ํ ์งํํ์๋ค.
API ๋ง๋ค๊ธฐ
NetworkClient
package com.~.postingapp.api;
import android.content.Context;
import com.~.postingapp.config.Config;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class NetworkClient {
public static Retrofit retrofit;
public static Retrofit getRetrofitClient(Context context){
if(retrofit == null){
// ํต์ ๋ก๊ทธ ํ์ธํ ๋ ํ์ํ ์ฝ๋
HttpLoggingInterceptor loggingInterceptor =
new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// ๋คํธ์ํฌ ์ฐ๊ฒฐ๊ด๋ จ ์ฝ๋
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.addInterceptor(loggingInterceptor)
.build();
// ๋คํธ์ํฌ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ณ ๋ฐ๋
// ๋ ํธ๋กํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ด๋ จ ์ฝ๋
retrofit = new Retrofit.Builder()
.baseUrl(Config.DOMAIN)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
UserApi
package com.~.postingapp.api;
import com.~.postingapp.model.User;
import com.~.postingapp.model.UserRes;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.Header;
import retrofit2.http.POST;
public interface UserApi {
// ํ์๊ฐ์
API
// ํจ์๋ช
์ ์์ฑํด์ฃผ๊ณ , ๋ณด๋ผ ๋ฐ์ดํฐ๋ ํ๋ผ๋ฏธํฐ์ ์์ฑ, ๋ฐ์ ๋ฐ์ดํฐ๋ ๋ฆฌํดํ์
์ ์์ฑ.
@POST("/user/register")
Call<UserRes> register(@Body User user);
// ๋ก๊ทธ์ธ API
@POST("/user/login")
Call<UserRes> login(@Body User user);
// ๋ก๊ทธ์์ API
@DELETE("/user/logout")
Call<UserRes> logout(@Header("Authorization") String token);
}
model ํจํค์ง User, UserRes ํด๋์ค ๋ง๋ค๊ธฐ
fileprovider.xml ํ์ผ ์์ฑ ํ ์์ฑ
- ์นด๋ฉ๋ผ์ ์จ๋ฒ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ฝ๋์ด๋ค.
<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path
name="root"
path="." />
<cache-path
name="cache"
path="." /> <!--Context.getCacheDir() ๋ด๋ถ ์ ์ฅ์-->
<files-path
name="files"
path="." /> <!--Context.getFilesDir() ๋ด๋ถ ์ ์ฅ์-->
<external-path
name="external"
path="."/> <!-- Environment.getExternalStorageDirectory() ์ธ๋ถ ์ ์ฅ์-->
<external-cache-path
name="external-cache"
path="."/> <!-- Context.getExternalCacheDir() ์ธ๋ถ ์ ์ฅ์-->
<external-files-path
name="images"
path="Pictures" /> <!-- Context.getExternalFilesDir() ์ธ๋ถ ์ ์ฅ์-->
</paths>
AndroidManifest.xml ์ค์
- ํ๊ฒฝ์ค์ ํ๊ธฐ
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:name="android.hardware.camera" android:required="true"/>
<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"/>
๊ฒ์ ์์ผ๋ก ์ง์ด ๋ถ๋ถ์, ์๋์์ฑ ๋ ๊ฒ์ด๋ค.
<provider
android:authorities="com.~.cameraapp.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/fileprovider"/>
</provider>
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
build.gradle.kts(:app) ์์, ๋งจ ์๋ซ์ค ์์ฑ.
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation("com.github.bumptech.glide:glide:4.16.0")
implementation("commons-io:commons-io:2.4")
activity_main.xml
ํ๋ฉด ์์ฑ
<?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:id="@+id/btnAdd"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/button" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="10dp"
android:text="ํฌ์คํ
์์ฑ"
android:textSize="24sp" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Activity ๋ง๋ค์ด์, activity_add.xml, AddActivity.java ์์ฑํ๊ธฐ
ํ๋ฉด ์์ฑ
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AddActivity">
<ImageView
android:id="@+id/imgPhoto"
android:layout_width="227dp"
android:layout_height="235dp"
android:layout_marginStart="80dp"
android:layout_marginTop="96dp"
android:layout_marginEnd="80dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/add_photo" />
<Button
android:id="@+id/btnSave"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="10dp"
android:text="ํฌ์คํธ ์์ฑ"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editContent" />
<EditText
android:id="@+id/editContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="36dp"
android:layout_marginEnd="10dp"
android:ems="10"
android:hint="๋ด์ฉ ์
๋ ฅ..."
android:inputType="text"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imgPhoto" />
</androidx.constraintlayout.widget.ConstraintLayout>
Addactivity.java
- ํ๋ฉด๊ณผ ์ฐ๊ฒฐํ๊ณ ๋ก์ง์ ์์ฑํ๊ณ ์นด๋ฉ๋ผ, ์จ๋ฒ์ ์ฌ์ฉํ๊ธฐ ์ํ ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
package com.~.postingapp;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.drawable.ColorDrawable;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.google.android.material.snackbar.Snackbar;
import com.~.postingapp.api.NetworkClient;
import com.~.postingapp.api.PostingApi;
import com.~.postingapp.config.Config;
import com.~.postingapp.model.Res;
import android.Manifest;
import org.apache.commons.io.IOUtils;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class AddActivity extends AppCompatActivity {
ImageView imgPhoto;
EditText editContent;
Button btnSave;
File photoFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add);
imgPhoto = findViewById(R.id.imgPhoto);
editContent = findViewById(R.id.editContent);
btnSave = findViewById(R.id.btnSave);
// ๋ฒํผ ๋๋ฅด๋ฉด, ์นด๋ฉ๋ผ๋ก ์ฐ์๊ฒ์ธ์ง, ์จ๋ฒ์์ ๊ณ ๋ฅผ๊ฒ์ธ์ง์ ๋ํ
// ์๋ฌํธ ๋ค์ด์ผ๋ก๊ทธ ๋์ด๋ค.
imgPhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showDialog();
}
});
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// ํ์ผ์ด ์์ด์ผ ํ๊ณ ๋ด์ฉ์ด ์์ด์ผ ํ๋ค
String content = editContent.getText().toString().trim();
if(content.isEmpty() || photoFile == null){
Snackbar.make(btnSave, "์ฌ์ง๊ณผ ๋ด์ฉ์ ํ์ํญ๋ชฉ์
๋๋ค",Snackbar.LENGTH_SHORT).show();
return;
}
// ๋ ํธ๋กํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด์ฉํด์ ๋คํธ์ํฌ๋ก ํธ์ถํ๋ค.
showProgress();
Retrofit retrofit = NetworkClient.getRetrofitClient(AddActivity.this);
PostingApi api = retrofit.create(PostingApi.class);
SharedPreferences sp = getSharedPreferences(Config.SP_NAME, MODE_PRIVATE);
String token = sp.getString("token","");
// ๋ณด๋ผ ์ด๋ฏธ์ง ํ์ผ ๋ง๋ค๊ธฐ
RequestBody fileBody = RequestBody.create(photoFile, MediaType.parse("image/jpeg"));
MultipartBody.Part image = MultipartBody.Part.createFormData("image", photoFile.getName(), fileBody);
// ๋ณด๋ผ ํ
์คํธ(content) ๋ง๋ค๊ธฐ
RequestBody textBody = RequestBody.create(content, MediaType.parse("text/plain"));
Call<Res> call = api.addPosting("Bearer " + token, image, textBody);
call.enqueue(new Callback<Res>() {
@Override
public void onResponse(Call<Res> call, Response<Res> response) {
dismissProgress();
if(response.isSuccessful()){
Snackbar.make(btnSave, "์ ์ ์ฅ๋์์ต๋๋ค.", Snackbar.LENGTH_SHORT).show();
finish();
} else {
}
}
@Override
public void onFailure(Call<Res> call, Throwable throwable) {
dismissProgress();
}
});
}
});
}
private void showDialog(){
AlertDialog.Builder builder = new AlertDialog.Builder(AddActivity.this);
builder.setTitle(R.string.alert_title);
builder.setItems(R.array.alert_photo, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if(i == 0){
// ์ฒซ๋ฒ์งธ ํญ๋ชฉ ๋๋ ์๋
// ์นด๋ฉ๋ผ๋ก ์ฌ์ง์ฐ๊ธฐ
camera();
}else if(i == 1){
// ๋๋ฒ์งธ ํญ๋ชฉ ๋๋ ์๋
album();
}
}
});
builder.show();
}
private void camera(){
int permissionCheck = ContextCompat.checkSelfPermission(
AddActivity.this, Manifest.permission.CAMERA);
if(permissionCheck != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(AddActivity.this,
new String[]{android.Manifest.permission.CAMERA} ,
1000);
Toast.makeText(AddActivity.this, "์นด๋ฉ๋ผ ๊ถํ ํ์ํฉ๋๋ค.",
Toast.LENGTH_SHORT).show();
return;
} else {
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if(i.resolveActivity(AddActivity.this.getPackageManager()) != null ){
// ์ฌ์ง์ ํ์ผ๋ช
์ ๋ง๋ค๊ธฐ
String fileName = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
photoFile = getPhotoFile(fileName);
Uri fileProvider = FileProvider.getUriForFile(AddActivity.this,Config.FILE_PROVIDER,photoFile);
i.putExtra(MediaStore.EXTRA_OUTPUT, fileProvider);
startActivityForResult(i, 100);
} else{
Toast.makeText(AddActivity.this, "์ดํฐ์๋ ์นด๋ฉ๋ผ ์ฑ์ด ์์ต๋๋ค.",
Toast.LENGTH_SHORT).show();
}
}
}
private void album(){
if(checkPermission()){
displayFileChoose();
}else{
requestPermission();
}
}
private boolean checkPermission(){
int result = ContextCompat.checkSelfPermission(AddActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(result == PackageManager.PERMISSION_DENIED){
return false;
}else{
return true;
}
}
private void requestPermission() {
if(ActivityCompat.shouldShowRequestPermissionRationale(AddActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)){
Log.i("DEBUGGING5", "true");
Toast.makeText(AddActivity.this, "๊ถํ ์๋ฝ์ด ํ์ํฉ๋๋ค.",
Toast.LENGTH_SHORT).show();
}else{
Log.i("DEBUGGING6", "false");
ActivityCompat.requestPermissions(AddActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 500);
}
}
private void displayFileChoose() {
Intent i = new Intent();
i.setType("image/*");
i.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(i, "SELECT IMAGE"), 300);
}
private File getPhotoFile(String fileName) {
File storageDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
try{
return File.createTempFile(fileName, ".jpg", storageDirectory);
}catch (IOException e){
e.printStackTrace();
return null;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1000: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(AddActivity.this, "๊ถํ ํ๊ฐ ๋์์",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(AddActivity.this, "์์ง ์น์ธํ์ง ์์์",
Toast.LENGTH_SHORT).show();
}
break;
}
case 500: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(AddActivity.this, "๊ถํ ํ๊ฐ ๋์์",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(AddActivity.this, "์์ง ์น์ธํ์ง ์์์",
Toast.LENGTH_SHORT).show();
}
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 100 && resultCode == RESULT_OK){
Bitmap photo = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
ExifInterface exif = null;
try {
exif = new ExifInterface(photoFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
photo = rotateBitmap(photo, orientation);
// ์์ถ์ํจ๋ค. ํด์๋ ๋ฎ์ถฐ์
OutputStream os;
try {
os = new FileOutputStream(photoFile);
photo.compress(Bitmap.CompressFormat.JPEG, 20, os);
os.flush();
os.close();
} catch (Exception e) {
Log.e(getClass().getSimpleName(), "Error writing bitmap", e);
}
photo = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
imgPhoto.setImageBitmap(photo);
imgPhoto.setScaleType(ImageView.ScaleType.CENTER_CROP);
// ๋คํธ์ํฌ๋ก ๋ฐ์ดํฐ ๋ณด๋ธ๋ค.
}else if(requestCode == 300 && resultCode == RESULT_OK && data != null &&
data.getData() != null){
Uri albumUri = data.getData( );
String fileName = getFileName( albumUri );
try {
ParcelFileDescriptor parcelFileDescriptor = getContentResolver( ).openFileDescriptor( albumUri, "r" );
if ( parcelFileDescriptor == null ) return;
FileInputStream inputStream = new FileInputStream( parcelFileDescriptor.getFileDescriptor( ) );
photoFile = new File( this.getCacheDir( ), fileName );
FileOutputStream outputStream = new FileOutputStream( photoFile );
IOUtils.copy( inputStream, outputStream );
// //์์ํ์ผ ์์ฑ
// File file = createImgCacheFile( );
// String cacheFilePath = file.getAbsolutePath( );
// ์์ถ์ํจ๋ค. ํด์๋ ๋ฎ์ถฐ์
Bitmap photo = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
OutputStream os;
try {
os = new FileOutputStream(photoFile);
photo.compress(Bitmap.CompressFormat.JPEG, 20, os);
os.flush();
os.close();
} catch (Exception e) {
Log.e(getClass().getSimpleName(), "Error writing bitmap", e);
}
imgPhoto.setImageBitmap(photo);
imgPhoto.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
// imageView.setImageBitmap( getBitmapAlbum( imageView, albumUri ) );
} catch ( Exception e ) {
e.printStackTrace( );
}
// ๋คํธ์ํฌ๋ก ๋ณด๋ธ๋ค.
}
super.onActivityResult(requestCode, resultCode, data);
}
//์จ๋ฒ์์ ์ ํํ ์ฌ์ง์ด๋ฆ ๊ฐ์ ธ์ค๊ธฐ
public String getFileName( Uri uri ) {
Cursor cursor = getContentResolver( ).query( uri, null, null, null, null );
try {
if ( cursor == null ) return null;
cursor.moveToFirst( );
@SuppressLint("Range") String fileName = cursor.getString( cursor.getColumnIndex( OpenableColumns.DISPLAY_NAME ) );
cursor.close( );
return fileName;
} catch ( Exception e ) {
e.printStackTrace( );
cursor.close( );
return null;
}
}
public static Bitmap rotateBitmap(Bitmap bitmap, int orientation) {
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
return bitmap;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
matrix.setScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.setRotate(180);
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.setRotate(90);
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.setRotate(-90);
break;
default:
return bitmap;
}
try {
Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
return bmRotated;
}
catch (OutOfMemoryError e) {
e.printStackTrace();
return null;
}
}
// ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ฑฐ๋, ์์ ํ๊ฑฐ๋, ์ญ์ ํ๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ค!
Dialog dialog;
void showProgress(){
dialog = new Dialog(this);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.setContentView(new ProgressBar(this));
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
void dismissProgress(){
dialog.dismiss();
}
}
PostingApi ํด๋์ค ๋ง๋ค๊ธฐ
package com.~.postingapp.api;
import com.~.postingapp.model.Res;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Header;
import retrofit2.http.POST;
import retrofit2.http.Part;
public interface PostingApi {
@POST("/posting")
Call<Res> addPosting(@Header("Authorization") String token,
@Part MultipartBody.Part image, //ํํธ๋ก ๋๋ ์ ๋ณด๋ด๋ผ ์ฉ๋ ํฌ๋ฏ๋ก
@Part("content")RequestBody content);
}
@Part MultipartBody
- Retrofit ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉํฐํํธ ํ์ผ ์ ๋ก๋๋ฅผ ๊ตฌํํ ๋ ์ฌ์ฉ๋๋ ์ ๋ ธํ ์ด์ ์ด๋ค.
- ์ด๋ฅผ ํตํด ํ์ผ ๋ฐ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ๋ก ์ ์กํ ์ ์๋ค.
Res ํด๋์ค ๋ง๋ค๊ธฐ
activity_row ๋ง๋ค๊ธฐ
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/imgPhoto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="4"
app:srcCompat="@drawable/add_photo" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="5"
android:orientation="vertical">
<TextView
android:id="@+id/txtContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="#000000"
android:textSize="23sp" />
<TextView
android:id="@+id/txtEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="#000000"
android:textSize="23sp" />
<TextView
android:id="@+id/txtCreatedAt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="#000000"
android:textSize="23sp" />
<ImageView
android:id="@+id/imgLike"
android:src="@drawable/like_24dp"
android:layout_width="35dp"
android:layout_height="35dp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
MainActivity. java ์์ฑ
package com.~.postingapp;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.~.postingapp.adapter.PostingAdapter;
import com.~.postingapp.api.NetworkClient;
import com.~.postingapp.api.PostingApi;
import com.~.postingapp.config.Config;
import com.~.postingapp.model.Posting;
import com.~.postingapp.model.PostingList;
import java.util.ArrayList;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class MainActivity extends AppCompatActivity {
ProgressBar progressBar;
Button btnAdd;
RecyclerView recyclerView;
ArrayList<Posting> postingArrayList = new ArrayList<>();
PostingAdapter adapter;
String token;
// ํ์ด์ง ์ฒ๋ฆฌ์ ํ์ํ ๋ฉค๋ฒ ๋ณ์๋ค!!
int offset = 0;
int limit = 5;
int count;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences sp = getSharedPreferences(Config.SP_NAME, MODE_PRIVATE);
token = sp.getString("token", "");
if(token.isEmpty()){
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
finish();
return;
}
progressBar = findViewById(R.id.progressBar);
btnAdd = findViewById(R.id.btnAdd);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int lastPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
int totalCount = recyclerView.getAdapter().getItemCount();
if(lastPosition + 1 == totalCount){
if(count == limit){
// ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐ๋ก ์์ฒญํ๋ค.
addNetworkData();
}
}
}
});
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, AddActivity.class);
startActivity(intent);
}
});
}
private void addNetworkData() {
progressBar.setVisibility(View.VISIBLE);
Retrofit retrofit = NetworkClient.getRetrofitClient(MainActivity.this);
PostingApi api = retrofit.create(PostingApi.class);
Call<PostingList> call = api.getPostingList("Bearer " + token, offset, limit );
call.enqueue(new Callback<PostingList>() {
@Override
public void onResponse(Call<PostingList> call, Response<PostingList> response) {
progressBar.setVisibility(View.GONE);
if(response.isSuccessful()){
PostingList postingList = response.body();
count = postingList.count;
offset = offset + count;
postingArrayList.addAll( postingList.items );
adapter.notifyDataSetChanged();
}else{
}
}
@Override
public void onFailure(Call<PostingList> call, Throwable throwable) {
progressBar.setVisibility(View.GONE);
}
});
}
@Override
protected void onResume() {
super.onResume();
// ๋คํธ์ํฌ๋ฅผ ํตํด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์จ๋ค.
getNetworkData();
}
private void getNetworkData() {
progressBar.setVisibility(View.VISIBLE);
postingArrayList.clear();
offset = 0;
Retrofit retrofit = NetworkClient.getRetrofitClient(MainActivity.this);
PostingApi api = retrofit.create(PostingApi.class);
Call<PostingList> call = api.getPostingList("Bearer "+token, offset, limit);
call.enqueue(new Callback<PostingList>() {
@Override
public void onResponse(Call<PostingList> call, Response<PostingList> response) {
progressBar.setVisibility(View.GONE);
if(response.isSuccessful()){
PostingList postingList = response.body();
count = postingList.count;
offset = offset + count;
postingArrayList.addAll( postingList.items );
adapter = new PostingAdapter(MainActivity.this, postingArrayList);
recyclerView.setAdapter(adapter);
}else{
}
}
@Override
public void onFailure(Call<PostingList> call, Throwable throwable) {
progressBar.setVisibility(View.GONE);
}
});
}
}
Posting ํด๋์ค ๋ง๋ค๊ธฐ
package com.~.postingapp.model;
public class Posting {
public int id;
public int userId;
public String imageUrl;
public String content;
public String createdAt;
public String updatedAt;
public String email;
public int isLike;
}
PostingList ํด๋์ค ๋ง๋ค๊ธฐ
package com.~.postingapp.model;
import java.util.ArrayList;
public class PostingList {
public String result;
public ArrayList<Posting> items;
public int count;
}
PostingAdapter ๋ง๋ค๊ธฐ
- activity_row์ MainActivity์ ํ๋ฉด์ ์ฐ๊ฒฐํด์ค๋ค.
package com.~.postingapp.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.~.postingapp.R;
import com.~.postingapp.model.Posting;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.TimeZone;
public class PostingAdapter extends RecyclerView.Adapter<PostingAdapter.ViewHolder> {
Context context;
ArrayList<Posting> postingArrayList;
SimpleDateFormat sf;
SimpleDateFormat df;
public PostingAdapter(Context context, ArrayList<Posting> postingArrayList) {
this.context = context;
this.postingArrayList = postingArrayList;
//2024-05-30T07:23:52
sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sf.setTimeZone(TimeZone.getTimeZone("UTC"));
df.setTimeZone(TimeZone.getDefault());
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.activity_row, parent, false);
return new PostingAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Posting posting = postingArrayList.get(position);
Glide.with(context).load( posting.imageUrl ).into( holder.imgPhoto );
holder.txtContent.setText( posting.content );
holder.txtEmail.setText( posting.email );
holder.txtCreatedAt.setText( posting.createdAt );
if( posting.isLike == 0 ){
holder.imgLike.setImageResource(R.drawable.like_24dp);
}else{
holder.imgLike.setImageResource(R.drawable.like2);
}
try {
Date date = sf.parse( posting.createdAt );
String localTime = df.format(date);
holder.txtCreatedAt.setText(localTime);
} catch (ParseException e) {
// ๋ก๊ทธ๋ฅผ ๋จ๊ฒจ์ ๋๋ฒ๊น
ํ๋ค.
}
}
@Override
public int getItemCount() {
return postingArrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView imgPhoto;
TextView txtContent;
TextView txtEmail;
TextView txtCreatedAt;
ImageView imgLike;
public ViewHolder(@NonNull View itemView) {
super(itemView);
imgPhoto = itemView.findViewById(R.id.imgPhoto);
txtContent = itemView.findViewById(R.id.txtContent);
txtEmail = itemView.findViewById(R.id.txtEmail);
txtCreatedAt = itemView.findViewById(R.id.txtCreatedAt);
imgLike = itemView.findViewById(R.id.imgLike);
}
}
}
PostingApi ๋ง๋ค๊ธฐ
package com.~.postingapp.api;
import com.~.postingapp.model.PostingList;
import com.~.postingapp.model.Res;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Query;
public interface PostingApi {
// ํฌ์คํ
์์ฑ API
@POST("/posting")
Call<Res> addPosting(@Header("Authorization") String token,
@Part MultipartBody.Part image,
@Part("content") RequestBody content);
// ์น๊ตฌ๋ค์ ํฌ์คํ
๋ฆฌ์คํธ ๊ฐ์ ธ์ค๋ API
@GET("/posting")
Call<PostingList> getPostingList(@Header("Authorization") String token,
@Query("offset") int offset,
@Query("limit") int limit);
}