코리아 IT아카데미/android

15,16,17일차 | 영화 평점 소개 사이트 <- 녹화본으로 흐름 파악할 것

Sharon kim 2022. 3. 8. 20:05

 //todo info bottom 안되는 것 해결하기 

코드 추가

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<application
...
	android:screenOrientation="portrait"
	android:usesCleartextTraffic="true">
</application>

build.gradle

 buildTypes {
       
    ...

    buildFeatures{
//        dataBinding true
        viewBinding {
            enabled true
        }
    }
}

def retrofit_version ="2.9.0"

dependencies {

     ...

    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"

    implementation 'com.github.bumptech.glide:glide:4.12.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

    implementation 'de.hdodenhof:circleimageview:3.1.0'
}

 

adapter>MovieAdapter.java

package com.example.myapplication.adapter;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RatingBar;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.example.myapplication.DetailActivity;
import com.example.myapplication.R;
import com.example.myapplication.models.Movie;
import com.example.myapplication.models.YtsData;
import com.example.myapplication.utils.Define;

import java.util.ArrayList;
import java.util.List;

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MyViewHolder> {

    private List<Movie> list = new ArrayList<>();
    private static final String TAG = MovieAdapter.class.getName();
    private Context context;

    public MovieAdapter(Context context) {
        this.context = context;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View movieItemView = inflater.inflate(R.layout.item_movie_card, parent, false);
        return new MyViewHolder(movieItemView);  // <---- R.layout.item_movie_card
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
       Movie movie = list.get(position);
       holder.setItem(movie);
       //Todo 옵저버 패턴 만들어서 콜백메서드 처리 후에 디테일 액티비티에서 인텐트를 처리하자
       holder.itemView.setOnClickListener(v -> {
           Intent intent = new Intent(context, DetailActivity.class);
           intent.putExtra(Define.PARAM_MOVIE_OBJ, movie);
           context.startActivity(intent);
       });
    }

    @Override
    public int getItemCount() {
        return list.size();
    }



    // 내부 클래스
    public static class MyViewHolder extends RecyclerView.ViewHolder {
        View itemView;
        ImageView posterIv;
        TextView titleTv;
        TextView ratingTv;
        RatingBar ratingBar;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            this.itemView = itemView;
            posterIv = itemView.findViewById(R.id.posterIv);
            titleTv = itemView.findViewById(R.id.titleTv);
            ratingTv = itemView.findViewById(R.id.ratingTv);
            ratingBar = itemView.findViewById(R.id.ratingBar);

//            itemView.setOnClickListener(new View.OnClickListener() {
//                @Override
//                public void onClick(View v) {
//                    Log.d(TAG, "aaaaaaa");
//                }
//            });
        }

        public void setItem(Movie movie) {
            titleTv.setText(movie.getTitle());
            ratingTv.setText(String.valueOf(movie.getRating()));
            Glide.with(posterIv.getContext())
                    .load(movie.getMediumCoverImage())
                    .placeholder(R.drawable.round_image)
                    .transform(new FitCenter(), new RoundedCorners(20))
                    .into(posterIv);
            ratingBar.setRating(movie.getRating());
        }

    }

    // 통신으로 데이터가 전달 되면 여기 메서드로 데이를 전달 받게 한다.
//    @SuppressLint("NotifyDataSetChanged")
//    public void addItems(List<Movie> list) {
//        this.list = list;
//        this.notifyDataSetChanged();
//    }
//
//    public void EachItem(Movie movie) {
//        this.list.add(movie);
//        this.notifyDataSetChanged();
//    }


    @SuppressLint("NotifyDataSetChanged")
    public void addItem(List<Movie> addList) {
        list.addAll(list.size(), addList);
        notifyDataSetChanged();
    }
}


interfaces>OnPageTypeChange.interface

package com.example.myapplication.interfaces;

public interface OnPageTypeChange {
    void typeToolbarChange(String title);
}


models>Data.java

package com.example.myapplication.models;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

import java.util.List;


public class Data {

    @SerializedName("movie_count")
    @Expose
    private Integer movieCount;
    @SerializedName("limit")
    @Expose
    private Integer limit;
    @SerializedName("page_number")
    @Expose
    private Integer pageNumber;
    @SerializedName("movies")
    @Expose
    private List<Movie> movies = null;

