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 LatLng currentLatlng = null; private LatLng initialLatlng; private LatLng shopLatlng; 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) { } }