Newer
Older
NemophilaClient / app / src / main / java / com / example / nemophila / MapsFragment.java
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) {

    }

}