    public Integer getMovieCount() {
        return movieCount;
    }

    public void setMovieCount(Integer movieCount) {
        this.movieCount = movieCount;
    }

    public Integer getLimit() {
        return limit;
    }

    public void setLimit(Integer limit) {
        this.limit = limit;
    }

    public Integer getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(Integer pageNumber) {
        this.pageNumber = pageNumber;
    }

    public List<Movie> getMovies() {
        return movies;
    }

    public void setMovies(List<Movie> movies) {
        this.movies = movies;
    }

}


models>Meta.java

package com.example.myapplication.models;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;


public class Meta {

    @SerializedName("server_time")
    @Expose
    private Integer serverTime;
    @SerializedName("server_timezone")
    @Expose
    private String serverTimezone;
    @SerializedName("api_version")
    @Expose
    private Integer apiVersion;
    @SerializedName("execution_time")
    @Expose
    private String executionTime;

    public Integer getServerTime() {
        return serverTime;
    }

    public void setServerTime(Integer serverTime) {
        this.serverTime = serverTime;
    }

    public String getServerTimezone() {
        return serverTimezone;
    }

    public void setServerTimezone(String serverTimezone) {
        this.serverTimezone = serverTimezone;
    }

    public Integer getApiVersion() {
        return apiVersion;
    }

    public void setApiVersion(Integer apiVersion) {
        this.apiVersion = apiVersion;
    }

    public String getExecutionTime() {
        return executionTime;
    }

    public void setExecutionTime(String executionTime) {
        this.executionTime = executionTime;
    }

}


models>Movie.java

package com.example.myapplication.models;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

import java.io.Serializable;
import java.util.List;


public class Movie implements Serializable {

    @SerializedName("id")
    @Expose
    private Integer id;
    @SerializedName("url")
    @Expose
    private String url;
    @SerializedName("imdb_code")
    @Expose
    private String imdbCode;
    @SerializedName("title")
    @Expose
    private String title;
    @SerializedName("title_english")
    @Expose
    private String titleEnglish;
    @SerializedName("title_long")
    @Expose
    private String titleLong;
    @SerializedName("slug")
    @Expose
    private String slug;
    @SerializedName("year")
    @Expose
    private Integer year;
    @SerializedName("rating")
    @Expose
    private Float rating;
    @SerializedName("runtime")
    @Expose
    private Integer runtime;
    @SerializedName("genres")
    @Expose
    private List<String> genres = null;
    @SerializedName("summary")
    @Expose
    private String summary;
    @SerializedName("description_full")
    @Expose
    private String descriptionFull;
    @SerializedName("synopsis")
    @Expose
    private String synopsis;
    @SerializedName("yt_trailer_code")
    @Expose
    private String ytTrailerCode;
    @SerializedName("language")
    @Expose
    private String language;
    @SerializedName("mpa_rating")
    @Expose
    private String mpaRating;
    @SerializedName("background_image")
    @Expose
    private String backgroundImage;
    @SerializedName("background_image_original")
    @Expose
    private String backgroundImageOriginal;
    @SerializedName("small_cover_image")
    @Expose
    private String smallCoverImage;
    @SerializedName("medium_cover_image")
    @Expose
    private String mediumCoverImage;
    @SerializedName("large_cover_image")
    @Expose
    private String largeCoverImage;
    @SerializedName("state")
    @Expose
    private String state;


    @SerializedName("date_uploaded")
    @Expose
    private String dateUploaded;
    @SerializedName("date_uploaded_unix")
    @Expose
    private Integer dateUploadedUnix;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getImdbCode() {
        return imdbCode;
    }

