Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
979 changes: 979 additions & 0 deletions codelab/codelab.md

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions codelab/solution/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
/secrets.properties
1 change: 1 addition & 0 deletions codelab/solution/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
93 changes: 93 additions & 0 deletions codelab/solution/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.secrets.gradle.plugin)
}

android {
namespace = "com.example.alohaexplorer"
compileSdk = libs.versions.compileSdk.get().toInt()

buildFeatures {
viewBinding = true
compose = true
buildConfig = true
}

defaultConfig {
applicationId = "com.example.alohaexplorer"
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate buildFeatures block, the first is on line 12. Kotlin DSL applies both to the same extension so it compiles and behaves identically, but it hides the one real difference between them: line 13 sets viewBinding = true, and this second block does not.

That viewBinding = true also looks unused. Both MainActivity.kt files reach for views through findViewById (solution line 92, plus lines 517-616), and nothing in either module imports a generated *Binding type. The starter's build.gradle.kts does not enable it at all, so the two projects differ in a way the codelab never explains.

Suggest merging into the single block below and dropping viewBinding unless a later step is meant to use it:

Suggested change
buildFeatures {
buildFeatures {
compose = true
buildConfig = true
}

compose = true
buildConfig = true
}
}

kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}

dependencies {
implementation(libs.androidx.activity)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.material3)
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.material)
implementation(platform(libs.androidx.compose.bom))

debugImplementation(libs.androidx.ui.tooling)

testImplementation(libs.junit)

androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

implementation(libs.play.services.maps3d)
}

secrets {
// Optionally specify a different file name containing your secrets.
// The plugin defaults to "local.properties"
propertiesFileName = "secrets.properties"

// A properties file containing default secret values. This file can be
// checked in version control.
defaultPropertiesFileName = "local.defaults.properties"
}

tasks.register<Exec>("launchDebug") {
dependsOn("installDebug")
commandLine("adb", "shell", "am", "start", "-n", "com.example.alohaexplorer/.MainActivity")
doLast {
println("Launched com.example.alohaexplorer/.MainActivity")
}
}
21 changes: 21 additions & 0 deletions codelab/solution/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.alohaexplorer

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.alohaexplorer", appContext.packageName)
}
}
36 changes: 36 additions & 0 deletions codelab/solution/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AlohaExplorer">

<meta-data
android:name="com.google.android.geo.maps3d.API_KEY"
android:value="${MAPS3D_API_KEY}"
/>

<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".Map3DComposeActivity"
android:exported="true"
android:label="Aloha Compose" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.example.alohaexplorer

import com.google.android.gms.maps3d.model.camera
import com.google.android.gms.maps3d.model.latLngAltitude

// Step 1: Honolulu
val HONOLULU = latLngAltitude {
latitude = 21.3069
longitude = -157.8583
altitude = 0.0
}

// Step 2: Iolani Palace
val IOLANI_PALACE = latLngAltitude {
latitude = 21.306740
longitude = -157.858803
altitude = 0.0
}

// Step 6: Waikiki Beach
val WAIKIKI = latLngAltitude {
latitude = 21.2766
longitude = -157.8286
altitude = 0.0
}

val DIAMOND_HEAD = latLngAltitude {
latitude = 21.26194
longitude = -157.80556
altitude = 0.0
}

val KOKO_HEAD = latLngAltitude {
latitude = 21.270856
longitude = -157.694442
altitude = 0.0
}

val PEARL_HARBOR = latLngAltitude {
latitude = 21.31861
longitude = -157.92250
altitude = 0.0
}

val MOUNT_KAALA = latLngAltitude {
latitude = 21.50694
longitude = -158.14278
altitude = 0.0
}

val LANIKAI_BEACH = latLngAltitude {
latitude = 21.39309
longitude = -157.71546
altitude = 0.0
}

// Models must be loaded from a URL. Here we use Cloud Storage.
const val BALLOON_MODEL_URL = "https://storage.googleapis.com/gmp-maps-demos/p3d-map/assets/balloon-pin-BlXF32yD.glb"
const val BALLOON_SCALE = 5.0

// Step 5: Iolani Palace Geometry (Lat, Lng pairs)
val IOLANI_PALACE_GEO = listOf(
21.307180365, -157.858769898,
21.306765552, -157.858390366,
21.306476932, -157.858755146,
21.306892995, -157.859134679,
)

val POLYGON_CAMERA = camera {
center = latLngAltitude {
latitude = 21.307051
longitude = -157.858546
altitude = 7.1
}
heading = 78.0
tilt = 53.0
range = 644.0
}

val MARKER_CAMERA = camera {
center = latLngAltitude {
latitude = 21.306648
longitude = -157.859336
altitude = 26.8
}
tilt = 68.0
range = 1024.0
heading = 61.0
}

val CUSTOM_MARKER_CAMERA = camera {
this.center = latLngAltitude {
this.latitude = 21.306370
this.longitude = -157.855474
this.altitude = 6.6
}
this.heading = 39.0
this.tilt = 68.0
this.range = 1399.0
}

val BALLOON_CAMERA = camera {
center = WAIKIKI
tilt = 60.0
range = 1000.0
heading = 0.0
}
Loading
Loading