Android

될 때까지 안드로이드 정리 (오준석의 생존코딩) 4

 

 

  1. 32장 채팅 앱 만들기 : 파이어베이스 활용 1
  2. 33장 미세먼지 앱 만들기1 – 앱 소개

 

 


 

될 때까지 안드로이드 #34 [32장 채팅 앱 만들기 : 파이어베이스 활용 1]

 

 

 

 

 


 

될 때까지 안드로이드 #42 [33장 미세먼지 앱 만들기1 – 앱 소개]

 

 

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

 

 

 

<application
    android:name=".MyApplication"

 

 

 

프로젝트 레벨
classpath "io.realm:realm-gradle-plugin:3.5.0"

 

 

앱 레벨
apply plugin: 'realm-android'

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'com.google.android.material:material:1.2.0'
    implementation 'androidx.navigation:navigation-fragment:2.3.0'
    implementation 'androidx.navigation:navigation-ui:2.3.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.google.android.gms:play-services-maps:17.0.0'
    implementation 'com.google.android.gms:play-services-location:17.0.0'


}

 

 

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

</androidx.drawerlayout.widget.DrawerLayout>

 

 

app_bar_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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="com.suwonsmartapp.quickdustinfo.MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_main" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@drawable/ic_baseline_add_24" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

 

 

content_main.xml
<?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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.suwonsmartapp.quickdustinfo.MainActivity"
    tools:showIn="@layout/app_bar_main">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabGravity="fill"
        app:tabMode="scrollable"
        app:tabTextColor="#000000" />

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

 

 

fragment_add_location.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="24dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="원하는 지역의 미세먼지 정보를 확인하세요"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

    <EditText
        android:id="@+id/city_edit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="도시 입력" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="예:'수원시','오산시' 등"
        android:textStyle="italic" />

</LinearLayout>

 

 

fragment_fine_dust.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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:padding="8dp">

            <androidx.cardview.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:cardCornerRadius="8dp"
                app:cardElevation="4dp"
                app:cardUseCompatPadding="true">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:padding="8dp">

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal"
                        android:padding="4dp">

                        <ImageView
                            android:layout_width="36dp"
                            android:layout_height="36dp"
                            app:srcCompat="@drawable/ic_baseline_location_on_24" />

                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center_vertical"
                            android:paddingLeft="4dp"
                            android:paddingStart="4dp"
                            android:text="관측장소"
                            android:textAppearance="@style/TextAppearance.AppCompat.Large" />

                    </LinearLayout>

                    <TextView
                        android:id="@+id/result_location_text"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:paddingBottom="8dp"
                        android:paddingLeft="24dp"
                        android:paddingStart="24dp"
                        android:paddingTop="8dp"
                        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

                </LinearLayout>
            </androidx.cardview.widget.CardView>

            <androidx.cardview.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:cardCornerRadius="8dp"
                app:cardElevation="4dp"
                app:cardUseCompatPadding="true">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:padding="8dp">

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal"
                        android:padding="4dp">

                        <ImageView
                            android:layout_width="36dp"
                            android:layout_height="36dp"
                            app:srcCompat="@drawable/ic_baseline_access_time_24" />

                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center_vertical"
                            android:paddingLeft="4dp"
                            android:paddingStart="4dp"
                            android:text="관측시간"
                            android:textAppearance="@style/TextAppearance.AppCompat.Large" />

                    </LinearLayout>

                    <TextView
                        android:id="@+id/result_time_text"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:paddingBottom="8dp"
                        android:paddingLeft="24dp"
                        android:paddingStart="24dp"
                        android:paddingTop="8dp"
                        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

                </LinearLayout>
            </androidx.cardview.widget.CardView>

            <androidx.cardview.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:cardCornerRadius="8dp"
                app:cardElevation="4dp"
                app:cardUseCompatPadding="true">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:padding="8dp">

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal"
                        android:padding="4dp">

                        <ImageView
                            android:layout_width="36dp"
                            android:layout_height="36dp"
                            app:srcCompat="@drawable/ic_baseline_blur_on_24" />

                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center_vertical"
                            android:paddingLeft="4dp"
                            android:paddingStart="4dp"
                            android:text="미세먼지(PM10)"
                            android:textAppearance="@style/TextAppearance.AppCompat.Large" />

                    </LinearLayout>

                    <TextView
                        android:id="@+id/result_dust_text"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:paddingBottom="8dp"
                        android:paddingLeft="24dp"
                        android:paddingStart="24dp"
                        android:paddingTop="8dp"
                        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

                </LinearLayout>
            </androidx.cardview.widget.CardView>


        </LinearLayout>
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