    public void setImdbCode(String imdbCode) {
        this.imdbCode = imdbCode;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitleEnglish() {
        return titleEnglish;
    }

    public void setTitleEnglish(String titleEnglish) {
        this.titleEnglish = titleEnglish;
    }

    public String getTitleLong() {
        return titleLong;
    }

    public void setTitleLong(String titleLong) {
        this.titleLong = titleLong;
    }

    public String getSlug() {
        return slug;
    }

    public void setSlug(String slug) {
        this.slug = slug;
    }

    public Integer getYear() {
        return year;
    }

    public void setYear(Integer year) {
        this.year = year;
    }

    public Float getRating() {
        return rating;
    }

    public void setRating(Float rating) {
        this.rating = rating;
    }

    public Integer getRuntime() {
        return runtime;
    }

    public void setRuntime(Integer runtime) {
        this.runtime = runtime;
    }

    public List<String> getGenres() {
        return genres;
    }

    public void setGenres(List<String> genres) {
        this.genres = genres;
    }

    public String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public String getDescriptionFull() {
        return descriptionFull;
    }

    public void setDescriptionFull(String descriptionFull) {
        this.descriptionFull = descriptionFull;
    }

    public String getSynopsis() {
        return synopsis;
    }

    public void setSynopsis(String synopsis) {
        this.synopsis = synopsis;
    }

    public String getYtTrailerCode() {
        return ytTrailerCode;
    }

    public void setYtTrailerCode(String ytTrailerCode) {
        this.ytTrailerCode = ytTrailerCode;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getMpaRating() {
        return mpaRating;
    }

    public void setMpaRating(String mpaRating) {
        this.mpaRating = mpaRating;
    }

    public String getBackgroundImage() {
        return backgroundImage;
    }

    public void setBackgroundImage(String backgroundImage) {
        this.backgroundImage = backgroundImage;
    }

    public String getBackgroundImageOriginal() {
        return backgroundImageOriginal;
    }

    public void setBackgroundImageOriginal(String backgroundImageOriginal) {
        this.backgroundImageOriginal = backgroundImageOriginal;
    }

    public String getSmallCoverImage() {
        return smallCoverImage;
    }

    public void setSmallCoverImage(String smallCoverImage) {
        this.smallCoverImage = smallCoverImage;
    }

    public String getMediumCoverImage() {
        return mediumCoverImage;
    }

    public void setMediumCoverImage(String mediumCoverImage) {
        this.mediumCoverImage = mediumCoverImage;
    }

    public String getLargeCoverImage() {
        return largeCoverImage;
    }

    public void setLargeCoverImage(String largeCoverImage) {
        this.largeCoverImage = largeCoverImage;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }


    public String getDateUploaded() {
        return dateUploaded;
    }

    public void setDateUploaded(String dateUploaded) {
        this.dateUploaded = dateUploaded;
    }

    public Integer getDateUploadedUnix() {
        return dateUploadedUnix;
    }

    public void setDateUploadedUnix(Integer dateUploadedUnix) {
        this.dateUploadedUnix = dateUploadedUnix;
    }

}


models>YtsData.java

package com.example.myapplication.models;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;


public class YtsData {

    @SerializedName("status")
    @Expose
    private String status;
    @SerializedName("status_message")
    @Expose
    private String statusMessage;
    @SerializedName("data")
    @Expose
    private Data data;


    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getStatusMessage() {
        return statusMessage;
    }

    public void setStatusMessage(String statusMessage) {
        this.statusMessage = statusMessage;
    }

    public Data getData() {
        return data;
    }

    public void setData(Data data) {
        this.data = data;
    }

}


repository>MovieService.interface

package com.example.myapplication.repository;

import com.example.myapplication.models.YtsData;
import com.example.myapplication.utils.Define;

import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Query;

// https://yts.lt/api/v2/list_movies.json?limit=20&page=1&sort_by=rating
public interface MovieService {

    // public static final
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(Define.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    // dto 만들어주기
    @GET("list_movies.json")
    Call<YtsData> repoContributors(@Query("sort_by") String sortBy,
                                               @Query("page") int page,
                                               @Query("limit") int limit);
}


utils>BottomSheetFragment.java

package com.example.myapplication;

import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.bumptech.glide.Glide;
import com.example.myapplication.databinding.FragmentBottomSheetBinding;
import com.example.myapplication.models.Movie;


public class BottomSheetFragment extends Fragment {

    private FragmentBottomSheetBinding binding;

    private Movie movie;

    public BottomSheetFragment(Movie movie) {
        this.movie = movie;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        binding = FragmentBottomSheetBinding.inflate(inflater, container, false);

        Glide.with(this)
                .load(movie.getMediumCoverImage())
                .into(binding.movieImageview);

        binding.summaryTextView.setText(movie.getSummary());
        binding.descriptionTextView.setText(movie.getDescriptionFull());
        binding.synopsisTextView.setText(movie.getSynopsis());

        return binding.getRoot();
    }
}


utils>DetailActivity.java

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import android.os.Bundle;
import android.util.Log;

import com.bumptech.glide.Glide;
import com.example.myapplication.databinding.ActivityDetailBinding;
import com.example.myapplication.models.Movie;
import com.example.myapplication.utils.Define;

public class DetailActivity extends AppCompatActivity {

    private final static String TAG = DetailActivity.class.getName();
    private ActivityDetailBinding binding;
    private BottomSheetFragment bottomSheetFragment;
    private Movie movie;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityDetailBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        if (getIntent() != null) {
            movie = (Movie) getIntent().getSerializableExtra(Define.PARAM_MOVIE_OBJ);
            initData();
            addEventListener();
        }
    }

    private void initData() {
        binding.titleTextView.setText(movie.getTitle());
        binding.yearTextView.setText( "제작년도: " + movie.getYear() + "년도");
        binding.runTimeTextView.setText("상영시간 : " + movie.getRuntime() + "분");

        Glide.with(this)
                .load(movie.getMediumCoverImage())
                .into(binding.moviePoster);

        Glide.with(this)
                .load(movie.getBackgroundImage())
                .into(binding.backgroundImageView);

        bottomSheetFragment = new BottomSheetFragment(movie);

    }

    private void addEventListener() {
        binding.showContentButton.setOnClickListener(v -> {
            addFragment();
        });
    }

    private void addFragment() {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // 잘 알려진 버그 out 시 애니메이션 처리 안됨
        transaction.setCustomAnimations(R.anim.slide_in_bottom, R.anim.slide_out_bottom);
        transaction.setReorderingAllowed(true);
        transaction.addToBackStack("aaa");
        transaction.replace(binding.bottomSheetContainer.getId(), bottomSheetFragment);
        transaction.commit();
    }
}


utils>InfoFragment.java

package com.example.myapplication;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.os.Bundle;

import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.example.myapplication.databinding.FragmentInfoBinding;
import com.example.myapplication.interfaces.OnPageTypeChange;
import com.example.myapplication.utils.Define;


public class InfoFragment extends Fragment {

    private static InfoFragment infoFragment;
    private FragmentInfoBinding binding;
    private final OnPageTypeChange onPageTypeChange;
    private OnBackPressedCallback onBackPressedCallback;

    // 싱글톤 패턴 적용
    private InfoFragment(OnPageTypeChange onPageTypeChange) {
        this.onPageTypeChange = onPageTypeChange;
    }

    public static InfoFragment getInstance(OnPageTypeChange onPageTypeChange) {
        if (infoFragment == null) {
            infoFragment = new InfoFragment(onPageTypeChange);
        }
        return infoFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        onPageTypeChange.typeToolbarChange("Stub!");
        fragmentBackPressCustom();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        binding = FragmentInfoBinding.inflate(inflater, container, false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        setupWebView();
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void setupWebView() {
        WebView webView = binding.webView;
        webView.setWebViewClient(new WebViewClient() {

            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
                binding.progressIndicator.setVisibility(View.GONE);
            }

//            @Override
//            public void onPageFinished(WebView view, String url) {
//                super.onPageFinished(view, url);
//                // 웹뷰가 렌더링이 다 되었을 때 콜백 되는 메서드
//                // HTML 뼈대, CSS , javascript
//            }
        });

        webView.loadUrl(Define.WEB_VIEW_URL);
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
    }

    // 프래그먼에서 뒤로가기 이벤트 커스텀 하기
    private void fragmentBackPressCustom() {
        onBackPressedCallback = new OnBackPressedCallback(true) {
            @Override
            public void handleOnBackPressed() {
               Log.d("TAG", "stub!");
            }
        };

        requireActivity().
                getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), onBackPressedCallback);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        onBackPressedCallback.remove();
    }

}


utils>MainActivity.java

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;

import com.example.myapplication.databinding.ActivityMainBinding;
import com.example.myapplication.interfaces.OnPageTypeChange;
import com.example.myapplication.models.YtsData;
import com.example.myapplication.repository.MovieService;
import com.example.myapplication.utils.Define;
import com.example.myapplication.utils.FragmentType;
import com.google.android.material.appbar.MaterialToolbar;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity implements OnPageTypeChange {

    // 1. viewBind 사용하기
    private ActivityMainBinding binding;
    private static final String TAG = MainActivity.class.getName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 뷰 바인딩 설정 방법 (Activity)
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        intData();
    }

