본문 바로가기
App 소개

심심해서 만들어보는 앱 "SuldenLion's Versatile App" 개발로그_3

by SuldenLion 2024. 1. 14.
반응형

SuldenLion's Versatile App 3주차 개발 로그

 

 

추가 사항)

- Splash 기능

- lottie 라이브러리를 사용한 애니메이션 효과

- 상단바 알림 기능

- Text to voice 기능

- Text File 생성 기능

 

 

스토어 출시 관련해서는 앱 사용중 비정상 종료되는 요소들 때문에 출시 거부되는 이슈 발생

스토어 등록은 당장 중요하진 않으므로 신경쓰지 않도록 함

 

 

메뉴 추가 요소

 

[Splash]

앱 실행시 바로 앱으로 들어가면 밋밋해 보이므로 짧은 시간 앱의 입장 화면을 보여주는 기능.

Thread로 4초간 화면을 보여줄 것임.

 

 

액티비티의 xml은 별도로 없으며 manifest와 themes.xml 에서 작업이 이루어짐.

 

manifest

 

themes.xml

 

위와 같은 구성으로 동작.

 

 

 

앱 실행시 이 화면이 4초간 나오고 로그인 화면으로 이동.

 

 

[Lottie]

 

Airbnb에서 만든 lottie.

 

로그인 시 간단하게 기다려달라는 애니메이션을 추가.

 

사용을 위해서는 아래의 dependency 추가

 

 

메뉴화면 xml 파일에 lottie 컴포넌트 추가

 

 

java 파일에선 코드 몇줄만 적어주면 쉽게 사용

 

 

사용할 애니메이션은 아래 사이트에서 json 형식의 파일을 받아 사용한다.

 

받은 파일은 app 하위에 assets 폴더를 만들어 넣어준다.

 

 

사용이 어렵지 않음.

 

 

[Message Click]

 

알림 호출 버튼을 눌러주면

 

 

기기에 메시지를 띄우고 Yes와 No 와 같은 옵션을 줘서 각 응답에 맞게 반응하도록 구성.

 

 

Yes 클릭시 YesActivity를 하나 만들어주고 

 

여기서도 lottie를 사용하여 움직이는 선물상자를 넣어줌.

상자 클릭시 이미지 날라가게 함. (클릭시 전면광고 표시 및 유저 보상 등으로 활용 가능할듯)

 

 

 

package suldenlion.tistory.com;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class NotificationActivity extends AppCompatActivity {
    public final String CHANNEL_ID = "my_notification_channel";
    public final int NOTIFICATION_ID = 101;

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

        Button notificationBtn = findViewById(R.id.button);
        notificationBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                displayNotification(v);
            }
        });
    }

    private void displayNotification(View v) {
        createNotificationChannel();

        //MenuActivity 설정
        Intent mainIntent = new Intent(this, MenuActivity.class);
        mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent MainPendingIntent = PendingIntent.getActivity(this , 0, mainIntent, PendingIntent.FLAG_ONE_SHOT);

        //YesActivity 설정
        Intent yesIntent = new Intent(this, NotificationYesActivity.class);
        yesIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent yesPendingIntent = PendingIntent.getActivity(this, 0, yesIntent, PendingIntent.FLAG_ONE_SHOT);

        //NoActivity 설정
        Intent noIntent = new Intent(this, NotificationNoActivity.class);
        noIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent noPendingIntent = PendingIntent.getActivity(this, 0, noIntent, PendingIntent.FLAG_ONE_SHOT);

        //알림설정
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
        builder.setSmallIcon(R.drawable.ic_sms);
        builder.setContentTitle("알림이 왔습니다");
        builder.setContentText("확인해 보시겠습니까?");
        builder.setPriority(NotificationManagerCompat.IMPORTANCE_DEFAULT);
        builder.setAutoCancel(true);
        builder.setContentIntent(MainPendingIntent);
        builder.addAction(R.drawable.ic_sms, "Yes", yesPendingIntent);
        builder.addAction(R.drawable.ic_sms, "No", noPendingIntent);

        NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);
        notificationManagerCompat.notify(NOTIFICATION_ID, builder.build());
    }

    private void createNotificationChannel() {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            CharSequence name = "채널이름";
            String description = "채널설명";
            int importance = NotificationManager.IMPORTANCE_DEFAULT;

            //채널생성
            NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, name, importance);
            notificationChannel.setDescription(description);

            //알림매니저 생성
            NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

            //알림매니저에 채널등록
            notificationManager.createNotificationChannel(notificationChannel);
        }
    }

}

 

알림 호출 화면.

채널 설정 및 각 Activity로 넘어가는 기능, 알림에 대한 기능 정의.

 

 

Yes 버튼 클릭시의 화면 중 로티 컴포넌트.

xml에서 파일 등록, 루프 설정 등이 가능함.

 

