MainActivity.java 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301
  1. package com.tsi.navi.activity;
  2. import android.Manifest;
  3. import android.annotation.SuppressLint;
  4. import android.content.BroadcastReceiver;
  5. import android.content.ComponentName;
  6. import android.content.Context;
  7. import android.content.Intent;
  8. import android.content.IntentFilter;
  9. import android.content.ServiceConnection;
  10. import android.content.res.Configuration;
  11. import android.graphics.PointF;
  12. import android.graphics.Rect;
  13. import android.location.Location;
  14. import android.os.Build;
  15. import android.os.Bundle;
  16. import android.os.IBinder;
  17. import android.util.Log;
  18. import android.view.MenuInflater;
  19. import android.view.MenuItem;
  20. import android.view.View;
  21. import android.view.WindowManager;
  22. import android.widget.ImageView;
  23. import android.widget.PopupMenu;
  24. import android.widget.TextView;
  25. import android.widget.Toast;
  26. import androidx.annotation.NonNull;
  27. import androidx.annotation.RequiresApi;
  28. import androidx.annotation.UiThread;
  29. import androidx.appcompat.app.AppCompatActivity;
  30. import androidx.core.app.ActivityCompat;
  31. import androidx.fragment.app.FragmentManager;
  32. import androidx.fragment.app.FragmentTransaction;
  33. import com.fasterxml.jackson.databind.ObjectMapper;
  34. import com.naver.maps.geometry.LatLng;
  35. import com.naver.maps.geometry.LatLngBounds;
  36. import com.naver.maps.map.CameraPosition;
  37. import com.naver.maps.map.CameraUpdate;
  38. import com.naver.maps.map.CameraUpdateParams;
  39. import com.naver.maps.map.LocationTrackingMode;
  40. import com.naver.maps.map.MapFragment;
  41. import com.naver.maps.map.NaverMap;
  42. import com.naver.maps.map.NaverMapOptions;
  43. import com.naver.maps.map.NaverMapSdk;
  44. import com.naver.maps.map.OnMapReadyCallback;
  45. import com.naver.maps.map.overlay.Align;
  46. import com.naver.maps.map.overlay.InfoWindow;
  47. import com.naver.maps.map.overlay.Marker;
  48. import com.naver.maps.map.overlay.OverlayImage;
  49. import com.naver.maps.map.util.FusedLocationSource;
  50. import com.tsi.navi.R;
  51. import com.tsi.navi.TsiNaviApplication;
  52. import com.tsi.navi.activity.dialog.AppInformationDialog;
  53. import com.tsi.navi.activity.dialog.IDialogClickListener;
  54. import com.tsi.navi.activity.dialog.MapSettingDialog;
  55. import com.tsi.navi.activity.dialog.NodeCrossDialog;
  56. import com.tsi.navi.activity.fragment.AreaCrossFragment;
  57. import com.tsi.navi.activity.fragment.DirectionNaviSignalFragment;
  58. import com.tsi.navi.activity.fragment.DirectionSignalFragment;
  59. import com.tsi.navi.activity.fragment.UserSignalFragment;
  60. import com.tsi.navi.activity.model.NodeCrossItem;
  61. import com.tsi.navi.database.DbQuery;
  62. import com.tsi.navi.databinding.ActivityMainBinding;
  63. import com.tsi.navi.dto.CpuNodeStatusDTO;
  64. import com.tsi.navi.preferences.PrefManager;
  65. import com.tsi.navi.util.BackPressedForFinish;
  66. import com.tsi.navi.util.BoundaryUtils;
  67. import com.tsi.navi.util.CameraInfo;
  68. import com.tsi.navi.util.TsiUtils;
  69. import com.tsi.navi.vo.NodeVo;
  70. import com.tsi.navi.vo.TsiGpsTrackingVo;
  71. import com.tsi.navi.websocket.NaviWebsocketClientService;
  72. import org.jetbrains.annotations.NotNull;
  73. import java.util.HashSet;
  74. import java.util.List;
  75. import java.util.Map;
  76. import java.util.Set;
  77. import java.util.Vector;
  78. import java.util.concurrent.ConcurrentHashMap;
  79. import java.util.concurrent.atomic.AtomicBoolean;
  80. @RequiresApi(api = Build.VERSION_CODES.R)
  81. public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
  82. private static final String TAG = MainActivity.class.getSimpleName();
  83. private Context context;
  84. private BackPressedForFinish backPressedForFinish = null;
  85. private ActivityMainBinding binding = null;
  86. private NaverMap naverMap = null;
  87. private static final int LOCATION_PERMISSION_REQUEST_CODE = 1000;
  88. private final String[] permissions = {
  89. Manifest.permission.ACCESS_FINE_LOCATION,
  90. Manifest.permission.ACCESS_COARSE_LOCATION
  91. };
  92. private FusedLocationSource locationSource;
  93. private boolean dispAllSignal = false;
  94. private boolean isTrackingMode = false;
  95. private boolean isDebugMode = false;
  96. private boolean dispCvibSignal = false;
  97. private double trackingZoom;
  98. private float currSpeed = 0;
  99. private float lastBearing = -1, currBearing = -1;
  100. private float moveDist = 0;
  101. private CameraInfo cameraInfo;
  102. private BoundaryUtils mapUtils;
  103. private float carX = 0;
  104. private float carY = 0;
  105. private double tilt = 45;
  106. private float trackingDistance = 5;
  107. private float driveDistance = 8;
  108. private TsiGpsTrackingVo gpsTracking;
  109. private Location lastLocation = null;
  110. private final float[] resultDistance = {-999, -999, -999};
  111. private String deviceId;
  112. private DbQuery dbQuery;
  113. private int overlayResourceId;
  114. private CameraUpdateParams cameraUpdateParams;
  115. private float width, height;
  116. private NodeVo selectedNode = null;
  117. private InfoWindow infoWindow;
  118. private Vector<Marker> activeMarkers;
  119. private ConcurrentHashMap<Long, NodeVo> activeNodes;
  120. //private TsiApiService tsiApiService;
  121. private FragmentManager fragmentManager;
  122. private DirectionSignalFragment directionSignalFragment;
  123. private DirectionNaviSignalFragment directionNaviSignalFragment;
  124. private UserSignalFragment userSignalFragment;
  125. private AreaCrossFragment areaCrossFragment;
  126. private String lastTopics;
  127. private NaviWebsocketClientService naviClientService;
  128. private final AtomicBoolean moveByGps = new AtomicBoolean(false);
  129. private final ServiceConnection serviceConnection = new ServiceConnection() {
  130. @Override
  131. public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
  132. //private NaviWebsocketClient naviClient;
  133. NaviWebsocketClientService.NaviWebsocketClientBinder naviClientBinder = (NaviWebsocketClientService.NaviWebsocketClientBinder) iBinder;
  134. naviClientService = naviClientBinder.getService();
  135. //naviClient = naviClientService.getNaviWebsocketClient();
  136. Toast.makeText(context, "서버 통신 시작", Toast.LENGTH_SHORT).show();
  137. }
  138. @Override
  139. public void onServiceDisconnected(ComponentName componentName) {
  140. Toast.makeText(context, "서버 통신 연결 종료", Toast.LENGTH_SHORT).show();
  141. }
  142. };
  143. private class NaviWebsocketMessageListener extends BroadcastReceiver {
  144. ObjectMapper mapper = new ObjectMapper();
  145. @Override
  146. public void onReceive(Context context, Intent intent) {
  147. String message = intent.getStringExtra("message");
  148. try {
  149. if (message.contains("offline")) {
  150. List<String> requests = TsiUtils.split(message, ":");
  151. if (requests.size() == 2) {
  152. NodeVo node = TsiNaviApplication.getNodeMap().get(Long.parseLong(requests.get(0)));
  153. if (node != null) {
  154. Toast.makeText(context, node.getName() + " 는(은) 통신 OffLine 입니다.", Toast.LENGTH_SHORT).show();
  155. }
  156. }
  157. }
  158. else {
  159. CpuNodeStatusDTO status = this.mapper.readValue(message, CpuNodeStatusDTO.class);
  160. if (directionNaviSignalFragment.isVisible() && directionNaviSignalFragment.getNodeId() == status.getA()) {
  161. directionNaviSignalFragment.updateSignal(status);
  162. }
  163. if (directionSignalFragment.isVisible() && directionSignalFragment.getNodeId() == status.getA()) {
  164. directionSignalFragment.updateSignal(status);
  165. }
  166. if (userSignalFragment.isVisible() && userSignalFragment.getNodeId() == status.getA()) {
  167. userSignalFragment.updateSignal(status, 10);
  168. }
  169. }
  170. } catch (Exception e) {
  171. Log.e(TAG, e.getMessage());
  172. }
  173. }
  174. }
  175. private void requestTopicMessage(String topic) {
  176. try {
  177. this.naviClientService.sendMsg("topics:" + topic);
  178. this.lastTopics = topic;
  179. if (topic.equalsIgnoreCase("x")) {
  180. this.lastTopics = ""; // 교차로 신호정보 요청 취소
  181. setSelectedNode(null);
  182. setRequestNode(null);
  183. if (this.userSignalFragment.isVisible()) {
  184. this.fragmentManager.beginTransaction().hide(this.userSignalFragment).commit();
  185. }
  186. if (this.directionNaviSignalFragment.isVisible()) {
  187. this.fragmentManager.beginTransaction().hide(this.directionNaviSignalFragment).commit();
  188. }
  189. if (this.directionSignalFragment.isVisible()) {
  190. this.fragmentManager.beginTransaction().hide(this.directionSignalFragment).commit();
  191. }
  192. }
  193. } catch (Exception e) {
  194. Log.e(TAG, "Request topic error: " + e.getMessage());
  195. }
  196. }
  197. private class NaviWebsocketEventListener extends BroadcastReceiver {
  198. @Override
  199. public void onReceive(Context context, Intent intent) {
  200. String event = intent.getStringExtra("event");
  201. //Log.e(TAG, "onEvent: " + event);
  202. if (event.equalsIgnoreCase("open")) {
  203. naviClientService.sendMsg("group:" + deviceId); // group id send
  204. if (!lastTopics.equals("")) {
  205. requestTopicMessage(lastTopics);
  206. }
  207. //Toast.makeText(context, "서버 통신 연결", Toast.LENGTH_SHORT).show();
  208. } else if (event.equalsIgnoreCase("close")) {
  209. Toast.makeText(context, "서버와 연결이 종료되었습니다.", Toast.LENGTH_SHORT).show();
  210. } else if (event.equalsIgnoreCase("error")) {
  211. Toast.makeText(context, "서버와 연결 중 오류가 발생했습니다.", Toast.LENGTH_SHORT).show();
  212. }
  213. }
  214. }
  215. private static class InfoWindowAdapter extends InfoWindow.ViewAdapter {
  216. @NonNull
  217. private final Context context;
  218. private View rootView;
  219. private ImageView icon;
  220. private TextView text;
  221. private InfoWindowAdapter(@NonNull Context context) {
  222. this.context = context;
  223. }
  224. @NonNull
  225. @Override
  226. public View getView(@NonNull InfoWindow infoWindow) {
  227. if (rootView == null) {
  228. rootView = View.inflate(context, R.layout.marker_info_window, null);
  229. icon = rootView.findViewById(R.id.icon);
  230. text = rootView.findViewById(R.id.text);
  231. }
  232. if (infoWindow.getMarker() != null) {
  233. icon.setImageResource(R.drawable.ic_place_black_24dp);
  234. text.setText((String) infoWindow.getMarker().getTag());
  235. } else {
  236. icon.setImageResource(R.drawable.ic_app);
  237. text.setText(context.getString(
  238. R.string.format_coord, infoWindow.getPosition().latitude, infoWindow.getPosition().longitude));
  239. }
  240. return rootView;
  241. }
  242. }
  243. @SuppressLint("NonConstantResourceId")
  244. @Override
  245. protected void onCreate(Bundle savedInstanceState) {
  246. super.onCreate(savedInstanceState);
  247. this.binding = ActivityMainBinding.inflate(getLayoutInflater());
  248. setContentView(this.binding.getRoot());
  249. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 화면 꺼짐 방지
  250. this.context = MainActivity.this;
  251. this.deviceId = PrefManager.getDeviceId(this);
  252. this.dispAllSignal = PrefManager.getDispAreaCross(this);
  253. this.dispCvibSignal = PrefManager.getDispCvibSignal(this);
  254. TsiNaviApplication.getMapConfig().loadConfig(this);
  255. //this.tsiApiService = ApiUtils.getApiService(PrefManager.getApiUrl(this));
  256. this.trackingZoom = TsiNaviApplication.getMapConfig().getTrackingZoom();
  257. this.tilt = TsiNaviApplication.getMapConfig().getRunTilt();
  258. this.trackingDistance = TsiNaviApplication.getMapConfig().getRunTrackingDistance();
  259. this.driveDistance = TsiNaviApplication.getMapConfig().getRunDriveDistance();
  260. this.isTrackingMode = false;
  261. this.cameraInfo = new CameraInfo(this);
  262. this.mapUtils = new BoundaryUtils(this);
  263. this.gpsTracking = new TsiGpsTrackingVo();
  264. this.backPressedForFinish = new BackPressedForFinish(this); // BackPressedForFinish 객체를 생성한다.
  265. //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  266. MainActivityUtils.initDatabase(this);
  267. this.dbQuery = new DbQuery(this);
  268. this.dbQuery.createDatabase();
  269. this.dbQuery.open();
  270. // 지도사용권한
  271. this.locationSource = new FusedLocationSource(this, LOCATION_PERMISSION_REQUEST_CODE);
  272. this.binding.tvLog.setText(" ");
  273. this.binding.tvLog.setVisibility(View.INVISIBLE);
  274. this.binding.tvSpeed.setText(" ");
  275. this.fragmentManager = getSupportFragmentManager();
  276. FragmentTransaction transaction = this.fragmentManager.beginTransaction();
  277. this.areaCrossFragment = new AreaCrossFragment();
  278. this.directionSignalFragment = new DirectionSignalFragment();
  279. this.directionNaviSignalFragment = new DirectionNaviSignalFragment();
  280. this.userSignalFragment = new UserSignalFragment();
  281. transaction.add(R.id.frame_area_cross, areaCrossFragment, "AreaCross");
  282. transaction.add(R.id.frame_direction_signal, this.directionSignalFragment, "DirectionSignal");
  283. transaction.add(R.id.frame_direction_navi_signal, this.directionNaviSignalFragment, "DirectionNaviSignal");
  284. transaction.add(R.id.frame_user_signal, this.userSignalFragment, "UserSignal");
  285. transaction.hide(areaCrossFragment);
  286. transaction.hide(this.directionSignalFragment);
  287. transaction.hide(this.directionNaviSignalFragment);
  288. transaction.hide(this.userSignalFragment);
  289. transaction.commit();
  290. /*this.areaCrossFragment.setOnItemClickListener(item -> {
  291. if (item != null) {
  292. requestNodeSignal(item.getNodeId(), true);
  293. }
  294. });
  295. */
  296. // Naver 지도객체 생성
  297. FragmentManager fragmentManager = getSupportFragmentManager();
  298. MapFragment mapFragment = (MapFragment)fragmentManager.findFragmentById(R.id.map);
  299. if (mapFragment == null) {
  300. mapFragment = MapFragment.newInstance(new NaverMapOptions());
  301. fragmentManager.beginTransaction().add(R.id.map, mapFragment).commit();
  302. }
  303. mapFragment.getMapAsync(this);
  304. startNaviWebsocketClientService();
  305. this.binding.fabSetCurrent.setVisibility(View.INVISIBLE);
  306. this.binding.fabSetCurrent.setOnClickListener(v -> {
  307. setTrackingMode(true);
  308. });
  309. this.binding.fabMenu.setOnClickListener(v -> {
  310. PopupMenu popup = new PopupMenu(MainActivity.this, v);
  311. final MenuInflater inflater = popup.getMenuInflater();
  312. inflater.inflate(R.menu.bottom_menu, popup.getMenu());
  313. MenuItem itemDispCross = popup.getMenu().findItem(R.id.menu_disp_cross);
  314. if (itemDispCross != null) {
  315. if (this.dispAllSignal) {
  316. itemDispCross.setIcon(R.drawable.ic_check_yes);
  317. }
  318. else {
  319. itemDispCross.setIcon(R.drawable.ic_check_no);
  320. }
  321. }
  322. MenuItem itemDebugMode = popup.getMenu().findItem(R.id.menu_debug_mode);
  323. if (itemDebugMode != null) {
  324. if (this.isDebugMode) {
  325. itemDebugMode.setIcon(R.drawable.ic_check_yes);
  326. }
  327. else {
  328. itemDebugMode.setIcon(R.drawable.ic_check_no);
  329. }
  330. }
  331. MenuItem itemDispCvib = popup.getMenu().findItem(R.id.menu_disp_cvib);
  332. if (itemDispCvib != null) {
  333. if (this.dispCvibSignal) {
  334. itemDispCvib.setIcon(R.drawable.ic_check_yes);
  335. }
  336. else {
  337. itemDispCvib.setIcon(R.drawable.ic_check_no);
  338. }
  339. }
  340. binding.fabMenu.setImageResource(R.drawable.ic_action_close);
  341. popup.setOnMenuItemClickListener(item -> {
  342. switch (item.getItemId()){
  343. case R.id.menu_cross:
  344. NodeCrossDialog nodeCrossDialog = new NodeCrossDialog(MainActivity.this,
  345. new IDialogClickListener() {
  346. @Override
  347. public void onPositiveClick() {
  348. }
  349. @Override
  350. public void onNegativeClick() {
  351. }
  352. });
  353. nodeCrossDialog.setOnDismissListener(d -> {
  354. NodeCrossItem nodeItem = nodeCrossDialog.getSelectedNode();
  355. if (nodeItem != null) {
  356. // 교차로 선택 다이얼로그에서 교차로 선택했음
  357. NodeVo node = TsiNaviApplication.getNodeMap().get(nodeItem.getId());
  358. if (node != null) {
  359. naverMap.moveCamera(CameraUpdate.scrollTo(node.getLatLng()));
  360. setSelectedNode(node);
  361. setTrackingMode(false);
  362. requestSelectedNode();
  363. }
  364. }
  365. });
  366. nodeCrossDialog.setCanceledOnTouchOutside(true); //다이얼로그 밖에 터치했을 때 다이얼로그가 꺼짐.
  367. nodeCrossDialog.setCancelable(true); //다이얼로그 취소 가능. ( back 버튼 )
  368. nodeCrossDialog.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
  369. nodeCrossDialog.show();
  370. break;
  371. case R.id.menu_map_settings:
  372. MapSettingDialog mapSettingDialog = new MapSettingDialog(MainActivity.this,
  373. new IDialogClickListener() {
  374. @Override
  375. public void onPositiveClick() {
  376. // 설정값을 레퍼런스에 저장
  377. TsiNaviApplication.getMapConfig().saveConfig(MainActivity.this);
  378. mapUtils.loadConfig();
  379. trackingZoom = TsiNaviApplication.getMapConfig().getTrackingZoom();
  380. }
  381. @Override
  382. public void onNegativeClick() {
  383. // 레퍼런스를 다시읽어 설정값 초기화
  384. TsiNaviApplication.getMapConfig().loadConfig(MainActivity.this);
  385. }
  386. }, naverMap, mapUtils);
  387. mapSettingDialog.setCanceledOnTouchOutside(true); //다이얼로그 밖에 터치했을 때 다이얼로그가 꺼짐.
  388. mapSettingDialog.setCancelable(true); //다이얼로그 취소 가능. ( back 버튼 )
  389. mapSettingDialog.setOnDismissListener(d -> {
  390. // 맵 설정정보 셋팅
  391. MainActivityUtils.setupMap(naverMap, trackingZoom);
  392. /* if (TsiNaviApplication.getMapConfig().isNightMode()) {
  393. this.overlayResourceId = (this.overlayResourceId == R.drawable.ic_navi_car_on) ? R.drawable.ic_navi_car_night_on : R.drawable.ic_navi_car_night_off;
  394. }
  395. else {
  396. this.overlayResourceId = (this.overlayResourceId == R.drawable.ic_navi_car_night_on) ? R.drawable.ic_navi_car_on : R.drawable.ic_navi_car_off;
  397. }
  398. this.naverMap.getLocationOverlay().setIcon(OverlayImage.fromResource(this.overlayResourceId));
  399. */ });
  400. mapSettingDialog.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
  401. mapSettingDialog.show();
  402. break;
  403. case R.id.menu_map_cache_delete:
  404. NaverMapSdk.getInstance(MainActivity.this).flushCache(new NaverMapSdk.CacheFlushCallback() {
  405. @Override
  406. public void onCacheFlushed() {
  407. Toast.makeText(MainActivity.this, "지도캐시초기화 완료.", Toast.LENGTH_SHORT).show();
  408. }
  409. });
  410. break;
  411. case R.id.menu_app_info:
  412. AppInformationDialog appInformationDialog = new AppInformationDialog(MainActivity.this, new IDialogClickListener() {
  413. @Override
  414. public void onPositiveClick() { }
  415. @Override
  416. public void onNegativeClick() { }
  417. });
  418. appInformationDialog.setCanceledOnTouchOutside(true); //다이얼로그 밖에 터치했을 때 다이얼로그가 꺼짐.
  419. appInformationDialog.setCancelable(true); //다이얼로그 취소 가능. ( back 버튼 )
  420. appInformationDialog.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
  421. appInformationDialog.show();
  422. break;
  423. case R.id.menu_disp_cross:
  424. this.dispAllSignal = !this.dispAllSignal;
  425. PrefManager.setDispAreaCross(this.context, this.dispAllSignal);
  426. if (!this.isTrackingMode) {
  427. if (this.dispAllSignal) {
  428. if (this.selectedNode != null) {
  429. if (!this.directionNaviSignalFragment.isVisible()) {
  430. this.fragmentManager.beginTransaction().show(this.directionNaviSignalFragment).commit();
  431. this.directionNaviSignalFragment.setNodeCross(this.selectedNode, 10);
  432. }
  433. if (this.dispCvibSignal && !this.directionSignalFragment.isVisible()) {
  434. this.fragmentManager.beginTransaction().show(this.directionSignalFragment).commit();
  435. this.directionSignalFragment.setNodeCross(this.selectedNode, 10);
  436. }
  437. }
  438. }
  439. else {
  440. if (this.directionNaviSignalFragment.isVisible()) {
  441. //this.fragmentManager.beginTransaction().hide(this.directionNaviSignalFragment).commit();
  442. this.directionNaviSignalFragment.setNodeCross(null, 10);
  443. }
  444. if (this.directionSignalFragment.isVisible()) {
  445. //this.fragmentManager.beginTransaction().hide(this.directionSignalFragment).commit();
  446. this.directionSignalFragment.setNodeCross(null, 10);
  447. }
  448. }
  449. }
  450. break;
  451. case R.id.menu_disp_cvib:
  452. this.dispCvibSignal = !this.dispCvibSignal;
  453. PrefManager.setDispAreaCross(this.context, this.dispCvibSignal);
  454. if (this.dispCvibSignal && this.directionNaviSignalFragment.isVisible() && !this.directionSignalFragment.isVisible()) {
  455. this.directionSignalFragment.setNodeCross(this.directionNaviSignalFragment.getNode(), this.directionNaviSignalFragment.getDirection());
  456. this.fragmentManager.beginTransaction().show(this.directionSignalFragment).commit();
  457. }
  458. if (!this.dispCvibSignal && this.directionSignalFragment.isVisible()) {
  459. this.directionSignalFragment.setNodeCross(this.directionNaviSignalFragment.getNode(), this.directionNaviSignalFragment.getDirection());
  460. this.fragmentManager.beginTransaction().hide(this.directionSignalFragment).commit();
  461. }
  462. break;
  463. case R.id.menu_debug_mode:
  464. this.isDebugMode = !this.isDebugMode;
  465. mapUtils.setDebugMode(this.isDebugMode);
  466. mapUtils.drawBounds(null);
  467. if (!this.isDebugMode) {
  468. if (this.gpsTracking.getRequestNode() != null) {
  469. requestTopicMessage("x");
  470. this.directionNaviSignalFragment.setNodeCross(null, 10);
  471. this.directionSignalFragment.setNodeCross(null, 10);
  472. }
  473. if (this.directionSignalFragment.isVisible()) {
  474. this.fragmentManager.beginTransaction().hide(this.directionSignalFragment).commit();
  475. }
  476. if (this.directionNaviSignalFragment.isVisible()) {
  477. this.fragmentManager.beginTransaction().hide(this.directionNaviSignalFragment).commit();
  478. }
  479. }
  480. break;
  481. default:
  482. Log.e(TAG, "뭐가 클릭된거야?");
  483. return false;
  484. }
  485. return true;
  486. });
  487. // show icons on popup menu
  488. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
  489. popup.setForceShowIcon(true);
  490. }
  491. popup.setOnDismissListener(menu -> {
  492. binding.fabMenu.setImageResource(R.drawable.ic_action_menu);
  493. });
  494. popup.show();
  495. });
  496. }
  497. @Override
  498. protected void onSaveInstanceState(@NonNull Bundle outState) {
  499. //https://developer.android.com/training/location/request-updates?hl=ko
  500. /*outState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY,
  501. requestingLocationUpdates);
  502. // ...*/
  503. super.onSaveInstanceState(outState);
  504. }
  505. private void startNaviWebsocketClientService() {
  506. this.lastTopics = "";
  507. // start service
  508. Intent intent = new Intent(this.context, NaviWebsocketClientService.class);
  509. startService(intent);
  510. // bind service
  511. Intent bindIntent = new Intent(this.context, NaviWebsocketClientService.class);
  512. bindService(bindIntent, serviceConnection, BIND_AUTO_CREATE);
  513. // register receiver
  514. NaviWebsocketMessageListener naviClientMessageListener = new NaviWebsocketMessageListener();
  515. IntentFilter messageFilter = new IntentFilter("com.websocket.content");
  516. registerReceiver(naviClientMessageListener, messageFilter);
  517. NaviWebsocketEventListener naviClientEventListener = new NaviWebsocketEventListener();
  518. IntentFilter eventFilter = new IntentFilter("com.websocket.event");
  519. registerReceiver(naviClientEventListener, eventFilter);
  520. }
  521. //권한 받아오기
  522. @Override
  523. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  524. if (this.locationSource.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
  525. if (!this.locationSource.isActivated()) {
  526. // 권한 거부됨
  527. this.naverMap.setLocationTrackingMode(LocationTrackingMode.None);
  528. }
  529. else {
  530. this.naverMap.setLocationTrackingMode(LocationTrackingMode.Follow);
  531. }
  532. return;
  533. }
  534. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  535. }
  536. @SuppressLint("SetTextI18n")
  537. @UiThread
  538. @Override
  539. public void onMapReady(@NonNull @NotNull NaverMap naverMap) {
  540. this.naverMap = naverMap;
  541. this.cameraUpdateParams = new CameraUpdateParams();
  542. this.mapUtils.setup(this.naverMap);
  543. // 네이버 맵에 locationSource 를 설정하면 위치 추적 기능을 사용 할 수 있다
  544. this.naverMap.setLocationSource(this.locationSource);
  545. ActivityCompat.requestPermissions(this, this.permissions, LOCATION_PERMISSION_REQUEST_CODE);
  546. setMapContentPadding();
  547. MainActivityUtils.initMap(this.naverMap);
  548. //TABS6(2560*1600). S105G(3040*1440)
  549. this.overlayResourceId = R.drawable.ic_car_red;//R.drawable.ic_navi_car_on;//TsiNaviApplication.getMapConfig().isNightMode() ? R.drawable.ic_navi_car_night_on : R.drawable.ic_navi_car_on;
  550. MainActivityUtils.initMapOverlay(this.naverMap, this.overlayResourceId, (1440 >= Math.min(this.width, this.height)));
  551. MainActivityUtils.setupMap(this.naverMap, this.trackingZoom);
  552. initNodeMarker();
  553. setTrackingMode(true);
  554. // 도로교통공단 서울지부로 위치 이동(최초 기본 위치)
  555. //LatLng initialPosition = new LatLng(37.465913939690815, 127.04432631214559);
  556. /*CameraUpdate cameraUpdate = CameraUpdate.scrollTo(initialPosition);
  557. this.naverMap.moveCamera(cameraUpdate);*/
  558. /*CameraPosition initialCameraPosition = new CameraPosition(
  559. initialPosition, // 위치 지정
  560. this.trackingZoom, // 줌 레벨
  561. this.tilt, // 기울임 각도
  562. 45 // 방향
  563. );
  564. this.naverMap.setCameraPosition(initialCameraPosition);*/
  565. // 카메라의 움직임이 끝나 대기 상태가 되면 카메라 대기 이벤트가 발생합니다.
  566. // 카메라가 애니메이션 없이 움직일 때. 단, 사용자가 제스처로 지도를 움직이는 경우
  567. // 제스처가 완전히 끝날 때까지(ACTION_UP이 발생할 때까지)는 연속적인 이동으로 간주되어 이벤트가 발생하지 않습니다.
  568. // 카메라 애니메이션이 완료될 때. 단, 카메라 애니메이션이 진행 중일 때 새로운 애니메이션이 발생하거나,
  569. // 기존 카메라 이동의 finishCallback() 또는 cancelCallback()으로 지정된 콜백 내에서 카메라 이동이 일어날 경우
  570. // 연속적인 이동으로 간주되어 이벤트가 발생하지 않습니다. NaverMap.cancelTransitions()가 호출되어 카메라 애니메이션이 명시적으로 취소될 때.
  571. this.naverMap.addOnCameraIdleListener(() -> {
  572. if (!this.cameraInfo.equals(this.naverMap.getCameraPosition())) {
  573. this.mapUtils.initMap();
  574. this.mapUtils.drawBounds(this.naverMap.getCameraPosition().target);
  575. }
  576. updateNodeMarker();
  577. });
  578. this.naverMap.addOnLocationChangeListener(location -> {
  579. //TODO Log.e(TAG, "LocationChangeListener");
  580. /*LatLng coord = new LatLng(location);
  581. LocationOverlay locationOverlay = map.getLocationOverlay();
  582. locationOverlay.setVisible(true);
  583. locationOverlay.setPosition(coord);
  584. locationOverlay.setBearing(location.getBearing());
  585. map.moveCamera(CameraUpdate.scrollTo(coord));*/
  586. this.moveByGps.set(false);
  587. this.currSpeed = location.hasSpeed() ? location.getSpeed() * 3.6F : 0.0F; // 속도 meter/sec ==> km/sec
  588. this.currBearing = location.hasBearing() ? location.getBearing() : -1; // 베어링
  589. this.moveDist = this.lastLocation == null ? 0 : this.lastLocation.distanceTo(location);
  590. this.lastLocation = location;
  591. this.binding.tvSpeed.setText(String.valueOf((int)this.currSpeed));
  592. // 속도값이 없거나 이동거리가 기준 이하이면 GPS 수신하지 않은것으로 처리한다.
  593. if (this.currSpeed <= 1 || this.moveDist <= 2) {
  594. this.naverMap.getLocationOverlay().setBearing(this.lastBearing);
  595. /*int disableResourceId = TsiNaviApplication.getMapConfig().isNightMode() ? R.drawable.ic_navi_car_night_off : R.drawable.ic_navi_car_off;
  596. if (overlayResourceId != disableResourceId) {
  597. this.naverMap.getLocationOverlay().setIcon(OverlayImage.fromResource(disableResourceId));
  598. overlayResourceId = disableResourceId;
  599. }*/
  600. //updateBounds(location);
  601. return;
  602. }
  603. /*int enabledResourceId = TsiNaviApplication.getMapConfig().isNightMode() ? R.drawable.ic_navi_car_night_on : R.drawable.ic_navi_car_on;
  604. if (overlayResourceId != enabledResourceId) {
  605. this.naverMap.getLocationOverlay().setIcon(OverlayImage.fromResource(enabledResourceId));
  606. overlayResourceId = enabledResourceId;
  607. }*/
  608. if (this.currBearing >= 0) {
  609. this.lastBearing = this.currBearing;
  610. }
  611. // GPS 이동한 것으로 판단.
  612. this.moveByGps.set(true);
  613. if (this.isTrackingMode) {
  614. CameraPosition gpsPosition = new CameraPosition(
  615. new LatLng(location.getLatitude(), location.getLongitude()), // 위치 지정
  616. this.trackingZoom, // 줌 레벨
  617. this.tilt, // 기울임 각도
  618. this.lastBearing // 방향
  619. );
  620. this.naverMap.setCameraPosition(gpsPosition);
  621. }
  622. });
  623. this.naverMap.addOnCameraChangeListener((reason, animated) -> {
  624. switch(reason) {
  625. case CameraUpdate.REASON_DEVELOPER: //REASON_DEVELOPER: 0, 개발자가 API를 호출해 카메라가 움직였음을 나타냅니다. 기본값입니다.
  626. break;
  627. case CameraUpdate.REASON_LOCATION: //REASON_LOCATION: -3, 위치 트래킹 기능으로 인해 카메라가 움직였음을 나타냅니다.
  628. break;
  629. case CameraUpdate.REASON_GESTURE: //REASON_GESTURE: -1, 사용자의 제스처로 인해 카메라가 움직였음을 나타냅니다. // == CameraUpdate.DEFAULT_ANIMATION_DURATION
  630. case CameraUpdate.REASON_CONTROL: //REASON_CONTROL: -2, 사용자의 버튼 선택으로 인해 카메라가 움직였음을 나타냅니다.
  631. setTrackingMode(false);
  632. break;
  633. }
  634. });
  635. this.naverMap.setOnMapClickListener((point, coord) -> {
  636. if (this.infoWindow != null && this.infoWindow.isVisible()) {
  637. //마커클릭후에 지도클릭 이벤트가 먹기 때문에 여기서 처리하면 안된다.(마커클릭후에 true 리턴하면 된다)
  638. this.infoWindow.close();
  639. }
  640. });
  641. }
  642. private void updateBounds(Location location) {
  643. //if (location == null) return;
  644. if (location != null) {
  645. this.mapUtils.drawBounds(new LatLng(location.getLatitude(), location.getLongitude()));
  646. }
  647. else {
  648. this.mapUtils.drawBounds(this.naverMap.getCameraPosition().target);
  649. }
  650. }
  651. // 지도상에 표시되고있는 마커들 지도에서 삭제
  652. private void freeActiveMarkers() {
  653. if (this.activeMarkers == null) {
  654. this.activeMarkers = new Vector<>();
  655. return;
  656. }
  657. //TODO Log.e(TAG, "freeActiveMarkers: " + this.activeMarkers.size());
  658. for (Marker activeMarker: this.activeMarkers) {
  659. activeMarker.setMap(null);
  660. }
  661. this.activeMarkers = new Vector<>();
  662. }
  663. @SuppressLint("SetTextI18n")
  664. private void updateNodeMarkerInDrive(boolean trackingMode) {
  665. boolean isMovingByGps = this.moveByGps.getAndSet(false);
  666. //TODO Log.e(TAG, "updateNodeMarkerInDrive: " + trackingMode + ", " + isMovingByGps);
  667. //if (!trackingMode || !isMovingByGps) {
  668. if (!trackingMode && !this.isDebugMode) {
  669. //TODO Log.e(TAG, "isMovingByGps == false");
  670. return;
  671. }
  672. /*if (this.naverMap.getCameraPosition().zoom != this.trackingZoom) {
  673. if (this.directionSignalFragment.isVisible()) {
  674. requestTopicMessage("x");
  675. this.fragmentManager.beginTransaction().hide(this.directionSignalFragment).commit();
  676. }
  677. if (this.areaCrossFragment.isVisible()) {
  678. this.fragmentManager.beginTransaction().hide(this.areaCrossFragment).commit();
  679. }
  680. Log.e(TAG, "updateNodeMarkerInDrive: zoom: " + this.naverMap.getCameraPosition().zoom + ", " + this.trackingZoom);
  681. return;
  682. }*/
  683. LatLngBounds carBounds = this.mapUtils.getCarBounds();
  684. PointF carPos = this.mapUtils.getCarPosition();
  685. //LatLng[] latLngBounds = this.mapUtils.getLatLngBounds();
  686. //LatLng carLatLng = this.naverMap.getLocationOverlay().getPosition(); // 현재 차량위치
  687. LatLng carLatLng = this.naverMap.getCameraPosition().target; // 현재 차량위치
  688. //int signalDirectionCode = TsiNaviApplication.getDirection().getSignalDirectionCode(this.lastBearing == 0 ? this.naverMap.getCameraPosition().bearing : this.lastBearing);
  689. int signalDirectionCode = TsiNaviApplication.getDirection().getSignalDirectionCode(this.naverMap.getCameraPosition().bearing);
  690. if (signalDirectionCode == 0) {
  691. //TODO Log.e(TAG, "Direction Error: " + signalDirectionCode);
  692. return;
  693. }
  694. if (this.gpsTracking.getSignalDirCode() != signalDirectionCode) {
  695. //TODO Log.e(TAG, "Bearing different: " + this.gpsTracking.getSignalDirCode() + ", " + signalDirectionCode);
  696. this.gpsTracking.setSignalDirCode(signalDirectionCode);
  697. return;
  698. }
  699. this.gpsTracking.setSignalDirCode(signalDirectionCode);
  700. int calDirectionCode;
  701. //TODO String sLog = "--Dir: " + String.format("%3d", (int)this.lastBearing) + ", " + signalDirectionCode + ", " + String.format("%3d", (int)this.naverMap.getCameraPosition().bearing);
  702. // 진출입 노드에 대한 계산
  703. double calBearing;
  704. double calDistance;
  705. double nearDistance = Float.MAX_VALUE;
  706. double crossDistance = Float.MAX_VALUE;
  707. NodeVo inCrossNode = null;
  708. NodeVo targetNode = null;
  709. NodeVo nearNode = null;
  710. Set<Long> inCrossNodeSet= new HashSet<>();
  711. //TODO Log.e(TAG, "Boundary nodes: " + this.activeNodes.size() + " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
  712. boolean crossPassOffSignal = false;
  713. for (Map.Entry<Long, NodeVo> obj : this.activeNodes.entrySet()) {
  714. NodeVo node = obj.getValue();
  715. PointF posNode = this.naverMap.getProjection().toScreenLocation(node.getLatLng());
  716. Location.distanceBetween(carLatLng.latitude, carLatLng.longitude, node.getLatLng().latitude, node.getLatLng().longitude, this.resultDistance);
  717. calDistance = this.resultDistance[0];
  718. calBearing = (this.resultDistance[1] < 0) ? (360 + this.resultDistance[1]) : this.resultDistance[1];
  719. calDirectionCode = TsiNaviApplication.getDirection().getSignalDirectionCode(calBearing);
  720. //TODO Log.e(TAG, "calDirectionCode: " + calDirectionCode + ", " + node.getName());
  721. if (carBounds.contains(node.getLatLng())) {
  722. // 노드 통과 영역에 포함
  723. //TODO sLog += "\n-C: " + String.format("%3d, %d, %3d m, %s, %s", (int)calBearing, calDirectionCode, (int)calDistance, node.getDirCode(), node.getName());
  724. if (calDistance < crossDistance) {
  725. inCrossNode = node;
  726. crossDistance = calDistance;
  727. //TODO sLog += "--Near";
  728. }
  729. //TODO if (!node.isReal()) sLog += "--Dummy";
  730. if (this.gpsTracking.getRequestNode() != null) {
  731. if (this.gpsTracking.getRequestNode().getNodeId() == node.getNodeId()) {
  732. if (posNode.y > carPos.y) {
  733. // 요청 신호정보가 존재하고 지금 요청 신호등을 지났으면 신호 정보 요청 취소
  734. crossPassOffSignal = true;
  735. requestTopicMessage("x");
  736. this.gpsTracking.setRequestNode(null);
  737. }
  738. }
  739. else {
  740. // 검지영역이 좁아서 검지역역내에 교차로가 2개 이상 검지되고 있는 경우
  741. // 이전 요청 교차로가 현재 교차로가 아니면 요청을 취소한다.
  742. requestTopicMessage("x");
  743. this.gpsTracking.setRequestNode(null);
  744. }
  745. }
  746. }
  747. if (this.mapUtils.contains((node.getLatLng()))) {
  748. // 진행방향 바운더리 영역내에 포함됨
  749. //TODO sLog += "\n-N: " + String.format("%3d, %d, %3d m, %s, %s", (int)calBearing, calDirectionCode, (int)calDistance, node.getDirCode(), node.getName());
  750. if (node.getInDirMap().contains(calDirectionCode)) {
  751. //TODO sLog += "--Match";
  752. if (node.getInDirMap().contains(calDirectionCode) && calDistance < nearDistance) {
  753. nearNode = node;
  754. nearDistance = calDistance;
  755. //TODO sLog += "--Near";
  756. }
  757. }
  758. //TODO if (!node.isReal()) sLog += "--Dummy";
  759. }
  760. }
  761. if (inCrossNode != null) {
  762. // 노드를 통과 중이면 처리하는게 없다. 노드를 통과한 후에 처리하도록 한다.
  763. this.gpsTracking.setInCrossNode(inCrossNode);
  764. if (crossPassOffSignal) {
  765. // 요청 신호 정보 교차로를 지나서 요청 신호를 취소한 경우
  766. // ====> 이부분 주석 처리하면 교차로 통과할 동안 신호정보 요청을 보내지 않는다.
  767. /*
  768. if (nearNode != null && nearNode.isReal()) {
  769. if (this.gpsTracking.getRequestNode() == null) {
  770. requestNodeSignal(nearNode.getNodeId(), signalDirectionCode);
  771. this.gpsTracking.setRequestNode(nearNode);
  772. }
  773. }
  774. */
  775. }
  776. }
  777. else {
  778. // 노드를 통과하는 것이 아니라 도로를 주행 중인 상태
  779. if (this.gpsTracking.getInCrossNode() != null) {
  780. // 노드를 빠져 나온 상태. 시작 노드 정보로 설정한다.
  781. this.gpsTracking.setStartNode(inCrossNode);
  782. // 노드를 빠져 나올때 근접 노드정보가 있을 경우와 없을 경우에 따라 처리
  783. if (nearNode != null) {
  784. if (nearNode.isReal()) {
  785. if (this.gpsTracking.getRequestNode() == null || this.gpsTracking.getRequestNode().getNodeId() != nearNode.getId()) {
  786. requestNodeSignal(nearNode.getNodeId(), signalDirectionCode);
  787. this.gpsTracking.setRequestNode(nearNode);
  788. }
  789. }
  790. else {
  791. this.gpsTracking.setEndNode(nearNode);
  792. }
  793. }
  794. else {
  795. if (this.gpsTracking.getRequestNode() != null) {
  796. // 더이상 요청할 노드가 진행 방향에 존재하지 않는다.
  797. requestTopicMessage("x");
  798. this.gpsTracking.setRequestNode(null);
  799. }
  800. }
  801. }
  802. else {
  803. // 도로 주행
  804. if (nearNode != null) {
  805. // 진행방향에 표출할 노드가 존재하면
  806. if (nearNode.isReal()) {
  807. if (this.gpsTracking.getRequestNode() == null || this.gpsTracking.getRequestNode().getNodeId() != nearNode.getId()) {
  808. requestNodeSignal(nearNode.getNodeId(), signalDirectionCode);
  809. this.gpsTracking.setRequestNode(nearNode);
  810. }
  811. }
  812. }
  813. else {
  814. // 진행방향에 노드가 존재하지 않음.
  815. if (this.gpsTracking.getRequestNode() != null) {
  816. requestTopicMessage("x");
  817. this.gpsTracking.setRequestNode(null);
  818. }
  819. }
  820. }
  821. this.gpsTracking.setInCrossNode(null);
  822. }
  823. if (this.gpsTracking.getRequestNode() != null) {
  824. if (this.activeNodes.get(this.gpsTracking.getRequestNode().getNodeId()) == null) {
  825. // 요청 중인데 영역내에 없으면 취소 시킨다.
  826. requestTopicMessage("x");
  827. this.gpsTracking.setRequestNode(null);
  828. }
  829. }
  830. //TODO this.binding.tvLog.setText(sLog);
  831. //TODO Log.e(TAG, sLog);
  832. }
  833. private void updateNodeMarker() {
  834. //Log.e(TAG, "updateNodeMarker: zoom: " + this.naverMap.getCameraPosition().zoom);
  835. updateBounds(null);
  836. int signalDirectionCode = TsiNaviApplication.getDirection().getSignalDirectionCode(this.naverMap.getCameraPosition().bearing);
  837. int runDirectionCode = TsiNaviApplication.getDirection().getSignal2RunDirection(signalDirectionCode);
  838. //TODO String sLog = "Bearing: " + (int)this.naverMap.getCameraPosition().bearing + ", " + signalDirectionCode + ", " + runDirectionCode + ", zoom:" + (int)this.naverMap.getCameraPosition().zoom;
  839. //TODO this.binding.tvLog.setText(sLog);
  840. Marker infoMarker = null;
  841. if (this.infoWindow != null) {
  842. infoMarker = this.infoWindow.getMarker();
  843. }
  844. freeActiveMarkers(); // 메모리 관리를 위해 여기서 삭제해 준다.
  845. if (this.naverMap.getCameraPosition().zoom < 13) {
  846. /*if (this.isTrackingMode && this.directionSignalFragment.isVisible()) {
  847. this.fragmentManager.beginTransaction().hide(this.directionSignalFragment).commit();
  848. }*/
  849. if (infoMarker != null) {
  850. this.infoWindow.close();
  851. }
  852. return;
  853. }
  854. LatLngBounds bounds = this.naverMap.getCoveringBounds();
  855. //LatLngBounds bounds = this.naverMap.getContentBounds();
  856. ConcurrentHashMap<Long, NodeVo> activeNodes = new ConcurrentHashMap<>();
  857. Vector<Marker> activeMarkers = new Vector<>();
  858. List<Long> nodes = MainActivityUtils.getBoundsNodes(this.dbQuery, bounds);
  859. if (nodes != null) {
  860. //TODO Log.e(TAG, "Bounds nodes: " + nodes.size() + " EA.");
  861. if (nodes.size() <= 300) {
  862. for (Long obj : nodes) {
  863. try {
  864. NodeVo node = TsiNaviApplication.getNodeMap().get(obj);
  865. if (node != null) {
  866. activeMarkers.add(node.getMarker());
  867. activeNodes.put(node.getNodeId(), node);
  868. }
  869. } catch (Exception e) {
  870. Log.e(TAG, "updateNodeMarker: " + e.getMessage());
  871. }
  872. }
  873. }
  874. }
  875. this.activeMarkers = activeMarkers;
  876. for (Marker activeMarker: this.activeMarkers) {
  877. if (!activeMarker.getTag().equals("x")) {
  878. activeMarker.setMap(this.naverMap);
  879. }
  880. }
  881. if (infoMarker != null) {
  882. this.infoWindow.open(infoMarker, Align.TopLeft);
  883. }
  884. if (this.isTrackingMode) {
  885. // 트래킹(드라이빙) 모드이고 조건을 만족할 경우
  886. this.activeNodes = activeNodes;
  887. updateNodeMarkerInDrive(true);
  888. }
  889. else {
  890. // 사용자 움직임인 경우 바운더리 계산해서 영역을 다시 찾은 다음 영역에 속한
  891. // 교차로 정보를 다시 조회해서 처리해야함
  892. this.activeNodes = activeNodes;
  893. updateNodeMarkerInDrive(false);
  894. }
  895. }
  896. private void requestNodeSignal(long nodeId, int directionCode) {
  897. if (!this.isTrackingMode) {
  898. //return;
  899. }
  900. NodeVo node = TsiNaviApplication.getNodeMap().get(nodeId);
  901. //if (node == null) return;
  902. setRequestNode(node);
  903. this.userSignalFragment.setNodeCross(null);
  904. this.directionNaviSignalFragment.setNodeCross(node, directionCode);
  905. this.directionSignalFragment.setNodeCross(node, directionCode);
  906. requestTopicMessage(String.valueOf(nodeId));
  907. if (this.dispCvibSignal && !this.directionSignalFragment.isVisible()) {
  908. this.fragmentManager.beginTransaction().show(this.directionSignalFragment).commit();
  909. }
  910. if (!this.directionNaviSignalFragment.isVisible()) {
  911. this.fragmentManager.beginTransaction().show(this.directionNaviSignalFragment).commit();
  912. }
  913. if (userSignalFragment.isVisible()) {
  914. this.fragmentManager.beginTransaction().hide(this.userSignalFragment).commit();
  915. }
  916. }
  917. private void setSelectedNode(NodeVo node) {
  918. this.gpsTracking.setRequestNode(null);
  919. if (this.selectedNode != null) {
  920. // 이전 선택한 아이콘은 기본 아이콘으로 변경
  921. this.selectedNode.marker.setIcon(OverlayImage.fromResource(R.drawable.ic_cross_normal));
  922. }
  923. if (this.infoWindow != null && this.infoWindow.isVisible()) {
  924. // 이전 인포 윈도우 닫음
  925. this.infoWindow.close();
  926. }
  927. this.selectedNode = node;
  928. if (this.selectedNode != null) {
  929. // 선택한 아이콘은 선택 아이콘으로 변경
  930. this.selectedNode.marker.setIcon(OverlayImage.fromResource(R.drawable.ic_cross_select));
  931. // 선택한 아이콘의 인포 윈도우 오픈
  932. this.infoWindow.open(this.selectedNode.marker, Align.TopLeft);
  933. }
  934. }
  935. private void setRequestNode(NodeVo node) {
  936. this.selectedNode = null;
  937. if (this.gpsTracking.getRequestNode() != null) {
  938. // 이전 선택한 아이콘은 기본 아이콘으로 변경
  939. this.gpsTracking.getRequestNode().marker.setIcon(OverlayImage.fromResource(R.drawable.ic_cross_normal));
  940. }
  941. if (this.infoWindow != null && this.infoWindow.isVisible()) {
  942. // 이전 인포 윈도우 닫음
  943. this.infoWindow.close();
  944. }
  945. this.gpsTracking.setRequestNode(node) ;
  946. if (this.gpsTracking.getRequestNode() != null) {
  947. // 선택한 아이콘은 선택 아이콘으로 변경
  948. this.gpsTracking.getRequestNode().marker.setIcon(OverlayImage.fromResource(R.drawable.ic_cross_select));
  949. // 선택한 아이콘의 인포 윈도우 오픈
  950. this.infoWindow.open(this.gpsTracking.getRequestNode().marker, Align.TopLeft);
  951. }
  952. }
  953. private void setTrackingMode(boolean trackingMode) {
  954. if (!this.isTrackingMode && trackingMode) {
  955. requestTopicMessage("x");
  956. this.moveByGps.set(true);
  957. }
  958. if (this.isTrackingMode && !trackingMode) {
  959. this.gpsTracking.init();
  960. }
  961. this.isTrackingMode = trackingMode;
  962. if (trackingMode) {
  963. if (this.naverMap.getLocationTrackingMode() != LocationTrackingMode.Follow) {
  964. this.naverMap.setLocationTrackingMode(LocationTrackingMode.Follow); // Tracking Mode 설정
  965. //binding.tvLog.setText("Follow");
  966. /*this.cameraUpdateParams//.scrollTo(this.cameraPosition.target)
  967. .zoomTo(this.trackingZoom)
  968. .rotateTo(this.lastBearing).tiltTo(this.tilt);
  969. this.naverMap.moveCamera(CameraUpdate.withParams(this.cameraUpdateParams));*/
  970. }
  971. this.cameraUpdateParams//.scrollTo(this.cameraPosition.target)
  972. .zoomTo(this.trackingZoom)
  973. .rotateTo(this.lastBearing).tiltTo(this.tilt);
  974. this.naverMap.moveCamera(CameraUpdate.withParams(this.cameraUpdateParams));
  975. if (this.naverMap.getUiSettings().isZoomControlEnabled()) {
  976. this.naverMap.getUiSettings().setZoomControlEnabled(false);
  977. }
  978. if (this.binding.tvSpeed.getVisibility() == View.INVISIBLE) {
  979. this.binding.tvSpeed.setVisibility(View.VISIBLE);
  980. }
  981. if (this.lastLocation != null) {
  982. CameraPosition initialCameraPosition = new CameraPosition(
  983. new LatLng(this.lastLocation.getLatitude(), this.lastLocation.getLongitude()), // 위치 지정
  984. this.trackingZoom, // 줌 레벨
  985. this.tilt, // 기울임 각도
  986. this.lastBearing // 방향
  987. );
  988. this.naverMap.setCameraPosition(initialCameraPosition);
  989. }
  990. if (View.VISIBLE == this.binding.fabSetCurrent.getVisibility()) {
  991. this.binding.fabSetCurrent.setVisibility(View.INVISIBLE);
  992. }
  993. if (this.infoWindow != null && this.infoWindow.isVisible()) {
  994. //마커클릭후에 지도클릭 이벤트가 먹기 때문에 여기서 처리하면 안된다.
  995. this.infoWindow.close();
  996. }
  997. }
  998. else {
  999. if (this.naverMap.getLocationTrackingMode() != LocationTrackingMode.NoFollow) {
  1000. this.naverMap.setLocationTrackingMode(LocationTrackingMode.NoFollow);
  1001. this.cameraUpdateParams//.scrollTo(this.cameraPosition.target)
  1002. .zoomTo(this.trackingZoom)
  1003. .rotateTo(0).tiltTo(this.tilt);
  1004. this.naverMap.moveCamera(CameraUpdate.withParams(this.cameraUpdateParams));
  1005. }
  1006. if (!this.naverMap.getUiSettings().isZoomControlEnabled()) {
  1007. this.naverMap.getUiSettings().setZoomControlEnabled(true);
  1008. }
  1009. if (this.binding.tvSpeed.getVisibility() == View.VISIBLE) {
  1010. this.binding.tvSpeed.setVisibility(View.INVISIBLE);
  1011. }
  1012. if (View.VISIBLE != this.binding.fabSetCurrent.getVisibility()) {
  1013. this.binding.fabSetCurrent.setVisibility(View.VISIBLE);
  1014. }
  1015. if (!this.userSignalFragment.isVisible()) {
  1016. if (this.selectedNode != null) this.fragmentManager.beginTransaction().show(this.userSignalFragment).commit();
  1017. }
  1018. if (this.dispAllSignal) {
  1019. if (!this.directionNaviSignalFragment.isVisible()) {
  1020. //TODO Log.e(TAG, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: " + this.selectedNode);
  1021. if (this.selectedNode != null) this.fragmentManager.beginTransaction().show(this.directionNaviSignalFragment).commit();
  1022. }
  1023. if (this.dispCvibSignal && !this.directionSignalFragment.isVisible()) {
  1024. //TODO Log.e(TAG, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: " + this.selectedNode);
  1025. if (this.selectedNode != null) this.fragmentManager.beginTransaction().show(this.directionSignalFragment).commit();
  1026. }
  1027. }
  1028. else {
  1029. if (this.directionNaviSignalFragment.isVisible()) {
  1030. //this.fragmentManager.beginTransaction().hide(this.directionNaviSignalFragment).commit();
  1031. }
  1032. if (this.directionSignalFragment.isVisible()) {
  1033. //this.fragmentManager.beginTransaction().hide(this.directionSignalFragment).commit();
  1034. }
  1035. }
  1036. }
  1037. }
  1038. private void requestSelectedNode() {
  1039. if (this.selectedNode != null) {
  1040. this.naverMap.moveCamera(CameraUpdate.scrollTo(this.selectedNode.getLatLng()));
  1041. this.userSignalFragment.setNodeCross(this.selectedNode);
  1042. this.directionNaviSignalFragment.setNodeCross(this.selectedNode, 10);
  1043. this.directionSignalFragment.setNodeCross(this.selectedNode, 10);
  1044. requestTopicMessage(String.valueOf(this.selectedNode.getNodeId()));
  1045. }
  1046. }
  1047. private void initNodeMarker() {
  1048. // 인포 윈도우 생성
  1049. this.infoWindow = new InfoWindow();
  1050. this.infoWindow.setAnchor(new PointF(0, 1));
  1051. this.infoWindow.setOffsetX(getResources().getDimensionPixelSize(R.dimen.marker_info_window_offset_x));
  1052. this.infoWindow.setOffsetY(getResources().getDimensionPixelSize(R.dimen.marker_info_window_offset_y));
  1053. this.infoWindow.setAdapter(new InfoWindowAdapter(this));
  1054. this.infoWindow.setOnClickListener(overlay -> {
  1055. if (this.selectedNode != null) {
  1056. if (!this.isTrackingMode) {
  1057. setTrackingMode(false);
  1058. requestSelectedNode();
  1059. }
  1060. }
  1061. else {
  1062. this.infoWindow.close();
  1063. }
  1064. return true;
  1065. });
  1066. // 교차로 마커 생성
  1067. for (Map.Entry<Long, NodeVo> obj : TsiNaviApplication.getNodeMap().entrySet()) {
  1068. NodeVo node = obj.getValue();
  1069. //node.marker = MainActivityUtils.createNodeMarker(this.naverMap, node, R.drawable.ic_cross_normal, R.drawable.ic_node_small);
  1070. node.marker = MainActivityUtils.createNodeMarker(null, node, R.drawable.ic_cross_normal, R.drawable.ic_node_small);
  1071. if (node.isReal()) {
  1072. node.marker.setOnClickListener(overlay -> {
  1073. setSelectedNode(node);
  1074. return true; // false 리턴하면 마커클릭 후에 지도 클릭 이벤트가 동시에 발생한다.
  1075. });
  1076. }
  1077. }
  1078. }
  1079. @Override
  1080. protected void onDestroy() {
  1081. super.onDestroy();
  1082. if (this.locationSource.isActivated()) {
  1083. this.locationSource.deactivate();
  1084. }
  1085. if (this.naviClientService.getNaviWebsocketClient().isOpen()) {
  1086. this.naviClientService.getNaviWebsocketClient().close();
  1087. }
  1088. this.dbQuery.close();
  1089. }
  1090. @Override
  1091. public void onBackPressed() {
  1092. if (this.userSignalFragment.isVisible() ||
  1093. ( !this.isTrackingMode && this.directionSignalFragment.isVisible() || !this.isTrackingMode && this.directionSignalFragment.isVisible() )
  1094. ) {
  1095. requestTopicMessage("x");
  1096. return;
  1097. }
  1098. /*if (this.directionSignalFragment.isVisible()) {
  1099. this.fragmentManager.beginTransaction().hide(this.directionSignalFragment).commit();
  1100. setTrackingMode(true);
  1101. requestTopicMessage("x");
  1102. return;
  1103. }*/
  1104. this.backPressedForFinish.onBackPressed(true);
  1105. }
  1106. @Override
  1107. public void onConfigurationChanged(@NonNull Configuration newConfig) {
  1108. super.onConfigurationChanged(newConfig);
  1109. /*if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
  1110. Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
  1111. } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
  1112. Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
  1113. }*/
  1114. setMapContentPadding();
  1115. }
  1116. private void setMapContentPadding() {
  1117. WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
  1118. Rect rc = wm.getCurrentWindowMetrics().getBounds();
  1119. float lWidth = rc.width();
  1120. float lHeight = rc.height();
  1121. /*@SuppressLint("WrongViewCast") FrameLayout frameMap = (FrameLayout) this.binding.getRoot().findViewById(R.id.map);
  1122. if (frameMap != null) {
  1123. this.width = frameMap.getWidth();
  1124. this.height = frameMap.getHeight();
  1125. Log.e(TAG, "width: " + this.width + ", height: " + this.height);
  1126. }*/
  1127. //TODO Log.e(TAG, "RECT: " + rc.width() + ", " + rc.height());
  1128. float lCarX = lWidth / 2;
  1129. float lCarY = (lHeight / 3) * 2;
  1130. if (this.naverMap != null) {
  1131. int padding = (int) (lHeight / 2);
  1132. //TODO Log.e(TAG, "Rect: " + rc.toString());
  1133. //TODO Log.e(TAG, "Padding: " + padding);
  1134. this.naverMap.setContentPadding(0, 0, 0, -padding);
  1135. //this.naverMap.setContentPadding(100, 100, 100, 100);
  1136. lCarX = (float)(this.naverMap.getContentWidth() / 2);
  1137. lCarY = (float)(this.naverMap.getContentHeight() / 2);
  1138. if (this.isTrackingMode) {
  1139. if (this.lastLocation != null) {
  1140. CameraPosition gpsPosition = new CameraPosition(
  1141. new LatLng(this.lastLocation.getLatitude(), this.lastLocation.getLongitude()), // 위치 지정
  1142. this.trackingZoom, // 줌 레벨
  1143. this.tilt, // 기울임 각도
  1144. this.lastBearing // 방향
  1145. );
  1146. this.naverMap.setCameraPosition(gpsPosition);
  1147. }
  1148. }
  1149. else {
  1150. naverMap.moveCamera(CameraUpdate.scrollTo(this.naverMap.getCameraPosition().target));
  1151. }
  1152. }
  1153. this.width = lWidth;
  1154. this.height = lHeight;
  1155. this.carX = lCarX;
  1156. this.carY = lCarY;
  1157. this.moveByGps.set(true);
  1158. }
  1159. }