Merge remote-tracking branch 'upstream/master'
@ -26,4 +26,6 @@ dependencies {
|
||||
implementation 'com.android.support:appcompat-v7:27.0.2'
|
||||
implementation 'com.google.android.gms:play-services-maps:11.6.2'
|
||||
implementation 'com.android.support:support-v4:27.0.2'
|
||||
// Needed for the LiteListDemo
|
||||
implementation "com.android.support:recyclerview-v7:27.0.2"
|
||||
}
|
||||
|
||||
@ -24,18 +24,18 @@ import com.google.android.gms.maps.OnMapReadyCallback;
|
||||
import com.google.android.gms.maps.model.LatLng;
|
||||
import com.google.android.gms.maps.model.MarkerOptions;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* This shows to include a map in lite mode in a ListView.
|
||||
* Note the use of the view holder pattern with the
|
||||
@ -43,169 +43,171 @@ import java.util.HashSet;
|
||||
*/
|
||||
public class LiteListDemoActivity extends AppCompatActivity {
|
||||
|
||||
private ListFragment mList;
|
||||
private RecyclerView mRecyclerView;
|
||||
|
||||
private MapAdapter mAdapter;
|
||||
private LinearLayoutManager mLinearLayoutManager;
|
||||
private GridLayoutManager mGridLayoutManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.lite_list_demo);
|
||||
|
||||
// Set a custom list adapter for a list of locations
|
||||
mAdapter = new MapAdapter(this, LIST_LOCATIONS);
|
||||
mList = (ListFragment) getSupportFragmentManager().findFragmentById(R.id.list);
|
||||
mList.setListAdapter(mAdapter);
|
||||
mGridLayoutManager = new GridLayoutManager(this, 2);
|
||||
mLinearLayoutManager = new LinearLayoutManager(this);
|
||||
|
||||
// Set a RecyclerListener to clean up MapView from ListView
|
||||
AbsListView lv = mList.getListView();
|
||||
lv.setRecyclerListener(mRecycleListener);
|
||||
// Set up the RecyclerView
|
||||
mRecyclerView = findViewById(R.id.recycler_view);
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
mRecyclerView.setLayoutManager(mLinearLayoutManager);
|
||||
mRecyclerView.setAdapter(new MapAdapter(LIST_LOCATIONS));
|
||||
mRecyclerView.setRecyclerListener(mRecycleListener);
|
||||
}
|
||||
|
||||
/** Create a menu to switch between Linear and Grid LayoutManager. */
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.lite_list_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.layout_linear:
|
||||
mRecyclerView.setLayoutManager(mLinearLayoutManager);
|
||||
break;
|
||||
case R.id.layout_grid:
|
||||
mRecyclerView.setLayoutManager(mGridLayoutManager);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter that displays a title and {@link com.google.android.gms.maps.MapView} for each item.
|
||||
* The layout is defined in <code>lite_list_demo_row.xml</code>. It contains a MapView
|
||||
* that is programatically initialised in
|
||||
* {@link #getView(int, android.view.View, android.view.ViewGroup)}
|
||||
* {@link #(int, android.view.View, android.view.ViewGroup)}
|
||||
*/
|
||||
private class MapAdapter extends ArrayAdapter<NamedLocation> {
|
||||
private class MapAdapter extends RecyclerView.Adapter<MapAdapter.ViewHolder> {
|
||||
|
||||
private final HashSet<MapView> mMaps = new HashSet<MapView>();
|
||||
private NamedLocation[] namedLocations;
|
||||
|
||||
public MapAdapter(Context context, NamedLocation[] locations) {
|
||||
super(context, R.layout.lite_list_demo_row, R.id.lite_listrow_text, locations);
|
||||
private MapAdapter(NamedLocation[] locations) {
|
||||
super();
|
||||
namedLocations = locations;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View row = convertView;
|
||||
ViewHolder holder;
|
||||
|
||||
// Check if a view can be reused, otherwise inflate a layout and set up the view holder
|
||||
if (row == null) {
|
||||
// Inflate view from layout file
|
||||
row = getLayoutInflater().inflate(R.layout.lite_list_demo_row, null);
|
||||
|
||||
// Set up holder and assign it to the View
|
||||
holder = new ViewHolder();
|
||||
holder.mapView = (MapView) row.findViewById(R.id.lite_listrow_map);
|
||||
holder.title = (TextView) row.findViewById(R.id.lite_listrow_text);
|
||||
// Set holder as tag for row for more efficient access.
|
||||
row.setTag(holder);
|
||||
|
||||
// Initialise the MapView
|
||||
holder.initializeMapView();
|
||||
|
||||
// Keep track of MapView
|
||||
mMaps.add(holder.mapView);
|
||||
} else {
|
||||
// View has already been initialised, get its holder
|
||||
holder = (ViewHolder) row.getTag();
|
||||
}
|
||||
|
||||
// Get the NamedLocation for this item and attach it to the MapView
|
||||
NamedLocation item = getItem(position);
|
||||
holder.mapView.setTag(item);
|
||||
|
||||
// Ensure the map has been initialised by the on map ready callback in ViewHolder.
|
||||
// If it is not ready yet, it will be initialised with the NamedLocation set as its tag
|
||||
// when the callback is received.
|
||||
if (holder.map != null) {
|
||||
// The map is already ready to be used
|
||||
setMapLocation(holder.map, item);
|
||||
}
|
||||
|
||||
// Set the text label for this item
|
||||
holder.title.setText(item.name);
|
||||
|
||||
return row;
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.lite_list_demo_row, parent, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retuns the set of all initialised {@link MapView} objects.
|
||||
*
|
||||
* @return All MapViews that have been initialised programmatically by this adapter
|
||||
* This function is called when the user scrolls through the screen and a new item needs
|
||||
* to be shown. So we will need to bind the holder with the details of the next item.
|
||||
*/
|
||||
public HashSet<MapView> getMaps() {
|
||||
return mMaps;
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
if (holder == null) {
|
||||
return;
|
||||
}
|
||||
holder.bindView(position);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a {@link LiteListDemoActivity.NamedLocation} on a
|
||||
* {@link com.google.android.gms.maps.GoogleMap}.
|
||||
* Adds a marker and centers the camera on the NamedLocation with the normal map type.
|
||||
*/
|
||||
private static void setMapLocation(GoogleMap map, NamedLocation data) {
|
||||
// Add a marker for this item and set the camera
|
||||
map.moveCamera(CameraUpdateFactory.newLatLngZoom(data.location, 13f));
|
||||
map.addMarker(new MarkerOptions().position(data.location));
|
||||
|
||||
// Set the map type back to normal.
|
||||
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder for Views used in the {@link LiteListDemoActivity.MapAdapter}.
|
||||
* Once the the <code>map</code> field is set, otherwise it is null.
|
||||
* When the {@link #onMapReady(com.google.android.gms.maps.GoogleMap)} callback is received and
|
||||
* the {@link com.google.android.gms.maps.GoogleMap} is ready, it stored in the {@link #map}
|
||||
* field. The map is then initialised with the NamedLocation that is stored as the tag of the
|
||||
* MapView. This ensures that the map is initialised with the latest data that it should
|
||||
* display.
|
||||
*/
|
||||
class ViewHolder implements OnMapReadyCallback {
|
||||
|
||||
MapView mapView;
|
||||
|
||||
TextView title;
|
||||
|
||||
GoogleMap map;
|
||||
|
||||
@Override
|
||||
public void onMapReady(GoogleMap googleMap) {
|
||||
MapsInitializer.initialize(getApplicationContext());
|
||||
map = googleMap;
|
||||
NamedLocation data = (NamedLocation) mapView.getTag();
|
||||
if (data != null) {
|
||||
setMapLocation(map, data);
|
||||
}
|
||||
public int getItemCount() {
|
||||
return namedLocations.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the MapView by calling its lifecycle methods.
|
||||
* Holder for Views used in the {@link LiteListDemoActivity.MapAdapter}.
|
||||
* Once the the <code>map</code> field is set, otherwise it is null.
|
||||
* When the {@link #onMapReady(com.google.android.gms.maps.GoogleMap)} callback is received and
|
||||
* the {@link com.google.android.gms.maps.GoogleMap} is ready, it stored in the {@link #map}
|
||||
* field. The map is then initialised with the NamedLocation that is stored as the tag of the
|
||||
* MapView. This ensures that the map is initialised with the latest data that it should
|
||||
* display.
|
||||
*/
|
||||
public void initializeMapView() {
|
||||
if (mapView != null) {
|
||||
// Initialise the MapView
|
||||
mapView.onCreate(null);
|
||||
// Set the map ready callback to receive the GoogleMap object
|
||||
mapView.getMapAsync(this);
|
||||
class ViewHolder extends RecyclerView.ViewHolder implements OnMapReadyCallback {
|
||||
|
||||
MapView mapView;
|
||||
TextView title;
|
||||
GoogleMap map;
|
||||
View layout;
|
||||
|
||||
private ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
layout = itemView;
|
||||
mapView = layout.findViewById(R.id.lite_listrow_map);
|
||||
title = layout.findViewById(R.id.lite_listrow_text);
|
||||
if (mapView != null) {
|
||||
// Initialise the MapView
|
||||
mapView.onCreate(null);
|
||||
// Set the map ready callback to receive the GoogleMap object
|
||||
mapView.getMapAsync(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapReady(GoogleMap googleMap) {
|
||||
MapsInitializer.initialize(getApplicationContext());
|
||||
map = googleMap;
|
||||
setMapLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a {@link LiteListDemoActivity.NamedLocation} on a
|
||||
* {@link com.google.android.gms.maps.GoogleMap}.
|
||||
* Adds a marker and centers the camera on the NamedLocation with the normal map type.
|
||||
*/
|
||||
private void setMapLocation() {
|
||||
if (map == null) return;
|
||||
|
||||
NamedLocation data = (NamedLocation) mapView.getTag();
|
||||
if (data == null) return;
|
||||
|
||||
// Add a marker for this item and set the camera
|
||||
map.moveCamera(CameraUpdateFactory.newLatLngZoom(data.location, 13f));
|
||||
map.addMarker(new MarkerOptions().position(data.location));
|
||||
|
||||
// Set the map type back to normal.
|
||||
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
|
||||
}
|
||||
|
||||
private void bindView(int pos) {
|
||||
NamedLocation item = namedLocations[pos];
|
||||
// Store a reference of the ViewHolder object in the layout.
|
||||
layout.setTag(this);
|
||||
// Store a reference to the item in the mapView's tag. We use it to get the
|
||||
// coordinate of a location, when setting the map location.
|
||||
mapView.setTag(item);
|
||||
setMapLocation();
|
||||
title.setText(item.name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* RecycleListener that completely clears the {@link com.google.android.gms.maps.GoogleMap}
|
||||
* attached to a row in the ListView.
|
||||
* attached to a row in the RecyclerView.
|
||||
* Sets the map type to {@link com.google.android.gms.maps.GoogleMap#MAP_TYPE_NONE} and clears
|
||||
* the map.
|
||||
*/
|
||||
private AbsListView.RecyclerListener mRecycleListener = new AbsListView.RecyclerListener() {
|
||||
private RecyclerView.RecyclerListener mRecycleListener = new RecyclerView.RecyclerListener() {
|
||||
|
||||
@Override
|
||||
public void onMovedToScrapHeap(View view) {
|
||||
ViewHolder holder = (ViewHolder) view.getTag();
|
||||
if (holder != null && holder.map != null) {
|
||||
// Clear the map and free up resources by changing the map type to none
|
||||
holder.map.clear();
|
||||
holder.map.setMapType(GoogleMap.MAP_TYPE_NONE);
|
||||
public void onViewRecycled(RecyclerView.ViewHolder holder) {
|
||||
MapAdapter.ViewHolder mapHolder = (MapAdapter.ViewHolder) holder;
|
||||
if (mapHolder != null && mapHolder.map != null) {
|
||||
// Clear the map and free up resources by changing the map type to none.
|
||||
// Also reset the map when it gets reattached to layout, so the previous map would
|
||||
// not be displayed.
|
||||
mapHolder.map.clear();
|
||||
mapHolder.map.setMapType(GoogleMap.MAP_TYPE_NONE);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@ -216,7 +218,6 @@ public class LiteListDemoActivity extends AppCompatActivity {
|
||||
private static class NamedLocation {
|
||||
|
||||
public final String name;
|
||||
|
||||
public final LatLng location;
|
||||
|
||||
NamedLocation(String name, LatLng location) {
|
||||
|
||||
@ -1,26 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright (C) 2014 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
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
|
||||
https://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.
|
||||
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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.v7.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/recycler_view"
|
||||
android:scrollbars="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/list"
|
||||
class="android.support.v4.app.ListFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
@ -1,29 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright (C) 2014 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
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
|
||||
https://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.
|
||||
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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:map="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="5dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lite_listrow_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
|
||||
<!-- MapView in lite mode. Note that it needs to be initialised
|
||||
|
||||
31
ApiDemos/java/app/src/main/res/menu/lite_list_menu.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
<menu xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/layout_linear"
|
||||
android:title="@string/linear"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/layout_grid"
|
||||
android:title="@string/grid"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
@ -177,14 +177,16 @@
|
||||
<string name="pattern_mixed">Mixed</string>
|
||||
|
||||
<!-- Lite Mode -->
|
||||
<string name="grid">Grid</string>
|
||||
<string name="lite_demo_label">Lite Mode</string>
|
||||
<string name="lite_demo_description">Demonstrates some features on a map in lite mode.</string>
|
||||
<string name="lite_demo_intro">Click the map with no, one or multiple markers displayed to open the Google Maps Mobile app.</string>
|
||||
<string name="lite_go_to_darwin">Go to Darwin (No Markers)</string>
|
||||
<string name="lite_go_to_adelaide">Go to Adelaide (One Marker)</string>
|
||||
<string name="lite_go_to_australia">Go to Australia (Five Markers)</string>
|
||||
<string name="lite_list_demo_label">Lite Mode ListView</string>
|
||||
<string name="lite_list_demo_description">Demonstrates using maps in lite mode in a ListView.</string>
|
||||
<string name="linear">Linear</string>
|
||||
<string name="lite_list_demo_label">Lite Mode List</string>
|
||||
<string name="lite_list_demo_description">Demonstrates using maps in lite mode in a RecyclerView using LinearLayoutManager and GridLayoutManager.</string>
|
||||
|
||||
<!-- Indoor -->
|
||||
<string name="activate_higher_level">Activate Higher Level</string>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#Tue Jun 06 10:13:43 AEST 2017
|
||||
#Thu Jan 18 10:58:29 AEDT 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
|
||||
@ -34,9 +34,9 @@ dependencies {
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.1'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
|
||||
// Below is the Google Play Services dependency required for using the Google Maps Android API
|
||||
implementation 'com.google.android.gms:play-services-maps:11.8.0'
|
||||
implementation 'com.google.android.gms:play-services-location:11.8.0'
|
||||
|
||||
// Below is used to run the easypermissions library to manage location permissions
|
||||
// EasyPermissions is needed to help us request for permission to access location
|
||||
implementation 'pub.devrel:easypermissions:1.1.1'
|
||||
}
|
||||
@ -16,6 +16,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.kotlindemos">
|
||||
|
||||
<!-- This app requires location permissions for the layers demo.
|
||||
The user's current location is displayed using the 'My Location' layer. -->
|
||||
<!-- Access to location is needed for the UI Settings Demo -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
@ -26,11 +28,9 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.geo.API_KEY"
|
||||
android:value="@string/google_maps_key" />
|
||||
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -39,11 +39,18 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".BasicMapDemoActivity" />
|
||||
<activity android:name=".CameraDemoActivity"/>
|
||||
<activity android:name=".CircleDemoActivity" />
|
||||
<activity android:name=".CloseInfoWindowDemoActivity" />
|
||||
<activity android:name=".LayersDemoActivity"/>
|
||||
<activity android:name=".LiteListDemoActivity" />
|
||||
<activity android:name=".MarkerDemoActivity"/>
|
||||
<activity android:name=".PolygonDemoActivity"/>
|
||||
<activity android:name=".PolylineDemoActivity"/>
|
||||
<activity android:name=".StreetViewPanoramaNavigationDemoActivity" />
|
||||
<activity android:name=".TagsDemoActivity"/>
|
||||
<activity android:name=".UiSettingsDemoActivity" />
|
||||
<activity android:name=".VisibleRegionDemoActivity"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.kotlindemos
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.SeekBar
|
||||
import android.widget.Toast
|
||||
import com.google.android.gms.maps.CameraUpdate
|
||||
import com.google.android.gms.maps.CameraUpdateFactory
|
||||
import com.google.android.gms.maps.GoogleMap
|
||||
import com.google.android.gms.maps.GoogleMap.CancelableCallback
|
||||
import com.google.android.gms.maps.GoogleMap.OnCameraIdleListener
|
||||
import com.google.android.gms.maps.GoogleMap.OnCameraMoveCanceledListener
|
||||
import com.google.android.gms.maps.GoogleMap.OnCameraMoveListener
|
||||
import com.google.android.gms.maps.GoogleMap.OnCameraMoveStartedListener
|
||||
import com.google.android.gms.maps.OnMapReadyCallback
|
||||
import com.google.android.gms.maps.SupportMapFragment
|
||||
import com.google.android.gms.maps.model.CameraPosition
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
import com.google.android.gms.maps.model.PolylineOptions
|
||||
|
||||
/**
|
||||
* This shows how to change the camera position for the map.
|
||||
*/
|
||||
class CameraDemoActivity :
|
||||
AppCompatActivity(),
|
||||
OnCameraMoveStartedListener,
|
||||
OnCameraMoveListener,
|
||||
OnCameraMoveCanceledListener,
|
||||
OnCameraIdleListener,
|
||||
OnMapReadyCallback {
|
||||
|
||||
/**
|
||||
* The amount by which to scroll the camera. Note that this amount is in raw pixels, not dp
|
||||
* (density-independent pixels).
|
||||
*/
|
||||
private val SCROLL_BY_PX = 100
|
||||
private val TAG = CameraDemoActivity::class.java.name
|
||||
private val sydneyLatLng = LatLng(-33.87365, 151.20689)
|
||||
private val bondiLocation: CameraPosition = CameraPosition.Builder()
|
||||
.target(LatLng(-33.891614, 151.276417))
|
||||
.zoom(15.5f)
|
||||
.bearing(300f)
|
||||
.tilt(50f)
|
||||
.build()
|
||||
|
||||
private val sydneyLocation: CameraPosition = CameraPosition.Builder().
|
||||
target(LatLng(-33.87365, 151.20689))
|
||||
.zoom(15.5f)
|
||||
.bearing(0f)
|
||||
.tilt(25f)
|
||||
.build()
|
||||
|
||||
|
||||
private lateinit var map: GoogleMap
|
||||
|
||||
private lateinit var animateToggle: CompoundButton
|
||||
private lateinit var customDurationToggle: CompoundButton
|
||||
private lateinit var customDurationBar: SeekBar
|
||||
private var currPolylineOptions: PolylineOptions? = null
|
||||
private var isCanceled = false
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.camera_demo)
|
||||
|
||||
animateToggle = findViewById(R.id.animate)
|
||||
customDurationToggle = findViewById(R.id.duration_toggle)
|
||||
customDurationBar = findViewById(R.id.duration_bar)
|
||||
|
||||
updateEnabledState()
|
||||
|
||||
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||
mapFragment.getMapAsync(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateEnabledState()
|
||||
}
|
||||
|
||||
override fun onMapReady(googleMap: GoogleMap?) {
|
||||
|
||||
// return early if the map was not initialised properly
|
||||
map = googleMap ?: return
|
||||
|
||||
with(googleMap) {
|
||||
setOnCameraIdleListener(this@CameraDemoActivity)
|
||||
setOnCameraMoveStartedListener(this@CameraDemoActivity)
|
||||
setOnCameraMoveListener(this@CameraDemoActivity)
|
||||
setOnCameraMoveCanceledListener(this@CameraDemoActivity)
|
||||
|
||||
// We will provide our own zoom controls.
|
||||
uiSettings.isZoomControlsEnabled = false
|
||||
uiSettings.isMyLocationButtonEnabled = true
|
||||
|
||||
// Show Sydney
|
||||
moveCamera(CameraUpdateFactory.newLatLngZoom(sydneyLatLng, 10f))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the map is not ready the CameraUpdateFactory cannot be used. This should be used to wrap
|
||||
* all entry points that call methods on the Google Maps API.
|
||||
*
|
||||
* @param stuffToDo the code to be executed if the map is initialised
|
||||
*/
|
||||
private fun checkReadyThen(stuffToDo: () -> Unit) {
|
||||
if (!::map.isInitialized) {
|
||||
Toast.makeText(this, R.string.map_not_ready, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
stuffToDo()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the Go To Bondi button is clicked.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onGoToBondi(view: View) {
|
||||
checkReadyThen {
|
||||
changeCamera(CameraUpdateFactory.newCameraPosition(bondiLocation))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the Animate To Sydney button is clicked.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onGoToSydney(view: View) {
|
||||
checkReadyThen {
|
||||
changeCamera(CameraUpdateFactory.newCameraPosition(sydneyLocation),
|
||||
object : CancelableCallback {
|
||||
override fun onFinish() {
|
||||
Toast.makeText(baseContext, "Animation to Sydney complete",
|
||||
Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
Toast.makeText(baseContext, "Animation to Sydney canceled",
|
||||
Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the stop button is clicked.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onStopAnimation(view: View) = checkReadyThen { map.stopAnimation() }
|
||||
|
||||
/**
|
||||
* Called when the zoom in button (the one with the +) is clicked.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onZoomIn(view: View) = checkReadyThen { changeCamera(CameraUpdateFactory.zoomIn()) }
|
||||
|
||||
/**
|
||||
* Called when the zoom out button (the one with the -) is clicked.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onZoomOut(view: View) = checkReadyThen { changeCamera(CameraUpdateFactory.zoomOut()) }
|
||||
|
||||
/**
|
||||
* Called when the tilt more button (the one with the /) is clicked.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onTiltMore(view: View) {
|
||||
checkReadyThen {
|
||||
|
||||
val newTilt = Math.min(map.cameraPosition.tilt + 10, 90F)
|
||||
val cameraPosition = CameraPosition.Builder(map.cameraPosition).tilt(newTilt).build()
|
||||
|
||||
changeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the tilt less button (the one with the \) is clicked.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onTiltLess(view: View) {
|
||||
checkReadyThen {
|
||||
|
||||
val newTilt = Math.max(map.cameraPosition.tilt - 10, 0F)
|
||||
val cameraPosition = CameraPosition.Builder(map.cameraPosition).tilt(newTilt).build()
|
||||
|
||||
changeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the left arrow button is clicked. This causes the camera to move to the left
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onScrollLeft(view: View) {
|
||||
checkReadyThen {
|
||||
changeCamera(CameraUpdateFactory.scrollBy((-SCROLL_BY_PX).toFloat(),0f))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the right arrow button is clicked. This causes the camera to move to the right.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onScrollRight(view: View) {
|
||||
checkReadyThen {
|
||||
changeCamera(CameraUpdateFactory.scrollBy(SCROLL_BY_PX.toFloat(), 0f))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the up arrow button is clicked. The causes the camera to move up.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onScrollUp(view: View) {
|
||||
checkReadyThen {
|
||||
changeCamera(CameraUpdateFactory.scrollBy(0f, (-SCROLL_BY_PX).toFloat()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the down arrow button is clicked. This causes the camera to move down.
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onScrollDown(view: View) {
|
||||
checkReadyThen {
|
||||
changeCamera(CameraUpdateFactory.scrollBy(0f, SCROLL_BY_PX.toFloat()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the animate button is toggled
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onToggleAnimate(view: View) = updateEnabledState()
|
||||
|
||||
/**
|
||||
* Called when the custom duration checkbox is toggled
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onToggleCustomDuration(view: View) = updateEnabledState()
|
||||
|
||||
/**
|
||||
* Update the enabled state of the custom duration controls.
|
||||
*/
|
||||
private fun updateEnabledState() {
|
||||
customDurationToggle.isEnabled = animateToggle.isChecked
|
||||
customDurationBar.isEnabled = animateToggle.isChecked && customDurationToggle.isChecked
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the camera position by moving or animating the camera depending on the state of the
|
||||
* animate toggle button.
|
||||
*/
|
||||
private fun changeCamera(update: CameraUpdate, callback: CancelableCallback? = null) {
|
||||
if (animateToggle.isChecked) {
|
||||
if (customDurationToggle.isChecked) {
|
||||
// The duration must be strictly positive so we make it at least 1.
|
||||
map.animateCamera(update, Math.max(customDurationBar.progress, 1), callback)
|
||||
} else {
|
||||
map.animateCamera(update, callback)
|
||||
}
|
||||
} else {
|
||||
map.moveCamera(update)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCameraMoveStarted(reason: Int) {
|
||||
if (!isCanceled) map.clear()
|
||||
|
||||
|
||||
var reasonText = "UNKNOWN_REASON"
|
||||
currPolylineOptions = PolylineOptions().width(5f)
|
||||
when (reason) {
|
||||
OnCameraMoveStartedListener.REASON_GESTURE -> {
|
||||
currPolylineOptions?.color(Color.BLUE)
|
||||
reasonText = "GESTURE"
|
||||
}
|
||||
OnCameraMoveStartedListener.REASON_API_ANIMATION -> {
|
||||
currPolylineOptions?.color(Color.RED)
|
||||
reasonText = "API_ANIMATION"
|
||||
}
|
||||
OnCameraMoveStartedListener.REASON_DEVELOPER_ANIMATION -> {
|
||||
currPolylineOptions?.color(Color.GREEN)
|
||||
reasonText = "DEVELOPER_ANIMATION"
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "onCameraMoveStarted($reasonText)")
|
||||
addCameraTargetToPath()
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that currPolyLine options is not null before accessing it
|
||||
*
|
||||
* @param stuffToDo the code to be executed if currPolylineOptions is not null
|
||||
*/
|
||||
private fun checkPolylineThen(stuffToDo: () -> Unit) {
|
||||
if (currPolylineOptions != null) stuffToDo()
|
||||
}
|
||||
|
||||
|
||||
override fun onCameraMove() {
|
||||
Log.i(TAG, "onCameraMove")
|
||||
// When the camera is moving, add its target to the current path we'll draw on the map.
|
||||
checkPolylineThen { addCameraTargetToPath() }
|
||||
}
|
||||
|
||||
override fun onCameraMoveCanceled() {
|
||||
// When the camera stops moving, add its target to the current path, and draw it on the map.
|
||||
checkPolylineThen {
|
||||
addCameraTargetToPath()
|
||||
map.addPolyline(currPolylineOptions)
|
||||
}
|
||||
|
||||
isCanceled = true // Set to clear the map when dragging starts again.
|
||||
currPolylineOptions = null
|
||||
Log.i(TAG, "onCameraMoveCancelled")
|
||||
}
|
||||
|
||||
override fun onCameraIdle() {
|
||||
checkPolylineThen {
|
||||
addCameraTargetToPath()
|
||||
map.addPolyline(currPolylineOptions)
|
||||
}
|
||||
|
||||
currPolylineOptions = null
|
||||
isCanceled = false // Set to *not* clear the map when dragging starts again.
|
||||
Log.i(TAG, "onCameraIdle")
|
||||
}
|
||||
|
||||
private fun addCameraTargetToPath() {
|
||||
currPolylineOptions?.add(map.cameraPosition.target)
|
||||
}
|
||||
}
|
||||
@ -24,19 +24,33 @@ class DemoDetailsList {
|
||||
val DEMOS = listOf<DemoDetails>(
|
||||
DemoDetails(R.string.basic_demo_label, R.string.basic_demo_details,
|
||||
BasicMapDemoActivity::class.java),
|
||||
DemoDetails(R.string.camera_demo_label, R.string.camera_demo_description,
|
||||
CameraDemoActivity::class.java),
|
||||
DemoDetails(R.string.circle_demo_label, R.string.circle_demo_details,
|
||||
CircleDemoActivity::class.java),
|
||||
DemoDetails(R.string.close_info_window_demo_label,
|
||||
R.string.close_info_window_demo_details,
|
||||
CloseInfoWindowDemoActivity::class.java),
|
||||
DemoDetails(R.string.circle_demo_label, R.string.circle_demo_details,
|
||||
CircleDemoActivity::class.java),
|
||||
DemoDetails(R.string.layers_demo_label, R.string.layers_demo_description,
|
||||
LayersDemoActivity::class.java),
|
||||
DemoDetails(R.string.lite_list_demo_label, R.string.lite_list_demo_details,
|
||||
LiteListDemoActivity::class.java),
|
||||
DemoDetails(R.string.markers_demo_label, R.string.markers_demo_description,
|
||||
MarkerDemoActivity::class.java),
|
||||
DemoDetails(R.string.polygon_demo_label, R.string.polygon_demo_details,
|
||||
PolygonDemoActivity::class.java),
|
||||
DemoDetails(R.string.polyline_demo_label, R.string.polyline_demo_description,
|
||||
PolylineDemoActivity::class.java),
|
||||
DemoDetails(
|
||||
R.string.street_view_panorama_navigation_demo_label,
|
||||
R.string.street_view_panorama_navigation_demo_details,
|
||||
StreetViewPanoramaNavigationDemoActivity::class.java),
|
||||
DemoDetails(R.string.tags_demo_label, R.string.tags_demo_details,
|
||||
TagsDemoActivity::class.java),
|
||||
DemoDetails(R.string.ui_settings_demo_label, R.string.ui_settings_demo_details,
|
||||
UiSettingsDemoActivity::class.java)
|
||||
UiSettingsDemoActivity::class.java),
|
||||
DemoDetails(R.string.region_demo_label, R.string.region_demo_details,
|
||||
VisibleRegionDemoActivity::class.java)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.kotlindemos
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.CheckBox
|
||||
import android.widget.Spinner
|
||||
import com.google.android.gms.maps.GoogleMap
|
||||
import com.google.android.gms.maps.GoogleMap.MAP_TYPE_HYBRID
|
||||
import com.google.android.gms.maps.GoogleMap.MAP_TYPE_NONE
|
||||
import com.google.android.gms.maps.GoogleMap.MAP_TYPE_NORMAL
|
||||
import com.google.android.gms.maps.GoogleMap.MAP_TYPE_SATELLITE
|
||||
import com.google.android.gms.maps.GoogleMap.MAP_TYPE_TERRAIN
|
||||
import com.google.android.gms.maps.OnMapReadyCallback
|
||||
import com.google.android.gms.maps.SupportMapFragment
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted
|
||||
import pub.devrel.easypermissions.EasyPermissions
|
||||
|
||||
private const val LOCATION_PERMISSION_REQUEST_CODE = 1
|
||||
|
||||
/**
|
||||
* Demonstrates the different base layers of a map.
|
||||
*/
|
||||
class LayersDemoActivity :
|
||||
AppCompatActivity(),
|
||||
OnMapReadyCallback,
|
||||
AdapterView.OnItemSelectedListener,
|
||||
EasyPermissions.PermissionCallbacks {
|
||||
|
||||
private val TAG = MarkerDemoActivity::class.java.name
|
||||
|
||||
private lateinit var map: GoogleMap
|
||||
|
||||
private lateinit var trafficCheckbox: CheckBox
|
||||
private lateinit var myLocationCheckbox: CheckBox
|
||||
private lateinit var buildingsCheckbox: CheckBox
|
||||
private lateinit var indoorCheckbox: CheckBox
|
||||
private lateinit var spinner: Spinner
|
||||
|
||||
/**
|
||||
* Flag indicating whether a requested permission has been denied after returning in
|
||||
* [.onRequestPermissionsResult].
|
||||
*/
|
||||
private var showPermissionDeniedDialog = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.layers_demo)
|
||||
|
||||
spinner = findViewById<Spinner>(R.id.layers_spinner).apply {
|
||||
adapter = ArrayAdapter.createFromResource(this@LayersDemoActivity,
|
||||
R.array.layers_array, android.R.layout.simple_spinner_item).apply {
|
||||
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
}
|
||||
|
||||
// set a listener for when the spinner to select map type is changed.
|
||||
onItemSelectedListener = this@LayersDemoActivity
|
||||
}
|
||||
|
||||
myLocationCheckbox = findViewById(R.id.my_location)
|
||||
buildingsCheckbox = findViewById(R.id.buildings)
|
||||
indoorCheckbox = findViewById(R.id.indoor)
|
||||
trafficCheckbox = findViewById(R.id.traffic)
|
||||
|
||||
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||
mapFragment.getMapAsync(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a dialog box asking the user to grant permissions if they were denied
|
||||
*/
|
||||
override fun onResumeFragments() {
|
||||
super.onResumeFragments()
|
||||
if (showPermissionDeniedDialog) {
|
||||
AlertDialog.Builder(this).apply {
|
||||
setPositiveButton(R.string.ok, null)
|
||||
setMessage(R.string.location_permission_denied)
|
||||
create()
|
||||
}.show()
|
||||
showPermissionDeniedDialog = false
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onMapReady(googleMap: GoogleMap?) {
|
||||
|
||||
// exit early if the map was not initialised properly
|
||||
map = googleMap ?: return
|
||||
|
||||
updateMapType()
|
||||
|
||||
// check the state of all checkboxes and update the map accordingly
|
||||
with(map) {
|
||||
isTrafficEnabled = trafficCheckbox.isChecked
|
||||
isBuildingsEnabled = buildingsCheckbox.isChecked
|
||||
isIndoorEnabled = indoorCheckbox.isChecked
|
||||
}
|
||||
|
||||
// Must deal with the location checkbox separately as must check that
|
||||
// location permission have been granted before enabling the 'My Location' layer.
|
||||
if (myLocationCheckbox.isChecked) enableMyLocation()
|
||||
|
||||
|
||||
// attach a listener to each checkbox
|
||||
trafficCheckbox.setOnClickListener {
|
||||
map.isTrafficEnabled = trafficCheckbox.isChecked
|
||||
}
|
||||
|
||||
buildingsCheckbox.setOnClickListener {
|
||||
map.isBuildingsEnabled = buildingsCheckbox.isChecked
|
||||
}
|
||||
|
||||
indoorCheckbox.setOnClickListener {
|
||||
map.isIndoorEnabled = indoorCheckbox.isChecked
|
||||
}
|
||||
|
||||
// if this box is checked, must check for permission before enabling the My Location layer
|
||||
myLocationCheckbox.setOnClickListener {
|
||||
if (!myLocationCheckbox.isChecked) {
|
||||
map.isMyLocationEnabled = false
|
||||
} else {
|
||||
enableMyLocation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@AfterPermissionGranted(LOCATION_PERMISSION_REQUEST_CODE)
|
||||
private fun enableMyLocation() {
|
||||
// Enable the location layer. Request the location permission if needed.
|
||||
val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
|
||||
if (EasyPermissions.hasPermissions(this, *permissions)) {
|
||||
map.isMyLocationEnabled = true
|
||||
|
||||
} else {
|
||||
// if permissions are not currently granted, request permissions
|
||||
EasyPermissions.requestPermissions(this,
|
||||
getString(R.string.permission_rationale_location),
|
||||
LOCATION_PERMISSION_REQUEST_CODE, *permissions)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the type of the map depending on the currently selected item in the spinner
|
||||
*/
|
||||
private fun updateMapType() {
|
||||
// This can also be called by the Android framework in onCreate() at which
|
||||
// point map may not be ready yet.
|
||||
if (!::map.isInitialized) return
|
||||
|
||||
map.mapType = when (spinner.selectedItem) {
|
||||
getString(R.string.normal) -> MAP_TYPE_NORMAL
|
||||
getString(R.string.hybrid) -> MAP_TYPE_HYBRID
|
||||
getString(R.string.satellite) -> MAP_TYPE_SATELLITE
|
||||
getString(R.string.terrain) -> MAP_TYPE_TERRAIN
|
||||
getString(R.string.none_map) -> MAP_TYPE_NONE
|
||||
else -> {
|
||||
map.mapType // do not change map type
|
||||
Log.e(TAG, "Error setting layer with name ${spinner.selectedItem}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
EasyPermissions.onRequestPermissionsResult(requestCode,
|
||||
permissions, grantResults, this)
|
||||
}
|
||||
|
||||
override fun onPermissionsDenied(requestCode: Int, list: List<String>) {
|
||||
// Un-check the box until the layer has been enabled
|
||||
// and show dialog box with permission rationale.
|
||||
myLocationCheckbox.isChecked = false
|
||||
showPermissionDeniedDialog = true
|
||||
}
|
||||
|
||||
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
|
||||
// do nothing, handled in updateMyLocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Called as part of the AdapterView.OnItemSelectedListener
|
||||
*/
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
|
||||
updateMapType()
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,494 @@
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.kotlindemos
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.Image
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.SystemClock
|
||||
import android.support.annotation.ColorInt
|
||||
import android.support.annotation.DrawableRes
|
||||
import android.support.v4.content.res.ResourcesCompat
|
||||
import android.support.v4.graphics.drawable.DrawableCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.animation.BounceInterpolator
|
||||
import android.widget.CheckBox
|
||||
import android.widget.ImageView
|
||||
import android.widget.RadioGroup
|
||||
import android.widget.SeekBar
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener
|
||||
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.GoogleMap.InfoWindowAdapter
|
||||
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener
|
||||
import com.google.android.gms.maps.GoogleMap.OnInfoWindowCloseListener
|
||||
import com.google.android.gms.maps.GoogleMap.OnInfoWindowLongClickListener
|
||||
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener
|
||||
import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener
|
||||
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.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 java.util.ArrayList
|
||||
import java.util.Random
|
||||
|
||||
/**
|
||||
* This shows how to place markers on a map.
|
||||
*/
|
||||
class MarkerDemoActivity :
|
||||
AppCompatActivity(),
|
||||
OnMarkerClickListener,
|
||||
OnInfoWindowClickListener,
|
||||
OnMarkerDragListener,
|
||||
OnInfoWindowLongClickListener,
|
||||
OnInfoWindowCloseListener,
|
||||
OnMapAndViewReadyListener.OnGlobalLayoutAndMapReadyListener {
|
||||
|
||||
private val TAG = MarkerDemoActivity::class.java.name
|
||||
|
||||
/** This is ok to be lateinit as it is initialised in onMapReady */
|
||||
private lateinit var map: GoogleMap
|
||||
|
||||
/**
|
||||
* Keeps track of the last selected marker (though it may no longer be selected). This is
|
||||
* useful for refreshing the info window.
|
||||
*
|
||||
* Must be nullable as it is null when no marker has been selected
|
||||
*/
|
||||
private var lastSelectedMarker: Marker? = null
|
||||
|
||||
private val markerRainbow = ArrayList<Marker>()
|
||||
|
||||
/** map to store place names and locations */
|
||||
private val places = mapOf(
|
||||
"BRISBANE" to LatLng(-27.47093, 153.0235),
|
||||
"MELBOURNE" to LatLng(-37.81319, 144.96298),
|
||||
"DARWIN" to LatLng(-12.4634, 130.8456),
|
||||
"SYDNEY" to LatLng(-33.87365, 151.20689),
|
||||
"ADELAIDE" to LatLng(-34.92873, 138.59995),
|
||||
"PERTH" to LatLng(-31.952854, 115.857342),
|
||||
"ALICE_SPRINGS" to LatLng(-24.6980, 133.8807)
|
||||
)
|
||||
|
||||
/** These can be lateinit as they are set in onCreate */
|
||||
private lateinit var topText: TextView
|
||||
private lateinit var rotationBar: SeekBar
|
||||
private lateinit var flatBox: CheckBox
|
||||
private lateinit var options: RadioGroup
|
||||
|
||||
private val random = Random()
|
||||
|
||||
/** Demonstrates customizing the info window and/or its contents. */
|
||||
internal inner class CustomInfoWindowAdapter : InfoWindowAdapter {
|
||||
|
||||
// These are both view groups containing an ImageView with id "badge" and two
|
||||
// TextViews with id "title" and "snippet".
|
||||
private val window: View = layoutInflater.inflate(R.layout.custom_info_window, null)
|
||||
private val contents: View = layoutInflater.inflate(R.layout.custom_info_contents, null)
|
||||
|
||||
override fun getInfoWindow(marker: Marker): View? {
|
||||
if (options.checkedRadioButtonId != R.id.custom_info_window) {
|
||||
// This means that getInfoContents will be called.
|
||||
return null
|
||||
}
|
||||
render(marker, window)
|
||||
return window
|
||||
}
|
||||
|
||||
override fun getInfoContents(marker: Marker): View? {
|
||||
if (options.checkedRadioButtonId != R.id.custom_info_contents) {
|
||||
// This means that the default info contents will be used.
|
||||
return null
|
||||
}
|
||||
render(marker, contents)
|
||||
return contents
|
||||
}
|
||||
|
||||
private fun render(marker: Marker, view: View) {
|
||||
val badge = when (marker.title) {
|
||||
"Brisbane" -> R.drawable.badge_qld
|
||||
"Adelaide" -> R.drawable.badge_sa
|
||||
"Sydney" -> R.drawable.badge_nsw
|
||||
"Melbourne" -> R.drawable.badge_victoria
|
||||
"Perth" -> R.drawable.badge_wa
|
||||
in "Darwin Marker 1".."Darwin Marker 4" -> R.drawable.badge_nt
|
||||
else -> 0 // Passing 0 to setImageResource will clear the image view.
|
||||
}
|
||||
|
||||
view.findViewById<ImageView>(R.id.badge).setImageResource(badge)
|
||||
|
||||
// Set the title and snippet for the custom info window
|
||||
val title: String? = marker.title
|
||||
val titleUi = view.findViewById<TextView>(R.id.title)
|
||||
|
||||
if (title != null) {
|
||||
// Spannable string allows us to edit the formatting of the text.
|
||||
titleUi.text = SpannableString(title).apply {
|
||||
setSpan(ForegroundColorSpan(Color.RED), 0, length, 0)
|
||||
}
|
||||
} else {
|
||||
titleUi.text = ""
|
||||
}
|
||||
|
||||
val snippet: String? = marker.snippet
|
||||
val snippetUi = view.findViewById<TextView>(R.id.snippet)
|
||||
if (snippet != null && snippet.length > 12) {
|
||||
snippetUi.text = SpannableString(snippet).apply {
|
||||
setSpan(ForegroundColorSpan(Color.MAGENTA), 0, 10, 0)
|
||||
setSpan(ForegroundColorSpan(Color.BLUE), 12, snippet.length, 0)
|
||||
}
|
||||
} else {
|
||||
snippetUi.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.marker_demo)
|
||||
|
||||
topText = findViewById(R.id.top_text)
|
||||
|
||||
rotationBar = findViewById<SeekBar>(R.id.rotationSeekBar).apply {
|
||||
max = 360
|
||||
setOnSeekBarChangeListener(object: OnSeekBarChangeListener {
|
||||
|
||||
/** Called when the Rotation progress bar is moved */
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
val rotation = seekBar?.progress?.toFloat()
|
||||
checkReadyThen { markerRainbow.map { it.rotation = rotation ?: 0f } }
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(p0: SeekBar?) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(p0: SeekBar?) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
} )
|
||||
}
|
||||
|
||||
flatBox = findViewById(R.id.flat)
|
||||
|
||||
options = findViewById<RadioGroup>(R.id.custom_info_window_options).apply {
|
||||
setOnCheckedChangeListener { _, _ ->
|
||||
if (lastSelectedMarker?.isInfoWindowShown == true) {
|
||||
// Refresh the info window when the info window's content has changed.
|
||||
// must deal with the possibility that lastSelectedMarker has changed in
|
||||
// another thread between the null check and this line, do this with !!
|
||||
lastSelectedMarker?.showInfoWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||
OnMapAndViewReadyListener(mapFragment, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the callback that is triggered when the GoogleMap has loaded and is ready for use
|
||||
*/
|
||||
override fun onMapReady(googleMap: GoogleMap?) {
|
||||
|
||||
// return early if the map was not initialised properly
|
||||
map = googleMap ?: return
|
||||
|
||||
// create bounds that encompass every location we reference
|
||||
val boundsBuilder = LatLngBounds.Builder()
|
||||
// include all places we have markers for on the map
|
||||
places.keys.map { place -> boundsBuilder.include(places.getValue(place)) }
|
||||
val bounds = boundsBuilder.build()
|
||||
|
||||
with(map) {
|
||||
// Hide the zoom controls as the button panel will cover it.
|
||||
uiSettings.isZoomControlsEnabled = false
|
||||
|
||||
// Setting an info window adapter allows us to change the both the contents and
|
||||
// look of the info window.
|
||||
setInfoWindowAdapter(CustomInfoWindowAdapter())
|
||||
|
||||
// Set listeners for marker events. See the bottom of this class for their behavior.
|
||||
setOnMarkerClickListener(this@MarkerDemoActivity)
|
||||
setOnInfoWindowClickListener(this@MarkerDemoActivity)
|
||||
setOnMarkerDragListener(this@MarkerDemoActivity)
|
||||
setOnInfoWindowCloseListener(this@MarkerDemoActivity)
|
||||
setOnInfoWindowLongClickListener(this@MarkerDemoActivity)
|
||||
|
||||
// Override the default content description on the view, for accessibility mode.
|
||||
// Ideally this string would be localised.
|
||||
setContentDescription("Map with lots of markers.")
|
||||
|
||||
moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50))
|
||||
}
|
||||
|
||||
// Add lots of markers to the googleMap.
|
||||
addMarkersToMap()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all the specified markers on the map
|
||||
*/
|
||||
private fun addMarkersToMap() {
|
||||
|
||||
val placeDetailsMap = mutableMapOf(
|
||||
// Uses a coloured icon
|
||||
"BRISBANE" to PlaceDetails(
|
||||
position = places.getValue("BRISBANE"),
|
||||
title = "Brisbane",
|
||||
snippet = "Population: 2,074,200",
|
||||
icon = BitmapDescriptorFactory
|
||||
.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)
|
||||
),
|
||||
|
||||
// Uses a custom icon with the info window popping out of the center of the icon.
|
||||
"SYDNEY" to PlaceDetails(
|
||||
position = places.getValue("SYDNEY"),
|
||||
title = "Sydney",
|
||||
snippet = "Population: 4,627,300",
|
||||
icon = BitmapDescriptorFactory.fromResource(R.drawable.arrow),
|
||||
infoWindowAnchorX = 0.5f,
|
||||
infoWindowAnchorY = 0.5f
|
||||
),
|
||||
|
||||
// Will create a draggable marker. Long press to drag.
|
||||
"MELBOURNE" to PlaceDetails(
|
||||
position = places.getValue("MELBOURNE"),
|
||||
title = "Melbourne",
|
||||
snippet = "Population: 4,137,400",
|
||||
draggable = true
|
||||
),
|
||||
|
||||
// Use a vector drawable resource as a marker icon.
|
||||
"ALICE_SPRINGS" to PlaceDetails(
|
||||
position = places.getValue("ALICE_SPRINGS"),
|
||||
title = "Alice Springs",
|
||||
icon = vectorToBitmap(
|
||||
R.drawable.ic_android, Color.parseColor("#A4C639"))
|
||||
),
|
||||
|
||||
// More markers for good measure
|
||||
"PERTH" to PlaceDetails(
|
||||
position = places.getValue("PERTH"),
|
||||
title = "Perth",
|
||||
snippet = "Population: 1,738,800"
|
||||
),
|
||||
|
||||
"ADELAIDE" to PlaceDetails(
|
||||
position = places.getValue("ADELAIDE"),
|
||||
title = "Adelaide",
|
||||
snippet = "Population: 1,213,000"
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
// add 4 markers on top of each other in Darwin with varying z-indexes
|
||||
(0 until 4).map {
|
||||
placeDetailsMap.put(
|
||||
"DARWIN ${it + 1}", PlaceDetails(
|
||||
position = places.getValue("DARWIN"),
|
||||
title = "Darwin Marker ${it + 1}",
|
||||
snippet = "z-index initially ${it + 1}",
|
||||
zIndex = it.toFloat()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// place markers for each of the defined locations
|
||||
placeDetailsMap.keys.map {
|
||||
with(placeDetailsMap.getValue(it)) {
|
||||
map.addMarker(MarkerOptions()
|
||||
.position(position)
|
||||
.title(title)
|
||||
.snippet(snippet)
|
||||
.icon(icon)
|
||||
.infoWindowAnchor(infoWindowAnchorX, infoWindowAnchorY)
|
||||
.draggable(draggable)
|
||||
.zIndex(zIndex))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a marker rainbow demonstrating how to create default marker icons of different
|
||||
// hues (colors).
|
||||
val numMarkersInRainbow = 12
|
||||
(0 until numMarkersInRainbow).mapTo(markerRainbow) {
|
||||
map.addMarker(MarkerOptions().apply{
|
||||
position(LatLng(
|
||||
-30 + 10 * Math.sin(it * Math.PI / (numMarkersInRainbow - 1)),
|
||||
135 - 10 * Math.cos(it * Math.PI / (numMarkersInRainbow - 1))))
|
||||
title("Marker $it")
|
||||
icon(BitmapDescriptorFactory.defaultMarker((it * 360 / numMarkersInRainbow)
|
||||
.toFloat()))
|
||||
flat(flatBox.isChecked)
|
||||
rotation(rotationBar.progress.toFloat())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Demonstrates converting a [Drawable] to a [BitmapDescriptor],
|
||||
* for use as a marker icon.
|
||||
*/
|
||||
private fun vectorToBitmap(@DrawableRes id : Int, @ColorInt color : Int): BitmapDescriptor {
|
||||
val vectorDrawable: Drawable? = ResourcesCompat.getDrawable(resources, id, null)
|
||||
if (vectorDrawable == null) {
|
||||
Log.e(TAG, "Resource not found")
|
||||
return BitmapDescriptorFactory.defaultMarker()
|
||||
}
|
||||
val bitmap = Bitmap.createBitmap(vectorDrawable.intrinsicWidth,
|
||||
vectorDrawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
vectorDrawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||
DrawableCompat.setTint(vectorDrawable, color)
|
||||
vectorDrawable.draw(canvas)
|
||||
return BitmapDescriptorFactory.fromBitmap(bitmap)
|
||||
}
|
||||
|
||||
/** Called when the Clear button is clicked. */
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onClearMap(view: View) {
|
||||
checkReadyThen { map.clear() }
|
||||
}
|
||||
|
||||
/** Called when the Reset button is clicked. */
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onResetMap(view: View) {
|
||||
checkReadyThen {
|
||||
map.clear()
|
||||
addMarkersToMap()
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the Flat check box is checked or unchecked */
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onToggleFlat(view: View) {
|
||||
checkReadyThen { markerRainbow.map { marker -> marker.isFlat = flatBox.isChecked } }
|
||||
}
|
||||
|
||||
//
|
||||
// Marker related listeners.
|
||||
//
|
||||
override fun onMarkerClick(marker : Marker): Boolean {
|
||||
|
||||
// Markers have a z-index that is settable and gettable.
|
||||
marker.zIndex += 1.0f
|
||||
Toast.makeText(this, "${marker.title} z-index set to ${marker.zIndex}",
|
||||
Toast.LENGTH_SHORT).show()
|
||||
|
||||
lastSelectedMarker = marker
|
||||
|
||||
if (marker.position == places.getValue("PERTH")) {
|
||||
// This causes the marker at Perth to bounce into position when it is clicked.
|
||||
val handler = Handler()
|
||||
val start = SystemClock.uptimeMillis()
|
||||
val duration = 1500
|
||||
|
||||
val interpolator = BounceInterpolator()
|
||||
|
||||
handler.post(object : Runnable {
|
||||
override fun run() {
|
||||
val elapsed = SystemClock.uptimeMillis() - start
|
||||
val t = Math.max(
|
||||
1 - interpolator.getInterpolation(elapsed.toFloat() / duration), 0f)
|
||||
marker.setAnchor(0.5f, 1.0f + 2 * t)
|
||||
|
||||
// Post again 16ms later.
|
||||
if (t > 0.0) {
|
||||
handler.postDelayed(this, 16)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (marker.position == places.getValue("ADELAIDE")) {
|
||||
// This causes the marker at Adelaide to change color and alpha.
|
||||
marker.apply {
|
||||
setIcon(BitmapDescriptorFactory.defaultMarker(random.nextFloat() * 360))
|
||||
alpha = random.nextFloat()
|
||||
}
|
||||
}
|
||||
|
||||
// We return false to indicate that we have not consumed the event and that we wish
|
||||
// for the default behavior to occur (which is for the camera to move such that the
|
||||
// marker is centered and for the marker's info window to open, if it has one).
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onInfoWindowClick(marker : Marker) {
|
||||
Toast.makeText(this, "Click Info Window", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onInfoWindowClose(marker : Marker) {
|
||||
Toast.makeText(this, "Close Info Window", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onInfoWindowLongClick(marker : Marker) {
|
||||
Toast.makeText(this, "Info Window long click", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onMarkerDragStart(marker : Marker) {
|
||||
topText.text = getString(R.string.on_marker_drag_start)
|
||||
}
|
||||
|
||||
override fun onMarkerDragEnd(marker : Marker) {
|
||||
topText.text = getString(R.string.on_marker_drag_end)
|
||||
}
|
||||
|
||||
override fun onMarkerDrag(marker : Marker) {
|
||||
topText.text = getString(R.string.on_marker_drag, marker.position.latitude, marker.position.longitude)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the map is ready, the executes the provided lambda function
|
||||
*
|
||||
* @param stuffToDo the code to be executed if the map is ready
|
||||
*/
|
||||
private fun checkReadyThen(stuffToDo : () -> Unit) {
|
||||
if (!::map.isInitialized) {
|
||||
Toast.makeText(this, R.string.map_not_ready, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
stuffToDo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This stores the details of a place that used to draw a marker
|
||||
*/
|
||||
class PlaceDetails(
|
||||
val position: LatLng,
|
||||
val title: String = "Marker",
|
||||
val snippet: String? = null,
|
||||
val icon: BitmapDescriptor = BitmapDescriptorFactory.defaultMarker(),
|
||||
val infoWindowAnchorX: Float = 0.5F,
|
||||
val infoWindowAnchorY: Float = 0F,
|
||||
val draggable: Boolean = false,
|
||||
val zIndex: Float = 0F)
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.kotlindemos
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||
import com.google.android.gms.maps.GoogleMap
|
||||
import com.google.android.gms.maps.OnMapReadyCallback
|
||||
import com.google.android.gms.maps.SupportMapFragment
|
||||
|
||||
/**
|
||||
* Helper class that will delay triggering the OnMapReady callback until both the GoogleMap and the
|
||||
* View having completed initialization. This is only necessary if a developer wishes to immediately
|
||||
* invoke any method on the GoogleMap that also requires the View to have finished layout
|
||||
* (ie. anything that needs to know the View's true size like snapshotting).
|
||||
*/
|
||||
class OnMapAndViewReadyListener(
|
||||
private val mapFragment: SupportMapFragment,
|
||||
private val toBeNotified: OnGlobalLayoutAndMapReadyListener
|
||||
) : OnGlobalLayoutListener,
|
||||
OnMapReadyCallback {
|
||||
|
||||
private val mapView: View? = mapFragment.view
|
||||
|
||||
private var isViewReady = false
|
||||
private var isMapReady = false
|
||||
private var map: GoogleMap? = null
|
||||
|
||||
/** A listener that needs to wait for both the GoogleMap and the View to be initialized. */
|
||||
interface OnGlobalLayoutAndMapReadyListener {
|
||||
fun onMapReady(googleMap: GoogleMap?)
|
||||
}
|
||||
|
||||
init {
|
||||
registerListeners()
|
||||
}
|
||||
|
||||
private fun registerListeners() {
|
||||
// View layout.
|
||||
if (mapView?.width != 0 && mapView?.height != 0) {
|
||||
// View has already completed layout.
|
||||
isViewReady = true
|
||||
} else {
|
||||
// Map has not undergone layout, register a View observer.
|
||||
mapView.viewTreeObserver.addOnGlobalLayoutListener(this)
|
||||
}
|
||||
|
||||
// GoogleMap. Note if the GoogleMap is already ready it will still fire the callback later.
|
||||
mapFragment.getMapAsync(this)
|
||||
}
|
||||
|
||||
override fun onMapReady(googleMap: GoogleMap?) {
|
||||
// NOTE: The GoogleMap API specifies the listener is removed just prior to invocation.
|
||||
map = googleMap ?: return
|
||||
isMapReady = true
|
||||
fireCallbackIfReady()
|
||||
}
|
||||
|
||||
// We use the new method when supported
|
||||
@SuppressLint("NewApi") // We check which build version we are using.
|
||||
override fun onGlobalLayout() {
|
||||
// Remove our listener.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
mapView?.viewTreeObserver?.removeGlobalOnLayoutListener(this)
|
||||
} else {
|
||||
mapView?.viewTreeObserver?.removeOnGlobalLayoutListener(this)
|
||||
}
|
||||
isViewReady = true
|
||||
fireCallbackIfReady()
|
||||
}
|
||||
|
||||
private fun fireCallbackIfReady() {
|
||||
if (isViewReady && isMapReady) {
|
||||
toBeNotified.onMapReady(map)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.kotlindemos
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.SeekBar
|
||||
import android.widget.Spinner
|
||||
import android.widget.CheckBox
|
||||
import android.widget.ArrayAdapter
|
||||
import com.google.android.gms.maps.CameraUpdateFactory
|
||||
import com.google.android.gms.maps.model.Dash
|
||||
import com.google.android.gms.maps.model.Dot
|
||||
import com.google.android.gms.maps.model.Gap
|
||||
import com.google.android.gms.maps.GoogleMap
|
||||
import com.google.android.gms.maps.model.JointType
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
import com.google.android.gms.maps.OnMapReadyCallback
|
||||
import com.google.android.gms.maps.model.PatternItem
|
||||
import com.google.android.gms.maps.model.Polygon
|
||||
import com.google.android.gms.maps.model.PolygonOptions
|
||||
import com.google.android.gms.maps.SupportMapFragment
|
||||
|
||||
import java.util.Arrays
|
||||
|
||||
/**
|
||||
* This shows how to draw polygons on a map.
|
||||
*/
|
||||
class PolygonDemoActivity :
|
||||
AppCompatActivity(),
|
||||
OnMapReadyCallback,
|
||||
SeekBar.OnSeekBarChangeListener,
|
||||
AdapterView.OnItemSelectedListener {
|
||||
|
||||
private val center = LatLng(-20.0, 130.0)
|
||||
private val MAX_WIDTH_PX = 100
|
||||
private val MAX_HUE_DEGREES = 360
|
||||
private val MAX_ALPHA = 255
|
||||
private val PATTERN_DASH_LENGTH_PX = 50
|
||||
private val PATTERN_GAP_LENGTH_PX = 10
|
||||
private val dot = Dot()
|
||||
private val dash = Dash(PATTERN_DASH_LENGTH_PX.toFloat())
|
||||
private val gap = Gap(PATTERN_GAP_LENGTH_PX.toFloat())
|
||||
private val patternDotted = Arrays.asList(dot, gap)
|
||||
private val patternDashed = Arrays.asList(dash, gap)
|
||||
private val patternMixed = Arrays.asList(dot, gap, dot, dash, gap)
|
||||
|
||||
private lateinit var mutablePolygon: Polygon
|
||||
private lateinit var fillHueBar: SeekBar
|
||||
private lateinit var fillAlphaBar: SeekBar
|
||||
private lateinit var strokeWidthBar: SeekBar
|
||||
private lateinit var strokeHueBar: SeekBar
|
||||
private lateinit var strokeAlphaBar: SeekBar
|
||||
private lateinit var strokeJointTypeSpinner: Spinner
|
||||
private lateinit var strokePatternSpinner: Spinner
|
||||
private lateinit var clickabilityCheckbox: CheckBox
|
||||
|
||||
// These are the options for polygon stroke joints and patterns. We use their
|
||||
// string resource IDs as identifiers.
|
||||
|
||||
private val jointTypeNameResourceIds = intArrayOf(R.string.joint_type_default, // Default
|
||||
R.string.joint_type_bevel, R.string.joint_type_round)
|
||||
|
||||
private val patternTypeNameResourceIds = intArrayOf(R.string.pattern_solid, // Default
|
||||
R.string.pattern_dashed, R.string.pattern_dotted, R.string.pattern_mixed)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.polygon_demo)
|
||||
|
||||
fillHueBar = findViewById<SeekBar>(R.id.fillHueSeekBar).apply {
|
||||
max = MAX_HUE_DEGREES
|
||||
progress = MAX_HUE_DEGREES / 2
|
||||
}
|
||||
|
||||
fillAlphaBar = findViewById<SeekBar>(R.id.fillAlphaSeekBar).apply {
|
||||
max = MAX_ALPHA
|
||||
progress = MAX_ALPHA / 2
|
||||
}
|
||||
|
||||
strokeWidthBar = findViewById<SeekBar>(R.id.strokeWidthSeekBar).apply {
|
||||
max = MAX_WIDTH_PX
|
||||
progress = MAX_WIDTH_PX / 3
|
||||
}
|
||||
|
||||
strokeHueBar = findViewById<SeekBar>(R.id.strokeHueSeekBar).apply {
|
||||
max = MAX_HUE_DEGREES
|
||||
progress = 0
|
||||
}
|
||||
|
||||
strokeAlphaBar = findViewById<SeekBar>(R.id.strokeAlphaSeekBar).apply {
|
||||
max = MAX_ALPHA
|
||||
progress = MAX_ALPHA
|
||||
}
|
||||
|
||||
strokeJointTypeSpinner = findViewById<Spinner>(R.id.strokeJointTypeSpinner).apply {
|
||||
adapter = ArrayAdapter(
|
||||
this@PolygonDemoActivity, android.R.layout.simple_spinner_item,
|
||||
getResourceStrings(jointTypeNameResourceIds))
|
||||
}
|
||||
|
||||
strokePatternSpinner = findViewById<Spinner>(R.id.strokePatternSpinner).apply {
|
||||
adapter = ArrayAdapter(
|
||||
this@PolygonDemoActivity, android.R.layout.simple_spinner_item,
|
||||
getResourceStrings(patternTypeNameResourceIds))
|
||||
}
|
||||
|
||||
clickabilityCheckbox = findViewById(R.id.toggleClickability)
|
||||
|
||||
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||
mapFragment.getMapAsync(this)
|
||||
}
|
||||
|
||||
private fun getResourceStrings(resourceIds: IntArray): List<String> {
|
||||
return resourceIds.map { getString(it) }
|
||||
}
|
||||
|
||||
override fun onMapReady(googleMap: GoogleMap?) {
|
||||
|
||||
// return early if the map was not initialised properly
|
||||
googleMap ?: return
|
||||
|
||||
val fillColorArgb = Color.HSVToColor(
|
||||
fillAlphaBar.progress, floatArrayOf(fillHueBar.progress.toFloat(), 1f, 1f))
|
||||
val strokeColorArgb = Color.HSVToColor(
|
||||
strokeAlphaBar.progress, floatArrayOf(strokeHueBar.progress.toFloat(), 1f, 1f))
|
||||
|
||||
with(googleMap) {
|
||||
// Override the default content description on the view, for accessibility mode.
|
||||
setContentDescription(getString(R.string.polygon_demo_description))
|
||||
// Move the googleMap so that it is centered on the mutable polygon.
|
||||
moveCamera(CameraUpdateFactory.newLatLngZoom(center, 4f))
|
||||
|
||||
// Create a rectangle with two rectangular holes.
|
||||
mutablePolygon = addPolygon(PolygonOptions().apply {
|
||||
addAll(createRectangle(center, 5.0, 5.0))
|
||||
addHole(createRectangle(LatLng(-22.0, 128.0), 1.0, 1.0))
|
||||
addHole(createRectangle(LatLng(-18.0, 133.0), 0.5, 1.5))
|
||||
fillColor(fillColorArgb)
|
||||
strokeColor(strokeColorArgb)
|
||||
strokeWidth(strokeWidthBar.progress.toFloat())
|
||||
clickable(clickabilityCheckbox.isChecked)
|
||||
})
|
||||
|
||||
// Add a listener for polygon clicks that changes the clicked polygon's stroke color.
|
||||
setOnPolygonClickListener { polygon ->
|
||||
// Flip the red, green and blue components of the polygon's stroke color.
|
||||
polygon.strokeColor = polygon.strokeColor xor 0x00ffffff
|
||||
}
|
||||
}
|
||||
|
||||
// set listeners on seekBars
|
||||
arrayOf(fillHueBar, fillAlphaBar, strokeWidthBar, strokeHueBar, strokeAlphaBar).map {
|
||||
it.setOnSeekBarChangeListener(this)
|
||||
}
|
||||
|
||||
// set listeners on spinners
|
||||
arrayOf(strokeJointTypeSpinner, strokePatternSpinner).map {
|
||||
it.onItemSelectedListener = this
|
||||
}
|
||||
|
||||
// set line pattern and joint type based on current spinner position
|
||||
with(mutablePolygon) {
|
||||
strokeJointType = getSelectedJointType(strokeJointTypeSpinner.selectedItemPosition)
|
||||
strokePattern = getSelectedPattern(strokePatternSpinner.selectedItemPosition)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a List of LatLngs that form a rectangle with the given dimensions.
|
||||
*/
|
||||
private fun createRectangle(
|
||||
center: LatLng,
|
||||
halfWidth: Double,
|
||||
halfHeight: Double
|
||||
): List<LatLng> {
|
||||
return Arrays.asList(
|
||||
LatLng(center.latitude - halfHeight, center.longitude - halfWidth),
|
||||
LatLng(center.latitude - halfHeight, center.longitude + halfWidth),
|
||||
LatLng(center.latitude + halfHeight, center.longitude + halfWidth),
|
||||
LatLng(center.latitude + halfHeight, center.longitude - halfWidth),
|
||||
LatLng(center.latitude - halfHeight, center.longitude - halfWidth))
|
||||
}
|
||||
|
||||
private fun getSelectedJointType(pos: Int): Int {
|
||||
return when (jointTypeNameResourceIds[pos]) {
|
||||
R.string.joint_type_bevel -> JointType.BEVEL
|
||||
R.string.joint_type_round -> JointType.ROUND
|
||||
R.string.joint_type_default -> JointType.DEFAULT
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectedPattern(pos: Int): List<PatternItem>? {
|
||||
return when (patternTypeNameResourceIds[pos]) {
|
||||
R.string.pattern_solid -> null
|
||||
R.string.pattern_dotted -> patternDotted
|
||||
R.string.pattern_dashed -> patternDashed
|
||||
R.string.pattern_mixed -> patternMixed
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the clickability of the polygon based on the state of the View that triggered this
|
||||
* call.
|
||||
* This callback is defined on the CheckBox in the layout for this Activity.
|
||||
*/
|
||||
fun toggleClickability(view: View) {
|
||||
if (view is CheckBox) {
|
||||
mutablePolygon.isClickable = view.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listener that is called when a seek bar is moved.
|
||||
* Can change polygon fill color/transparency, stroke color/transparency and stroke width.
|
||||
*/
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int,
|
||||
fromUser: Boolean) {
|
||||
|
||||
mutablePolygon.fillColor = when (seekBar) {
|
||||
fillHueBar -> Color.HSVToColor(Color.alpha(mutablePolygon.fillColor),
|
||||
floatArrayOf(progress.toFloat(), 1f, 1f))
|
||||
fillAlphaBar -> {
|
||||
val prevColor = mutablePolygon.fillColor
|
||||
Color.argb(progress, Color.red(prevColor), Color.green(prevColor),
|
||||
Color.blue(prevColor))
|
||||
}
|
||||
else -> mutablePolygon.fillColor
|
||||
}
|
||||
|
||||
mutablePolygon.strokeColor = when (seekBar) {
|
||||
strokeHueBar -> Color.HSVToColor(
|
||||
Color.alpha(mutablePolygon.strokeColor),
|
||||
floatArrayOf(progress.toFloat(), 1f, 1f))
|
||||
strokeAlphaBar -> {
|
||||
val prevColorArgb = mutablePolygon.strokeColor
|
||||
Color.argb(progress, Color.red(prevColorArgb),
|
||||
Color.green(prevColorArgb), Color.blue(prevColorArgb))
|
||||
}
|
||||
else -> mutablePolygon.strokeColor
|
||||
}
|
||||
|
||||
if (seekBar == strokeWidthBar) mutablePolygon.strokeWidth = progress.toFloat()
|
||||
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for when an item is selected using a spinner.
|
||||
* Can change line pattern and joint type.
|
||||
*/
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int,
|
||||
id: Long) {
|
||||
when (parent?.id) {
|
||||
R.id.strokeJointTypeSpinner ->
|
||||
mutablePolygon.strokeJointType = getSelectedJointType(pos)
|
||||
R.id.strokePatternSpinner ->
|
||||
mutablePolygon.strokePattern = getSelectedPattern(pos)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
// don't do anything here
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.kotlindemos
|
||||
|
||||
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.SupportMapFragment
|
||||
import com.google.android.gms.maps.model.BitmapDescriptorFactory
|
||||
import com.google.android.gms.maps.model.ButtCap
|
||||
import com.google.android.gms.maps.model.Cap
|
||||
import com.google.android.gms.maps.model.CustomCap
|
||||
import com.google.android.gms.maps.model.Dash
|
||||
import com.google.android.gms.maps.model.Dot
|
||||
import com.google.android.gms.maps.model.Gap
|
||||
import com.google.android.gms.maps.model.JointType
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
import com.google.android.gms.maps.model.PatternItem
|
||||
import com.google.android.gms.maps.model.Polyline
|
||||
import com.google.android.gms.maps.model.PolylineOptions
|
||||
import com.google.android.gms.maps.model.RoundCap
|
||||
import com.google.android.gms.maps.model.SquareCap
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.CheckBox
|
||||
import android.widget.SeekBar
|
||||
import android.widget.Spinner
|
||||
import java.util.Arrays
|
||||
|
||||
/**
|
||||
* This shows how to draw polylines on a map.
|
||||
*/
|
||||
class PolylineDemoActivity :
|
||||
AppCompatActivity(),
|
||||
OnMapReadyCallback,
|
||||
SeekBar.OnSeekBarChangeListener,
|
||||
AdapterView.OnItemSelectedListener {
|
||||
|
||||
private val CUSTOM_CAP_IMAGE_REF_WIDTH_PX = 50
|
||||
private val INITIAL_STROKE_WIDTH_PX = 5
|
||||
private val MAX_WIDTH_PX = 100
|
||||
private val MAX_HUE_DEGREES = 360
|
||||
private val MAX_ALPHA = 255
|
||||
private val PATTERN_DASH_LENGTH_PX = 50
|
||||
private val PATTERN_GAP_LENGTH_PX = 20
|
||||
|
||||
// City locations for mutable polyline.
|
||||
private val adelaideLatLng = LatLng(-34.92873, 138.59995)
|
||||
private val darwinLatLng = LatLng(-12.4258647, 130.7932231)
|
||||
private val melbourneLatLng = LatLng(-37.81319, 144.96298)
|
||||
private val perthLatLng = LatLng(-31.95285, 115.85734)
|
||||
|
||||
// Airport locations for geodesic polyline.
|
||||
private val aklLatLng = LatLng(-37.006254, 174.783018)
|
||||
private val jfkLatLng = LatLng(40.641051, -73.777485)
|
||||
private val laxLatLng = LatLng(33.936524, -118.377686)
|
||||
private val lhrLatLng = LatLng(51.471547, -0.460052)
|
||||
|
||||
private val dot = Dot()
|
||||
private val dash = Dash(PATTERN_DASH_LENGTH_PX.toFloat())
|
||||
private val gap = Gap(PATTERN_GAP_LENGTH_PX.toFloat())
|
||||
private val patternDotted = Arrays.asList(dot, gap)
|
||||
private val patternDashed = Arrays.asList(dash, gap)
|
||||
private val patternMixed = Arrays.asList(dot, gap, dot, dash, gap)
|
||||
|
||||
private lateinit var mutablePolyline: Polyline
|
||||
private lateinit var hueBar: SeekBar
|
||||
private lateinit var alphaBar: SeekBar
|
||||
private lateinit var widthBar: SeekBar
|
||||
private lateinit var startCapSpinner: Spinner
|
||||
private lateinit var endCapSpinner: Spinner
|
||||
private lateinit var jointTypeSpinner: Spinner
|
||||
private lateinit var patternSpinner: Spinner
|
||||
private lateinit var clickabilityCheckbox: CheckBox
|
||||
|
||||
// These are the options for polyline caps, joints and patterns. We use their
|
||||
// string resource IDs as identifiers.
|
||||
private val capTypeNameResourceIds = intArrayOf(R.string.cap_butt,
|
||||
R.string.cap_round, R.string.cap_square, R.string.cap_image)
|
||||
|
||||
private val jointTypeNameResourceIds = intArrayOf(R.string.joint_type_default,
|
||||
R.string.joint_type_bevel, R.string.joint_type_round)
|
||||
|
||||
private val patternTypeNameResourceIds = intArrayOf(R.string.pattern_solid,
|
||||
R.string.pattern_dashed, R.string.pattern_dotted, R.string.pattern_mixed)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.polyline_demo)
|
||||
|
||||
hueBar = findViewById<SeekBar>(R.id.hueSeekBar).apply {
|
||||
max = MAX_HUE_DEGREES
|
||||
progress = 0
|
||||
}
|
||||
|
||||
alphaBar = findViewById<SeekBar>(R.id.alphaSeekBar).apply {
|
||||
max = MAX_ALPHA
|
||||
progress = MAX_ALPHA
|
||||
}
|
||||
|
||||
widthBar = findViewById<SeekBar>(R.id.widthSeekBar).apply {
|
||||
max = MAX_WIDTH_PX
|
||||
progress = MAX_WIDTH_PX / 2
|
||||
}
|
||||
|
||||
startCapSpinner = findViewById<Spinner>(R.id.startCapSpinner).apply {
|
||||
adapter = ArrayAdapter(this@PolylineDemoActivity,
|
||||
android.R.layout.simple_spinner_item,
|
||||
getResourceStrings(capTypeNameResourceIds))
|
||||
}
|
||||
|
||||
endCapSpinner = findViewById<Spinner>(R.id.endCapSpinner).apply {
|
||||
adapter = ArrayAdapter(this@PolylineDemoActivity,
|
||||
android.R.layout.simple_spinner_item,
|
||||
getResourceStrings(capTypeNameResourceIds))
|
||||
}
|
||||
|
||||
jointTypeSpinner = findViewById<Spinner>(R.id.jointTypeSpinner).apply {
|
||||
adapter = ArrayAdapter(this@PolylineDemoActivity,
|
||||
android.R.layout.simple_spinner_item,
|
||||
getResourceStrings(jointTypeNameResourceIds))
|
||||
}
|
||||
|
||||
patternSpinner = findViewById<Spinner>(R.id.patternSpinner).apply {
|
||||
adapter = ArrayAdapter(
|
||||
this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
|
||||
getResourceStrings(patternTypeNameResourceIds))
|
||||
}
|
||||
|
||||
clickabilityCheckbox = findViewById<CheckBox>(R.id.toggleClickability)
|
||||
|
||||
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||
mapFragment.getMapAsync(this)
|
||||
}
|
||||
|
||||
private fun getResourceStrings(resourceIds: IntArray): List<String> {
|
||||
return resourceIds.map { getString(it) }
|
||||
}
|
||||
|
||||
override fun onMapReady(googleMap: GoogleMap?) {
|
||||
|
||||
// exit early if the map was not initialised properly
|
||||
googleMap ?: return
|
||||
|
||||
with(googleMap) {
|
||||
// Override the default content description on the view, for accessibility mode.
|
||||
setContentDescription(getString(R.string.polyline_demo_description))
|
||||
|
||||
// A geodesic polyline that goes around the world.
|
||||
addPolyline(PolylineOptions().apply {
|
||||
add(lhrLatLng, aklLatLng, laxLatLng, jfkLatLng, lhrLatLng)
|
||||
width(INITIAL_STROKE_WIDTH_PX.toFloat())
|
||||
color(Color.BLUE)
|
||||
geodesic(true)
|
||||
clickable(clickabilityCheckbox.isChecked)
|
||||
})
|
||||
|
||||
// Move the googleMap so that it is centered on the mutable polyline.
|
||||
moveCamera(CameraUpdateFactory.newLatLngZoom(melbourneLatLng, 3f))
|
||||
|
||||
// Add a listener for polyline clicks that changes the clicked polyline's color.
|
||||
setOnPolylineClickListener { polyline ->
|
||||
// Flip the values of the red, green and blue components of the polyline's color.
|
||||
polyline.color = polyline.color xor 0x00ffffff
|
||||
}
|
||||
}
|
||||
|
||||
// A simple polyline across Australia. This polyline will be mutable.
|
||||
mutablePolyline = googleMap.addPolyline(PolylineOptions().apply{
|
||||
color(Color.HSVToColor(
|
||||
alphaBar.progress, floatArrayOf(hueBar.progress.toFloat(), 1f, 1f)))
|
||||
width(widthBar.progress.toFloat())
|
||||
clickable(clickabilityCheckbox.isChecked)
|
||||
add(melbourneLatLng, adelaideLatLng, perthLatLng, darwinLatLng)
|
||||
})
|
||||
|
||||
arrayOf(hueBar, alphaBar, widthBar).map {
|
||||
it.setOnSeekBarChangeListener(this)
|
||||
}
|
||||
|
||||
arrayOf(startCapSpinner, endCapSpinner, jointTypeSpinner, patternSpinner).map {
|
||||
it.onItemSelectedListener = this
|
||||
}
|
||||
|
||||
with(mutablePolyline) {
|
||||
startCap = getSelectedCap(startCapSpinner.selectedItemPosition) ?: ButtCap()
|
||||
endCap = getSelectedCap(endCapSpinner.selectedItemPosition) ?: ButtCap()
|
||||
jointType = getSelectedJointType(jointTypeSpinner.selectedItemPosition)
|
||||
pattern = getSelectedPattern(patternSpinner.selectedItemPosition)
|
||||
}
|
||||
|
||||
clickabilityCheckbox.setOnClickListener {
|
||||
view -> mutablePolyline.isClickable = (view as CheckBox).isChecked
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectedCap(pos: Int): Cap? {
|
||||
return when (capTypeNameResourceIds[pos]) {
|
||||
R.string.cap_butt -> ButtCap()
|
||||
R.string.cap_square -> SquareCap()
|
||||
R.string.cap_round -> RoundCap()
|
||||
R.string.cap_image -> CustomCap(
|
||||
BitmapDescriptorFactory.fromResource(R.drawable.chevron),
|
||||
CUSTOM_CAP_IMAGE_REF_WIDTH_PX.toFloat())
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectedJointType(pos: Int): Int {
|
||||
return when (jointTypeNameResourceIds[pos]) {
|
||||
R.string.joint_type_bevel -> JointType.BEVEL
|
||||
R.string.joint_type_round -> JointType.ROUND
|
||||
R.string.joint_type_default -> JointType.DEFAULT
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectedPattern(pos: Int): List<PatternItem>? {
|
||||
return when (patternTypeNameResourceIds[pos]) {
|
||||
R.string.pattern_solid -> null
|
||||
R.string.pattern_dotted -> patternDotted
|
||||
R.string.pattern_dashed -> patternDashed
|
||||
R.string.pattern_mixed -> patternMixed
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for changes in a seekbar's position.
|
||||
* Can change polyline color, width and transparency.
|
||||
*/
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
|
||||
when(seekBar) {
|
||||
hueBar -> mutablePolyline.color = Color.HSVToColor(
|
||||
Color.alpha(mutablePolyline.color), floatArrayOf(progress.toFloat(), 1f, 1f))
|
||||
alphaBar -> {
|
||||
val prevHSV = FloatArray(3)
|
||||
Color.colorToHSV(mutablePolyline.color, prevHSV)
|
||||
mutablePolyline.color = Color.HSVToColor(progress, prevHSV)
|
||||
}
|
||||
widthBar -> mutablePolyline.width = progress.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
// Don't do anything here.
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
// Don't do anything here.
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for changes in a spinner's position.
|
||||
* Can change the polyline's start and end caps, pattern and joint type.
|
||||
*/
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
|
||||
when (parent.id) {
|
||||
R.id.startCapSpinner -> mutablePolyline.startCap = getSelectedCap(pos) ?: ButtCap()
|
||||
R.id.endCapSpinner -> mutablePolyline.endCap = getSelectedCap(pos) ?: ButtCap()
|
||||
R.id.jointTypeSpinner -> mutablePolyline.jointType = getSelectedJointType(pos)
|
||||
R.id.patternSpinner -> mutablePolyline.pattern = getSelectedPattern(pos)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// Don't do anything here.
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.kotlindemos
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.widget.TextView
|
||||
import com.google.android.gms.maps.CameraUpdateFactory
|
||||
import com.google.android.gms.maps.GoogleMap
|
||||
import com.google.android.gms.maps.SupportMapFragment
|
||||
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.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.android.gms.maps.model.Polygon
|
||||
import com.google.android.gms.maps.model.PolygonOptions
|
||||
import com.google.android.gms.maps.model.Polyline
|
||||
import com.google.android.gms.maps.model.PolylineOptions
|
||||
|
||||
/**
|
||||
* This shows how to use setTag/getTag on API objects.
|
||||
*/
|
||||
class TagsDemoActivity : AppCompatActivity(),
|
||||
GoogleMap.OnCircleClickListener,
|
||||
GoogleMap.OnGroundOverlayClickListener,
|
||||
GoogleMap.OnMarkerClickListener,
|
||||
OnMapAndViewReadyListener.OnGlobalLayoutAndMapReadyListener,
|
||||
GoogleMap.OnPolygonClickListener,
|
||||
GoogleMap.OnPolylineClickListener {
|
||||
|
||||
private lateinit var map: GoogleMap
|
||||
|
||||
private lateinit var tagText: TextView
|
||||
|
||||
private val places = mapOf(
|
||||
"BRISBANE" to LatLng(-27.47093, 153.0235),
|
||||
"MELBOURNE" to LatLng(-37.81319, 144.96298),
|
||||
"DARWIN" to LatLng(-12.4634, 130.8456),
|
||||
"SYDNEY" to LatLng(-33.87365, 151.20689),
|
||||
"ADELAIDE" to LatLng(-34.92873, 138.59995),
|
||||
"PERTH" to LatLng(-31.952854, 115.857342),
|
||||
"ALICE_SPRINGS" to LatLng(-24.6980, 133.8807),
|
||||
"HOBART" to LatLng(-42.8823388, 147.311042)
|
||||
)
|
||||
|
||||
/**
|
||||
* Class to store a tag to attach to a map object to keep track of
|
||||
* how many times it has been clicked
|
||||
*/
|
||||
private class CustomTag(private val description: String) {
|
||||
private var clickCount: Int = 0
|
||||
|
||||
fun incrementClickCount() {
|
||||
clickCount++
|
||||
}
|
||||
|
||||
override fun toString() = "The $description has been clicked $clickCount times."
|
||||
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.tags_demo)
|
||||
|
||||
tagText = findViewById(R.id.tag_text)
|
||||
|
||||
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||
OnMapAndViewReadyListener(mapFragment, this)
|
||||
}
|
||||
|
||||
override fun onMapReady(googleMap: GoogleMap?) {
|
||||
|
||||
// return early if the map was not initialised properly
|
||||
map = googleMap ?: return
|
||||
|
||||
// Add a circle, a ground overlay, a marker, a polygon and a polyline to the googleMap.
|
||||
addObjectsToMap()
|
||||
|
||||
with(map.uiSettings) {
|
||||
// Turn off the map toolbar.
|
||||
isMapToolbarEnabled = false
|
||||
|
||||
// Disable interaction with the map - other than clicking.
|
||||
isZoomControlsEnabled = false
|
||||
isScrollGesturesEnabled = false
|
||||
isZoomGesturesEnabled = false
|
||||
isTiltGesturesEnabled = false
|
||||
isRotateGesturesEnabled = false
|
||||
}
|
||||
|
||||
with(map) {
|
||||
// Set listeners for click events. See the bottom of this class for their behavior.
|
||||
setOnCircleClickListener(this@TagsDemoActivity)
|
||||
setOnGroundOverlayClickListener(this@TagsDemoActivity)
|
||||
setOnMarkerClickListener(this@TagsDemoActivity)
|
||||
setOnPolygonClickListener(this@TagsDemoActivity)
|
||||
setOnPolylineClickListener(this@TagsDemoActivity)
|
||||
|
||||
// Override the default content description on the view, for accessibility mode.
|
||||
// Ideally this string would be localised.
|
||||
setContentDescription(getString(R.string.tags_demo_map_description))
|
||||
|
||||
// include all places we have markers for in the initial view of the map
|
||||
val boundsBuilder = LatLngBounds.Builder()
|
||||
places.keys.map { boundsBuilder.include(places.getValue(it)) }
|
||||
// Move the camera to view all listed locations
|
||||
moveCamera(CameraUpdateFactory.newLatLngBounds(boundsBuilder.build(), 100))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun addObjectsToMap() {
|
||||
with(map) {
|
||||
// A circle centered on Adelaide.
|
||||
addCircle(CircleOptions().apply {
|
||||
center(places.getValue("ADELAIDE"))
|
||||
radius(500000.0)
|
||||
fillColor(Color.argb(150, 66, 173, 244))
|
||||
strokeColor(Color.rgb(66, 173, 244))
|
||||
clickable(true)
|
||||
}).run {
|
||||
// add a tag to the circle to count clicks
|
||||
//tag = String("Adelaide circle")
|
||||
tag = "hello"
|
||||
}
|
||||
|
||||
// A ground overlay at Sydney.
|
||||
addGroundOverlay(GroundOverlayOptions().apply {
|
||||
image(BitmapDescriptorFactory.fromResource(R.drawable.harbour_bridge))
|
||||
position(places.getValue("SYDNEY"), 700000f)
|
||||
clickable(true)
|
||||
}).run {
|
||||
// add a tag to the overlay to count clicks
|
||||
tag = CustomTag("Sydney ground overlay")
|
||||
}
|
||||
|
||||
// A marker at Hobart.
|
||||
addMarker(MarkerOptions().apply {
|
||||
position(places.getValue("HOBART"))
|
||||
}).run {
|
||||
// add a tag to the marker to count clicks
|
||||
tag = CustomTag("Hobart marker")
|
||||
}
|
||||
|
||||
// A polygon centered at Darwin.
|
||||
addPolygon(PolygonOptions().apply{
|
||||
add(LatLng(places.getValue("DARWIN").latitude + 3,
|
||||
places.getValue("DARWIN").longitude - 3),
|
||||
LatLng(places.getValue("DARWIN").latitude + 3,
|
||||
places.getValue("DARWIN").longitude + 3),
|
||||
LatLng(places.getValue("DARWIN").latitude - 3,
|
||||
places.getValue("DARWIN").longitude + 3),
|
||||
LatLng(places.getValue("DARWIN").latitude - 3,
|
||||
places.getValue("DARWIN").longitude - 3))
|
||||
fillColor(Color.argb(150, 34, 173, 24))
|
||||
strokeColor(Color.rgb(34, 173, 24))
|
||||
clickable(true)
|
||||
}).run {
|
||||
// add a tag to the marker to count clicks
|
||||
tag = CustomTag("Darwin polygon")
|
||||
}
|
||||
|
||||
// A polyline from Perth to Brisbane.
|
||||
addPolyline(PolylineOptions().apply{
|
||||
add(places.getValue("PERTH"), places.getValue("BRISBANE"))
|
||||
color(Color.rgb(103, 24, 173))
|
||||
width(30f)
|
||||
clickable(true)
|
||||
}).run {
|
||||
// add a tag to the polyline to count clicks
|
||||
tag = CustomTag("Perth to Brisbane polyline")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Click event listeners.
|
||||
private fun onClick(tag: CustomTag) {
|
||||
tag.incrementClickCount()
|
||||
tagText.text = tag.toString()
|
||||
}
|
||||
|
||||
override fun onCircleClick(circle: Circle) {
|
||||
onClick(circle.tag as? CustomTag ?: return)
|
||||
}
|
||||
|
||||
override fun onGroundOverlayClick(groundOverlay: GroundOverlay) {
|
||||
onClick(groundOverlay.tag as? CustomTag ?: return)
|
||||
}
|
||||
|
||||
override fun onMarkerClick(marker: Marker): Boolean {
|
||||
onClick(marker.tag as? CustomTag ?: return false)
|
||||
// We return true to indicate that we have consumed the event and that we do not wish
|
||||
// for the default behavior to occur (which is for the camera to move such that the
|
||||
// marker is centered and for the marker's info window to open, if it has one).
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPolygonClick(polygon: Polygon) {
|
||||
onClick(polygon.tag as? CustomTag ?: return)
|
||||
}
|
||||
|
||||
override fun onPolylineClick(polyline: Polyline) {
|
||||
onClick(polyline.tag as? CustomTag ?: return)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.kotlindemos
|
||||
|
||||
import com.google.android.gms.maps.CameraUpdateFactory
|
||||
import com.google.android.gms.maps.GoogleMap
|
||||
import com.google.android.gms.maps.SupportMapFragment
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
import com.google.android.gms.maps.model.LatLngBounds
|
||||
import com.google.android.gms.maps.model.MarkerOptions
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.SystemClock
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
|
||||
/**
|
||||
* This shows how to use setPadding to allow overlays that obscure part of the map without
|
||||
* obscuring the map UI or copyright notices.
|
||||
*/
|
||||
class VisibleRegionDemoActivity :
|
||||
AppCompatActivity(),
|
||||
OnMapAndViewReadyListener.OnGlobalLayoutAndMapReadyListener {
|
||||
|
||||
private val operaHouseLatLng = LatLng(-33.85704, 151.21522)
|
||||
private val sfoLatLng = LatLng(37.614631, -122.385153)
|
||||
private val australiaBounds = LatLngBounds(LatLng(-44.0, 113.0),
|
||||
LatLng(-10.0, 154.0))
|
||||
|
||||
private lateinit var map: GoogleMap
|
||||
|
||||
private lateinit var messageView: TextView
|
||||
private lateinit var normalButton: Button
|
||||
private lateinit var morePaddedButton: Button
|
||||
private lateinit var operaHouseButton: Button
|
||||
private lateinit var sfoButton: Button
|
||||
private lateinit var australiaButton: Button
|
||||
|
||||
/** Keep track of current values for padding, so we can animate from them. */
|
||||
private var currentLeft = 150
|
||||
private var currentTop = 0
|
||||
private var currentRight = 0
|
||||
private var currentBottom = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.visible_region_demo)
|
||||
messageView = findViewById(R.id.message_text)
|
||||
|
||||
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||
OnMapAndViewReadyListener(mapFragment, this)
|
||||
|
||||
normalButton = findViewById(R.id.vr_normal_button)
|
||||
morePaddedButton = findViewById(R.id.vr_more_padded_button)
|
||||
operaHouseButton = findViewById(R.id.vr_soh_button)
|
||||
sfoButton = findViewById(R.id.vr_sfo_button)
|
||||
australiaButton = findViewById(R.id.vr_aus_button)
|
||||
|
||||
}
|
||||
|
||||
override fun onMapReady(googleMap: GoogleMap?) {
|
||||
|
||||
// exit early if the map was not initialised properly
|
||||
map = googleMap ?: return
|
||||
|
||||
map.apply{
|
||||
// Set padding for the current camera view
|
||||
setPadding(currentLeft, currentTop, currentRight, currentBottom)
|
||||
// Move to a place with indoor (sfoLatLng airport).
|
||||
moveCamera(CameraUpdateFactory.newLatLngZoom(sfoLatLng, 18f))
|
||||
// Add a marker to the Opera House.
|
||||
addMarker(MarkerOptions().position(operaHouseLatLng).title("Sydney Opera House"))
|
||||
// Add a camera idle listener that displays the current camera position in a TextView
|
||||
setOnCameraIdleListener {
|
||||
messageView.text = getString(R.string.camera_change_message,
|
||||
this@VisibleRegionDemoActivity.map.cameraPosition)
|
||||
}
|
||||
}
|
||||
|
||||
normalButton.setOnClickListener {
|
||||
animatePadding(150, 0, 0, 0)
|
||||
}
|
||||
|
||||
// listener for when the 'more' padding button is clicked
|
||||
// increases the amount of padding along the right and bottom of the map
|
||||
morePaddedButton.setOnClickListener {
|
||||
// get the view that contains the map
|
||||
val mapView: View? = supportFragmentManager.findFragmentById(R.id.map).view
|
||||
animatePadding(150, 0, (mapView?.width ?: 0) / 3,
|
||||
(mapView?.height ?: 0)/ 4)
|
||||
}
|
||||
|
||||
operaHouseButton.setOnClickListener {
|
||||
map.moveCamera(CameraUpdateFactory.newLatLngZoom(operaHouseLatLng, 16f))
|
||||
}
|
||||
|
||||
sfoButton.setOnClickListener {
|
||||
map.moveCamera(CameraUpdateFactory.newLatLngZoom(sfoLatLng, 18f))
|
||||
}
|
||||
|
||||
australiaButton.setOnClickListener {
|
||||
map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 0))
|
||||
}
|
||||
}
|
||||
|
||||
// this function smoothly changes the amount of padding over a period of time
|
||||
private fun animatePadding(toLeft: Int, toTop: Int, toRight: Int, toBottom: Int) {
|
||||
|
||||
val handler = Handler()
|
||||
val start = SystemClock.uptimeMillis()
|
||||
val duration: Long = 1000
|
||||
|
||||
val interpolator = OvershootInterpolator()
|
||||
|
||||
val startLeft: Int = currentLeft
|
||||
val startTop: Int = currentTop
|
||||
val startRight: Int = currentRight
|
||||
val startBottom: Int = currentBottom
|
||||
|
||||
currentLeft = toLeft
|
||||
currentTop = toTop
|
||||
currentRight = toRight
|
||||
currentBottom = toBottom
|
||||
|
||||
handler.post(object : Runnable {
|
||||
override fun run() {
|
||||
val elapsed = SystemClock.uptimeMillis() - start
|
||||
val t: Float = interpolator.getInterpolation(elapsed.toFloat() / duration)
|
||||
|
||||
val leftDiff = ((toLeft - startLeft) * t).toInt()
|
||||
val topDiff = ((toTop - startTop) * t).toInt()
|
||||
val rightDiff = ((toRight - startRight) * t).toInt()
|
||||
val bottomDiff = ((toBottom - startBottom) * t).toInt()
|
||||
|
||||
val left = startLeft + leftDiff
|
||||
val top = startTop + topDiff
|
||||
val right = startRight + rightDiff
|
||||
val bottom = startBottom + bottomDiff
|
||||
|
||||
map.setPadding(left, top, right, bottom)
|
||||
|
||||
// Post again 16ms later.
|
||||
if (elapsed < duration) { handler.postDelayed(this, 16) }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
BIN
ApiDemos/kotlin/app/src/main/res/drawable/arrow.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_nsw.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_nt.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_qld.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_sa.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_victoria.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_wa.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/chevron.png
Normal file
|
After Width: | Height: | Size: 675 B |
|
After Width: | Height: | Size: 1.2 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/harbour_bridge.jpg
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
8
ApiDemos/kotlin/app/src/main/res/drawable/ic_android.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path android:fillColor="#FF000000" android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
|
||||
</vector>
|
||||
195
ApiDemos/kotlin/app/src/main/res/layout/camera_demo.xml
Normal file
@ -0,0 +1,195 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/stop_animation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onStopAnimation"
|
||||
android:text="@string/stop_animation" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/animate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:onClick="onToggleAnimate"
|
||||
android:textOn="@string/animate"
|
||||
android:textOff="@string/animate" />
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_weight="1">
|
||||
|
||||
<Button
|
||||
android:id="@+id/scroll_left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:onClick="onScrollLeft"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="@string/left_arrow"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/scroll_up"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:onClick="onScrollUp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@id/scroll_left"
|
||||
android:text="@string/up_arrow"
|
||||
android:layout_toEndOf="@id/scroll_left" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/scroll_down"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:onClick="onScrollDown"
|
||||
android:layout_below="@id/scroll_up"
|
||||
android:layout_toRightOf="@id/scroll_left"
|
||||
android:text="@string/down_arrow"
|
||||
android:layout_toEndOf="@id/scroll_left" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/scroll_right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:onClick="onScrollRight"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@id/scroll_down"
|
||||
android:text="@string/right_arrow"
|
||||
android:layout_toEndOf="@id/scroll_down" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/zoom_in"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:onClick="onZoomIn"
|
||||
android:text="@string/zoom_in" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/zoom_out"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:onClick="onZoomOut"
|
||||
android:text="@string/zoom_out" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<Button
|
||||
android:id="@+id/tilt_more"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:text="@string/tilt_more"
|
||||
android:onClick="onTiltMore" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/tilt_less"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
android:text="@string/tilt_less"
|
||||
android:onClick="onTiltLess" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/duration_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onToggleCustomDuration"
|
||||
android:text="@string/duration" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/duration_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:max="5000" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/sydney"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onGoToSydney"
|
||||
android:layout_weight="0.5"
|
||||
android:text="@string/go_to_sydney"
|
||||
style="?android:attr/borderlessButtonStyle"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/bondi"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onGoToBondi"
|
||||
android:layout_weight="0.5"
|
||||
android:text="@string/go_to_bondi"
|
||||
style="?android:attr/borderlessButtonStyle"/>
|
||||
</LinearLayout>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
class="com.google.android.gms.maps.SupportMapFragment" />
|
||||
</LinearLayout>
|
||||
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/badge"
|
||||
android:contentDescription="@string/state_badge_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:adjustViewBounds="true"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="#ff000000"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/snippet"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="#ff7f7f7f"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/custom_info_bubble">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/badge"
|
||||
android:contentDescription="@string/state_badge_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:adjustViewBounds="true"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="#ff000000"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/snippet"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="#ff7f7f7f"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
70
ApiDemos/kotlin/app/src/main/res/layout/layers_demo.xml
Executable file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
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.
|
||||
-->
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
class="com.google.android.gms.maps.SupportMapFragment" />
|
||||
|
||||
<!-- A set of test checkboxes. -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignTop="@id/map"
|
||||
android:padding="6dp"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/layers_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/traffic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/traffic" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/my_location"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/my_location" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/buildings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/buildings" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/indoor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/indoor" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
120
ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/top_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="2"
|
||||
android:text="@string/drag_melbourne" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/flat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/flat"
|
||||
android:textSize="14sp"
|
||||
android:onClick="onToggleFlat" />
|
||||
|
||||
<TextView
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/rotation" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/rotationSeekBar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
class="com.google.android.gms.maps.SupportMapFragment" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
android:padding="5dp">
|
||||
|
||||
<LinearLayout
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
android:onClick="onClearMap"
|
||||
android:text="@string/clear_map"
|
||||
style="?android:attr/borderlessButtonStyle"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
android:onClick="onResetMap"
|
||||
android:text="@string/reset_map"
|
||||
style="?android:attr/borderlessButtonStyle"/>
|
||||
</LinearLayout>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/custom_info_window_options"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/default_info_window"
|
||||
android:checked="true"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:text="@string/default_info_window" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/custom_info_contents"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:text="@string/custom_info_contents" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/custom_info_window"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:text="@string/custom_info_window" />
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
115
ApiDemos/kotlin/app/src/main/res/layout/polygon_demo.xml
Normal file
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/properties_australia_polygon" />
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="1">
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<TextView android:text="@string/fill_hue" />
|
||||
<SeekBar android:id="@+id/fillHueSeekBar" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<TextView android:text="@string/fill_alpha" />
|
||||
<SeekBar android:id="@+id/fillAlphaSeekBar" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<TextView android:text="@string/stroke_width" />
|
||||
<SeekBar android:id="@+id/strokeWidthSeekBar" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<TextView android:text="@string/stroke_hue" />
|
||||
<SeekBar android:id="@+id/strokeHueSeekBar" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<TextView android:text="@string/stroke_alpha" />
|
||||
<SeekBar android:id="@+id/strokeAlphaSeekBar" />
|
||||
</TableRow>
|
||||
|
||||
</TableLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="@string/stroke_joint_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Spinner
|
||||
android:id="@+id/strokeJointTypeSpinner"
|
||||
android:spinnerMode="dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/stroke_pattern"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Spinner
|
||||
android:id="@+id/strokePatternSpinner"
|
||||
android:spinnerMode="dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/toggleClickability"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:onClick="toggleClickability"
|
||||
android:text="@string/clickable" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/map"
|
||||
class="com.google.android.gms.maps.SupportMapFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
117
ApiDemos/kotlin/app/src/main/res/layout/polyline_demo.xml
Normal file
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="1">
|
||||
|
||||
<TableRow
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<TextView android:text="@string/hue" />
|
||||
<SeekBar android:id="@+id/hueSeekBar" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<TextView android:text="@string/alpha" />
|
||||
<SeekBar android:id="@+id/alphaSeekBar" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
<TextView android:text="@string/width" />
|
||||
<SeekBar android:id="@+id/widthSeekBar" />
|
||||
</TableRow>
|
||||
|
||||
</TableLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="@string/start_cap"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
<Spinner
|
||||
android:id="@+id/startCapSpinner"
|
||||
android:spinnerMode="dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/end_cap"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Spinner
|
||||
android:id="@+id/endCapSpinner"
|
||||
android:spinnerMode="dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="@string/joint_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Spinner
|
||||
android:id="@+id/jointTypeSpinner"
|
||||
android:spinnerMode="dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/pattern"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Spinner
|
||||
android:id="@+id/patternSpinner"
|
||||
android:spinnerMode="dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/toggleClickability"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/clickable" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/map"
|
||||
class="com.google.android.gms.maps.SupportMapFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
32
ApiDemos/kotlin/app/src/main/res/layout/tags_demo.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tag_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<fragment
|
||||
android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
class="com.google.android.gms.maps.SupportMapFragment"/>
|
||||
|
||||
</LinearLayout>
|
||||
85
ApiDemos/kotlin/app/src/main/res/layout/visible_region_demo.xml
Executable file
@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
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.
|
||||
--><!-- This can go anywhere in your layout (see other demos for some examples). -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
class="com.google.android.gms.maps.SupportMapFragment" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_gravity="bottom|right"
|
||||
android:background="#A000"
|
||||
android:orientation="vertical"
|
||||
android:padding="5dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="150px">
|
||||
|
||||
<Button
|
||||
android:id="@+id/vr_normal_button"
|
||||
android:padding="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="setNoPadding"
|
||||
android:text="@string/vr_normal_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/vr_more_padded_button"
|
||||
android:padding="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="setMorePadding"
|
||||
android:text="@string/vr_more_padded_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/vr_soh_button"
|
||||
android:padding="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="moveToOperaHouse"
|
||||
android:text="@string/vr_opera_house_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/vr_sfo_button"
|
||||
android:padding="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="moveToSFO"
|
||||
android:text="@string/vr_sfo_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/vr_aus_button"
|
||||
android:padding="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="moveToAUS"
|
||||
android:text="@string/vr_australia_button" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
17
ApiDemos/kotlin/app/src/main/res/values/google_maps_api.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<resources>
|
||||
<!--
|
||||
TODO: Before you run your application, you need a Google Maps API key.
|
||||
|
||||
See this page for more information:
|
||||
https://developers.google.com/maps/documentation/android/start#get_an_android_certificate_and_the_google_maps_api_key
|
||||
|
||||
Once you have your key (it starts with "AIza"), replace the "google_maps_key"
|
||||
string in this file.
|
||||
|
||||
Note: This resource is used for the debug build target. Update this file if you just want to run
|
||||
the demo app.
|
||||
-->
|
||||
<string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">
|
||||
"ADD_YOUR_KEY_HERE"
|
||||
</string>
|
||||
</resources>
|
||||
@ -23,20 +23,80 @@
|
||||
<string name="basic_demo_label">Basic Map</string>
|
||||
<string name="basic_demo_details">Launches a map with marker pointing at Sydney.</string>
|
||||
|
||||
<!-- Camera Demo -->
|
||||
<string name="animate">Animate</string>
|
||||
<string name="camera_demo_description">Demonstrates Camera Functions</string>
|
||||
<string name="camera_demo_label">Camera</string>
|
||||
<string name="down_arrow">\u2193</string>
|
||||
<string name="duration">Custom Duration</string>
|
||||
<string name="go_to_bondi">Go to Bondi</string>
|
||||
<string name="go_to_sydney">Go to Sydney</string>
|
||||
<string name="left_arrow">\u2190</string>
|
||||
<string name="right_arrow">\u2192</string>
|
||||
<string name="stop_animation">\u25A0</string>
|
||||
<string name="tilt_less">\u2193</string>
|
||||
<string name="tilt_more">\u2191</string>
|
||||
<string name="up_arrow">\u2191</string>
|
||||
<string name="zoom_in">+</string>
|
||||
<string name="zoom_out">-</string>
|
||||
|
||||
<!-- Circle Demo -->
|
||||
<string name="circle_demo_label">Circle Demo</string>
|
||||
<string name="circle_demo_details">Demonstrate how to add circles to a map</string>
|
||||
<string name="properties_circle">Properties for Circle(s)</string>
|
||||
|
||||
<!-- Close Info Window On Retap Demo -->
|
||||
<string name="close_info_window_demo_label">Close Info Window Demo</string>
|
||||
<string name="close_info_window_demo_details">Demonstrates how to close the info window when the currently selected marker is retapped.</string>
|
||||
|
||||
<!-- Layers Demo -->
|
||||
<string name="buildings">Buildings</string>
|
||||
<string name="hybrid">Hybrid</string>
|
||||
<string name="indoor">Indoor</string>
|
||||
<string-array name="layers_array">
|
||||
<item>@string/normal</item>
|
||||
<item>@string/hybrid</item>
|
||||
<item>@string/satellite</item>
|
||||
<item>@string/terrain</item>
|
||||
<item>@string/none_map</item>
|
||||
</string-array>
|
||||
<string name="layers_demo_label">Layers</string>
|
||||
<string name="layers_demo_description">Demonstrates the different map layers.</string>
|
||||
<string name="my_location">My Location</string>
|
||||
<string name="none_map">None</string>
|
||||
<string name="normal">Normal</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="satellite">Satellite</string>
|
||||
<string name="terrain">Terrain</string>
|
||||
<string name="traffic">Traffic</string>
|
||||
|
||||
<!-- Lite List Demo -->
|
||||
<string name="lite_list_demo_label">Lite List Demo</string>
|
||||
<string name="lite_list_demo_details">Show a list of Lite Map of different locations using RecyclerView with LinearLayoutManager and GridLayoutManager</string>
|
||||
<string name="linear">Linear</string>
|
||||
<string name="grid">Grid</string>
|
||||
|
||||
<!-- Marker Close Info Window On Retap Demo -->
|
||||
<string name="close_info_window_demo_label">Close Info Window Demo</string>
|
||||
<string name="close_info_window_demo_details">Demonstrates how to close the info window when the currently selected marker is retapped.</string>
|
||||
<!-- Markers Demo -->
|
||||
<string name="custom_info_contents">Custom Info Contents</string>
|
||||
<string name="custom_info_window">Custom Info Window</string>
|
||||
<string name="default_info_window">Default Info Window</string>
|
||||
<string name="drag_melbourne">Drag Melbourne</string>
|
||||
<string name="clear_map">Clear</string>
|
||||
<string name="flat">Flat</string>
|
||||
<string name="reset_map">Reset</string>
|
||||
<string name="map_not_ready">Map is not ready</string>
|
||||
<string name="markers_demo_description">Demonstrates how to add Markers to a map.</string>
|
||||
<string name="markers_demo_label">Markers</string>
|
||||
<string name="on_marker_drag">onMarkerDrag. Current Position: (%1$f, %2$f)</string>
|
||||
<string name="on_marker_drag_start">Started dragging marker</string>
|
||||
<string name="on_marker_drag_end">Marker stopped dragging</string>
|
||||
<string name="rotation">Rotation</string>
|
||||
<string name="state_badge_label">State badge</string>
|
||||
|
||||
<!-- Permissions -->
|
||||
<string name="permission_rationale_location">Access to the location service is required to demonstrate the \'my location\' feature, which shows your current location on the map.</string>
|
||||
<string name="location_permission_denied">This sample requires location permission to enable the \'my location\' layer. Please try again and grant access to use the location.\nIf the permission has been permanently denied, it can be enabled from the System Settings > Apps > \'Google Maps API Demos\'.</string>
|
||||
<string name="permission_required_toast">Location permission is required for this demo.</string>
|
||||
|
||||
<!-- Polygon Demo -->
|
||||
<string name="clickable">Clickable</string>
|
||||
@ -59,6 +119,21 @@
|
||||
<string name="stroke_pattern">Stroke Pattern</string>
|
||||
<string name="stroke_width">Stroke Width</string>
|
||||
|
||||
<!-- Polylines Demo -->
|
||||
<string name="alpha">Alpha</string>
|
||||
<string name="cap_butt">Butt</string>
|
||||
<string name="cap_image">Image</string>
|
||||
<string name="cap_round">Round</string>
|
||||
<string name="cap_square">Square</string>
|
||||
<string name="end_cap">End Cap</string>
|
||||
<string name="hue">Hue</string>
|
||||
<string name="joint_type">Joint Type</string>
|
||||
<string name="pattern">Pattern</string>
|
||||
<string name="polyline_demo_description">Demonstrates how to add Polylines to a map</string>
|
||||
<string name="polyline_demo_label">Polyline</string>
|
||||
<string name="start_cap">Start Cap</string>
|
||||
<string name="width">Width</string>
|
||||
|
||||
<!-- StreetView Panorama Navigation Demo -->
|
||||
<string name="street_view_panorama_navigation_demo_label">Street View Panorama Navigation Demo</string>
|
||||
<string name="street_view_panorama_navigation_demo_details">Street View Panorama with programmatic navigation</string>
|
||||
@ -68,6 +143,11 @@
|
||||
<string name="go_to_san_francisco">Go to San Francisco</string>
|
||||
<string name="go_to_santorini">Go to Santorini</string>
|
||||
|
||||
<!-- Tags Demo -->
|
||||
<string name="tags_demo_label">Tags</string>
|
||||
<string name="tags_demo_details">Demonstrates how to get and set tags on API objects.</string>
|
||||
<string name="tags_demo_map_description">Map with a circle, ground overlay, marker, polygon and polyline.</string>
|
||||
|
||||
<!-- UI Settings Demo -->
|
||||
<string name="ui_settings_demo_label">UI Settings Demo</string>
|
||||
<string name="ui_settings_demo_details">Demonstrate how to toggle the UI of a map</string>
|
||||
@ -81,4 +161,14 @@
|
||||
<string name="checkbox_scroll_gesture">Scroll Gestures</string>
|
||||
<string name="checkbox_tilt_gesture">Tilt Gestures</string>
|
||||
<string name="checkbox_rotate_gesture">Rotate Gestures</string>
|
||||
|
||||
<!-- Visible Regions Demo -->
|
||||
<string name="camera_change_message">CameraChangeListener: %1$s</string>
|
||||
<string name="region_demo_label">Visible Regions</string>
|
||||
<string name="region_demo_details">Demonstrates how to use Visible Regions.</string>
|
||||
<string name="vr_normal_button">Normal</string>
|
||||
<string name="vr_more_padded_button">More</string>
|
||||
<string name="vr_opera_house_button">SOH</string>
|
||||
<string name="vr_sfo_button">SFO</string>
|
||||
<string name="vr_australia_button">AUS</string>
|
||||
</resources>
|
||||