</FrameLayout>

 

 

nav_header_main.xml
<?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="@dimen/nav_header_height"
    android:background="#ffffff"
    android:gravity="bottom"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/nav_header_vertical_spacing"
        android:scaleType="centerInside"
        app:srcCompat="@drawable/custom_logo" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/nav_header_vertical_spacing"
        android:text="Android Studio"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="android.studio@android.com" />

</LinearLayout>

 

 

common/AddLocationDialogFragment
public class AddLocationDialogFragment extends DialogFragment {

    private OnClickListener mOnClickListener;


    public interface OnClickListener {
        void onOkClicked(String city);
    }

    public void setOnClickListener(OnClickListener onClickListener) {
        mOnClickListener = onClickListener;
    }

    private EditText mCityEditText;

    public static AddLocationDialogFragment newInstance(OnClickListener listener) {

        Bundle args = new Bundle();

        AddLocationDialogFragment fragment = new AddLocationDialogFragment();
        fragment.setOnClickListener(listener);
        fragment.setArguments(args);
        return fragment;
    }

    public AddLocationDialogFragment() {
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_add_location, null, false);

        mCityEditText = (EditText) view.findViewById(R.id.city_edit);

        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("위치 추가");
        builder.setView(view);
        builder.setPositiveButton("확인", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                String city = mCityEditText.getText().toString();
                mOnClickListener.onOkClicked(city);
            }
        });
        builder.setNegativeButton("취소", null);
        return builder.create();
    }
}

 

 

data/

public interface FineDustRepository {

    boolean isAvailable();

    void getFineDustData(Callback<FineDust> callback);
}

 

 

data/

public class LocationFineDustRepository implements FineDustRepository {

    private FineDustUtil mFineDustUtil;
    private double mLatitude;
    private double mLongitude;

    public LocationFineDustRepository(double lat, double lng) {
        mFineDustUtil = new FineDustUtil();
        mLatitude = lat;
        mLongitude = lng;
    }

    @Override
    public void getFineDustData(Callback callback) {
        mFineDustUtil.getApi().getFineDust(mLatitude, mLongitude).enqueue(callback);
    }

    @Override
    public boolean isAvailable() {
        if (mLatitude != 0.0 && mLongitude != 0.0) {
            return true;

        }
        return false;
    }
}

 

 

db/

public class LocationRealmObject extends RealmObject {
    private String name;
    private double lat;
    private double lng;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getLat() {
        return lat;
    }

    public void setLat(double lat) {
        this.lat = lat;
    }

    public double getLng() {
        return lng;
    }

    public void setLng(double lng) {
        this.lng = lng;
    }
}

 

 

finedust/

public interface FineDustContract {
    interface View {
        void showFineDustResult(FineDust fineDust);

        void showLoadError(String message);

        void loadingStart();

        void loadingEnd();

        void reload(double lat, double lng);
    }

    interface UserActionsListener {
        void loadFineDustData();
    }
}

 

 

finedust/

public class FineDustFragment extends Fragment implements FineDustContract.View {

    private TextView mLocationTextView;
    private TextView mTimeTextView;
    private TextView mDustTextView;

    private SwipeRefreshLayout mSwipeRefreshLayout;

    private FineDustRepository mRepository;
    private FineDustPresenter mPresenter;