    private void intData() {
        addTopAppbarEventListener();
        addBottomNavigationListener();
        replaceFragment(FragmentType.MOVIE);
    }

    private void addTopAppbarEventListener() {
        binding.topAppBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                if (item.getItemId() == R.id.favorite) {
                    // 새로운 화면을 띄움 !!
                    // 도전 Log.d(TAG, "1111111");
                }
                return true;
            }
        });
    }


    private void addBottomNavigationListener() {

        binding.bottomNavigation.setOnItemSelectedListener(item -> {
            switch (item.getItemId()) {
                case R.id.page_1:
                    replaceFragment(FragmentType.MOVIE);
                    break;
                case R.id.page_2:
                    replaceFragment(FragmentType.INFO);
                    break;
            }
            return true;
        });
    }

    private void replaceFragment(FragmentType type) {

        Fragment fragment;
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        if (type == FragmentType.MOVIE) {
            fragment = MovieFragment.getInstance(this);
        } else {
            fragment = InfoFragment.getInstance(this);
        }                                                                               //movie , info
        transaction.replace(binding.mainContainer.getId(), fragment, type.toString());
        transaction.commit();
    }


    /**
     * 콜백 메서드
     *
     * @param title : app title 변수
     */
    @Override
    public void typeToolbarChange(String title) {
        // 여기에 알람이 옴.
        MaterialToolbar toolbar = binding.topAppBar;
        if (title.equals(Define.PAGE_TITLE_MOVIE)) {
            toolbar.setTitle(title);
            toolbar.setVisibility(View.VISIBLE);
        } else {
            toolbar.setVisibility(View.GONE);
        }
    }

    @Override
    public void onBackPressed() {
        Log.d("TAG", "액티비티에서 이벤트 캐치");
        //super.onBackPressed();
        // 여기에서 이벤트가 들어 왔을 때
        // 현재 화면에 movie, info 확인을 한 다음
        // info --> movie(화면에 그려줘)
        // movie --> (앱 종료)
        //INFO
        String fragmentByTag = getSupportFragmentManager()
                .findFragmentByTag(FragmentType.INFO.toString()).getTag();
        if (fragmentByTag.equals(FragmentType.INFO.toString())) {
            replaceFragment(FragmentType.MOVIE);
        } else {
            super.onBackPressed();
        }
    }
}


