Java tutorial
/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.pyo.edit; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.location.Location; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.view.MotionEvent; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.Projection; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.UiSettings; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.Circle; import com.google.android.gms.maps.model.CircleOptions; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.Vector; public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback, // () GoogleMap.OnMyLocationButtonClickListener, // ?? GoogleMap.OnMapClickListener, // ? GoogleMap.OnMapLongClickListener, // ? GoogleMap.OnMarkerDragListener, // ActivityCompat.OnRequestPermissionsResultCallback // Permission Request Callback { // Request code for location permission request. // #onRequestPermissionResult(int, String[], int[]) private static final int LOCATION_PERMISSION_REQUEST_CODE = 1; // Flag indicating whether a requested permission has been denied after returning in // {#onRequestPermissionsResult(int, String[], int[])} private boolean mPermissionDenied = false; private GoogleMap mMap; private UiSettings mUiSettings; // ? ?? ?? . private boolean mLock = true; private List<DraggableCircle> mCircles = new ArrayList<DraggableCircle>(1); private FrameLayout mDrawMap; private boolean IS_MAP_MOVEABLE; private Vector<Vector<Point>> mDrawPoints; private DrawCanvas mDrawCanvas; private double mDeviceRange = 100; private enum mMode { DEFAULT, DRAW, LOAD, RUN, EXIT }; private mMode mCurrentMode = mMode.DEFAULT; private ListView mDeviceListView; private ArrayList<String> mDeviceList; // ?? ? private class DraggableCircle { private static final int WIDTH = 0; // ~50 private static final int HUE = 0; // ~360 private static final int ALPHA = 30; // ~255 private final Marker centerMarker; private final Marker radiusMarker; private final Circle circle; private double radius; public DraggableCircle(LatLng center, double radius) { this.radius = radius; centerMarker = mMap.addMarker(new MarkerOptions().position(center).draggable(true)); radiusMarker = mMap .addMarker(new MarkerOptions().position(toRadiusLatLng(center, radius)).draggable(true) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE))); centerMarker.setTitle(Double.toString(radius)); circle = mMap.addCircle(new CircleOptions().center(center).radius(radius).strokeWidth(WIDTH) .strokeColor(Color.BLACK).fillColor(Color.HSVToColor(ALPHA, new float[] { HUE, 1, 1 }))); } public boolean onMarkerMoved(Marker marker) { if (marker.equals(centerMarker)) { circle.setCenter(marker.getPosition()); radiusMarker.setPosition(toRadiusLatLng(marker.getPosition(), radius)); centerMarker.setTitle(Double.toString(radius)); return true; } if (marker.equals(radiusMarker)) { radius = toRadiusMeters(centerMarker.getPosition(), radiusMarker.getPosition()); circle.setRadius(radius); centerMarker.setTitle(Double.toString(radius)); return true; } return false; } public void markerDraggable(boolean b) { centerMarker.setDraggable(b); radiusMarker.setDraggable(b); centerMarker.setVisible(b); radiusMarker.setVisible(b); } public void remove() { centerMarker.remove(); radiusMarker.remove(); circle.remove(); } public LatLng getCenter() { return centerMarker.getPosition(); } public double getRadius() { return radius; } } @Override public void onMarkerDragStart(Marker marker) { onMarkerMoved(marker); } @Override public void onMarkerDragEnd(Marker marker) { onMarkerMoved(marker); } public void onMarkerDrag(Marker marker) { onMarkerMoved(marker); } public void onMarkerMoved(Marker marker) { for (DraggableCircle draggableCircle : mCircles) { if (draggableCircle.onMarkerMoved(marker)) { break; } } } private static final double RADIUS_OF_EARTH_METERS = 6371009; // Generate LatLng of radius marker private static LatLng toRadiusLatLng(LatLng center, double radius) { double radiusAngle = Math.toDegrees(radius / RADIUS_OF_EARTH_METERS) / Math.cos(Math.toRadians(center.latitude)); return new LatLng(center.latitude, center.longitude + radiusAngle); } private static double toRadiusMeters(LatLng center, LatLng radius) { float[] result = new float[1]; Location.distanceBetween(center.latitude, center.longitude, radius.latitude, radius.longitude, result); return result[0]; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_maps); // Obtain the SupportMapFragment and get notified when the map is ready to be used. SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); mDeviceListView = (ListView) findViewById(R.id.deviceListView); mDeviceList = new ArrayList<String>(); mDeviceList.add("Device1 10m"); mDeviceList.add("Device2 15m"); mDeviceList.add("Device3 20m"); mDeviceList.add("Device4 25m"); ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, mDeviceList); mDeviceListView.setAdapter(adapter); mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mDeviceRange = 10 + 5 * position; mDeviceListView.setVisibility(View.INVISIBLE); } }); mDeviceListView.setVisibility(View.INVISIBLE); // REGISTER DRAW MAP TOUCH LISENTER mDrawMap = (FrameLayout) findViewById(R.id.draw_map); IS_MAP_MOVEABLE = false; mDrawPoints = new Vector<Vector<Point>>(); mDrawMap.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Point point = new Point(Math.round(event.getX()), Math.round(event.getY())); if (IS_MAP_MOVEABLE) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mDrawPoints.add(new Vector<Point>()); mDrawPoints.lastElement().add(point); break; case MotionEvent.ACTION_MOVE: mDrawPoints.lastElement().add(point); break; case MotionEvent.ACTION_UP: //mDrawPoints.clear(); break; } } System.out.println("[!] drag event : " + point.toString()); return IS_MAP_MOVEABLE; } }); mDrawCanvas = new DrawCanvas(this); mDrawMap.addView(mDrawCanvas); } class DrawCanvas extends View { private Paint mPaint; private Bitmap mBitmap; public DrawCanvas(Context context) { super(context); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(10); mPaint.setColor(Color.BLACK); this.setDrawingCacheEnabled(true); } @Override protected void onDraw(Canvas canvas) { synchronized (this) { super.onDraw(canvas); if (mCurrentMode == mMode.RUN) { canvas.drawBitmap(mBitmap, 0, 0, null); } mPaint.setColor(Color.RED); // Draw Circle for (DraggableCircle circle : mCircles) { Point center = mMap.getProjection().toScreenLocation(circle.getCenter()); canvas.drawCircle(center.x, center.y, 5, mPaint); } mPaint.setColor(Color.BLACK); // Draw Line if (mDrawPoints.isEmpty()) { ; } else { for (Vector<Point> points : mDrawPoints) { Point a = points.get(0); int size = points.size(); for (int i = 1; i < size; i++) { Point b = points.get(i); canvas.drawLine(a.x, a.y, b.x, b.y, mPaint); a = b; } } } invalidate(); } } public void simulate() { this.buildDrawingCache(); mBitmap = Bitmap.createBitmap(this.getDrawingCache()); this.setDrawingCacheEnabled(false); // TODO ? // ? ? ? <-> ? ? ? int width = mBitmap.getWidth(); int height = mBitmap.getHeight(); Projection projection = mMap.getProjection(); // ? ? (?? ?) for (DraggableCircle circle : mCircles) { // Point center = projection.toScreenLocation(circle.getCenter()); Point radius = projection.toScreenLocation(toRadiusLatLng(circle.getCenter(), circle.getRadius())); int r = Math.abs(radius.x - center.x); // ??? ?? ?? ? ? // ? ???? ? ? ? int min_x = center.x - r; if (min_x < 0) min_x = 0; int max_x = center.x + r; if (max_x > width) max_x = width; int min_y = center.y - r; if (min_y < 0) min_y = 0; int max_y = center.y + r; if (max_y > height) max_y = height; // ? ? ?? BFS ? int w = max_x - min_x + 1; int h = max_y - min_y + 1; int[][] map = new int[w][h]; Vector<Point> queue = new Vector<Point>(); queue.add(new Point(center.x - min_x, center.y - min_y)); map[center.x - min_x][center.y - min_y] = r; // BFS while (!queue.isEmpty()) { Point point = queue.firstElement(); int x = point.x; int y = point.y; int bx = x + min_x; int by = y + min_y; if (map[x][y] > 0 && bx >= 0 && bx < width && by >= 0 && by < height) { // ? ? ? ? int d = 3; if (mBitmap.getPixel(bx, by) == Color.BLACK) d = 8; // Bresenham Algorithm ? ? BFS ? (? ) int xk = 0; int yk = 5; int pk = -7; // 3 - 2*yk int rx, ry; while (xk <= yk) { rx = x + xk; ry = y - yk; if (rx >= 0 && rx < w && ry >= 0 && ry < h && (map[x][y] - map[rx][ry] > 10)) { map[rx][ry] = map[x][y] - d; queue.add(new Point(rx, ry)); } rx = x - xk; ry = y - yk; if (rx >= 0 && rx < w && ry >= 0 && ry < h && (map[x][y] - map[rx][ry] > 10)) { map[rx][ry] = map[x][y] - d; queue.add(new Point(rx, ry)); } rx = x + xk; ry = y + yk; if (rx >= 0 && rx < w && ry >= 0 && ry < h && (map[x][y] - map[rx][ry] > 10)) { map[rx][ry] = map[x][y] - d; queue.add(new Point(rx, ry)); } rx = x - xk; ry = y + yk; if (rx >= 0 && rx < w && ry >= 0 && ry < h && (map[x][y] - map[rx][ry] > 10)) { map[rx][ry] = map[x][y] - d; queue.add(new Point(rx, ry)); } rx = x + yk; ry = y - xk; if (rx >= 0 && rx < w && ry >= 0 && ry < h && (map[x][y] - map[rx][ry] > 10)) { map[rx][ry] = map[x][y] - d; queue.add(new Point(rx, ry)); } rx = x + yk; ry = y + xk; if (rx >= 0 && rx < w && ry >= 0 && ry < h && (map[x][y] - map[rx][ry] > 10)) { map[rx][ry] = map[x][y] - d; queue.add(new Point(rx, ry)); } rx = x - yk; ry = y - xk; if (rx >= 0 && rx < w && ry >= 0 && ry < h && (map[x][y] - map[rx][ry] > 10)) { map[rx][ry] = map[x][y] - d; queue.add(new Point(rx, ry)); } rx = x - yk; ry = y + xk; if (rx >= 0 && rx < w && ry >= 0 && ry < h && (map[x][y] - map[rx][ry] > 10)) { map[rx][ry] = map[x][y] - d; queue.add(new Point(rx, ry)); } xk += 1; if (pk < 0) pk += (xk * 4) + 6; else { yk -= 1; pk += ((xk - yk) * 4) + 10; } } } queue.remove(0); } // ? for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { int bx = x + min_x; int by = y + min_y; // ? if (bx >= 0 && bx < width && by >= 0 && by < height) { int level = map[x][y] * 255 / r; int color = mBitmap.getPixel(bx, by); if (level < 100) { //mBitmap.setPixel(bx, by, Color.argb(0,0,0,0)); } else if (level < 150) { if (color != Color.argb(80, 0, 0, 255) && color != Color.argb(80, 0, 255, 0) && color != Color.argb(80, 150, 150, 0) && color != Color.argb(80, 200, 100, 0)) mBitmap.setPixel(bx, by, Color.argb(80, 255, 0, 0)); } else if (level < 170) { if (color != Color.argb(80, 0, 0, 255) && color != Color.argb(80, 0, 255, 0) && color != Color.argb(80, 150, 150, 0)) mBitmap.setPixel(bx, by, Color.argb(80, 200, 100, 0)); } else if (level < 190) { if (color != Color.argb(80, 0, 0, 255) && color != Color.argb(80, 0, 255, 0)) mBitmap.setPixel(bx, by, Color.argb(80, 150, 150, 0)); } else if (level < 220) { if (color != Color.argb(80, 0, 0, 255)) mBitmap.setPixel(bx, by, Color.argb(80, 0, 255, 0)); } else { mBitmap.setPixel(bx, by, Color.argb(80, 0, 0, 255)); } } } } } this.setDrawingCacheEnabled(true); } } /** * Manipulates the map once available. * This callback is triggered when the map is ready to be used. * This is where we can add markers or lines, add listeners or move the camera. In this case, * we just add a marker near Sydney, Australia. * If Google Play services is not installed on the device, the user will be prompted to install * it inside the SupportMapFragment. This method will only be triggered once the user has * installed Google Play services and returned to the app. */ @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mUiSettings = mMap.getUiSettings(); mUiSettings.setAllGesturesEnabled(mLock); mMap.setOnMyLocationButtonClickListener(this); mMap.setOnMapClickListener(this); mMap.setOnMapLongClickListener(this); mMap.setOnMarkerDragListener(this); enableMyLocation(); } // Enables the My Location layer if the fine location permission has been granted. private void enableMyLocation() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // Permission to access the location is missing. PermissionUtils.requestPermission(this, LOCATION_PERMISSION_REQUEST_CODE, Manifest.permission.ACCESS_FINE_LOCATION, true); } else if (mMap != null) { // Access to the location has been granted to the app. mMap.setMyLocationEnabled(true); } } @Override public boolean onMyLocationButtonClick() { Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT).show(); // Return false so that we don't consume the event and the default behavior still occurs // (the camera animates to the user's current position). return false; } //@Override public void onRequestPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) { return; } if (PermissionUtils.isPermissionGranted(permissions, grantResults, Manifest.permission.ACCESS_FINE_LOCATION)) { // Enable the my location layer if the permission has been granted. enableMyLocation(); } else { // Display the missing permission error dialog when the fragments resume. mPermissionDenied = true; } } @Override protected void onResumeFragments() { super.onResumeFragments(); if (mPermissionDenied) { // Permission was not granted, display error dialog. showMissingPermissionError(); mPermissionDenied = false; } } // Displays a dialog with error message explaining that the location permission is missing. private void showMissingPermissionError() { PermissionUtils.PermissionDeniedDialog.newInstance(true).show(getSupportFragmentManager(), "dialog"); } @Override public void onMapClick(LatLng latLng) { Point point = mMap.getProjection().toScreenLocation(latLng); } @Override public void onMapLongClick(LatLng latLng) { // if load mode then create circle if (mCurrentMode == mMode.LOAD) { DraggableCircle circle = new DraggableCircle(latLng, mDeviceRange); mCircles.add(circle); } } public void onClickDraw(View view) { mLock = false; mUiSettings.setAllGesturesEnabled(mLock); IS_MAP_MOVEABLE = true; mCurrentMode = mMode.DRAW; for (DraggableCircle circle : mCircles) { circle.markerDraggable(false); } } public void onClickBack(View view) { if (!mDrawPoints.isEmpty()) { mDrawPoints.remove(mDrawPoints.size() - 1); } } public void onClickLoad(View view) { mLock = true; mUiSettings.setAllGesturesEnabled(mLock); IS_MAP_MOVEABLE = false; mCurrentMode = mMode.LOAD; for (DraggableCircle circle : mCircles) { circle.markerDraggable(true); } mDeviceListView.setVisibility(View.VISIBLE); } public void onClickClear(View view) { IS_MAP_MOVEABLE = false; mDrawPoints.clear(); for (DraggableCircle circle : mCircles) { circle.remove(); } mCircles.clear(); mLock = true; mUiSettings.setAllGesturesEnabled(mLock); mCurrentMode = mMode.DEFAULT; } public void onClickRun(View view) { IS_MAP_MOVEABLE = false; for (DraggableCircle circle : mCircles) { circle.markerDraggable(false); } // TODO // ? mDrawCanvas.simulate(); mCurrentMode = mMode.RUN; } public void onClickExit(View view) { IS_MAP_MOVEABLE = false; mCurrentMode = mMode.EXIT; // TODO // Activity } }