    public static FineDustFragment newInstance(double lat, double lng) {
        Bundle args = new Bundle();
        args.putDouble("lat", lat);
        args.putDouble("lng", lng);

        FineDustFragment fragment = new FineDustFragment();
        fragment.setArguments(args);
        return fragment;
    }

    public FineDustFragment() {
        // 반드시 필요함
    }

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

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_fine_dust, container, false);

        mLocationTextView = (TextView) view.findViewById(R.id.result_location_text);
        mTimeTextView = (TextView) view.findViewById(R.id.result_time_text);
        mDustTextView = (TextView) view.findViewById(R.id.result_dust_text);

        if (savedInstanceState != null) {
            mLocationTextView.setText(savedInstanceState.getString("location"));
            mTimeTextView.setText(savedInstanceState.getString("time"));
            mDustTextView.setText(savedInstanceState.getString("dust"));
        }

        mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout);
        mSwipeRefreshLayout.setColorSchemeColors(Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE);
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                mPresenter.loadFineDustData();
            }
        });

        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (getArguments() != null) {
            double lat = getArguments().getDouble("lat");
            double lng = getArguments().getDouble("lng");
            mRepository = new LocationFineDustRepository(lat, lng);
        } else {
            mRepository = new LocationFineDustRepository(0, 0);
            ((MainActivity) getActivity()).getLastKnownLocation();
        }

        mPresenter = new FineDustPresenter(mRepository, this);

        mPresenter.loadFineDustData();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.frag_fine_dust, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_refresh) {
            mPresenter.loadFineDustData();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }


    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putString("location", mLocationTextView.getText().toString());
        outState.putString("time", mTimeTextView.getText().toString());
        outState.putString("dust", mDustTextView.getText().toString());
    }

    @Override
    public void showFineDustResult(FineDust fineDust) {
        try {
            mLocationTextView.setText(fineDust.getWeather().getDust().get(0).getStation().getName());
            mTimeTextView.setText(fineDust.getWeather().getDust().get(0).getTimeObservation());
            mDustTextView.setText(fineDust.getWeather().getDust().get(0).getPm10().getValue() + " ㎍/㎥, "
                    + fineDust.getWeather().getDust().get(0).getPm10().getGrade());
        } catch (Exception e) {
            mLocationTextView.setText("일일 허용량 초과");
            mTimeTextView.setText("일일 허용량 초과");
            mDustTextView.setText("일일 허용량 초과");
        }
    }

    @Override
    public void showLoadError(String message) {
        Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void loadingStart() {
        mSwipeRefreshLayout.setRefreshing(true);
    }

    @Override
    public void loadingEnd() {
        mSwipeRefreshLayout.setRefreshing(false);
    }

    @Override
    public void reload(double lat, double lng) {
        mRepository = new LocationFineDustRepository(lat, lng);
        mPresenter = new FineDustPresenter(mRepository, this);
        mPresenter.loadFineDustData();
    }
}

 

 

finedust/

public class FineDustPresenter implements FineDustContract.UserActionsListener {


    private final FineDustRepository mRepository;
    private final FineDustContract.View mView;

    public FineDustPresenter(FineDustRepository repository, FineDustContract.View view) {
        mRepository = repository;
        mView = view;
    }

    @Override
    public void loadFineDustData() {
        // 데이터 제공이 가능하면
        if (mRepository.isAvailable()) {
            // 로딩 시작
            mView.loadingStart();

            // 데이터 가져오기
            mRepository.getFineDustData(new Callback<FineDust>() {
                @Override
                public void onResponse(Call<FineDust> call, Response<FineDust> response) {
                    // 데이터 표시하기
                    mView.showFineDustResult(response.body());
                    // 로딩 끝
                    mView.loadingEnd();
                }

                @Override
                public void onFailure(Call<FineDust> call, Throwable t) {
                    // 에러 표시하기
                    mView.showLoadError(t.getLocalizedMessage());
                    // 로딩 끝
                    mView.loadingEnd();
                }
            });
        }
    }
}

 

 

 

