될 때까지 안드로이드 정리 (오준석의 생존코딩) 4
- 32장 채팅 앱 만들기 : 파이어베이스 활용 1
- 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