package suldenlion.tistory.com;

import androidx.appcompat.app.AppCompatActivity;

import android.app.NotificationManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.airbnb.lottie.LottieAnimationView;

public class NotificationYesActivity extends AppCompatActivity {
    private LottieAnimationView animationView;

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

        animationView = (LottieAnimationView)findViewById(R.id.lottie);
        animationView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animationView.setVisibility(View.GONE);
                Toast.makeText(NotificationYesActivity.this,"선물상자 클릭!",Toast.LENGTH_SHORT).show();
            }
        });

        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationManager.cancel(101);

        Button notificationBtn = findViewById(R.id.button);
        notificationBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), MenuActivity.class);
                startActivity((intent));
            }
        });
    }
}

 

YesActivity

 

 

[Voice converter]

 

메시지 입력 후 버튼을 클릭하면 음성으로 읽어주는 프로그램.

 

package suldenlion.tistory.com;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.util.Locale;

import javax.annotation.Nullable;

public class VoiceConverterActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {

    public static final int TTS_ENGINE_REQUEST = 101;
    private TextToSpeech textToSpeech;
    private EditText textForSpeech;

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

        textForSpeech = findViewById(R.id.speech_text);
    }

    @Override
    public void onInit(int status) {
        if (status == TextToSpeech.SUCCESS) {
            int languageStatus = textToSpeech.setLanguage(Locale.KOREAN);

            if (languageStatus == TextToSpeech.LANG_MISSING_DATA || languageStatus == TextToSpeech.LANG_NOT_SUPPORTED) {
                Toast.makeText(this, "Unsupported language!", Toast.LENGTH_SHORT).show();
            } else {
                String data = textForSpeech.getText().toString();
                int speechStatus = textToSpeech.speak(data, TextToSpeech.QUEUE_FLUSH, null);

                if (speechStatus == TextToSpeech.ERROR) {
                    Toast.makeText(this, "Convert error!", Toast.LENGTH_SHORT).show();
                }
            }
        } else {
            Toast.makeText(this, "Convert engine error!", Toast.LENGTH_SHORT).show();
        }
    }

    // View를 인자로 받음으로 xml에서 선언한 onClick 사용가능
    public void textSpeech(View view) {
        Intent checkIntent = new Intent();
        checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
        startActivityForResult(checkIntent, TTS_ENGINE_REQUEST);
    }

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

        if (requestCode == TTS_ENGINE_REQUEST && resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
            textToSpeech = new TextToSpeech(this, this);
        } else {
            Intent installIntent = new Intent();
            installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
            startActivity(installIntent);
        }
    }

}

 

TextToSpeech 인터페이스를 implements 받아서 사용

 

언어 설정을 한국어로 하고 에러 상황에 메시지를 띄워주게 함

 

textSpeech() 함수는 xml에서 onclick을 주어 선언함.

 

activityResult 함수에선 스피치를 위한 엔진이 안깔린 경우 설치, 조건 충족시 말하기 시행.

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".VoiceConverterActivity">

    <EditText
        android:id="@+id/speech_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="메시지를 입력하세요"
        android:layout_marginLeft="25dp"
        android:layout_marginRight="25dp"
        android:layout_marginTop="55dp"
        android:lines="8"
        android:minLines="3"
        android:gravity="top|left"
        android:maxLength="100"
        android:padding="3dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="말하기"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:background="#E91E63"
        android:onClick="textSpeech"
        />

</LinearLayout>

 

 

 

[Text file generator]

 

텍스트 입력후 저장하기 버튼을 누르면 외부 저장소에 txt 파일을 저장하는 프로그램

 

먼저 매니페스트에 권한 추가

 

 

package suldenlion.tistory.com;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class CreateTxtActivity extends AppCompatActivity {
    private final static String folderName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()+"/File";
    private String fileName = "file.txt";
    private EditText textBox;
    private Button btnSave;

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

        textBox = findViewById(R.id.textbox);
        btnSave = findViewById(R.id.btn_save);

        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String now = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss").format(new Date());

                String contents = "파일 내용 : " + textBox.getText().toString() + "\n 파일 작성 시간 : " + now + "\n";
                WriteTextFile(folderName, fileName, contents);
            }
        });
    }

    private void WriteTextFile(String folderName, String fileName, String contents) {
        try {
            File dir = new File(folderName);

            if (!dir.exists()) {
                dir.mkdir();
            }

            FileOutputStream fos = new FileOutputStream(folderName+"/"+fileName, true);
            fos.write(contents.getBytes());
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

저정할 폴더와 파일 이름을 지정 후, 저장하는 현재 시각을 지정해준다.

그리고 FileOutputStream을 이용하여 저장된 내용을 바이트 단위로 write하여 만드는 방식.

 

 

빠뜨린 내용이 있을 수 있음

일단 여기까지 정리

반응형

댓글