model/

public class Common {

    private String alertYn;
    private String stormYn;

    public String getAlertYn() {
        return alertYn;
    }

    public void setAlertYn(String alertYn) {
        this.alertYn = alertYn;
    }

    public String getStormYn() {
        return stormYn;
    }

    public void setStormYn(String stormYn) {
        this.stormYn = stormYn;
    }

    @Override
    public String toString() {
        return "Common{" +
                "alertYn='" + alertYn + '\'' +
                ", stormYn='" + stormYn + '\'' +
                '}';
    }
}

 

 

model/dust_material

public class Dust {

    private Station station;
    private String timeObservation;
    private Pm10 pm10;

    public Station getStation() {
        return station;
    }

    public void setStation(Station station) {
        this.station = station;
    }

    public String getTimeObservation() {
        return timeObservation;
    }

    public void setTimeObservation(String timeObservation) {
        this.timeObservation = timeObservation;
    }

    public Pm10 getPm10() {
        return pm10;
    }

    public void setPm10(Pm10 pm10) {
        this.pm10 = pm10;
    }

    @Override
    public String toString() {
        return "Dust{" +
                "station=" + station +
                ", timeObservation='" + timeObservation + '\'' +
                ", pm10=" + pm10 +
                '}';
    }
}

 

 

model/dust_material

public class Pm10 {

    private String grade;
    private String value;

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Pm10{" +
                "grade='" + grade + '\'' +
                ", value='" + value + '\'' +
                '}';
    }
}

 

 

model/dust_material

public class Result {

    private Integer code;
    private String requestUrl;
    private String message;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getRequestUrl() {
        return requestUrl;
    }

    public void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", requestUrl='" + requestUrl + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

 

 

model/dust_material

public class Station {

    private String latitude;
    private String longitude;
    private String name;
    private String id;

    public String getLatitude() {
        return latitude;
    }

    public void setLatitude(String latitude) {
        this.latitude = latitude;
    }

    public String getLongitude() {
        return longitude;
    }

    public void setLongitude(String longitude) {
        this.longitude = longitude;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

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

    @Override
    public String toString() {
        return "Station{" +
                "latitude='" + latitude + '\'' +
                ", longitude='" + longitude + '\'' +
                ", name='" + name + '\'' +
                ", id='" + id + '\'' +
                '}';
    }
}

 

 

model/

public class Weather {

    private List<Dust> dust;

    public List<Dust> getDust() {
        return dust;
    }

    public void setDust(List<Dust> dust) {
        this.dust = dust;
    }

    @Override
    public String toString() {
        return "Weather{" +
                "dust=" + dust +
                '}';
    }
}

 

 

 

public class FineDust {

    private Weather weather;
    private Common common;
    private Result result;

    public Weather getWeather() {
        return weather;
    }

    public void setWeather(Weather weather) {
        this.weather = weather;
    }

    public Common getCommon() {
        return common;
    }

    public void setCommon(Common common) {
        this.common = common;
    }

    public Result getResult() {
        return result;
    }

    public void setResult(Result result) {
        this.result = result;
    }

    @Override
    public String toString() {
        return "FineDust{" +
                "weather=" + weather +
                ", common=" + common +
                ", result=" + result +
                '}';
    }
}

 

 

util/

public interface FineDustApi {
    String BASE_URL = "https://api.weatherplanet.co.kr/";

    @Headers("appKey: 6b200e091d1a4d7e83fb9b4732809b33")
    // 쿼리
    @GET("weather/dust?version=1")
    Call<FineDust> getFineDust(@Query("lat") double latitude,
                               @Query("lon") double longitude);
}

 

 

util/

public class FineDustUtil {

    private FineDustApi mGetApi;