utils>MovieFragment.java

package com.example.myapplication;

import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.example.myapplication.adapter.MovieAdapter;
import com.example.myapplication.databinding.FragmentMovieBinding;
import com.example.myapplication.interfaces.OnPageTypeChange;
import com.example.myapplication.models.Movie;
import com.example.myapplication.models.YtsData;
import com.example.myapplication.repository.MovieService;
import com.example.myapplication.utils.Define;

import java.util.ArrayList;
import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;


public class MovieFragment extends Fragment {

    private static final String TAG = MovieFragment.class.getName();
    private FragmentMovieBinding binding;// view Binding
    private static MovieFragment movieFragment; // 싱글톤 패턴
    private MovieAdapter movieAdapter;
    private MovieService service;
    private final OnPageTypeChange onPageTypeChange;


    private int currentPageNumber = 1;
    private static final int DATA_LIMIT = 10;
    private List<Movie> movieList = new ArrayList<>();
    private boolean preventDuplicateScrollEvent = true;
    private boolean isFirstFragmentStart = true;

    private MovieFragment(OnPageTypeChange onPageTypeChange) {
        this.onPageTypeChange = onPageTypeChange;
    }


    public static MovieFragment getInstance(OnPageTypeChange onPageTypeChange) {
        if (movieFragment == null) {
            movieFragment = new MovieFragment(onPageTypeChange);
        }
        return movieFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        service = MovieService.retrofit.create(MovieService.class);
        onPageTypeChange.typeToolbarChange(Define.PAGE_TITLE_MOVIE);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 플래그먼트에서 view binding 사용 방법
        binding = FragmentMovieBinding.inflate(inflater, container, false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        initRecyclerView();
        if (isFirstFragmentStart) {
            requestMoviesData(currentPageNumber);
        } else {
            binding.progressIndicator.setVisibility(View.GONE);
        }

    }

    private void initRecyclerView() {
        // 1. 어댑터
        // 2. 매니저
        // 3. 셋팅
        movieAdapter = new MovieAdapter(getContext());
        movieAdapter.addItem(movieList);

        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
        final RecyclerView recyclerView = binding.movieRecyclerView;

        recyclerView.setAdapter(movieAdapter);
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.hasFixedSize();
        recyclerView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                if (preventDuplicateScrollEvent) {
                    int lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();
                    int itemTotalCount = recyclerView.getAdapter().getItemCount() - 1;
                    if (lastVisibleItemPosition == itemTotalCount) {
//                        Toast.makeText(getContext(), "마지막 위치 입니다!", Toast.LENGTH_SHORT).show();
                        if (currentPageNumber != 1) {
                            requestMoviesData(currentPageNumber);
                            preventDuplicateScrollEvent = false;
                        }
                    }
                }
            }
        });
    }

    private void requestMoviesData(int page) {
        String orderBy = "rating";

        service.repoContributors(orderBy, page, DATA_LIMIT)
                .enqueue(new Callback<YtsData>() {
                    @Override
                    public void onResponse(Call<YtsData> call, Response<YtsData> response) {
                        if (response.isSuccessful()) {
                            YtsData ytsData = response.body();
                            // List<Movie> list = ytsData.getData().getMovies();
                            movieList = ytsData.getData().getMovies();
                            movieAdapter.addItem(ytsData.getData().getMovies());
                            currentPageNumber++; // 2
                            preventDuplicateScrollEvent = true;
                            isFirstFragmentStart = false;

                            binding.progressIndicator.setVisibility(View.GONE);
                        } else {
                            //assert 란 개발자들이 디버깅을 빠르게 하기위한 도구
                            //즉, 에러 검출용 코드이지 코를 다 완성하고 동작할 때 돌아가는 함수가 아니다.
                            //log 보다 더 효율적으로 사용될 수 있다.
                            //컴파일, 실제 앱을 배포(컴파일러가 무시)
                            assert response.errorBody() != null;
                            Log.d(TAG, response.errorBody().toString());
                        }
                    }

                    @Override
                    public void onFailure(Call<YtsData> call, Throwable t) {
                        Log.d(TAG, t.getMessage());
                    }
                });
    }
}


