package com.example.nemophila;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.text.TextUtils;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import com.example.nemophila.databinding.ActivityMainBinding;
import com.example.nemophila.entities.AccountNameJson;
import com.example.nemophila.entities.Post;
import com.example.nemophila.entities.Shop;
import com.example.nemophila.viewmodels.FriendViewModel;
import com.example.nemophila.viewmodels.PostsViewModel;
import com.example.nemophila.viewmodels.ShopsViewModel;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.maps.*;
import com.google.maps.model.GeocodingResult;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.GroundOverlayOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
public class MapsFragment extends Fragment implements LocationListener {
ShopsViewModel shopsViewModel;
FriendViewModel friendViewModel;
PostsViewModel postsViewModel;
Nemophila nemophila;
private GoogleMap mMap;
private GeoApiContext geoApiContext;
private ActivityMainBinding binding;
private LatLng currentLatlng = null;
private LatLng initialLatlng;
private LatLng shopLatlng;
private LatLng tapLatlng;
private Marker currentMarker;
private BitmapDescriptor bd;
ArrayList<String> friendsData;
LocationManager locationManager;
private OnMapReadyCallback callback = new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
nemophila = (Nemophila) getActivity().getApplication();
mMap = googleMap;
//ViewModelへのアクセス
shopsViewModel = new ViewModelProvider(getActivity()).get(ShopsViewModel.class);
friendViewModel = new ViewModelProvider(getActivity()).get(FriendViewModel.class);
postsViewModel = new ViewModelProvider(getActivity()).get(PostsViewModel.class);
friendsData = new ArrayList<>();
//フレンドの更新が入った時の処理(LiveDataへの購読)
friendViewModel.getFriends(nemophila.getUid());
friendViewModel.getFriendsLiveData().observe(getActivity(), friends -> {
//nemophilaにセットしておく
nemophila.setFriends(friends);
for (AccountNameJson friend : friends) {
friendsData.add(friend.getUid());
}
});
// 店情報の更新が入った時の処理(LiveDataへの購読)
shopsViewModel.getShopsLiveData().observe(getActivity(), shops -> {
for (Shop shop : shops) {
if (shopsViewModel.getMarker(shop) == null) { //フレンド以外の投稿のピンも立てたい場合
//各shopに対応するMarkerがなければMarkerを立てる
shopLatlng = new LatLng(shop.getLatitude(), shop.getLongitude());
System.out.println(shopLatlng);
Marker createMarker = mMap.addMarker(new MarkerOptions().position(shopLatlng).title(""));
//マーカーに店情報を持たせる
createMarker.setTag(shop);
//ShopToMarkerに紐づけ
shopsViewModel.setShopAndMarker(shop, createMarker);
System.out.println(friendsData);
ArrayList<String> userIdSet = (ArrayList<String>) shop.getUserIdSet().clone();
friendsData.add(nemophila.getUid());
userIdSet.retainAll(friendsData);
//自分とそのフレンド以外の投稿は確認しない
if (userIdSet.size() > 0) {
//場合分け
//フィルターを一切かけていないとき
if (nemophila.getSelectGenres().isEmpty() && nemophila.getSelectFriends().isEmpty()) {
createMarker.setVisible(true);
}
//ジャンルのみフィルターをかけているとき
else if (nemophila.getSelectFriends().isEmpty()) {
for (String genre : nemophila.getSelectGenres()) {
if (shop.getGenreSet().contains(genre)) {
createMarker.setVisible(true);
break;
} else {
createMarker.setVisible(false);
}
}
}
//フレンドのみフィルターをかけているとき
else if (nemophila.getSelectGenres().isEmpty()) {
for (String friendId : nemophila.getSelectFriends()) {
if (shop.getUserIdSet().contains(friendId)) {
createMarker.setVisible(true);
break;
} else {
createMarker.setVisible(false);
}
}
}
//どちらもフィルターをかけているとき
else {
for (String genre : nemophila.getSelectGenres()) {
for (String friendId : nemophila.getSelectFriends()) {
if (shop.getGenreSet().contains(genre) && shop.getUserIdSet().contains(friendId)) {
createMarker.setVisible(true);
break;
} else {
createMarker.setVisible(false);
}
}
}
}
} else {
createMarker.setVisible(false);
}
}
}
});
// 長押しを認識した後の処理(LiveDataへの購読)
shopsViewModel.getNearShopsLiveData().observe(getActivity(), shops -> {
//長押し時は周辺のピンを全て取得し、ダイアログに表示する
DialogFragment dialogFragment = new MapsDialogFragment(shops);
dialogFragment.show(getActivity().getSupportFragmentManager(), "mapsdialog");
});
//初期画面の座標(現在地をロードするまで表示)
initialLatlng = new LatLng(nemophila.getCameraLatitude(), nemophila.getCameraLongitude());
//初期画面に移動
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(initialLatlng, nemophila.getZoom()));
//画面が動いたとき
mMap.setOnCameraIdleListener(() -> {
//カメラの座標とZOOM倍率を保存
nemophila.setCameraLatitude(mMap.getCameraPosition().target.latitude);
nemophila.setCameraLongitude(mMap.getCameraPosition().target.longitude);
nemophila.setZoom(mMap.getCameraPosition().zoom);
//Shopの描画範囲を指定
shopsViewModel.setViewArea(nemophila.getCameraLongitude() + 7.5, nemophila.getCameraLatitude() + 7.5, nemophila.getCameraLongitude() - 7.5, nemophila.getCameraLatitude() - 7.5);
});
// test用 座標を確認するため
// タップした時のリスナーをセット
mMap.setOnMapClickListener(tapLocation -> {
// map(ピン以外)をtapされた位置の緯度経度を取得
tapLatlng = new LatLng(tapLocation.latitude, tapLocation.longitude);
Toast.makeText(getActivity(),
String.format("%s", tapLatlng),
Toast.LENGTH_SHORT)
.show();
});
//長押し時に店を作成し、その座標にピンを立てる
//長押し時にその座標を保存し、投稿画面に移り、Shop作成完了時にはピンを立て、Shop画面に移行
mMap.setOnMapLongClickListener(longpushLocation -> {
//Nemophilaに座標を保存
nemophila.setCurrentLatitude(longpushLocation.latitude);
nemophila.setCurrentLongitude(longpushLocation.longitude);
//長押しした場合は今からShopを作成するので、CurrentShopをnullで登録しておく
nemophila.setCurrentShop(null);
//長押しでダイアログを表示
//テスト用
//shopsViewModel.longClickViewArea(longpushLocation.longitude+1, longpushLocation.latitude+1, longpushLocation.longitude-1, longpushLocation.latitude-1);
//本番環境は↓の範囲で
shopsViewModel.longClickViewArea(longpushLocation.longitude + 0.001, longpushLocation.latitude + 0.001, longpushLocation.longitude - 0.001, longpushLocation.latitude - 0.001);
});
// ピンをクリックした場合
mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
//現在地マーカーをクリックしたときのみ例外
if (marker.getTag() == null) {
//以下の処理をストップ
return false;
}
//選んだ店をsetする
nemophila.setCurrentShop((Shop) marker.getTag());
System.out.println(nemophila.getCurrentShop().getName());
//ShopActivity画面に遷移
Intent intent = new Intent(getActivity(), ShopActivity.class);
startActivity(intent);
return false;
}
});
}
};
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_maps, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (ActivityCompat.checkSelfPermission(
getContext(),
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(
Manifest.permission.ACCESS_FINE_LOCATION);
} else {
locationStart();
}
((MapsActivity) getContext()).setMapsFragment(this);
SupportMapFragment mapFragment =
(SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
if (mapFragment != null) {
mapFragment.getMapAsync(callback);
}
//現在地アイコン用の準備
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_current);
Bitmap afterBitmap = Bitmap.createScaledBitmap(bitmap, 100, 100, true);
bd = BitmapDescriptorFactory.fromBitmap(afterBitmap);
}
public void zoomMap(double latitude, double longitude) {
// 表示する東西南北の緯度経度を設定
double south = latitude * (1 - 0.00005);
double west = longitude * (1 - 0.00005);
double north = latitude * (1 + 0.00005);
double east = longitude * (1 + 0.00005);
LatLng latlng = new LatLng(latitude, longitude);
//左下、右上
LatLngBounds bounds = LatLngBounds.builder()
.include(new LatLng(south, west))
.include(new LatLng(north, east))
.build();
int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
// static CameraUpdate.newLatLngBounds(LatLngBounds bounds, int width, int height, int padding)
mMap.moveCamera(CameraUpdateFactory.
newLatLngBounds(bounds, width, height, 0));
//ズーム処理
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latlng, nemophila.getZoom()));
}
//現在地の表示
private void setIcon(double latitude, double longitude) {
//現在地をLatLng型で取得
LatLng current_location = new LatLng(latitude, longitude);
//初回現在地取得時
if (currentMarker == null) {
currentMarker = mMap.addMarker(new MarkerOptions().position(current_location).title("現在地").icon(bd));
} else {
//2回目以降
currentMarker.setPosition(current_location);
}
}
private final ActivityResultLauncher<String>
requestPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
isGranted -> {
if (isGranted) {
locationStart();
} else {
Toast toast = Toast.makeText(getActivity(),
"エラー", Toast.LENGTH_SHORT);
toast.show();
}
});
//現在地の取得
@SuppressLint("MissingPermission")
private void locationStart() {
Log.d("debug", "locationStart()");
// LocationManager インスタンス生成
locationManager =
(LocationManager) getContext().getSystemService(getContext().LOCATION_SERVICE);
if (locationManager != null && locationManager.isProviderEnabled(
LocationManager.GPS_PROVIDER)) {
Log.d("debug", "location manager Enabled");
} else {
// GPSを設定するように促す
Intent settingsIntent =
new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(settingsIntent);
Log.d("debug", "not gpsEnable, startActivity");
}
if (ContextCompat.checkSelfPermission(getContext(),
Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getActivity(),
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,}, 1000);
Log.d("debug", "checkSelfPermission false");
return;
}
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
1000, 50, this);
}
@Override
public void onLocationChanged(Location location) {
//初期画面は現在地を中心にするため...
if (currentLatlng == null) {
//↓現在地ロード後画面中心を現在地にする場合
//zoomMap(location.getLatitude(), location.getLongitude());
//ロード画面の終了
//getView().findViewById(R.id.LL_Load).setVisibility(View.GONE);
//現在地ボタンを表示
getView().findViewById(R.id.currentButton).setVisibility(View.VISIBLE);
ImageButton button1 = getView().findViewById(R.id.currentButton);
button1.setOnClickListener(v -> {
Log.d("debug", "currentbutton, 現在地にカメラを移動");
System.out.println(shopsViewModel.getShopsLiveData().getValue());
//現在地にカメラを移動
zoomMap(currentLatlng.latitude, currentLatlng.longitude);
});
}
//現在地が変更されるたびに現在地アイコンを移動
//LatLng型で受け取っておく
currentLatlng = new LatLng(location.getLatitude(), location.getLongitude());
//現在地アイコンを表示.このsetIcon内にzoomMap処理もあるので注意
setIcon(location.getLatitude(), location.getLongitude());
}
//住所・施設名などから緯度経度を検索するメソッド
public void SeachLocation(String seachText) {
// Geocoderを使用して緯度経度を取得し移動
LatLng latLng = getLatLngFromAddress(seachText);
if (latLng != null) {
System.out.println("-------検索地点の座標取得完了!--------");
System.out.println("緯度: " + latLng.latitude + "\n経度: " + latLng.longitude);
System.out.println("-----------------------------------");
Toast.makeText(getActivity(),
String.format("該当の地点に移動します"),
Toast.LENGTH_SHORT)
.show();
nemophila.setZoom(18f);
zoomMap(latLng.latitude, latLng.longitude);
} else {
//緯度経度取得ミス
System.out.println("-------検索地点の座標取得失敗--------");
Toast.makeText(getActivity(),
String.format("該当の地点が見つかりませんでした"),
Toast.LENGTH_SHORT)
.show();
}
}
// 住所から緯度経度を取得するメソッド
private LatLng getLatLngFromAddress(String address) {
Geocoder geocoder = new Geocoder(requireContext(), Locale.getDefault());
try {
List<Address> addresses = geocoder.getFromLocationName(address, 1);
if (addresses != null && !addresses.isEmpty()) {
Address firstAddress = addresses.get(0);
double latitude = firstAddress.getLatitude();
double longitude = firstAddress.getLongitude();
return new LatLng(latitude, longitude);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
}