    public FineDustUtil() {
        Retrofit mRetrofit = new Retrofit.Builder()
                .baseUrl(FineDustApi.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        mGetApi = mRetrofit.create(FineDustApi.class);
    }

    public FineDustApi getApi() {
        return mGetApi;
    }
}

 

 

util/

public class GeoUtil {

    public interface GeoUtilListener {
        void onSuccess(double lat, double lng);

        void onError(String message);
    }

    public static void getLocationFromName(Context context, final String city, final GeoUtilListener listener) {
        Geocoder geocoder = new Geocoder(context, Locale.getDefault());
        List<Address> addresses = new ArrayList<>();
        try {
            addresses = geocoder.getFromLocationName(city, 1);
            if (addresses.size() > 0) {
                double lat = addresses.get(0).getLatitude();
                double lng = addresses.get(0).getLongitude();
                listener.onSuccess(lat, lng);
            } else {
                listener.onError("주소 결과가 없습니다");
            }
        } catch (IOException e) {
            listener.onError(e.getMessage());
        }
    }
}

 

 

util/

public class IntentUtil {
    // 소스 코드
    public static Intent getSourceCodeIntent() {
        return new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/junsuk5/android-first"));
    }

    public static Intent getBuyBookIntent() {
        return new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.yes24.com/"));
    }

    public static Intent getFacebookPageIntent() {
        return new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.facebook.com/untilandroid"));
    }

    public static Intent getPublisherIntent() {
        return new Intent(Intent.ACTION_VIEW, Uri.parse("https://rubypaper.tistory.com/"));
    }

    public static Intent getHomepageIntent() {
        return new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.suwonsmartapp.com"));
    }

    public static Intent getLocationIntent() {
        return new Intent(Intent.ACTION_VIEW, Uri.parse("geo:37.274105,127.02262100000007(세민직업전문학교)?z=11"));
    }

    public static Intent getDialPhoneIntent() {
        return new Intent(Intent.ACTION_DIAL, Uri.parse("tel:031-236-5040"));
    }
}

 

 

 

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    public static final int REQUEST_CODE_FINE_COARSE_PERMISSION = 1000;
    private FusedLocationProviderClient mFusedLocationClient;
    private Toolbar mToolbar;
    private TabLayout mTabLayout;
    private ViewPager mViewPager;

    private ArrayList<Pair<Fragment, String>> mFragmentList;

    private Realm mRealm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);