utils>SplashActivity.java

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.TextView;

public class SplashActivity extends AppCompatActivity {

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

        getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN
        );
        TextView textView = findViewById(R.id.splashTextView);

        Animation slideAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_side);
        textView.startAnimation(slideAnimation);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Intent intent = new Intent(getApplicationContext(), MainActivity.class);
                startActivity(intent);
                finish();
            }
        }, 2000);
    }
}

 

anim>slide_in_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="500"
        android:fromYDelta="100%"
        android:toYDelta="0%" />
</set>


anim>slide_out_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="500"
        android:fromYDelta="0%"
        android:toYDelta="100%" />
</set>


anim>slide_side.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="2000"
        android:fromXDelta="-100%"
        android:fromYDelta="0%" />

    <alpha
        android:duration="2000"
        android:fromAlpha="0.1"
        android:toAlpha="1.0" />
</set>


drawable>ic_baseline_favorite_24.xml => ❤ icon

drawable>ic_baseline_info_24.xml =>  icon
drawable>ic_baseline_movie_24.xml => 🎬 icon


drawable>round_image.png


drawable>top_round.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners
        android:topLeftRadius="30dp"
        android:topRightRadius="30dp" />

    <solid android:color="@color/white" />

    <stroke
        android:width="1dp"
        android:color="@color/brand_100" />

</shape>


layout>activity_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".DetailActivity">

        <ImageView
            android:id="@+id/backgroundImageView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/black" />

        <TextView
            android:id="@+id/titleTextView"
            style="@style/info_text_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxWidth="250dp"
            android:text="dlaksjdhflkjashdflkjahsdflkjhasdlfkjalksjdhflkajshd"
            android:textAlignment="center"
            app:layout_constraintBottom_toTopOf="@id/yearTextView"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_chainStyle="packed" />


        <TextView
            android:id="@+id/yearTextView"
            style="@style/info_text_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxWidth="250dp"
            android:text="2020"
            android:textAlignment="center"
            app:layout_constraintBottom_toTopOf="@id/runTimeTextView"
            app:layout_constraintEnd_toEndOf="@id/titleTextView"
            app:layout_constraintStart_toStartOf="@id/titleTextView"
            app:layout_constraintTop_toBottomOf="@id/titleTextView" />

        <TextView
            android:id="@+id/runTimeTextView"
            style="@style/info_text_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxWidth="250dp"
            android:text="120분"
            android:textAlignment="center"
            app:layout_constraintBottom_toTopOf="@id/moviePoster"
            app:layout_constraintEnd_toEndOf="@id/titleTextView"
            app:layout_constraintStart_toStartOf="@id/titleTextView"
            app:layout_constraintTop_toBottomOf="@id/yearTextView" />

        <ImageView
            android:id="@+id/moviePoster"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="12dp"
            android:src="@drawable/round_image"
            app:layout_constraintBottom_toTopOf="@id/showContentButton"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/runTimeTextView" />

        <Button
            android:id="@+id/showContentButton"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="@string/str_show_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="@id/moviePoster"
            app:layout_constraintStart_toStartOf="@id/moviePoster"
            app:layout_constraintTop_toBottomOf="@id/moviePoster" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <LinearLayout
        android:id="@+id/bottomSheetContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:isScrollContainer="true"
        android:orientation="vertical"
        tools:layout_height="500dp" />

</FrameLayout>


layout>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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/topAppBar"
        style="@style/Widget.MaterialComponents.Toolbar.Primary"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:menu="@menu/top_app_bar"
        app:title="@string/app_name" />

    <LinearLayout
        android:id="@+id/mainContainer"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toTopOf="@id/bottomNaviContainer"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/topAppBar" />

    <LinearLayout
        android:id="@+id/bottomNaviContainer"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/mainContainer"
        tools:layout_height="48dp">

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigation"
            style="@style/Widget.MaterialComponents.BottomNavigationView.Colored"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:menu="@menu/bottom_navigation_menu" />


    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>


layout>activity_splash.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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    tools:context=".SplashActivity">

    <TextView
        android:id="@+id/splashTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_name"
        android:textColor="@color/brand_100"
        android:textSize="40sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>


layout>fragment_bottom_sheet.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".BottomSheetFragment">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="500dp"
        android:layout_marginTop="100dp"
        android:background="@drawable/top_round">


        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginTop="50dp"
                android:orientation="vertical"
                android:paddingStart="10dp"
                android:paddingEnd="10dp">

                <TextView
                    android:id="@+id/summaryTextView"
                    style="@style/bottom_sheet_text_style"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    tools:text="" />

                <TextView
                    android:id="@+id/descriptionTextView"
                    style="@style/bottom_sheet_text_style"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    tools:text="" />

                <TextView
                    android:id="@+id/synopsisTextView"
                    style="@style/bottom_sheet_text_style"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    tools:text="" />

            </LinearLayout>
        </ScrollView>

    </androidx.constraintlayout.widget.ConstraintLayout>

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/movieImageview"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_gravity="center_horizontal"
        android:src="@drawable/round_image"
        app:civ_border_color="@color/success"
        app:civ_border_width="2dp" />

</FrameLayout>


layout>fragment_info.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".InfoFragment">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


    <com.google.android.material.progressindicator.LinearProgressIndicator
        android:id="@+id/progressIndicator"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:indeterminate="true" />

</FrameLayout>


layout>fragment_movie.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MovieFragment">


    <!-- RecyclerView 선언 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/movieRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:listitem="@layout/item_movie_card" />

    <com.google.android.material.progressindicator.LinearProgressIndicator
        android:id="@+id/progressIndicator"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />



</androidx.constraintlayout.widget.ConstraintLayout>