        mRealm = Realm.getDefaultInstance();

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AddLocationDialogFragment.newInstance(new AddLocationDialogFragment.OnClickListener() {
                    @Override
                    public void onOkClicked(final String city) {
                        GeoUtil.getLocationFromName(MainActivity.this,
                                city, new GeoUtil.GeoUtilListener() {
                                    @Override
                                    public void onSuccess(double lat, double lng) {
                                        saveNewCity(lat, lng, city);
                                        addNewFragment(lat, lng, city);
                                    }

                                    @Override
                                    public void onError(String message) {
                                        Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
                                    }
                                });
                    }
                }).show(getSupportFragmentManager(), "dialog");
            }
        });

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        setUpViewPager();

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
    }

    private void saveNewCity(double lat, double lng, String city) {
        mRealm.beginTransaction();
        LocationRealmObject newLocationRealmObject = mRealm.createObject(LocationRealmObject.class);
        newLocationRealmObject.setName(city);
        newLocationRealmObject.setLat(lat);
        newLocationRealmObject.setLng(lng);
        mRealm.commitTransaction();
    }

    private void addNewFragment(double lat, double lng, String city) {
        mFragmentList.add(new Pair<Fragment, String>(FineDustFragment.newInstance(lat, lng), city));
        mViewPager.getAdapter().notifyDataSetChanged();
    }

    private void setUpViewPager() {
        mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
        mViewPager = (ViewPager) findViewById(R.id.view_pager);

        loadDbData();

        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager(), mFragmentList);
        mViewPager.setAdapter(adapter);

        mTabLayout.setupWithViewPager(mViewPager);
    }

    private void loadDbData() {
        RealmResults<LocationRealmObject> realmResults = mRealm.where(LocationRealmObject.class).findAll();

        mFragmentList = new ArrayList<>();
        mFragmentList.add(new Pair<Fragment, String>(new FineDustFragment(), "현재 위치"));
        for (LocationRealmObject realmObject : realmResults) {
            mFragmentList.add(new Pair<Fragment, String>(FineDustFragment.newInstance(realmObject.getLat(), realmObject.getLng()), realmObject.getName()));
        }
    }

    public void getLastKnownLocation() {
        if (ActivityCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // 권한 요청
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
                    REQUEST_CODE_FINE_COARSE_PERMISSION);
            return;
        }

        mFusedLocationClient.getLastLocation()
                .addOnSuccessListener(this, new OnSuccessListener<Location>() {
                    @Override
                    public void onSuccess(Location location) {
                        // Got last known location. In some rare situations this can be null.
                        if (location != null) {
                            FineDustContract.View view = (FineDustContract.View) mFragmentList.get(0).first;
                            view.reload(location.getLatitude(), location.getLongitude());
                        }
                    }
                });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == REQUEST_CODE_FINE_COARSE_PERMISSION) {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED
                    && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
                getLastKnownLocation();
            }
        }
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_all_delete) {
            mRealm.beginTransaction();
            mRealm.where(LocationRealmObject.class).findAll().deleteAllFromRealm();
            mRealm.commitTransaction();
            setUpViewPager();
            return true;
        } else if (id == R.id.action_delete) {
            if (mTabLayout.getSelectedTabPosition() == 0) {
                Toast.makeText(this, "현재 위치 탭은 삭제할 수 없습니다", Toast.LENGTH_SHORT).show();
                return true;
            }
            mRealm.beginTransaction();
            mRealm.where(LocationRealmObject.class).findAll().get(mTabLayout.getSelectedTabPosition() - 1).deleteFromRealm();
            mRealm.commitTransaction();
            setUpViewPager();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        Intent intent = null;
        if (id == R.id.nav_source) {
            intent = IntentUtil.getSourceCodeIntent();
        } else if (id == R.id.nav_book) {
            intent = IntentUtil.getBuyBookIntent();
        } else if (id == R.id.nav_facebook) {
            intent = IntentUtil.getFacebookPageIntent();
        } else if (id == R.id.nav_publisher) {
            intent = IntentUtil.getPublisherIntent();
        } else if (id == R.id.nav_home) {
            intent = IntentUtil.getHomepageIntent();
        } else if (id == R.id.nav_location) {
            intent = IntentUtil.getLocationIntent();
        } else if (id == R.id.nav_phone) {
            intent = IntentUtil.getDialPhoneIntent();
        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);

        if (intent != null && intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

        return true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        mRealm.close();
    }

    private static class MyPagerAdapter extends FragmentStatePagerAdapter {

        private final List<Pair<Fragment, String>> mFragmentList;

        public MyPagerAdapter(FragmentManager fm, List<Pair<Fragment, String>> fragmentList) {
            super(fm);
            mFragmentList = fragmentList;
        }

        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position).first;
        }

        @Override
        public int getCount() {
            return mFragmentList.size();
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mFragmentList.get(position).second;
        }
    }
}

 

 

 

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Realm.init(this);
        RealmConfiguration config = new RealmConfiguration.Builder().build();
        Realm.setDefaultConfiguration(config);
    }
}

 

 

 

 

 

 


 

될 때까지 안드로이드 (오준석의 생존코딩) 유튜브 강의
https://www.youtube.com/watch?v=euTtMnN-TgI&list=PLxTmPHxRH3VWTd-8KB67Itegihkl4SVKe&index=2

 

될 때까지 안드로이드 (오준석의 생존코딩) 깃헙
https://github.com/junsuk5/android-first-book

 

될 때까지 안드로이드 소스 다운로드
android-first-book-master.zip

 

 

 

 

 

Related posts

Leave a Comment