layout>item_movie_card.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="wrap_content">


    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.cardview.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="120dp"
            android:layout_margin="20dp"
            android:backgroundTint="@color/danger"
            app:cardCornerRadius="10dp">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <androidx.constraintlayout.widget.Guideline
                    android:id="@+id/guideline1"
                    android:layout_width="1dp"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    app:layout_constraintGuide_begin="135dp" />


                <androidx.constraintlayout.widget.Guideline
                    android:id="@+id/guideline2"
                    android:layout_width="1dp"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    app:layout_constraintGuide_end="16dp" />

                <TextView
                    android:id="@+id/titleTv"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:ellipsize="end"
                    android:gravity="end"
                    android:maxLines="2"
                    android:textColor="#000000"
                    app:layout_constraintBottom_toTopOf="@id/ratingTv"
                    app:layout_constraintEnd_toEndOf="@id/guideline2"
                    app:layout_constraintStart_toStartOf="@id/guideline1"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintVertical_bias="0.7"
                    app:layout_constraintVertical_chainStyle="packed"
                    tools:text="벤자민의 시간은 꺼꾸로 간다 " />

                <TextView
                    android:id="@+id/ratingTv"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:gravity="end"
                    android:text="9.9"
                    app:layout_constraintBottom_toTopOf="@id/ratingBar"
                    app:layout_constraintEnd_toEndOf="@id/guideline2"
                    app:layout_constraintStart_toStartOf="@id/guideline1"
                    app:layout_constraintTop_toBottomOf="@id/titleTv" />

                <RatingBar
                    android:id="@+id/ratingBar"
                    style="?android:ratingBarStyleSmall"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:numStars="10"
                    android:stepSize="1"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="@id/guideline2"
                    app:layout_constraintStart_toStartOf="@id/guideline1"
                    app:layout_constraintTop_toBottomOf="@id/ratingTv"
                    tools:rating="1" />


            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.cardview.widget.CardView>


    </RelativeLayout>


    <ImageView
        android:id="@+id/posterIv"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_marginStart="35dp"
        android:layout_marginTop="10dp"
        android:scaleType="fitCenter"
        android:src="@drawable/round_image" />

</FrameLayout>


menu>bottom_navigation_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/page_1"
        android:icon="@drawable/ic_baseline_movie_24"
        android:title="@string/str_movie" />

    <item
        android:id="@+id/page_2"
        android:icon="@drawable/ic_baseline_info_24"
        android:title="@string/str_info" />
</menu>

 

 

 

 

 

 


menu>top>app_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/favorite"
        android:icon="@drawable/ic_baseline_favorite_24"
        android:title="@string/str_favorite"
        app:showAsAction="ifRoom" />

</menu>

 

 

 

 

 

 

 

 

color.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<color name="brand_100">#102A70</color>
    <color name="success">#547702</color>
    <color name="info">#008AA5</color>
    <color name="warning">#BC7203</color>
    <color name="danger">#8E1818</color>
</resources>

string.xml

<resources>
    <string name="app_name">YTS MOVIE</string>
    <string name="str_favorite">favorite</string>
    <string name="str_movie">movie</string>
    <string name="str_info">info</string>
    <!-- TODO: Remove or change this placeholder text -->
    <string name="hello_blank_fragment">Hello blank fragment</string>
    <string name="str_show_content">show content</string>
    <string name="str_temp">제작연도 %s</string>
</resources>

style.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="info_text_style" parent="android:Widget.TextView">
        <item name="android:textColor">@color/info</item>
        <item name="android:textSize">20sp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:layout_marginBottom">12dp</item>
    </style>

    <style name="bottom_sheet_text_style" parent="android:Widget.TextView">
        <item name="android:textColor">@color/black</item>
        <item name="android:textSize">16sp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:layout_marginBottom">16dp</item>
        <item name="android:gravity">start|top</item>
    </style>

</resources>

themes>themes.xml

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/brand_100</item>
        <item name="colorPrimaryVariant">@color/info</item>
        <item name="colorOnPrimary">@color/success</item>

        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/warning</item>
        <item name="colorSecondaryVariant">@color/danger</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">@color/brand_100</item>
        <!-- Customize your theme here. -->
    </style>
</resources>