Commit 30526709 authored by Weesinghe W.M.P.D's avatar Weesinghe W.M.P.D

Initial commit by IT20019204

parent 6e753e51
Pipeline #6956 failed with stages
# Auto detect text files and perform LF normalization
* text=auto
*.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
/build
\ No newline at end of file
plugins {
id 'com.android.application'
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
android {
namespace 'com.example.thetrek'
compileSdk 33
defaultConfig {
applicationId "com.example.thetrek"
minSdk 26
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
// ARCore (Google Play Services for AR) library.
implementation 'com.google.ar:core:1.37.0'
implementation 'com.google.android.gms:play-services-location:19.0.1'
implementation 'com.google.android.gms:play-services-auth:19.0.0'
// Obj - a simple Wavefront OBJ file loader
// https://github.com/javagl/Obj
implementation 'de.javagl:obj:0.2.1'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.concurrent:concurrent-futures:1.1.0'
implementation 'com.google.guava:guava:31.1-android'
implementation 'com.google.android.gms:play-services-maps:17.0.1'
implementation 'com.google.android.libraries.places:places:2.4.0'
implementation 'com.android.volley:volley:1.2.1'
// implementation 'androidx.appcompat:appcompat:1.3.1'
// implementation 'androidx.core:core-ktx:1.6.0'
// implementation 'com.google.android.material:material:1.4.0'
// implementation 'androidx.work:work-runtime:2.7.0'
implementation 'androidx.core:core:1.7.0' // Add this line
androidTestImplementation 'com.android.support.test:runner:1.0.2'
implementation 'org.tensorflow:tensorflow-android:1.13.1'
}
\ No newline at end of file
# 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
\ No newline at end of file
package com.example.thetrek;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.example.thetrek", appContext.getPackageName());
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
\ No newline at end of file
<?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.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!--
Limits app visibility in the Google Play Store to ARCore supported devices
(https://developers.google.com/ar/devices).
-->
<uses-permission android:name="android.permission.BODY_SENSORS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-feature
android:name="android.hardware.camera.ar"
android:required="true" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<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.TheTrek"
android:usesCleartextTraffic="false"
tools:ignore="GoogleAppIndexingWarning"
tools:targetApi="31">
<receiver
android:name=".screens.mobileUsage.ScreenOnReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.SCREEN_ON" />
</intent-filter>
</receiver>
<service
android:name=".screens.mobileUsage.NotificationService"
android:exported="false" />
<service
android:name=".screens.mobileUsage.ScreenMonitoringService"
android:enabled="true"
android:exported="false" />
<service
android:name=".UsageMonitorService"
android:exported="false" />
<activity
android:name=".geospatial.RewardingScreenActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".geospatial.GeospatialActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:screenOrientation="locked"
android:theme="@style/Theme.AppCompat.NoActionBar">
<intent-filter>
<!-- <action android:name="android.intent.action.MAIN"/> -->
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".screens.rewards.RewardsActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".screens.locationsHandler.AddLocations"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".screens.locationsHandler.LocationsCrud"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".screens.mobileUsage.MobileUsageActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".screens.getPhysical.GetPhysicalActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".screens.findPlaces.FindPlacesActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".SuggestActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".HomeActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".MonitorActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".MainActivityLogin"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".SignUp"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<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>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".MainActivityRec"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".MainActivityRecognition"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<meta-data
android:name="com.google.ar.core"
android:value="required" />
<meta-data
android:name="com.google.android.ar.API_KEY"
android:value="AIzaSyAynx5iCqfkqmUyUp3b2FNzf3OCm1dOrbU"
/>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyAynx5iCqfkqmUyUp3b2FNzf3OCm1dOrbU" />
</application>
</manifest>
\ No newline at end of file
The file "dfg.raw" is a raw image file of dimensions 64x64 with two color
channels stored in 16-bit floats. It can be regenerated by using the script
"generate_dfg_texture.py" provided in the ARCore SDK under /tools/.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
#version 300 es
/*
* Copyright 2022 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
*
* 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.
*/
precision mediump float;
uniform sampler2D u_Texture;
in vec2 v_TexCoord;
layout(location = 0) out vec4 o_FragColor;
void main() {
// Mirror texture coordinates over the X axis
vec2 texCoord = vec2(v_TexCoord.x, 1.0 - v_TexCoord.y);
o_FragColor = vec4(texture(u_Texture, texCoord).rgb, 1.0);
return;
}
#version 300 es
/*
* Copyright 2022 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
*
* 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.
*/
uniform mat4 u_ModelView;
uniform mat4 u_ModelViewProjection;
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec2 a_TexCoord;
layout(location = 2) in vec3 a_Normal;
out vec2 v_TexCoord;
void main() {
v_TexCoord = a_TexCoord;
gl_Position = u_ModelViewProjection * a_Position;
}
#version 300 es
/*
* Copyright 2017 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
*
* 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.
*/
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;
uniform samplerExternalOES u_CameraColorTexture;
in vec2 v_CameraTexCoord;
layout(location = 0) out vec4 o_FragColor;
void main() { o_FragColor = texture(u_CameraColorTexture, v_CameraTexCoord); }
#version 300 es
/*
* Copyright 2017 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
*
* 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.
*/
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec2 a_CameraTexCoord;
// The virtual scene texture coordinate is unused in the background shader, but
// is defined in the BackgroundRenderer Mesh.
layout(location = 2) in vec2 a_VirtualSceneTexCoord;
out vec2 v_CameraTexCoord;
void main() {
gl_Position = a_Position;
v_CameraTexCoord = a_CameraTexCoord;
}
#version 300 es
/*
* Copyright 2020 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
*
* 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.
*/
precision mediump float;
// This shader pair shows the depth estimation instead of the camera image as
// the background. This behavior is mostly only useful as a demonstration of the
// depth feature.
uniform sampler2D u_CameraDepthTexture;
uniform sampler2D u_ColorMap;
in vec2 v_CameraTexCoord;
layout(location = 0) out vec4 o_FragColor;
float Depth_GetCameraDepthInMillimeters(const sampler2D depthTexture,
const vec2 depthUv) {
// Depth is packed into the red and green components of its texture.
// The texture is a normalized format, storing millimeters.
vec3 packedDepthAndVisibility = texture(depthTexture, depthUv).xyz;
return dot(packedDepthAndVisibility.xy, vec2(255.0, 256.0 * 255.0));
}
// Returns a color corresponding to the depth passed in.
//
// Uses Turbo color mapping:
// https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html
//
// The input x is normalized in range 0 to 1.
vec3 Depth_GetColorVisualization(float x) {
return texture(u_ColorMap, vec2(x, 0.5)).rgb;
}
// Returns linear interpolation position of value between min and max bounds.
// E.g. InverseLerp(1100, 1000, 2000) returns 0.1.
float InverseLerp(float value, float min_bound, float max_bound) {
return clamp((value - min_bound) / (max_bound - min_bound), 0.0, 1.0);
}
void main() {
const float kMidDepthMeters = 8.0;
const float kMaxDepthMeters = 30.0;
// Interpolating in units of meters is more stable, due to limited floating
// point precision on GPU.
float depth_mm =
Depth_GetCameraDepthInMillimeters(u_CameraDepthTexture, v_CameraTexCoord);
float depth_meters = depth_mm * 0.001;
// Selects the portion of the color palette to use.
float normalizedDepth = 0.0;
if (depth_meters < kMidDepthMeters) {
// Short-range depth (0m to 8m) maps to first half of the color palette.
normalizedDepth = InverseLerp(depth_meters, 0.0, kMidDepthMeters) * 0.5;
} else {
// Long-range depth (8m to 30m) maps to second half of the color palette.
normalizedDepth =
InverseLerp(depth_meters, kMidDepthMeters, kMaxDepthMeters) * 0.5 + 0.5;
}
// Converts depth to color by with the selected value in the color map.
vec4 depth_color = vec4(Depth_GetColorVisualization(normalizedDepth), 1.0);
// Invalid depth (pixels with value 0) mapped to black.
depth_color.rgb *= sign(depth_meters);
o_FragColor = depth_color;
}
#version 300 es
/*
* Copyright 2020 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
*
* 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 shader pair shows the depth estimation instead of the camera image as
// the background. This behavior is mostly only useful as a demonstration of the
// depth feature.
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec2 a_CameraTexCoord;
// The virtual scene texture coordinate is unused in the background shader, but
// is defined in the BackgroundRenderer Mesh.
layout(location = 2) in vec2 a_VirtualSceneTexCoord;
out vec2 v_CameraTexCoord;
void main() {
gl_Position = a_Position;
v_CameraTexCoord = a_CameraTexCoord;
}
#version 300 es
/*
* Copyright 2020 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
*
* 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.
*/
precision mediump float;
// The number of mipmap levels in the cubemap is equal to the number of
// roughness levels that we precalculate for filtering the cubemap for roughness
// in real-time.
const int kNumberOfRoughnessLevels = NUMBER_OF_MIPMAP_LEVELS;
// The number of importance samples to use for roughness filtering of the
// cubemap.
const int kNumberOfImportanceSamples = NUMBER_OF_IMPORTANCE_SAMPLES;
struct ImportanceSampleCacheEntry {
vec3 direction; // Direction to sample in tangent space
float contribution; // Weighted contribution of the sample's radiance
float level; // The mipmap level to sample from the cubemap. Can be
// in-between integer levels for trilinear filtering.
};
struct ImportanceSampleCache {
int number_of_entries;
ImportanceSampleCacheEntry entries[kNumberOfImportanceSamples];
};
// This array's length is one less than the number of roughness levels since the
// first roughness level can be skipped.
uniform ImportanceSampleCache
u_ImportanceSampleCaches[kNumberOfRoughnessLevels - 1];
// The source radiance cubemap to be filtered.
uniform samplerCube u_Cubemap;
// The roughness level that we are filtering for.
uniform int u_RoughnessLevel;
in vec2 v_Position;
#ifdef PX_LOCATION
layout(location = PX_LOCATION) out vec4 o_FragColorPX;
#endif
#ifdef NX_LOCATION
layout(location = NX_LOCATION) out vec4 o_FragColorNX;
#endif
#ifdef PY_LOCATION
layout(location = PY_LOCATION) out vec4 o_FragColorPY;
#endif
#ifdef NY_LOCATION
layout(location = NY_LOCATION) out vec4 o_FragColorNY;
#endif
#ifdef PZ_LOCATION
layout(location = PZ_LOCATION) out vec4 o_FragColorPZ;
#endif
#ifdef NZ_LOCATION
layout(location = NZ_LOCATION) out vec4 o_FragColorNZ;
#endif
vec4 Filter(const vec3 n) {
if (u_RoughnessLevel == 0) {
// Roughness level 0 is just a straight copy.
return vec4(textureLod(u_Cubemap, n, 0.0).rgb, 1.0);
}
vec3 up = abs(n.z) < 0.9999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
mat3 tangentToWorld;
tangentToWorld[0] = normalize(cross(up, n));
tangentToWorld[1] = cross(n, tangentToWorld[0]);
tangentToWorld[2] = n;
// XXX: This clamp should not be necessary, but is here due to a compiler
// optimization bug affecting certain devices.
//
// Some drivers pre-calculate uniform expressions to reduce redundant
// computations. Certain drivers will incorrectly ignore uniform control flow
// and try to pre-calculate expressions that wouldn't be executed. So even
// though we explicitly short-circuit if `u_RoughnessLevel' is 0, the compiled
// shader still tries to calculate `u_ImportanceSampleCaches[u_RoughnessLevel
// - 1]', with obviously bad results.
ImportanceSampleCache cache = u_ImportanceSampleCaches[max(0, u_RoughnessLevel - 1)];
vec3 radiance = vec3(0.0);
for (int i = 0; i < cache.number_of_entries; ++i) {
ImportanceSampleCacheEntry entry = cache.entries[i];
radiance +=
textureLod(u_Cubemap, tangentToWorld * entry.direction, entry.level)
.rgb *
entry.contribution;
}
return vec4(radiance, 1.0);
}
void main() {
float u = v_Position.x;
float v = v_Position.y;
#ifdef PX_LOCATION
o_FragColorPX = Filter(normalize(vec3(+1, -v, -u)));
#endif
#ifdef NX_LOCATION
o_FragColorNX = Filter(normalize(vec3(-1, -v, +u)));
#endif
#ifdef PY_LOCATION
o_FragColorPY = Filter(normalize(vec3(+u, +1, +v)));
#endif
#ifdef NY_LOCATION
o_FragColorNY = Filter(normalize(vec3(+u, -1, -v)));
#endif
#ifdef PZ_LOCATION
o_FragColorPZ = Filter(normalize(vec3(+u, -v, +1)));
#endif
#ifdef NZ_LOCATION
o_FragColorNZ = Filter(normalize(vec3(-u, -v, -1)));
#endif
}
#version 300 es
/*
* Copyright 2020 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
*
* 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.
*/
layout(location = 0) in vec4 a_Position;
out vec2 v_Position;
void main() {
gl_Position = a_Position;
v_Position = a_Position.xy;
}
#version 300 es
/*
* Copyright 2020 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
*
* 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.
*/
precision mediump float;
// This shader will light scenes based on ARCore's Environmental HDR mode with a
// physically based rendering model.
//
// When using the HDR Cubemap from ARCore for specular reflections, please note
// that the following equation is true of ARCore's Environmental HDR lighting
// estimation, where E(x) is irradiance of x.
//
// E(spherical harmonics) + E(main light) == E(cubemap)
//
// In order to not duplicate the specular lighting contribution of the main
// light, we must use the following equation, where Lo is total reflected
// radiance (i.e. linear color output), Ld(x) is the reflected diffuse radiance
// of x, and Ls(x) is reflected specular radiance of x.
//
// Lo = Ld(spherical harmonics) + Ld(main light) + Ls(cubemap)
//
// The Filament documentation has excellent documentation on the subject of
// image based lighting:
// https://google.github.io/filament/Filament.md.html#lighting/imagebasedlights
//
// If you would rather not use the HDR cubemap in your application, you would
// need to adjust the lighting calculations to reflect the following equation
// instead.
//
// Lo = Ld(spherical harmonics) + Ld(main light) + Ls(main light)
//
// See the definitions of Pbr_CalculateMainLightRadiance and
// Pbr_CalculateEnvironmentalRadiance.
// Number of mipmap levels in the filtered cubemap.
const int kNumberOfRoughnessLevels = NUMBER_OF_MIPMAP_LEVELS;
// The albedo and roughness/metallic textures.
uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_RoughnessMetallicAmbientOcclusionTexture;
// The intensity of the main directional light.
uniform vec3 u_LightIntensity;
// The direction of the main directional light in view space.
uniform vec4 u_ViewLightDirection;
// The coefficients for the spherical harmonic function which models the diffuse
// irradiance of a distant environmental light for a given surface normal in
// world space. These coefficients must be premultiplied with their
// corresponding spherical harmonics constants. See
// HelloArActivity.updateSphericalHarmonicsCoefficients for more information.
uniform vec3 u_SphericalHarmonicsCoefficients[9];
// The filtered cubemap texture which models the LD term (i.e. radiance (L)
// times distribution function (D)) of the environmental specular calculation as
// a function of direction and roughness.
uniform samplerCube u_Cubemap;
// The DFG lookup texture which models the DFG1 and DFG2 terms of the
// environmental specular calculation as a function of normal dot view and
// perceptual roughness.
uniform sampler2D u_DfgTexture;
// Inverse view matrix. Used for converting normals back into world space for
// environmental radiance calculations.
uniform mat4 u_ViewInverse;
// If the current light estimate is valid. Used to short circuit the entire
// shader when the light estimate is not valid.
uniform bool u_LightEstimateIsValid;
struct MaterialParameters {
vec3 diffuse;
float perceptualRoughness; // perceptually linear roughness
float roughness; // non-perceptually linear roughness
float metallic;
float ambientOcclusion;
vec3 f0; // reflectance
vec2 dfg; // DFG1 and DFG2 terms
vec3 energyCompensation; // energy preservation for multiscattering
};
struct ShadingParameters {
// Halfway here refers to halfway between the view and light directions.
float normalDotView;
float normalDotHalfway;
float normalDotLight;
float viewDotHalfway;
float oneMinusNormalDotHalfwaySquared;
// These unit vectors are in world space and are used for the environmental
// lighting math.
vec3 worldNormalDirection;
vec3 worldReflectDirection;
};
in vec3 v_ViewPosition;
in vec3 v_ViewNormal;
in vec2 v_TexCoord;
layout(location = 0) out vec4 o_FragColor;
const float kPi = 3.14159265359;
vec3 Pbr_CalculateMainLightRadiance(const ShadingParameters shading,
const MaterialParameters material,
const vec3 mainLightIntensity) {
// Lambertian diffuse
vec3 diffuseTerm = material.diffuse / kPi;
// Note that if we were not using the HDR cubemap from ARCore for specular
// lighting, we would be adding a specular contribution from the main light
// here. See the top of the file for a more detailed explanation.
return diffuseTerm * mainLightIntensity * shading.normalDotLight;
}
vec3 Pbr_CalculateDiffuseEnvironmentalRadiance(const vec3 normal,
const vec3 coefficients[9]) {
// See HelloArActivity.updateSphericalHarmonicsCoefficients() for more
// information about this calculation.
vec3 radiance = coefficients[0] + coefficients[1] * (normal.y) +
coefficients[2] * (normal.z) + coefficients[3] * (normal.x) +
coefficients[4] * (normal.y * normal.x) +
coefficients[5] * (normal.y * normal.z) +
coefficients[6] * (3.0 * normal.z * normal.z - 1.0) +
coefficients[7] * (normal.z * normal.x) +
coefficients[8] * (normal.x * normal.x - normal.y * normal.y);
return max(radiance, 0.0);
}
vec3 Pbr_CalculateSpecularEnvironmentalRadiance(
const ShadingParameters shading, const MaterialParameters material,
const samplerCube cubemap) {
// Lagarde and de Rousiers 2014, "Moving Frostbite to PBR"
float specularAO =
clamp(pow(shading.normalDotView + material.ambientOcclusion,
exp2(-16.0 * material.roughness - 1.0)) -
1.0 + material.ambientOcclusion,
0.0, 1.0);
// Combine DFG and LD terms
float lod =
material.perceptualRoughness * float(kNumberOfRoughnessLevels - 1);
vec3 LD = textureLod(cubemap, shading.worldReflectDirection, lod).rgb;
vec3 E = mix(material.dfg.xxx, material.dfg.yyy, material.f0);
return E * LD * specularAO * material.energyCompensation;
}
vec3 Pbr_CalculateEnvironmentalRadiance(
const ShadingParameters shading, const MaterialParameters material,
const vec3 sphericalHarmonicsCoefficients[9], const samplerCube cubemap) {
// The lambertian diffuse BRDF term (1/pi) is baked into
// HelloArActivity.sphericalHarmonicsFactors.
vec3 diffuseTerm =
Pbr_CalculateDiffuseEnvironmentalRadiance(
shading.worldNormalDirection, sphericalHarmonicsCoefficients) *
material.diffuse * material.ambientOcclusion;
vec3 specularTerm =
Pbr_CalculateSpecularEnvironmentalRadiance(shading, material, cubemap);
return diffuseTerm + specularTerm;
}
void Pbr_CreateShadingParameters(const in vec3 viewNormal,
const in vec3 viewPosition,
const in vec4 viewLightDirection,
const in mat4 viewInverse,
out ShadingParameters shading) {
vec3 normalDirection = normalize(viewNormal);
vec3 viewDirection = -normalize(viewPosition);
vec3 lightDirection = normalize(viewLightDirection.xyz);
vec3 halfwayDirection = normalize(viewDirection + lightDirection);
// Clamping the minimum bound yields better results with values less than or
// equal to 0, which would otherwise cause discontinuity in the geometry
// factor. Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline
// for The Order: 1886"
shading.normalDotView = max(dot(normalDirection, viewDirection), 1e-4);
shading.normalDotHalfway =
clamp(dot(normalDirection, halfwayDirection), 0.0, 1.0);
shading.normalDotLight =
clamp(dot(normalDirection, lightDirection), 0.0, 1.0);
shading.viewDotHalfway =
clamp(dot(viewDirection, halfwayDirection), 0.0, 1.0);
// The following calculation can be proven as being equivalent to 1-(N.H)^2 by
// using Lagrange's identity.
//
// ||a x b||^2 = ||a||^2 ||b||^2 - (a . b)^2
//
// Since we're using unit vectors: ||N x H||^2 = 1 - (N . H)^2
//
// We are calculating it in this way to preserve floating point precision.
vec3 NxH = cross(normalDirection, halfwayDirection);
shading.oneMinusNormalDotHalfwaySquared = dot(NxH, NxH);
shading.worldNormalDirection = (viewInverse * vec4(normalDirection, 0.0)).xyz;
vec3 reflectDirection = reflect(-viewDirection, normalDirection);
shading.worldReflectDirection =
(viewInverse * vec4(reflectDirection, 0.0)).xyz;
}
void Pbr_CreateMaterialParameters(const in vec2 texCoord,
const in sampler2D albedoTexture,
const in sampler2D pbrTexture,
const in sampler2D dfgTexture,
const in ShadingParameters shading,
out MaterialParameters material) {
// Read the material parameters from the textures
vec3 albedo = texture(albedoTexture, texCoord).rgb;
vec3 roughnessMetallicAmbientOcclusion = texture(pbrTexture, texCoord).rgb;
// Roughness inputs are perceptually linear; convert them to regular roughness
// values. Roughness levels approaching 0 will make specular reflections
// completely invisible, so cap the lower bound. This value was chosen such
// that (kMinPerceptualRoughness^4) > 0 in fp16 (i.e. 2^(-14/4), rounded up).
// https://github.com/google/filament/blob/main/shaders/src/common_material.fs#L2
const float kMinPerceptualRoughness = 0.089;
material.perceptualRoughness =
max(roughnessMetallicAmbientOcclusion.r, kMinPerceptualRoughness);
material.roughness =
material.perceptualRoughness * material.perceptualRoughness;
material.metallic = roughnessMetallicAmbientOcclusion.g;
material.ambientOcclusion = roughnessMetallicAmbientOcclusion.b;
material.diffuse = albedo * (1.0 - material.metallic);
// F0 is defined as "Fresnel reflectance at 0 degrees", i.e. specular
// reflectance when light is grazing a surface perfectly perpendicularly. This
// value is derived from the index of refraction for a material. Most
// dielectric materials have an F0 value of 0.00-0.08, which leaves 0.04 as a
// reasonable constant for a simple roughness/metallic material workflow as
// implemented by this shader.
material.f0 = mix(vec3(0.04), albedo, material.metallic);
// The DFG texture is a simple lookup table indexed by [normal dot view,
// perceptualRoughness].
material.dfg =
textureLod(dfgTexture,
vec2(shading.normalDotView, material.perceptualRoughness), 0.0)
.xy;
// Energy preservation for multiscattering (see
// https://google.github.io/filament/Filament.md.html#materialsystem/improvingthebrdfs)
material.energyCompensation =
1.0 + material.f0 * (1.0 / material.dfg.y - 1.0);
}
vec3 LinearToSrgb(const vec3 color) {
vec3 kGamma = vec3(1.0 / 2.2);
return clamp(pow(color, kGamma), 0.0, 1.0);
}
void main() {
// Mirror texture coordinates over the X axis
vec2 texCoord = vec2(v_TexCoord.x, 1.0 - v_TexCoord.y);
// Skip all lighting calculations if the estimation is not valid.
if (!u_LightEstimateIsValid) {
o_FragColor = vec4(texture(u_AlbedoTexture, texCoord).rgb, 1.0);
return;
}
ShadingParameters shading;
Pbr_CreateShadingParameters(v_ViewNormal, v_ViewPosition,
u_ViewLightDirection, u_ViewInverse, shading);
MaterialParameters material;
Pbr_CreateMaterialParameters(texCoord, u_AlbedoTexture,
u_RoughnessMetallicAmbientOcclusionTexture,
u_DfgTexture, shading, material);
// Combine the radiance contributions of both the main light and environment
vec3 mainLightRadiance =
Pbr_CalculateMainLightRadiance(shading, material, u_LightIntensity);
vec3 environmentalRadiance = Pbr_CalculateEnvironmentalRadiance(
shading, material, u_SphericalHarmonicsCoefficients, u_Cubemap);
vec3 radiance = mainLightRadiance + environmentalRadiance;
// Convert final color to sRGB color space
o_FragColor = vec4(LinearToSrgb(radiance), 1.0);
}
#version 300 es
/*
* Copyright 2020 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
*
* 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.
*/
uniform mat4 u_ModelView;
uniform mat4 u_ModelViewProjection;
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec2 a_TexCoord;
layout(location = 2) in vec3 a_Normal;
out vec3 v_ViewPosition;
out vec3 v_ViewNormal;
out vec2 v_TexCoord;
void main() {
v_ViewPosition = (u_ModelView * a_Position).xyz;
v_ViewNormal = normalize((u_ModelView * vec4(a_Normal, 0.0)).xyz);
v_TexCoord = a_TexCoord;
gl_Position = u_ModelViewProjection * a_Position;
}
#version 300 es
/*
* Copyright 2020 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
*
* 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.
*/
precision mediump float;
// The virtual scene as rendered to a texture via a framebuffer. This will be
// composed with the background image depending on which modes were set in
// DepthCompositionRenderer.setDepthModes.
uniform sampler2D u_VirtualSceneColorTexture;
#if USE_OCCLUSION
// The AR camera depth texture.
uniform sampler2D u_CameraDepthTexture;
// The depth texture for the virtual scene.
uniform sampler2D u_VirtualSceneDepthTexture;
// The near and far clipping planes, used to transform the virtual scene depth
// back into view space to compare with the camera depth texture.
uniform float u_ZNear;
uniform float u_ZFar;
// The aspect ratio of the screen. This is used during to create uniform
// blurring for occluded objects.
uniform float u_DepthAspectRatio;
#endif // USE_OCCLUSION
#if USE_OCCLUSION
in vec2 v_CameraTexCoord;
#endif // USE_OCCLUSION
in vec2 v_VirtualSceneTexCoord;
layout(location = 0) out vec4 o_FragColor;
#if USE_OCCLUSION
float Depth_GetCameraDepthInMillimeters(const sampler2D depthTexture,
const vec2 depthUv) {
// Depth is packed into the red and green components of its texture.
// The texture is a normalized format, storing millimeters.
vec3 packedDepthAndVisibility = texture(depthTexture, depthUv).xyz;
return dot(packedDepthAndVisibility.xy, vec2(255.0, 256.0 * 255.0));
}
float Depth_GetVirtualSceneDepthMillimeters(const sampler2D depthTexture,
const vec2 depthUv, float zNear,
float zFar) {
// Determine the depth of the virtual scene fragment in millimeters.
const float kMetersToMillimeters = 1000.0;
// This value was empirically chosen to correct errors with objects appearing
// to phase through the floor. In millimeters.
const float kBias = -80.0;
float ndc = 2.0 * texture(depthTexture, depthUv).x - 1.0;
return 2.0 * zNear * zFar / (zFar + zNear - ndc * (zFar - zNear)) *
kMetersToMillimeters +
kBias;
}
// Returns a value between 0.0 (completely visible) and 1.0 (completely
// occluded), representing how visible or occluded is the pixel in relation to
// the depth map.
float Depth_GetOcclusion(const sampler2D depthTexture, const vec2 depthUv,
float assetDepthMm) {
float depthMm = Depth_GetCameraDepthInMillimeters(depthTexture, depthUv);
// Instead of a hard z-buffer test, allow the asset to fade into the
// background along a 2 * kDepthTolerancePerMm * assetDepthMm
// range centered on the background depth.
const float kDepthTolerancePerMm = 0.01;
return clamp(1.0 -
0.5 * (depthMm - assetDepthMm) /
(kDepthTolerancePerMm * assetDepthMm) +
0.5,
0.0, 1.0);
}
float Depth_GetBlurredOcclusionAroundUV(const sampler2D depthTexture,
const vec2 uv, float assetDepthMm) {
// Kernel used:
// 0 4 7 4 0
// 4 16 26 16 4
// 7 26 41 26 7
// 4 16 26 16 4
// 0 4 7 4 0
const float kKernelTotalWeights = 269.0;
float sum = 0.0;
const float kOcclusionBlurAmount = 0.01;
vec2 blurriness =
vec2(kOcclusionBlurAmount, kOcclusionBlurAmount * u_DepthAspectRatio);
float current = 0.0;
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-1.0, -2.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+1.0, -2.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-1.0, +2.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+1.0, +2.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-2.0, +1.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+2.0, +1.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-2.0, -1.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+2.0, -1.0) * blurriness, assetDepthMm);
sum += current * 4.0;
current = 0.0;
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-2.0, -0.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+2.0, +0.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+0.0, +2.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-0.0, -2.0) * blurriness, assetDepthMm);
sum += current * 7.0;
current = 0.0;
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-1.0, -1.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+1.0, -1.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-1.0, +1.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+1.0, +1.0) * blurriness, assetDepthMm);
sum += current * 16.0;
current = 0.0;
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+0.0, +1.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-0.0, -1.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(-1.0, -0.0) * blurriness, assetDepthMm);
current += Depth_GetOcclusion(
depthTexture, uv + vec2(+1.0, +0.0) * blurriness, assetDepthMm);
sum += current * 26.0;
sum += Depth_GetOcclusion(depthTexture, uv, assetDepthMm) * 41.0;
return sum / kKernelTotalWeights;
}
#endif // USE_OCCLUSION
void main() {
o_FragColor = texture(u_VirtualSceneColorTexture, v_VirtualSceneTexCoord);
#if USE_OCCLUSION
if (o_FragColor.a == 0.0) {
// There's no sense in calculating occlusion for a fully transparent pixel.
return;
}
float assetDepthMm = Depth_GetVirtualSceneDepthMillimeters(
u_VirtualSceneDepthTexture, v_VirtualSceneTexCoord, u_ZNear, u_ZFar);
float occlusion = Depth_GetBlurredOcclusionAroundUV(
u_CameraDepthTexture, v_CameraTexCoord, assetDepthMm);
// If the above blur operation is too expensive, you can replace it with the
// following lines.
/* float occlusion = Depth_GetOcclusion(u_CameraDepthTexture,
v_CameraTexCoord, assetDepthMm); */
// The virtual object mask is blurred, we make the falloff steeper to simulate
// erosion operator. This is needed to make the fully occluded virtual object
// invisible.
float objectMaskEroded = pow(occlusion, 10.0);
// occlusionTransition equal to 1 means fully occluded object. This operation
// boosts occlusion near the edges of the virtual object, but does not affect
// occlusion within the object.
float occlusionTransition =
clamp(occlusion * (2.0 - objectMaskEroded), 0.0, 1.0);
// Clips occlusion if we want to partially show fully occluded object.
float kMaxOcclusion = 1.0;
occlusionTransition = min(occlusionTransition, kMaxOcclusion);
o_FragColor *= 1.0 - occlusion;
#endif // USE_OCCLUSION
}
#version 300 es
/*
* Copyright 2020 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
*
* 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.
*/
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec2 a_CameraTexCoord;
layout(location = 2) in vec2 a_VirtualSceneTexCoord;
#if USE_OCCLUSION
out vec2 v_CameraTexCoord;
#endif
out vec2 v_VirtualSceneTexCoord;
void main() {
gl_Position = a_Position;
#if USE_OCCLUSION
v_CameraTexCoord = a_CameraTexCoord;
#endif
v_VirtualSceneTexCoord = a_VirtualSceneTexCoord;
}
#version 300 es
/*
* Copyright 2017 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
*
* 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.
*/
precision highp float;
uniform sampler2D u_Texture;
uniform vec4 u_GridControl; // dotThreshold, lineThreshold, lineFadeShrink,
// occlusionShrink
in vec3 v_TexCoordAlpha;
layout(location = 0) out vec4 o_FragColor;
void main() {
vec4 control = texture(u_Texture, v_TexCoordAlpha.xy);
float dotScale = v_TexCoordAlpha.z;
float lineFade =
max(0.0, u_GridControl.z * v_TexCoordAlpha.z - (u_GridControl.z - 1.0));
float alpha = (control.r * dotScale > u_GridControl.x) ? 1.0
: (control.g > u_GridControl.y) ? lineFade
: (0.1 * lineFade);
o_FragColor = vec4(alpha * v_TexCoordAlpha.z);
}
#version 300 es
/*
* Copyright 2017 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
*
* 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.
*/
uniform mat4 u_Model;
uniform mat4 u_ModelViewProjection;
uniform mat2 u_PlaneUvMatrix;
uniform vec3 u_Normal;
layout(location = 0) in vec3 a_XZPositionAlpha; // (x, z, alpha)
out vec3 v_TexCoordAlpha;
void main() {
vec4 local_pos = vec4(a_XZPositionAlpha.x, 0.0, a_XZPositionAlpha.y, 1.0);
vec4 world_pos = u_Model * local_pos;
// Construct two vectors that are orthogonal to the normal.
// This arbitrary choice is not co-linear with either horizontal
// or vertical plane normals.
const vec3 arbitrary = vec3(1.0, 1.0, 0.0);
vec3 vec_u = normalize(cross(u_Normal, arbitrary));
vec3 vec_v = normalize(cross(u_Normal, vec_u));
// Project vertices in world frame onto vec_u and vec_v.
vec2 uv = vec2(dot(world_pos.xyz, vec_u), dot(world_pos.xyz, vec_v));
v_TexCoordAlpha = vec3(u_PlaneUvMatrix * uv, a_XZPositionAlpha.z);
gl_Position = u_ModelViewProjection * local_pos;
}
#version 300 es
/*
* Copyright 2017 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
*
* 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.
*/
precision mediump float;
uniform vec4 u_Color;
out vec4 o_FragColor;
void main() {
o_FragColor = u_Color;
}
#version 300 es
/*
* Copyright 2017 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
*
* 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.
*/
uniform mat4 u_ModelViewProjection;
uniform float u_PointSize;
layout(location = 0) in vec4 a_Position;
void main() {
gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0);
gl_PointSize = u_PointSize;
}
/*
* Copyright 2023 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
*
* 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.
*/
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
/*
* Copyright 2023 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
*
* 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.
*/
uniform mat4 u_ModelViewProjection;
uniform vec4 u_Color;
uniform float u_PointSize;
attribute vec4 a_Position;
varying vec4 v_Color;
void main() {
v_Color = u_Color;
gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0);
gl_PointSize = u_PointSize;
}
package com.example.thetrek;
import android.content.Context;
import org.tensorflow.contrib.android.TensorFlowInferenceInterface;
public class HARClassifier {
static {
System.loadLibrary("tensorflow_inference");
}
private TensorFlowInferenceInterface inferenceInterface;
private static final String MODEL_FILE = "file:///android_asset/frozen_HAR.pb";
private static final String INPUT_NODE = "LSTM_1_input";
private static final String[] OUTPUT_NODES = {"Dense_2/Softmax"};
private static final String OUTPUT_NODE = "Dense_2/Softmax";
private static final long[] INPUT_SIZE = {1, 100, 12};
private static final int OUTPUT_SIZE = 7;
public HARClassifier(final Context context) {
inferenceInterface = new TensorFlowInferenceInterface(context.getAssets(), MODEL_FILE);
}
public float[] predictProbabilities(float[] data) {
float[] result = new float[OUTPUT_SIZE];
inferenceInterface.feed(INPUT_NODE, data, INPUT_SIZE);
inferenceInterface.run(OUTPUT_NODES);
inferenceInterface.fetch(OUTPUT_NODE, result);
//Biking Downstairs Jogging Sitting Standing Upstairs Walking
return result;
}
}
package com.example.thetrek;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.example.thetrek.db.LocationDAO;
import com.example.thetrek.db.LocationsDbHelper;
import com.example.thetrek.db.RewardDbHelper;
import com.example.thetrek.db.RewardStrings;
import com.example.thetrek.geospatial.GeospatialActivity;
import com.example.thetrek.screens.findPlaces.FindPlacesActivity;
import com.example.thetrek.screens.getPhysical.GetPhysicalActivity;
import com.example.thetrek.screens.locationsHandler.AddLocations;
import com.example.thetrek.screens.locationsHandler.Location;
import com.example.thetrek.screens.locationsHandler.LocationsCrud;
import com.example.thetrek.screens.mobileUsage.MobileUsageActivity;
import com.example.thetrek.screens.rewards.Reward;
import com.example.thetrek.screens.rewards.RewardsActivity;
import java.util.ArrayList;
import java.util.List;
public class HomeActivity extends AppCompatActivity {
private LocationDAO locationDAO;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
getSupportActionBar().hide();
locationDAO = new LocationDAO(this);
locationDAO.open();
Button findPlacesButton = (Button) findViewById(R.id.btn_homeActivity_findPlaces);
findPlacesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), FindPlacesActivity.class);
v.getContext().startActivity(intent);
}
});
Button activityRecognitionButton = (Button) findViewById(R.id.btn_activityRecognition);
activityRecognitionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), MainActivityRecognition.class);
v.getContext().startActivity(intent);
}
});
Button recorderButton = (Button) findViewById(R.id.btn_recorder);
recorderButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), MainActivityRec.class);
v.getContext().startActivity(intent);
}
});
Button mobileUsageButton = (Button) findViewById(R.id.btn_homeActivity_mobileUsage);
mobileUsageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), MobileUsageActivity.class);
v.getContext().startActivity(intent);
}
});
Button suggestpredictor = (Button) findViewById(R.id.btn_predictor);
suggestpredictor.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), SuggestActivity.class);
v.getContext().startActivity(intent);
}
});
Button getPhysicalButton = (Button) findViewById(R.id.btn_homeActivity_getPhysical);
getPhysicalButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), GeospatialActivity.class);
v.getContext().startActivity(intent);
}
});
Button rewards = (Button) findViewById(R.id.btn_homeActivity_rewards);
rewards.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), RewardsActivity.class);
v.getContext().startActivity(intent);
}
});
List<Location> locationList = getLocationDataFromDatabase();
Button locationsHandler = (Button) findViewById(R.id.btn_homeActivity_locationsHandler);
locationsHandler.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// if (locationList.isEmpty()){
// Intent intent = new Intent(v.getContext(), AddLocations.class);
// v.getContext().startActivity(intent);
// }else {
Intent intent = new Intent(v.getContext(), LocationsCrud.class);
v.getContext().startActivity(intent);
// }
}
});
}
private List<Location> getLocationDataFromDatabase() {
List<Location> locationList = new ArrayList<>();
Cursor cursor = locationDAO.getAllLocations();
if (cursor != null && cursor.moveToFirst()) {
int idIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_ID);
int latitudeIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_LATITUDE);
int longitudeIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_LONGITUDE);
int isVisitedIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_IS_VISITED);
int locationNameIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_NAME);
int elevationIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_ELEVATION);
do {
int id = cursor.getInt(idIndex);
double latitude = cursor.getDouble(latitudeIndex);
double longitude = cursor.getDouble(longitudeIndex);
boolean isVisited = cursor.getInt(isVisitedIndex) == 1;
String locationName = cursor.getString(locationNameIndex);
double elevation = cursor.getDouble(elevationIndex);
Location location = new Location(id, latitude, longitude, isVisited, locationName, elevation);
locationList.add(location);
} while (cursor.moveToNext());
}
if (cursor != null) {
cursor.close();
}
return locationList;
}
}
\ No newline at end of file
package com.example.thetrek;
//import android.content.Intent;
//import android.database.Cursor;
//import android.os.Bundle;
//import android.view.View;
//import android.widget.Button;
//import android.widget.EditText;
//import android.widget.TextView;
//import android.widget.Toast;
//
//import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
//import com.example.thetrek.Sql.DBHelper;
public class Login extends AppCompatActivity {
// EditText email , password;
// Button btnSubmit;
// TextView createAcc;
// DBHelper dbHelper;
//
//
// @Override
// protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// Boolean e=false,p=false;
// setContentView(R.layout.activity_login);
// email=findViewById(R.id.user_name);
// password=findViewById(R.id.text_password);
// btnSubmit = findViewById(R.id.btnSubmit_login);
// dbHelper = new DBHelper(this);
// btnSubmit.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
//
// String emailCheck = email.getText().toString();
// String passCheck = password.getText().toString();
// Cursor cursor = dbHelper.getData();
// if(cursor.getCount() == 0){
// Toast.makeText(Login.this,"No entries Exists",Toast.LENGTH_LONG).show();
// }
// if (loginCheck(cursor,emailCheck,passCheck)) {
// Intent intent = new Intent(Login.this,FinalPage.class);
// intent.putExtra("email",emailCheck);
// email.setText("");
// password.setText("");
// startActivity(intent);
// }else {
// AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
// builder.setCancelable(true);
// builder.setTitle("Wrong Credential");
// builder.setMessage("Wrong Credential");
// builder.show();
// }
// dbHelper.close();
// }
// });
// createAcc=findViewById(R.id.createAcc);
// createAcc.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// Intent intent = new Intent(Login.this,SignUp.class);
// startActivity(intent);
// }
// });
//
// }
// public static boolean loginCheck(Cursor cursor,String emailCheck,String passCheck) {
// while (cursor.moveToNext()){
// if (cursor.getString(0).equals(emailCheck)) {
// if (cursor.getString(2).equals(passCheck)) {
// return true;
// }
// return false;
// }
// }
// return false;
// }
}
\ No newline at end of file
//package com.example.thetrek;
//
//import android.content.Intent;
//import android.os.Bundle;
//import android.view.View;
//import android.widget.Button;
//
//import androidx.appcompat.app.AppCompatActivity;
//import androidx.appcompat.widget.Toolbar;
//
//import com.example.thetrek.Sql.DBHelper;
//
//public class MainActivity extends AppCompatActivity {
// Button login,Reg;
// Toolbar toolbar;
// DBHelper dbHelper;
//
// @Override
// public void onBackPressed() {
// MainActivity.this.finish();
// }
//
// @Override
// protected void onCreate(Bundle savedInstanceState) {
//
// super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main1);
// dbHelper = new DBHelper(this);
// login =(Button) findViewById(R.id.btnSubmit_login);
//
// login.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// Intent intent = new Intent(MainActivity.this, MainActivityLogin.class);
// startActivity(intent);
// }
// });
// Reg = findViewById(R.id.createAcc);
// Reg.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// Intent intent = new Intent(MainActivity.this,SignUp.class);
// startActivity(intent);
// }
// });
//
// }}
package com.example.thetrek;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.example.thetrek.Sql.DBHelper;
public class MainActivity extends AppCompatActivity {
Button login, Reg;
Toolbar toolbar;
DBHelper dbHelper;
@Override
public void onBackPressed() {
MainActivity.this.finish();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main1);
dbHelper = new DBHelper(this);
// Add this code to start the service
startService(new Intent(this, UsageMonitorService.class));
login = (Button) findViewById(R.id.btnSubmit_login);
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, MainActivityLogin.class);
startActivity(intent);
}
});
Reg = findViewById(R.id.createAcc);
Reg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, SignUp.class);
startActivity(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// Add this code to stop the service when the activity is destroyed
stopService(new Intent(this, UsageMonitorService.class));
}
}
package com.example.thetrek;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.example.thetrek.screens.mobileUsage.ScreenMonitoringService;
public class MainActivityLogin extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
getSupportActionBar().hide();
startService(new Intent(this, ScreenMonitoringService.class));
Button loginButton = (Button) findViewById(R.id.btnSubmit_login);
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), HomeActivity.class);
v.getContext().startActivity(intent);
}
});
}
}
\ No newline at end of file
package com.example.thetrek;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.provider.Settings;
import android.text.InputType;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class MainActivityRec extends AppCompatActivity {
private String TAG = "MainActivityRec";
private int READ_PERMISSION_CODE = 1001;
private int WRITE_PERMISSION_CODE = 1002;
private int MANAGE_PERMISSION_CODE = 1003;
private String dataDirName = "HAR_Recordings";
//UI Variables
private Button recordButton;
private TextView statusTextView;
private Spinner label_spinner;
private Sensor linear_acceleration;
private Sensor gyroscope;
private Sensor accelerometer;
private SensorManager sensorManager;
// Sensor Event Listeners
private OnSensorDataListener onSensorDataListener;
private CountDownTimer timer;
private float[][] values = new float[3][3];
// selective variables
private int SENSOR_REFRESH_RATE = 1;
private boolean isRunning = false;
private String label;
// Data List
private ArrayList<String> acc_data;
private ArrayList<String> gyro_data;
private ArrayList<String> la_data;
// Thread for updating values in UI
private Thread refreshThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_rec);
recordButton = findViewById(R.id.record_button);
statusTextView = findViewById(R.id.status_textView);
label_spinner = findViewById(R.id.label_spinner);
// Sensors variables
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
linear_acceleration = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
// Place label in spinner
label_spinner.setAdapter(getLabelList());
label_spinner.setOnItemSelectedListener(new OnSpinnerItemSelected());
// Listeners
onSensorDataListener = new OnSensorDataListener();
// Lists
acc_data = new ArrayList<>();
gyro_data = new ArrayList<>();
la_data = new ArrayList<>();
}
public void onRecordButtonClickListener(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", this.getPackageName(), null);
intent.setData(uri);
startActivity(intent);
return;
}
} else {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle("Read/Write permission")
.setMessage(getString(R.string.rw_permission_string))
.setPositiveButton("Ok", (dialogInterface, i) -> ActivityCompat.requestPermissions(MainActivityRec.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, READ_PERMISSION_CODE))
.create();
dialog.show();
return;
}
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
ActivityCompat.requestPermissions(MainActivityRec.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_PERMISSION_CODE);
return;
}
}
if (isRunning) {
refreshThread.interrupt();
Log.i(TAG, "onRecordButtonClickListener: " + refreshThread.isAlive() + " " + refreshThread.getState());
try {
refreshThread.join();
Log.i(TAG, "onRecordButtonClickListener: " + refreshThread.isAlive() + " " + refreshThread.getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
stopRecording();
RecordingDB recordingDB = new RecordingDB(this, acc_data, gyro_data, la_data, label);
recordingDB.saveData("record_" + label + "_" + System.currentTimeMillis());
for (String i : acc_data) {
Log.i(TAG, "onRecordButtonClickListener: " + i);
}
return;
}
if (label != null && label.equals("Choose label")) {
Toast.makeText(this, "Choose label as activity", Toast.LENGTH_SHORT).show();
return;
}
File dataDir = new File(Environment.getExternalStorageDirectory() + File.separator + dataDirName);
if (!dataDir.isDirectory()) {
dataDir.mkdir();
return;
}
acc_data.clear();
gyro_data.clear();
la_data.clear();
timer = new CountDownTimer(3000, 1000) {
@Override
public void onTick(long l) {
statusTextView.setText("Starting: " + (int) Math.ceil(l / 1000.0));
}
@Override
public void onFinish() {
registerListeners(sensorManager);
recordButton.setText(getString(R.string.recording));
refreshThread = new Thread(refresh());
refreshThread.start();
}
};
timer.start();
}
private void stopRecording() {
unregisterListener();
recordButton.setText(getString(R.string.record));
statusTextView.setText(getString(R.string.recording_stopped));
}
// Menu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.add_label) {
createAddLabelDialog().show();
return true;
}
return false;
}
private AlertDialog createAddLabelDialog() {
EditText editText = new EditText(this);
editText.setInputType(InputType.TYPE_TEXT_FLAG_CAP_WORDS);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
editText.setLayoutParams(layoutParams);
return new AlertDialog.Builder(this)
.setTitle("Enter label")
.setView(editText)
.setPositiveButton("Add", (dialogInterface, i) -> {
String text = editText.getText().toString().trim();
if (text.length() == 0) {
Toast.makeText(MainActivityRec.this, "Empty label can't be added", Toast.LENGTH_SHORT).show();
} else {
saveNewLabel(text);
label_spinner.setAdapter(getLabelList());
Toast.makeText(MainActivityRec.this, text + " added as new label", Toast.LENGTH_SHORT).show();
}
})
.setNegativeButton("Cancel", (dialogInterface, i) -> dialogInterface.dismiss())
.setCancelable(true)
.create();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == READ_PERMISSION_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, "Read/Write permission denied", Toast.LENGTH_SHORT).show();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
private void registerListeners(SensorManager sensorManager) {
isRunning = true;
sensorManager.registerListener(onSensorDataListener, accelerometer, SENSOR_REFRESH_RATE);
sensorManager.registerListener(onSensorDataListener, gyroscope, SENSOR_REFRESH_RATE);
sensorManager.registerListener(onSensorDataListener, linear_acceleration, SENSOR_REFRESH_RATE);
}
private void unregisterListener() {
isRunning = false;
sensorManager.unregisterListener(onSensorDataListener);
}
private ArrayAdapter<String> getLabelList() {
String[] labels = {"Running", "Standing", "Sitting", "Walking"};
SharedPreferences sharedPreferences = getSharedPreferences("label_prefs", MODE_PRIVATE);
ArrayList<String> label_list = new ArrayList<>(Arrays.asList(labels));
label_list.addAll(sharedPreferences.getAll().keySet());
Collections.sort(label_list);
label_list.add(0, "Choose label");
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, label_list);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
return adapter;
}
private void saveNewLabel(String label) {
SharedPreferences sharedPreferences = getSharedPreferences("label_prefs", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(label, label);
editor.apply();
}
private Runnable refresh() {
Runnable runnable = new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(2);
runOnUiThread(new Runnable() {
@Override
public void run() {
statusTextView.setText(
"Accelerometer: " + values[0][0] + " " + values[0][1] + " " + values[0][2] + "\n" +
"Gyroscope: " + values[1][0] + " " + values[1][1] + " " + values[1][2] + "\n" +
"Linear Acceleration: " + values[2][0] + " " + values[2][1] + " " + values[2][2] +
"\nHit recording button to stop");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
};
return runnable;
}
public void onRadioButtonClicked(View view) {
boolean checked = ((RadioButton) view).isChecked();
if (view.getId() == R.id.normal_radio)
SENSOR_REFRESH_RATE = SensorManager.SENSOR_DELAY_NORMAL;
if (view.getId() == R.id.fast_radio) SENSOR_REFRESH_RATE = SensorManager.SENSOR_DELAY_GAME;
if (view.getId() == R.id.fastest_radio)
SENSOR_REFRESH_RATE = SensorManager.SENSOR_DELAY_FASTEST;
}
private class OnSpinnerItemSelected implements AdapterView.OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
label = adapterView.getItemAtPosition(i).toString();
Log.i(TAG, "onItemSelected: " + label);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}
private class OnSensorDataListener implements SensorEventListener {
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
values[0] = sensorEvent.values;
acc_data.add(Arrays.toString(sensorEvent.values));
}
if (sensorEvent.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
values[1] = sensorEvent.values;
gyro_data.add(Arrays.toString(sensorEvent.values));
}
if (sensorEvent.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
values[2] = sensorEvent.values;
la_data.add(Arrays.toString(sensorEvent.values));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
@Override
protected void onStop() {
super.onStop();
unregisterListener();
}
}
package com.example.thetrek;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.view.View;
import android.widget.Button;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.res.ResourcesCompat;
import com.example.thetrek.screens.mobileUsage.MobileUsageActivity;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivityRecognition extends AppCompatActivity implements SensorEventListener, TextToSpeech.OnInitListener {
private static final int N_SAMPLES = 100;
private static int prevIdx = -1;
private static List<Float> ax;
private static List<Float> ay;
private static List<Float> az;
private static List<Float> lx;
private static List<Float> ly;
private static List<Float> lz;
private static List<Float> gx;
private static List<Float> gy;
private static List<Float> gz;
private static List<Float> ma;
private static List<Float> ml;
private static List<Float> mg;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Sensor mGyroscope;
private Sensor mLinearAcceleration;
private TextView bikingTextView;
private TextView downstairsTextView;
private TextView joggingTextView;
private TextView sittingTextView;
private TextView standingTextView;
private TextView upstairsTextView;
private TextView walkingTextView;
private TableRow bikingTableRow;
private TableRow downstairsTableRow;
private TableRow joggingTableRow;
private TableRow sittingTableRow;
private TableRow standingTableRow;
private TableRow upstairsTableRow;
private TableRow walkingTableRow;
private TextToSpeech textToSpeech;
private float[] results;
private HARClassifier classifier;
private String[] labels = {"Biking", "Downstairs", "Jogging", "Sitting", "Standing", "Upstairs", "Walking"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_recognize);
ax = new ArrayList<>(); ay = new ArrayList<>(); az = new ArrayList<>();
lx = new ArrayList<>(); ly = new ArrayList<>(); lz = new ArrayList<>();
gx = new ArrayList<>(); gy = new ArrayList<>(); gz = new ArrayList<>();
ma = new ArrayList<>(); ml = new ArrayList<>(); mg = new ArrayList<>();
bikingTextView = (TextView) findViewById(R.id.biking_prob);
downstairsTextView = (TextView) findViewById(R.id.downstairs_prob);
joggingTextView = (TextView) findViewById(R.id.jogging_prob);
sittingTextView = (TextView) findViewById(R.id.sitting_prob);
standingTextView = (TextView) findViewById(R.id.standing_prob);
upstairsTextView = (TextView) findViewById(R.id.upstairs_prob);
walkingTextView = (TextView) findViewById(R.id.walking_prob);
bikingTableRow = (TableRow) findViewById(R.id.biking_row);
downstairsTableRow = (TableRow) findViewById(R.id.downstairs_row);
joggingTableRow = (TableRow) findViewById(R.id.jogging_row);
sittingTableRow = (TableRow) findViewById(R.id.sitting_row);
standingTableRow = (TableRow) findViewById(R.id.standing_row);
upstairsTableRow = (TableRow) findViewById(R.id.upstairs_row);
walkingTableRow = (TableRow) findViewById(R.id.walking_row);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer , SensorManager.SENSOR_DELAY_FASTEST);
mLinearAcceleration = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
mSensorManager.registerListener(this, mLinearAcceleration , SensorManager.SENSOR_DELAY_FASTEST);
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensorManager.registerListener(this, mGyroscope , SensorManager.SENSOR_DELAY_FASTEST);
classifier = new HARClassifier(getApplicationContext());
textToSpeech = new TextToSpeech(this, this);
textToSpeech.setLanguage(Locale.US);
}
@Override
public void onInit(int status) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (results == null || results.length == 0) {
return;
}
float max = -1;
int idx = -1;
for (int i = 0; i < results.length; i++) {
if (results[i] > max) {
idx = i;
max = results[i];
}
}
if(max > 0.50 && idx != prevIdx) {
textToSpeech.speak(labels[idx], TextToSpeech.QUEUE_ADD, null,
Integer.toString(new Random().nextInt()));
prevIdx = idx;
}
}
}, 1000, 3000);
}
protected void onResume() {
super.onResume();
getSensorManager().registerListener(this, getSensorManager().getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST);
getSensorManager().registerListener(this, getSensorManager().getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION), SensorManager.SENSOR_DELAY_FASTEST);
getSensorManager().registerListener(this, getSensorManager().getDefaultSensor(Sensor.TYPE_GYROSCOPE), SensorManager.SENSOR_DELAY_FASTEST);
}
@Override
public void onDestroy() {
if (textToSpeech != null) {
textToSpeech.stop();
textToSpeech.shutdown();
}
super.onDestroy();
}
@Override
public void onSensorChanged(SensorEvent event) {
activityPrediction();
Sensor sensor = event.sensor;
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
ax.add(event.values[0]);
ay.add(event.values[1]);
az.add(event.values[2]);
} else if (sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
lx.add(event.values[0]);
ly.add(event.values[1]);
lz.add(event.values[2]);
} else if (sensor.getType() == Sensor.TYPE_GYROSCOPE) {
gx.add(event.values[0]);
gy.add(event.values[1]);
gz.add(event.values[2]);
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
private void activityPrediction() {
List<Float> data = new ArrayList<>();
if (ax.size() >= N_SAMPLES && ay.size() >= N_SAMPLES && az.size() >= N_SAMPLES
&& lx.size() >= N_SAMPLES && ly.size() >= N_SAMPLES && lz.size() >= N_SAMPLES
&& gx.size() >= N_SAMPLES && gy.size() >= N_SAMPLES && gz.size() >= N_SAMPLES
) {
double maValue; double mgValue; double mlValue;
for( int i = 0; i < N_SAMPLES ; i++ ) {
maValue = Math.sqrt(Math.pow(ax.get(i), 2) + Math.pow(ay.get(i), 2) + Math.pow(az.get(i), 2));
mlValue = Math.sqrt(Math.pow(lx.get(i), 2) + Math.pow(ly.get(i), 2) + Math.pow(lz.get(i), 2));
mgValue = Math.sqrt(Math.pow(gx.get(i), 2) + Math.pow(gy.get(i), 2) + Math.pow(gz.get(i), 2));
ma.add((float)maValue);
ml.add((float)mlValue);
mg.add((float)mgValue);
}
data.addAll(ax.subList(0, N_SAMPLES));
data.addAll(ay.subList(0, N_SAMPLES));
data.addAll(az.subList(0, N_SAMPLES));
data.addAll(lx.subList(0, N_SAMPLES));
data.addAll(ly.subList(0, N_SAMPLES));
data.addAll(lz.subList(0, N_SAMPLES));
data.addAll(gx.subList(0, N_SAMPLES));
data.addAll(gy.subList(0, N_SAMPLES));
data.addAll(gz.subList(0, N_SAMPLES));
data.addAll(ma.subList(0, N_SAMPLES));
data.addAll(ml.subList(0, N_SAMPLES));
data.addAll(mg.subList(0, N_SAMPLES));
results = classifier.predictProbabilities(toFloatArray(data));
float max = -1;
int idx = -1;
for (int i = 0; i < results.length; i++) {
if (results[i] > max) {
idx = i;
max = results[i];
}
}
setProbabilities();
setRowsColor(idx);
ax.clear(); ay.clear(); az.clear();
lx.clear(); ly.clear(); lz.clear();
gx.clear(); gy.clear(); gz.clear();
ma.clear(); ml.clear(); mg.clear();
}
}
private void setProbabilities() {
bikingTextView.setText(Float.toString(round(results[0], 2)));
downstairsTextView.setText(Float.toString(round(results[1], 2)));
joggingTextView.setText(Float.toString(round(results[2], 2)));
sittingTextView.setText(Float.toString(round(results[3], 2)));
standingTextView.setText(Float.toString(round(results[4], 2)));
upstairsTextView.setText(Float.toString(round(results[5], 2)));
walkingTextView.setText(Float.toString(round(results[6], 2)));
}
private void setRowsColor(int idx) {
bikingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorTransparent, null));
downstairsTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorTransparent, null));
joggingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorTransparent, null));
sittingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorTransparent, null));
standingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorTransparent, null));
upstairsTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorTransparent, null));
walkingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorTransparent, null));
if(idx == 0)
//bikingTextView.setBackgroundColor(Color.parseColor("#33B5E5"));
bikingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorBlue, null));
else if (idx == 1)
downstairsTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorBlue, null));
else if (idx == 2)
joggingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorBlue, null));
else if (idx == 3)
sittingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorBlue, null));
else if (idx == 4)
standingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorBlue, null));
else if (idx == 5)
upstairsTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorBlue, null));
else if (idx == 6)
walkingTableRow.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorBlue, null));
}
private float[] toFloatArray(List<Float> list) {
int i = 0;
float[] array = new float[list.size()];
for (Float f : list) {
array[i++] = (f != null ? f : Float.NaN);
}
return array;
}
private static float round(float d, int decimalPlace) {
BigDecimal bd = new BigDecimal(Float.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.floatValue();
}
private SensorManager getSensorManager() {
return (SensorManager) getSystemService(SENSOR_SERVICE);
}
}
package com.example.thetrek;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MonitorActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, UsageMonitorService.class));
}
@Override
protected void onDestroy() {
super.onDestroy();
stopService(new Intent(this, UsageMonitorService.class));
}
}
package com.example.thetrek;
import android.content.Context;
import android.os.Environment;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
public class RecordingDB {
private static final String TAG = "RecordingDB";
// Key variable
private final String ACC_X = "acc_x";
private final String ACC_Y = "acc_y";
private final String ACC_Z = "acc_z";
private final String GYRO_X = "gyro_x";
private final String GYRO_Y = "gyro_y";
private final String GYRO_Z = "gyro_z";
private final String LA_X = "la_x";
private final String LA_Y = "la_y";
private final String LA_Z = "la_z";
private final String LABEL_TEXT = "Activity";
private final String COMMA_SEP = ",";
private final Context context;
private final ArrayList<String> acc_data;
private final ArrayList<String> gyro_data;
private final ArrayList<String> la_data;
private final String label;
public RecordingDB(Context context, ArrayList<String> acc_data, ArrayList<String> gyro_data, ArrayList<String> la_data, String label) {
this.context = context;
this.acc_data = acc_data;
this.gyro_data = gyro_data;
this.la_data = la_data;
this.label = label;
}
public int saveData(String filename) {
int min = Math.min(acc_data.size(), Math.min(gyro_data.size(), la_data.size()));
if (acc_data.size() > min) for (int i = 0; i <= Math.abs(acc_data.size() - min); i++)
acc_data.remove(acc_data.size() - 1);
if (gyro_data.size() > min) for (int i = 0; i <= Math.abs(gyro_data.size() - min); i++)
gyro_data.remove(gyro_data.size() - 1);
if (la_data.size() > min) for (int i = 0; i <= Math.abs(la_data.size() - min); i++)
la_data.remove(la_data.size() - 1);
File outputFile = new File(Environment.getExternalStorageDirectory() + File.separator + "HAR_Recordings" + File.separator + filename + ".csv");
try {
PrintWriter writer = new PrintWriter(outputFile);
writer.println(ACC_X + COMMA_SEP + ACC_Y + COMMA_SEP + ACC_Z + COMMA_SEP +
GYRO_X + COMMA_SEP + GYRO_Y + COMMA_SEP + GYRO_Z + COMMA_SEP +
LA_X + COMMA_SEP + LA_Y + COMMA_SEP + LA_Z + COMMA_SEP + LABEL_TEXT);
for (int i = 0; i < min; i++) {
String a_data = acc_data.get(i).replace("[", "").replace("]", "");
String g_data = gyro_data.get(i).replace("[", "").replace("]", "");
String l_data = la_data.get(i).replace("[", "").replace("]", "");
writer.println(a_data + COMMA_SEP + g_data + COMMA_SEP + l_data + COMMA_SEP + label);
}
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return min;
}
}
package com.example.thetrek;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.thetrek.Sql.DBHelper;
public class SignUp extends AppCompatActivity {
EditText name , number , email,pass;
TextView login;
DBHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
name=findViewById(R.id.textName);
number=findViewById(R.id.textNumber);
email=findViewById(R.id.textEmail);
pass=findViewById(R.id.textPass);
Button signUpAcc = findViewById(R.id.btnSignUpAcc);
dbHelper = new DBHelper(this);
signUpAcc.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String name1 = name.getText().toString();
String number1 = number.getText().toString();
String email1 = email.getText().toString();
String pass1 = pass.getText().toString();
boolean b =dbHelper.insetUserData(name1,number1,email1,pass1);
if (b){
Toast.makeText(SignUp.this,"Data inserted",Toast.LENGTH_SHORT).show();
Intent i = new Intent(SignUp.this,Login.class);
startActivity(i);
}else {
Toast.makeText(SignUp.this,"Failed To insert Data",Toast.LENGTH_SHORT).show();
}
}
});
login=findViewById(R.id.loginAcc);
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(SignUp.this,MainActivityLogin.class);
startActivity(i);
}
});
}
}
\ No newline at end of file
package com.example.thetrek.Sql;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context ) {
super(context,"UserData",null, 1);
}
@Override
public void onCreate(SQLiteDatabase DB) {
DB.execSQL("create Table UserDetails(userID TEXT primary key,name TEXT,password PASSWORD,number NUMBER)");
}
@Override
public void onUpgrade(SQLiteDatabase DB, int i, int i1) {
DB.execSQL("drop Table if exists UserDetails");
}
public Boolean insetUserData(String name,String number,String email,String password){
SQLiteDatabase DB = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("userID",email);
contentValues.put("name",name);
contentValues.put("password",password);
contentValues.put("number",number);
long result= DB.insert("UserDetails",null,contentValues);
if (result == -1){
return false;
}else {
return true;
}
}
public Cursor getData(){
SQLiteDatabase DB = this.getWritableDatabase();
Cursor cursor = DB.rawQuery("Select * from Userdetails ",null);
return cursor;
}
}
\ No newline at end of file
package com.example.thetrek;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class SuggestActivity extends AppCompatActivity {
EditText age,gender,weather, Place_change, Time;
Button predict;
TextView result;
String url = "https://researchsuggestapp-2056b0768919.herokuapp.com/predict";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_suggest);
age = findViewById(R.id.age);
gender = findViewById(R.id.gender);
weather = findViewById(R.id.weather);
Place_change = findViewById(R.id.Place_change);
Time = findViewById(R.id.Time);
predict = findViewById(R.id.predict);
result = findViewById(R.id.result);
predict.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// hit the API -> Volley
StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
String data = jsonObject.getString("Suguest");
if(data.equals("1")){
result.setText("\uD83C\uDF1F \"You Need to Move!\" \uD83C\uDF1F");
}else{
result.setText("\uD83C\uDF1F \"No Need to Move.\" \uD83C\uDF1F");
}
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(SuggestActivity.this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
}){
@Override
protected Map<String,String> getParams(){
Map<String,String> params = new HashMap<String,String>();
params.put("age",age.getText().toString());
params.put("gender",gender.getText().toString());
params.put("weather",weather.getText().toString());
params.put("Place_change", Place_change.getText().toString());
params.put("Time",Time.getText().toString());
return params;
}
};
RequestQueue queue = Volley.newRequestQueue(SuggestActivity.this);
queue.add(stringRequest);
}
});
}
}
\ No newline at end of file
package com.example.thetrek;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.IBinder;
import androidx.core.app.NotificationCompat;
public class UsageMonitorService extends Service {
private static final String TAG = "UsageMonitorService";
private static final int NOTIFICATION_ID = 1;
private static final String CHANNEL_ID = "UsageMonitorChannel";
private static final long USAGE_THRESHOLD = 2 * 60 * 1000; // 2 minutes in milliseconds
private boolean isScreenOn = true;
private long usageStartTime = 0;
private Handler handler;
private Runnable checkUsageRunnable;
@Override
public void onCreate() {
super.onCreate();
handler = new Handler();
checkUsageRunnable = new Runnable() {
@Override
public void run() {
if (isScreenOn) {
if (usageStartTime == 0) {
usageStartTime = System.currentTimeMillis();
} else {
long currentTime = System.currentTimeMillis();
long usageTime = currentTime - usageStartTime;
if (usageTime > USAGE_THRESHOLD) {
sendNotification();
}
}
} else {
usageStartTime = 0;
}
handler.postDelayed(this, 1000); // Check every second
}
};
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
startForeground(NOTIFICATION_ID, createNotification());
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenReceiver, filter);
handler.post(checkUsageRunnable);
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(screenReceiver);
handler.removeCallbacks(checkUsageRunnable);
}
private void createNotificationChannel() {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"Usage Monitor Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
private Notification createNotification() {
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Mobile Usage Monitor")
.setContentText("Monitoring your mobile usage...")
.setSmallIcon(R.mipmap.ic_launcher2)
.setContentIntent(pendingIntent)
.build();
}
private void sendNotification() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher1)
.setContentTitle("Mobile Usage Alert")
.setContentText("📱🕒 You've been glued to your phone for over 2 minutes! 🚶‍♂️ It's time to stretch those legs and get moving! 🏃‍♀️💨")
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
private final BroadcastReceiver screenReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
isScreenOn = true;
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
isScreenOn = false;
}
}
}
};
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
/*
* Copyright 2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
/** Helper to ask camera permission. */
public final class CameraPermissionHelper {
private static final int CAMERA_PERMISSION_CODE = 0;
private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA;
/** Check to see we have the necessary permissions for this app. */
public static boolean hasCameraPermission(Activity activity) {
return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
/** Check to see we have the necessary permissions for this app, and ask for them if we don't. */
public static void requestCameraPermission(Activity activity) {
ActivityCompat.requestPermissions(
activity, new String[] {CAMERA_PERMISSION}, CAMERA_PERMISSION_CODE);
}
/** Check to see if we need to show the rationale for this permission. */
public static boolean shouldShowRequestPermissionRationale(Activity activity) {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, CAMERA_PERMISSION);
}
/** Launch Application Setting to grant permission. */
public static void launchPermissionSettings(Activity activity) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
activity.startActivity(intent);
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.content.Context;
import android.content.SharedPreferences;
/** Manages the Occlusion option setting and shared preferences. */
public class DepthSettings {
public static final String SHARED_PREFERENCES_ID = "SHARED_PREFERENCES_OCCLUSION_OPTIONS";
public static final String SHARED_PREFERENCES_SHOW_DEPTH_ENABLE_DIALOG_OOBE =
"show_depth_enable_dialog_oobe";
public static final String SHARED_PREFERENCES_USE_DEPTH_FOR_OCCLUSION = "use_depth_for_occlusion";
// Current depth-based settings used by the app.
private boolean depthColorVisualizationEnabled = false;
private boolean useDepthForOcclusion = false;
private SharedPreferences sharedPreferences;
/** Initializes the current settings based on when the app was last used. */
public void onCreate(Context context) {
sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_ID, Context.MODE_PRIVATE);
useDepthForOcclusion =
sharedPreferences.getBoolean(SHARED_PREFERENCES_USE_DEPTH_FOR_OCCLUSION, false);
}
/** Retrieves whether depth-based occlusion is enabled. */
public boolean useDepthForOcclusion() {
return useDepthForOcclusion;
}
public void setUseDepthForOcclusion(boolean enable) {
if (enable == useDepthForOcclusion) {
return; // No change.
}
// Updates the stored default settings.
useDepthForOcclusion = enable;
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(SHARED_PREFERENCES_USE_DEPTH_FOR_OCCLUSION, useDepthForOcclusion);
editor.apply();
}
/** Retrieves whether to render the depth map visualization instead of the camera feed. */
public boolean depthColorVisualizationEnabled() {
return depthColorVisualizationEnabled;
}
public void setDepthColorVisualizationEnabled(boolean depthColorVisualizationEnabled) {
this.depthColorVisualizationEnabled = depthColorVisualizationEnabled;
}
/** Determines if the initial prompt to use depth-based occlusion should be shown. */
public boolean shouldShowDepthEnableDialog() {
// Checks if this dialog has been called before on this device.
boolean showDialog =
sharedPreferences.getBoolean(SHARED_PREFERENCES_SHOW_DEPTH_ENABLE_DIALOG_OOBE, true);
if (showDialog) {
// Only ever shows the dialog on the first time. If the user wants to adjust these settings
// again, they can use the gear icon to invoke the settings menu dialog.
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(SHARED_PREFERENCES_SHOW_DEPTH_ENABLE_DIALOG_OOBE, false);
editor.apply();
}
return showDialog;
}
}
/*
* Copyright 2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.app.Activity;
import android.content.Context;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import com.google.ar.core.Session;
/**
* Helper to track the display rotations. In particular, the 180 degree rotations are not notified
* by the onSurfaceChanged() callback, and thus they require listening to the android display
* events.
*/
public final class DisplayRotationHelper implements DisplayListener {
private boolean viewportChanged;
private int viewportWidth;
private int viewportHeight;
private final Display display;
private final DisplayManager displayManager;
private final CameraManager cameraManager;
/**
* Constructs the DisplayRotationHelper but does not register the listener yet.
*
* @param context the Android {@link Context}.
*/
public DisplayRotationHelper(Context context) {
displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
display = windowManager.getDefaultDisplay();
}
/** Registers the display listener. Should be called from {@link Activity#onResume()}. */
public void onResume() {
displayManager.registerDisplayListener(this, null);
}
/** Unregisters the display listener. Should be called from {@link Activity#onPause()}. */
public void onPause() {
displayManager.unregisterDisplayListener(this);
}
/**
* Records a change in surface dimensions. This will be later used by {@link
* #updateSessionIfNeeded(Session)}. Should be called from {@link
* android.opengl.GLSurfaceView.Renderer
* #onSurfaceChanged(javax.microedition.khronos.opengles.GL10, int, int)}.
*
* @param width the updated width of the surface.
* @param height the updated height of the surface.
*/
public void onSurfaceChanged(int width, int height) {
viewportWidth = width;
viewportHeight = height;
viewportChanged = true;
}
/**
* Updates the session display geometry if a change was posted either by {@link
* #onSurfaceChanged(int, int)} call or by {@link #onDisplayChanged(int)} system callback. This
* function should be called explicitly before each call to {@link Session#update()}. This
* function will also clear the 'pending update' (viewportChanged) flag.
*
* @param session the {@link Session} object to update if display geometry changed.
*/
public void updateSessionIfNeeded(Session session) {
if (viewportChanged) {
int displayRotation = display.getRotation();
session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight);
viewportChanged = false;
}
}
/**
* Returns the aspect ratio of the GL surface viewport while accounting for the display rotation
* relative to the device camera sensor orientation.
*/
public float getCameraSensorRelativeViewportAspectRatio(String cameraId) {
float aspectRatio;
int cameraSensorToDisplayRotation = getCameraSensorToDisplayRotation(cameraId);
switch (cameraSensorToDisplayRotation) {
case 90:
case 270:
aspectRatio = (float) viewportHeight / (float) viewportWidth;
break;
case 0:
case 180:
aspectRatio = (float) viewportWidth / (float) viewportHeight;
break;
default:
throw new RuntimeException("Unhandled rotation: " + cameraSensorToDisplayRotation);
}
return aspectRatio;
}
/**
* Returns the rotation of the back-facing camera with respect to the display. The value is one of
* 0, 90, 180, 270.
*/
public int getCameraSensorToDisplayRotation(String cameraId) {
CameraCharacteristics characteristics;
try {
characteristics = cameraManager.getCameraCharacteristics(cameraId);
} catch (CameraAccessException e) {
throw new RuntimeException("Unable to determine display orientation", e);
}
// Camera sensor orientation.
int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
// Current display orientation.
int displayOrientation = toDegrees(display.getRotation());
// Make sure we return 0, 90, 180, or 270 degrees.
return (sensorOrientation - displayOrientation + 360) % 360;
}
private int toDegrees(int rotation) {
switch (rotation) {
case Surface.ROTATION_0:
return 0;
case Surface.ROTATION_90:
return 90;
case Surface.ROTATION_180:
return 180;
case Surface.ROTATION_270:
return 270;
default:
throw new RuntimeException("Unknown rotation " + rotation);
}
}
@Override
public void onDisplayAdded(int displayId) {}
@Override
public void onDisplayRemoved(int displayId) {}
@Override
public void onDisplayChanged(int displayId) {
viewportChanged = true;
}
}
/*
* Copyright 2023 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.content.Context;
import android.content.SharedPreferences;
/**
* A class providing persistent EIS preference across instances using {@code
* android.content.SharedPreferences}.
*/
public class EisSettings {
public static final String SHARED_PREFERENCE_ID = "SHARED_PREFERENCE_EIS_OPTIONS";
public static final String SHARED_PREFERENCE_EIS_ENABLED = "eis_enabled";
private boolean eisEnabled = false;
private SharedPreferences sharedPreferences;
/** Creates shared preference entry for EIS setting. */
public void onCreate(Context context) {
sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCE_ID, Context.MODE_PRIVATE);
eisEnabled = sharedPreferences.getBoolean(SHARED_PREFERENCE_EIS_ENABLED, false);
}
/** Returns saved EIS state. */
public boolean isEisEnabled() {
return eisEnabled;
}
/** Sets and saves the EIS using {@code android.content.SharedPreferences} */
public void setEisEnabled(boolean enable) {
if (enable == eisEnabled) {
return;
}
eisEnabled = enable;
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(SHARED_PREFERENCE_EIS_ENABLED, eisEnabled);
editor.apply();
}
}
/*
* Copyright 2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.app.Activity;
import android.view.View;
/** Helper to set up the Android full screen mode. */
public final class FullScreenHelper {
/**
* Sets the Android fullscreen flags. Expected to be called from {@link
* Activity#onWindowFocusChanged(boolean hasFocus)}.
*
* @param activity the Activity on which the full screen mode will be set.
* @param hasFocus the hasFocus flag passed from the {@link Activity#onWindowFocusChanged(boolean
* hasFocus)} callback.
*/
public static void setFullScreenOnWindowFocusChanged(Activity activity, boolean hasFocus) {
if (hasFocus) {
// https://developer.android.com/training/system-ui/immersive.html#sticky
activity
.getWindow()
.getDecorView()
.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.content.Context;
import android.content.SharedPreferences;
/** Manages the Instant Placement option setting and shared preferences. */
public class InstantPlacementSettings {
public static final String SHARED_PREFERENCES_ID = "SHARED_PREFERENCES_INSTANT_PLACEMENT_OPTIONS";
public static final String SHARED_PREFERENCES_INSTANT_PLACEMENT_ENABLED =
"instant_placement_enabled";
private boolean instantPlacementEnabled = true;
private SharedPreferences sharedPreferences;
/** Initializes the current settings based on the saved value. */
public void onCreate(Context context) {
sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_ID, Context.MODE_PRIVATE);
instantPlacementEnabled =
sharedPreferences.getBoolean(SHARED_PREFERENCES_INSTANT_PLACEMENT_ENABLED, false);
}
/** Retrieves whether Instant Placement is enabled, */
public boolean isInstantPlacementEnabled() {
return instantPlacementEnabled;
}
public void setInstantPlacementEnabled(boolean enable) {
if (enable == instantPlacementEnabled) {
return; // No change.
}
// Updates the stored default settings.
instantPlacementEnabled = enable;
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(SHARED_PREFERENCES_INSTANT_PLACEMENT_ENABLED, instantPlacementEnabled);
editor.apply();
}
}
/*
* Copyright 2022 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
/** Helper to ask location permission. */
public final class LocationPermissionHelper {
private static final int LOCATION_PERMISSION_CODE = 1;
private static final String LOCATION_PERMISSION = Manifest.permission.ACCESS_FINE_LOCATION;
/** Check to see we have the necessary permissions for this app. */
public static boolean hasFineLocationPermission(Activity activity) {
return ContextCompat.checkSelfPermission(activity, LOCATION_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
/** Check to see we have the necessary permissions for this app, and ask for them if we don't. */
public static void requestFineLocationPermission(Activity activity) {
ActivityCompat.requestPermissions(
activity, new String[] {LOCATION_PERMISSION}, LOCATION_PERMISSION_CODE);
}
/** Check to see if the array of given permissions contain the location permission. */
public static boolean hasFineLocationPermissionsResponseInResult(String[] permissions) {
for (String permission : permissions) {
if (LOCATION_PERMISSION.equals(permission)) {
return true;
}
}
return false;
}
/** Check to see if we need to show the rationale for this permission. */
public static boolean shouldShowRequestPermissionRationale(Activity activity) {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, LOCATION_PERMISSION);
}
/** Launch Application Setting to grant permission. */
public static void launchPermissionSettings(Activity activity) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
activity.startActivity(intent);
}
}
/*
* Copyright 2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.app.Activity;
import android.view.View;
import android.widget.TextView;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
/**
* Helper to manage the sample snackbar. Hides the Android boilerplate code, and exposes simpler
* methods.
*/
public final class SnackbarHelper {
private static final int BACKGROUND_COLOR = 0xbf323232;
private Snackbar messageSnackbar;
private enum DismissBehavior { HIDE, SHOW, FINISH };
private int maxLines = 2;
private String lastMessage = "";
private View snackbarView;
public boolean isShowing() {
return messageSnackbar != null;
}
/** Shows a snackbar with a given message. */
public void showMessage(Activity activity, String message) {
if (!message.isEmpty() && (!isShowing() || !lastMessage.equals(message))) {
lastMessage = message;
show(activity, message, DismissBehavior.HIDE);
}
}
/** Shows a snackbar with a given message, and a dismiss button. */
public void showMessageWithDismiss(Activity activity, String message) {
show(activity, message, DismissBehavior.SHOW);
}
/** Shows a snackbar with a given message for Snackbar.LENGTH_SHORT milliseconds */
public void showMessageForShortDuration(Activity activity, String message) {
show(activity, message, DismissBehavior.SHOW, Snackbar.LENGTH_SHORT);
}
/** Shows a snackbar with a given message for Snackbar.LENGTH_LONG milliseconds */
public void showMessageForLongDuration(Activity activity, String message) {
show(activity, message, DismissBehavior.SHOW, Snackbar.LENGTH_LONG);
}
/**
* Shows a snackbar with a given error message. When dismissed, will finish the activity. Useful
* for notifying errors, where no further interaction with the activity is possible.
*/
public void showError(Activity activity, String errorMessage) {
show(activity, errorMessage, DismissBehavior.FINISH);
}
/**
* Hides the currently showing snackbar, if there is one. Safe to call from any thread. Safe to
* call even if snackbar is not shown.
*/
public void hide(Activity activity) {
if (!isShowing()) {
return;
}
lastMessage = "";
Snackbar messageSnackbarToHide = messageSnackbar;
messageSnackbar = null;
activity.runOnUiThread(
new Runnable() {
@Override
public void run() {
messageSnackbarToHide.dismiss();
}
});
}
public void setMaxLines(int lines) {
maxLines = lines;
}
/**
* Sets the view that will be used to find a suitable parent view to hold the Snackbar view.
*
* <p>To use the root layout ({@link android.R.id.content}), pass in {@code null}.
*
* @param snackbarView the view to pass to {@link
* Snackbar#make(…)} which will be used to find a
* suitable parent, which is a {@link androidx.coordinatorlayout.widget.CoordinatorLayout}, or
* the window decor's content view, whichever comes first.
*/
public void setParentView(View snackbarView) {
this.snackbarView = snackbarView;
}
private void show(Activity activity, String message, DismissBehavior dismissBehavior) {
show(activity, message, dismissBehavior, Snackbar.LENGTH_INDEFINITE);
}
private void show(
final Activity activity,
final String message,
final DismissBehavior dismissBehavior,
int duration) {
activity.runOnUiThread(
new Runnable() {
@Override
public void run() {
messageSnackbar =
Snackbar.make(
snackbarView == null
? activity.findViewById(android.R.id.content)
: snackbarView,
message,
duration);
messageSnackbar.getView().setBackgroundColor(BACKGROUND_COLOR);
if (dismissBehavior != DismissBehavior.HIDE && duration == Snackbar.LENGTH_INDEFINITE) {
messageSnackbar.setAction(
"Dismiss",
new View.OnClickListener() {
@Override
public void onClick(View v) {
messageSnackbar.dismiss();
}
});
if (dismissBehavior == DismissBehavior.FINISH) {
messageSnackbar.addCallback(
new BaseTransientBottomBar.BaseCallback<Snackbar>() {
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
super.onDismissed(transientBottomBar, event);
activity.finish();
}
});
}
}
((TextView)
messageSnackbar
.getView()
.findViewById(com.google.android.material.R.id.snackbar_text))
.setMaxLines(maxLines);
messageSnackbar.show();
}
});
}
}
/*
* Copyright 2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* Helper to detect taps using Android GestureDetector, and pass the taps between UI thread and
* render thread.
*/
public final class TapHelper implements OnTouchListener {
private final GestureDetector gestureDetector;
private final BlockingQueue<MotionEvent> queuedSingleTaps = new ArrayBlockingQueue<>(16);
/**
* Creates the tap helper.
*
* @param context the application's context.
*/
public TapHelper(Context context) {
gestureDetector =
new GestureDetector(
context,
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
// Queue tap if there is space. Tap is lost if queue is full.
queuedSingleTaps.offer(e);
return true;
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
});
}
/**
* Polls for a tap.
*
* @return if a tap was queued, a MotionEvent for the tap. Otherwise null if no taps are queued.
*/
public MotionEvent poll() {
return queuedSingleTaps.poll();
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return gestureDetector.onTouchEvent(motionEvent);
}
}
/*
* Copyright 2019 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.helpers;
import android.app.Activity;
import android.view.WindowManager;
import com.google.ar.core.Camera;
import com.google.ar.core.TrackingFailureReason;
import com.google.ar.core.TrackingState;
/** Gets human readibly tracking failure reasons and suggested actions. */
public final class TrackingStateHelper {
private static final String INSUFFICIENT_FEATURES_MESSAGE =
"Can't find anything. Aim device at a surface with more texture or color.";
private static final String EXCESSIVE_MOTION_MESSAGE = "Moving too fast. Slow down.";
private static final String INSUFFICIENT_LIGHT_MESSAGE =
"Too dark. Try moving to a well-lit area.";
private static final String INSUFFICIENT_LIGHT_ANDROID_S_MESSAGE =
"Too dark. Try moving to a well-lit area."
+ " Also, make sure the Block Camera is set to off in system settings.";
private static final String BAD_STATE_MESSAGE =
"Tracking lost due to bad internal state. Please try restarting the AR experience.";
private static final String CAMERA_UNAVAILABLE_MESSAGE =
"Another app is using the camera. Tap on this app or try closing the other one.";
private static final int ANDROID_S_SDK_VERSION = 31;
private final Activity activity;
private TrackingState previousTrackingState;
public TrackingStateHelper(Activity activity) {
this.activity = activity;
}
/** Keep the screen unlocked while tracking, but allow it to lock when tracking stops. */
public void updateKeepScreenOnFlag(TrackingState trackingState) {
if (trackingState == previousTrackingState) {
return;
}
previousTrackingState = trackingState;
switch (trackingState) {
case PAUSED:
case STOPPED:
activity.runOnUiThread(
() -> activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
break;
case TRACKING:
activity.runOnUiThread(
() -> activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
break;
}
}
public static String getTrackingFailureReasonString(Camera camera) {
TrackingFailureReason reason = camera.getTrackingFailureReason();
switch (reason) {
case NONE:
return "";
case BAD_STATE:
return BAD_STATE_MESSAGE;
case INSUFFICIENT_LIGHT:
if (android.os.Build.VERSION.SDK_INT < ANDROID_S_SDK_VERSION) {
return INSUFFICIENT_LIGHT_MESSAGE;
} else {
return INSUFFICIENT_LIGHT_ANDROID_S_MESSAGE;
}
case EXCESSIVE_MOTION:
return EXCESSIVE_MOTION_MESSAGE;
case INSUFFICIENT_FEATURES:
return INSUFFICIENT_FEATURES_MESSAGE;
case CAMERA_UNAVAILABLE:
return CAMERA_UNAVAILABLE_MESSAGE;
}
return "Unknown tracking failure reason: " + reason;
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import android.opengl.GLES30;
import android.util.Log;
import java.io.Closeable;
/** A framebuffer associated with a texture. */
public class Framebuffer implements Closeable {
private static final String TAG = Framebuffer.class.getSimpleName();
private final int[] framebufferId = {0};
private final Texture colorTexture;
private final Texture depthTexture;
private int width = -1;
private int height = -1;
/**
* Constructs a {@link Framebuffer} which renders internally to a texture.
*
* <p>In order to render to the {@link Framebuffer}, use {@link SampleRender#draw(Mesh, Shader,
* Framebuffer)}.
*/
public Framebuffer(SampleRender render, int width, int height) {
try {
colorTexture =
new Texture(
render,
Texture.Target.TEXTURE_2D,
Texture.WrapMode.CLAMP_TO_EDGE,
/*useMipmaps=*/ false);
depthTexture =
new Texture(
render,
Texture.Target.TEXTURE_2D,
Texture.WrapMode.CLAMP_TO_EDGE,
/*useMipmaps=*/ false);
// Set parameters of the depth texture so that it's readable by shaders.
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, depthTexture.getTextureId());
GLError.maybeThrowGLException("Failed to bind depth texture", "glBindTexture");
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_COMPARE_MODE, GLES30.GL_NONE);
GLError.maybeThrowGLException("Failed to set texture parameter", "glTexParameteri");
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
GLError.maybeThrowGLException("Failed to set texture parameter", "glTexParameteri");
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_NEAREST);
GLError.maybeThrowGLException("Failed to set texture parameter", "glTexParameteri");
// Set initial dimensions.
resize(width, height);
// Create framebuffer object and bind to the color and depth textures.
GLES30.glGenFramebuffers(1, framebufferId, 0);
GLError.maybeThrowGLException("Framebuffer creation failed", "glGenFramebuffers");
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, framebufferId[0]);
GLError.maybeThrowGLException("Failed to bind framebuffer", "glBindFramebuffer");
GLES30.glFramebufferTexture2D(
GLES30.GL_FRAMEBUFFER,
GLES30.GL_COLOR_ATTACHMENT0,
GLES30.GL_TEXTURE_2D,
colorTexture.getTextureId(),
/*level=*/ 0);
GLError.maybeThrowGLException(
"Failed to bind color texture to framebuffer", "glFramebufferTexture2D");
GLES30.glFramebufferTexture2D(
GLES30.GL_FRAMEBUFFER,
GLES30.GL_DEPTH_ATTACHMENT,
GLES30.GL_TEXTURE_2D,
depthTexture.getTextureId(),
/*level=*/ 0);
GLError.maybeThrowGLException(
"Failed to bind depth texture to framebuffer", "glFramebufferTexture2D");
int status = GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER);
if (status != GLES30.GL_FRAMEBUFFER_COMPLETE) {
throw new IllegalStateException("Framebuffer construction not complete: code " + status);
}
} catch (Throwable t) {
close();
throw t;
}
}
@Override
public void close() {
if (framebufferId[0] != 0) {
GLES30.glDeleteFramebuffers(1, framebufferId, 0);
GLError.maybeLogGLError(Log.WARN, TAG, "Failed to free framebuffer", "glDeleteFramebuffers");
framebufferId[0] = 0;
}
colorTexture.close();
depthTexture.close();
}
/** Resizes the framebuffer to the given dimensions. */
public void resize(int width, int height) {
if (this.width == width && this.height == height) {
return;
}
this.width = width;
this.height = height;
// Color texture
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, colorTexture.getTextureId());
GLError.maybeThrowGLException("Failed to bind color texture", "glBindTexture");
GLES30.glTexImage2D(
GLES30.GL_TEXTURE_2D,
/*level=*/ 0,
GLES30.GL_RGBA,
width,
height,
/*border=*/ 0,
GLES30.GL_RGBA,
GLES30.GL_UNSIGNED_BYTE,
/*pixels=*/ null);
GLError.maybeThrowGLException("Failed to specify color texture format", "glTexImage2D");
// Depth texture
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, depthTexture.getTextureId());
GLError.maybeThrowGLException("Failed to bind depth texture", "glBindTexture");
GLES30.glTexImage2D(
GLES30.GL_TEXTURE_2D,
/*level=*/ 0,
GLES30.GL_DEPTH_COMPONENT32F,
width,
height,
/*border=*/ 0,
GLES30.GL_DEPTH_COMPONENT,
GLES30.GL_FLOAT,
/*pixels=*/ null);
GLError.maybeThrowGLException("Failed to specify depth texture format", "glTexImage2D");
}
/** Returns the color texture associated with this framebuffer. */
public Texture getColorTexture() {
return colorTexture;
}
/** Returns the depth texture associated with this framebuffer. */
public Texture getDepthTexture() {
return depthTexture;
}
/** Returns the width of the framebuffer. */
public int getWidth() {
return width;
}
/** Returns the height of the framebuffer. */
public int getHeight() {
return height;
}
/* package-private */
int getFramebufferId() {
return framebufferId[0];
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import android.opengl.GLES30;
import android.opengl.GLException;
import android.opengl.GLU;
import android.util.Log;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/** Module for handling OpenGL errors. */
public class GLError {
/** Throws a {@link GLException} if a GL error occurred. */
public static void maybeThrowGLException(String reason, String api) {
List<Integer> errorCodes = getGlErrors();
if (errorCodes != null) {
throw new GLException(errorCodes.get(0), formatErrorMessage(reason, api, errorCodes));
}
}
/** Logs a message with the given logcat priority if a GL error occurred. */
public static void maybeLogGLError(int priority, String tag, String reason, String api) {
List<Integer> errorCodes = getGlErrors();
if (errorCodes != null) {
Log.println(priority, tag, formatErrorMessage(reason, api, errorCodes));
}
}
private static String formatErrorMessage(String reason, String api, List<Integer> errorCodes) {
StringBuilder builder = new StringBuilder(String.format("%s: %s: ", reason, api));
Iterator<Integer> iterator = errorCodes.iterator();
while (iterator.hasNext()) {
int errorCode = iterator.next();
builder.append(String.format("%s (%d)", GLU.gluErrorString(errorCode), errorCode));
if (iterator.hasNext()) {
builder.append(", ");
}
}
return builder.toString();
}
private static List<Integer> getGlErrors() {
int errorCode = GLES30.glGetError();
// Shortcut for no errors
if (errorCode == GLES30.GL_NO_ERROR) {
return null;
}
List<Integer> errorCodes = new ArrayList<>();
errorCodes.add(errorCode);
while (true) {
errorCode = GLES30.glGetError();
if (errorCode == GLES30.GL_NO_ERROR) {
break;
}
errorCodes.add(errorCode);
}
return errorCodes;
}
private GLError() {}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import android.opengl.GLES30;
import android.util.Log;
import java.nio.Buffer;
/* package-private */
class GpuBuffer {
private static final String TAG = GpuBuffer.class.getSimpleName();
// These values refer to the byte count of the corresponding Java datatypes.
public static final int INT_SIZE = 4;
public static final int FLOAT_SIZE = 4;
private final int target;
private final int numberOfBytesPerEntry;
private final int[] bufferId = {0};
private int size;
private int capacity;
public GpuBuffer(int target, int numberOfBytesPerEntry, Buffer entries) {
if (entries != null) {
if (!entries.isDirect()) {
throw new IllegalArgumentException("If non-null, entries buffer must be a direct buffer");
}
// Some GPU drivers will fail with out of memory errors if glBufferData or glBufferSubData is
// called with a size of 0, so avoid this case.
if (entries.limit() == 0) {
entries = null;
}
}
this.target = target;
this.numberOfBytesPerEntry = numberOfBytesPerEntry;
if (entries == null) {
this.size = 0;
this.capacity = 0;
} else {
this.size = entries.limit();
this.capacity = entries.limit();
}
try {
// Clear VAO to prevent unintended state change.
GLES30.glBindVertexArray(0);
GLError.maybeThrowGLException("Failed to unbind vertex array", "glBindVertexArray");
GLES30.glGenBuffers(1, bufferId, 0);
GLError.maybeThrowGLException("Failed to generate buffers", "glGenBuffers");
GLES30.glBindBuffer(target, bufferId[0]);
GLError.maybeThrowGLException("Failed to bind buffer object", "glBindBuffer");
if (entries != null) {
entries.rewind();
GLES30.glBufferData(
target, entries.limit() * numberOfBytesPerEntry, entries, GLES30.GL_DYNAMIC_DRAW);
}
GLError.maybeThrowGLException("Failed to populate buffer object", "glBufferData");
} catch (Throwable t) {
free();
throw t;
}
}
public void set(Buffer entries) {
// Some GPU drivers will fail with out of memory errors if glBufferData or glBufferSubData is
// called with a size of 0, so avoid this case.
if (entries == null || entries.limit() == 0) {
size = 0;
return;
}
if (!entries.isDirect()) {
throw new IllegalArgumentException("If non-null, entries buffer must be a direct buffer");
}
GLES30.glBindBuffer(target, bufferId[0]);
GLError.maybeThrowGLException("Failed to bind vertex buffer object", "glBindBuffer");
entries.rewind();
if (entries.limit() <= capacity) {
GLES30.glBufferSubData(target, 0, entries.limit() * numberOfBytesPerEntry, entries);
GLError.maybeThrowGLException("Failed to populate vertex buffer object", "glBufferSubData");
size = entries.limit();
} else {
GLES30.glBufferData(
target, entries.limit() * numberOfBytesPerEntry, entries, GLES30.GL_DYNAMIC_DRAW);
GLError.maybeThrowGLException("Failed to populate vertex buffer object", "glBufferData");
size = entries.limit();
capacity = entries.limit();
}
}
public void free() {
if (bufferId[0] != 0) {
GLES30.glDeleteBuffers(1, bufferId, 0);
GLError.maybeLogGLError(Log.WARN, TAG, "Failed to free buffer object", "glDeleteBuffers");
bufferId[0] = 0;
}
}
public int getBufferId() {
return bufferId[0];
}
public int getSize() {
return size;
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import android.opengl.GLES30;
import java.io.Closeable;
import java.nio.IntBuffer;
/**
* A list of vertex indices stored GPU-side.
*
* <p>When constructing a {@link Mesh}, an {@link IndexBuffer} may be passed to describe the
* ordering of vertices when drawing each primitive.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glDrawElements.xhtml">glDrawElements</a>
*/
public class IndexBuffer implements Closeable {
private final GpuBuffer buffer;
/**
* Construct an {@link IndexBuffer} populated with initial data.
*
* <p>The GPU buffer will be filled with the data in the <i>direct</i> buffer {@code entries},
* starting from the beginning of the buffer (not the current cursor position). The cursor will be
* left in an undefined position after this function returns.
*
* <p>The {@code entries} buffer may be null, in which case an empty buffer is constructed
* instead.
*/
public IndexBuffer(SampleRender render, IntBuffer entries) {
buffer = new GpuBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, GpuBuffer.INT_SIZE, entries);
}
/**
* Populate with new data.
*
* <p>The entire buffer is replaced by the contents of the <i>direct</i> buffer {@code entries}
* starting from the beginning of the buffer, not the current cursor position. The cursor will be
* left in an undefined position after this function returns.
*
* <p>The GPU buffer is reallocated automatically if necessary.
*
* <p>The {@code entries} buffer may be null, in which case the buffer will become empty.
*/
public void set(IntBuffer entries) {
buffer.set(entries);
}
@Override
public void close() {
buffer.free();
}
/* package-private */
int getBufferId() {
return buffer.getBufferId();
}
/* package-private */
int getSize() {
return buffer.getSize();
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import android.opengl.GLES30;
import android.util.Log;
import de.javagl.obj.Obj;
import de.javagl.obj.ObjData;
import de.javagl.obj.ObjReader;
import de.javagl.obj.ObjUtils;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
/**
* A collection of vertices, faces, and other attributes that define how to render a 3D object.
*
* <p>To render the mesh, use {@link SampleRender#draw()}.
*/
public class Mesh implements Closeable {
private static final String TAG = Mesh.class.getSimpleName();
/**
* The kind of primitive to render.
*
* <p>This determines how the data in {@link VertexBuffer}s are interpreted. See <a
* href="https://www.khronos.org/opengl/wiki/Primitive">here</a> for more on how primitives
* behave.
*/
public enum PrimitiveMode {
POINTS(GLES30.GL_POINTS),
LINE_STRIP(GLES30.GL_LINE_STRIP),
LINE_LOOP(GLES30.GL_LINE_LOOP),
LINES(GLES30.GL_LINES),
TRIANGLE_STRIP(GLES30.GL_TRIANGLE_STRIP),
TRIANGLE_FAN(GLES30.GL_TRIANGLE_FAN),
TRIANGLES(GLES30.GL_TRIANGLES);
/* package-private */
final int glesEnum;
private PrimitiveMode(int glesEnum) {
this.glesEnum = glesEnum;
}
}
private final int[] vertexArrayId = {0};
private final PrimitiveMode primitiveMode;
private final IndexBuffer indexBuffer;
private final VertexBuffer[] vertexBuffers;
/**
* Construct a {@link Mesh}.
*
* <p>The data in the given {@link IndexBuffer} and {@link VertexBuffer}s does not need to be
* finalized; they may be freely changed throughout the lifetime of a {@link Mesh} using their
* respective {@code set()} methods.
*
* <p>The ordering of the {@code vertexBuffers} is significant. Their array indices will
* correspond to their attribute locations, which must be taken into account in shader code. The
* <a href="https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL)">layout qualifier</a> must
* be used in the vertex shader code to explicitly associate attributes with these indices.
*/
public Mesh(
SampleRender render,
PrimitiveMode primitiveMode,
IndexBuffer indexBuffer,
VertexBuffer[] vertexBuffers) {
if (vertexBuffers == null || vertexBuffers.length == 0) {
throw new IllegalArgumentException("Must pass at least one vertex buffer");
}
this.primitiveMode = primitiveMode;
this.indexBuffer = indexBuffer;
this.vertexBuffers = vertexBuffers;
try {
// Create vertex array
GLES30.glGenVertexArrays(1, vertexArrayId, 0);
GLError.maybeThrowGLException("Failed to generate a vertex array", "glGenVertexArrays");
// Bind vertex array
GLES30.glBindVertexArray(vertexArrayId[0]);
GLError.maybeThrowGLException("Failed to bind vertex array object", "glBindVertexArray");
if (indexBuffer != null) {
GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, indexBuffer.getBufferId());
}
for (int i = 0; i < vertexBuffers.length; ++i) {
// Bind each vertex buffer to vertex array
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBuffers[i].getBufferId());
GLError.maybeThrowGLException("Failed to bind vertex buffer", "glBindBuffer");
GLES30.glVertexAttribPointer(
i, vertexBuffers[i].getNumberOfEntriesPerVertex(), GLES30.GL_FLOAT, false, 0, 0);
GLError.maybeThrowGLException(
"Failed to associate vertex buffer with vertex array", "glVertexAttribPointer");
GLES30.glEnableVertexAttribArray(i);
GLError.maybeThrowGLException(
"Failed to enable vertex buffer", "glEnableVertexAttribArray");
}
} catch (Throwable t) {
close();
throw t;
}
}
/**
* Constructs a {@link Mesh} from the given Wavefront OBJ file.
*
* <p>The {@link Mesh} will be constructed with three attributes, indexed in the order of local
* coordinates (location 0, vec3), texture coordinates (location 1, vec2), and vertex normals
* (location 2, vec3).
*/
public static Mesh createFromAsset(SampleRender render, String assetFileName) throws IOException {
try (InputStream inputStream = render.getAssets().open(assetFileName)) {
Obj obj = ObjUtils.convertToRenderable(ObjReader.read(inputStream));
// Obtain the data from the OBJ, as direct buffers:
IntBuffer vertexIndices = ObjData.getFaceVertexIndices(obj, /*numVerticesPerFace=*/ 3);
FloatBuffer localCoordinates = ObjData.getVertices(obj);
FloatBuffer textureCoordinates = ObjData.getTexCoords(obj, /*dimensions=*/ 2);
FloatBuffer normals = ObjData.getNormals(obj);
VertexBuffer[] vertexBuffers = {
new VertexBuffer(render, 3, localCoordinates),
new VertexBuffer(render, 2, textureCoordinates),
new VertexBuffer(render, 3, normals),
};
IndexBuffer indexBuffer = new IndexBuffer(render, vertexIndices);
return new Mesh(render, PrimitiveMode.TRIANGLES, indexBuffer, vertexBuffers);
}
}
@Override
public void close() {
if (vertexArrayId[0] != 0) {
GLES30.glDeleteVertexArrays(1, vertexArrayId, 0);
GLError.maybeLogGLError(
Log.WARN, TAG, "Failed to free vertex array object", "glDeleteVertexArrays");
}
}
/**
* Draws the mesh. Don't call this directly unless you are doing low level OpenGL code; instead,
* prefer {@link SampleRender#draw}.
*/
public void lowLevelDraw() {
if (vertexArrayId[0] == 0) {
throw new IllegalStateException("Tried to draw a freed Mesh");
}
GLES30.glBindVertexArray(vertexArrayId[0]);
GLError.maybeThrowGLException("Failed to bind vertex array object", "glBindVertexArray");
if (indexBuffer == null) {
// Sanity check for debugging
int vertexCount = vertexBuffers[0].getNumberOfVertices();
for (int i = 1; i < vertexBuffers.length; ++i) {
int iterCount = vertexBuffers[i].getNumberOfVertices();
if (iterCount != vertexCount) {
throw new IllegalStateException(
String.format(
"Vertex buffers have mismatching numbers of vertices ([0] has %d but [%d] has"
+ " %d)",
vertexCount, i, iterCount));
}
}
GLES30.glDrawArrays(primitiveMode.glesEnum, 0, vertexCount);
GLError.maybeThrowGLException("Failed to draw vertex array object", "glDrawArrays");
} else {
GLES30.glDrawElements(
primitiveMode.glesEnum, indexBuffer.getSize(), GLES30.GL_UNSIGNED_INT, 0);
GLError.maybeThrowGLException(
"Failed to draw vertex array object with indices", "glDrawElements");
}
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import android.content.res.AssetManager;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/** A SampleRender context. */
public class SampleRender {
private static final String TAG = SampleRender.class.getSimpleName();
private final AssetManager assetManager;
private int viewportWidth = 1;
private int viewportHeight = 1;
/**
* Constructs a SampleRender object and instantiates GLSurfaceView parameters.
*
* @param glSurfaceView Android GLSurfaceView
* @param renderer Renderer implementation to receive callbacks
* @param assetManager AssetManager for loading Android resources
*/
public SampleRender(GLSurfaceView glSurfaceView, Renderer renderer, AssetManager assetManager) {
this.assetManager = assetManager;
glSurfaceView.setPreserveEGLContextOnPause(true);
glSurfaceView.setEGLContextClientVersion(3);
glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
glSurfaceView.setRenderer(
new GLSurfaceView.Renderer() {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES30.glEnable(GLES30.GL_BLEND);
GLError.maybeThrowGLException("Failed to enable blending", "glEnable");
renderer.onSurfaceCreated(SampleRender.this);
}
@Override
public void onSurfaceChanged(GL10 gl, int w, int h) {
viewportWidth = w;
viewportHeight = h;
renderer.onSurfaceChanged(SampleRender.this, w, h);
}
@Override
public void onDrawFrame(GL10 gl) {
clear(/*framebuffer=*/ null, 0f, 0f, 0f, 1f);
renderer.onDrawFrame(SampleRender.this);
}
});
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
glSurfaceView.setWillNotDraw(false);
}
/** Draw a {@link Mesh} with the specified {@link Shader}. */
public void draw(Mesh mesh, Shader shader) {
draw(mesh, shader, /*framebuffer=*/ null);
}
/**
* Draw a {@link Mesh} with the specified {@link Shader} to the given {@link Framebuffer}.
*
* <p>The {@code framebuffer} argument may be null, in which case the default framebuffer is used.
*/
public void draw(Mesh mesh, Shader shader, Framebuffer framebuffer) {
useFramebuffer(framebuffer);
shader.lowLevelUse();
mesh.lowLevelDraw();
}
/**
* Clear the given framebuffer.
*
* <p>The {@code framebuffer} argument may be null, in which case the default framebuffer is
* cleared.
*/
public void clear(Framebuffer framebuffer, float r, float g, float b, float a) {
useFramebuffer(framebuffer);
GLES30.glClearColor(r, g, b, a);
GLError.maybeThrowGLException("Failed to set clear color", "glClearColor");
GLES30.glDepthMask(true);
GLError.maybeThrowGLException("Failed to set depth write mask", "glDepthMask");
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
GLError.maybeThrowGLException("Failed to clear framebuffer", "glClear");
}
/** Interface to be implemented for rendering callbacks. */
public static interface Renderer {
/**
* Called by {@link SampleRender} when the GL render surface is created.
*
* <p>See {@link GLSurfaceView.Renderer#onSurfaceCreated}.
*/
public void onSurfaceCreated(SampleRender render);
/**
* Called by {@link SampleRender} when the GL render surface dimensions are changed.
*
* <p>See {@link GLSurfaceView.Renderer#onSurfaceChanged}.
*/
public void onSurfaceChanged(SampleRender render, int width, int height);
/**
* Called by {@link SampleRender} when a GL frame is to be rendered.
*
* <p>See {@link GLSurfaceView.Renderer#onDrawFrame}.
*/
public void onDrawFrame(SampleRender render);
}
/* package-private */
AssetManager getAssets() {
return assetManager;
}
private void useFramebuffer(Framebuffer framebuffer) {
int framebufferId;
int viewportWidth;
int viewportHeight;
if (framebuffer == null) {
framebufferId = 0;
viewportWidth = this.viewportWidth;
viewportHeight = this.viewportHeight;
} else {
framebufferId = framebuffer.getFramebufferId();
viewportWidth = framebuffer.getWidth();
viewportHeight = framebuffer.getHeight();
}
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, framebufferId);
GLError.maybeThrowGLException("Failed to bind framebuffer", "glBindFramebuffer");
GLES30.glViewport(0, 0, viewportWidth, viewportHeight);
GLError.maybeThrowGLException("Failed to set viewport dimensions", "glViewport");
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import static java.nio.charset.StandardCharsets.UTF_8;
import android.content.res.AssetManager;
import android.opengl.GLES30;
import android.opengl.GLException;
import android.util.Log;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
/**
* Represents a GPU shader, the state of its associated uniforms, and some additional draw state.
*/
public class Shader implements Closeable {
private static final String TAG = Shader.class.getSimpleName();
/**
* A factor to be used in a blend function.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBlendFunc.xhtml">glBlendFunc</a>
*/
public static enum BlendFactor {
ZERO(GLES30.GL_ZERO),
ONE(GLES30.GL_ONE),
SRC_COLOR(GLES30.GL_SRC_COLOR),
ONE_MINUS_SRC_COLOR(GLES30.GL_ONE_MINUS_SRC_COLOR),
DST_COLOR(GLES30.GL_DST_COLOR),
ONE_MINUS_DST_COLOR(GLES30.GL_ONE_MINUS_DST_COLOR),
SRC_ALPHA(GLES30.GL_SRC_ALPHA),
ONE_MINUS_SRC_ALPHA(GLES30.GL_ONE_MINUS_SRC_ALPHA),
DST_ALPHA(GLES30.GL_DST_ALPHA),
ONE_MINUS_DST_ALPHA(GLES30.GL_ONE_MINUS_DST_ALPHA),
CONSTANT_COLOR(GLES30.GL_CONSTANT_COLOR),
ONE_MINUS_CONSTANT_COLOR(GLES30.GL_ONE_MINUS_CONSTANT_COLOR),
CONSTANT_ALPHA(GLES30.GL_CONSTANT_ALPHA),
ONE_MINUS_CONSTANT_ALPHA(GLES30.GL_ONE_MINUS_CONSTANT_ALPHA);
/* package-private */
final int glesEnum;
private BlendFactor(int glesEnum) {
this.glesEnum = glesEnum;
}
}
private int programId = 0;
private final Map<Integer, Uniform> uniforms = new HashMap<>();
private int maxTextureUnit = 0;
private final Map<String, Integer> uniformLocations = new HashMap<>();
private final Map<Integer, String> uniformNames = new HashMap<>();
private boolean depthTest = true;
private boolean depthWrite = true;
private boolean cullFace = true;
private BlendFactor sourceRgbBlend = BlendFactor.ONE;
private BlendFactor destRgbBlend = BlendFactor.ZERO;
private BlendFactor sourceAlphaBlend = BlendFactor.ONE;
private BlendFactor destAlphaBlend = BlendFactor.ZERO;
/**
* Constructs a {@link Shader} given the shader code.
*
* @param defines A map of shader precompiler symbols to be defined with the given names and
* values
*/
public Shader(
SampleRender render,
String vertexShaderCode,
String fragmentShaderCode,
Map<String, String> defines) {
int vertexShaderId = 0;
int fragmentShaderId = 0;
String definesCode = createShaderDefinesCode(defines);
try {
vertexShaderId =
createShader(
GLES30.GL_VERTEX_SHADER, insertShaderDefinesCode(vertexShaderCode, definesCode));
fragmentShaderId =
createShader(
GLES30.GL_FRAGMENT_SHADER, insertShaderDefinesCode(fragmentShaderCode, definesCode));
programId = GLES30.glCreateProgram();
GLError.maybeThrowGLException("Shader program creation failed", "glCreateProgram");
GLES30.glAttachShader(programId, vertexShaderId);
GLError.maybeThrowGLException("Failed to attach vertex shader", "glAttachShader");
GLES30.glAttachShader(programId, fragmentShaderId);
GLError.maybeThrowGLException("Failed to attach fragment shader", "glAttachShader");
GLES30.glLinkProgram(programId);
GLError.maybeThrowGLException("Failed to link shader program", "glLinkProgram");
final int[] linkStatus = new int[1];
GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == GLES30.GL_FALSE) {
String infoLog = GLES30.glGetProgramInfoLog(programId);
GLError.maybeLogGLError(
Log.WARN, TAG, "Failed to retrieve shader program info log", "glGetProgramInfoLog");
throw new GLException(0, "Shader link failed: " + infoLog);
}
} catch (Throwable t) {
close();
throw t;
} finally {
// Shader objects can be flagged for deletion immediately after program creation.
if (vertexShaderId != 0) {
GLES30.glDeleteShader(vertexShaderId);
GLError.maybeLogGLError(Log.WARN, TAG, "Failed to free vertex shader", "glDeleteShader");
}
if (fragmentShaderId != 0) {
GLES30.glDeleteShader(fragmentShaderId);
GLError.maybeLogGLError(Log.WARN, TAG, "Failed to free fragment shader", "glDeleteShader");
}
}
}
/**
* Creates a {@link Shader} from the given asset file names.
*
* <p>The file contents are interpreted as UTF-8 text.
*
* @param defines A map of shader precompiler symbols to be defined with the given names and
* values
*/
public static Shader createFromAssets(
SampleRender render,
String vertexShaderFileName,
String fragmentShaderFileName,
Map<String, String> defines)
throws IOException {
AssetManager assets = render.getAssets();
return new Shader(
render,
inputStreamToString(assets.open(vertexShaderFileName)),
inputStreamToString(assets.open(fragmentShaderFileName)),
defines);
}
@Override
public void close() {
if (programId != 0) {
GLES30.glDeleteProgram(programId);
programId = 0;
}
}
/**
* Sets depth test state.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glEnable.xhtml">glEnable(GL_DEPTH_TEST)</a>.
*/
public Shader setDepthTest(boolean depthTest) {
this.depthTest = depthTest;
return this;
}
/**
* Sets depth write state.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glDepthMask.xhtml">glDepthMask</a>.
*/
public Shader setDepthWrite(boolean depthWrite) {
this.depthWrite = depthWrite;
return this;
}
/**
* Sets cull face state.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glEnable.xhtml">glEnable(GL_CULL_FACE)</a>.
*/
public Shader setCullFace(boolean cullFace) {
this.cullFace = cullFace;
return this;
}
/**
* Sets blending function.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBlendFunc.xhtml">glBlendFunc</a>
*/
public Shader setBlend(BlendFactor sourceBlend, BlendFactor destBlend) {
this.sourceRgbBlend = sourceBlend;
this.destRgbBlend = destBlend;
this.sourceAlphaBlend = sourceBlend;
this.destAlphaBlend = destBlend;
return this;
}
/**
* Sets blending functions separately for RGB and alpha channels.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBlendFuncSeparate.xhtml">glBlendFunc</a>
*/
public Shader setBlend(
BlendFactor sourceRgbBlend,
BlendFactor destRgbBlend,
BlendFactor sourceAlphaBlend,
BlendFactor destAlphaBlend) {
this.sourceRgbBlend = sourceRgbBlend;
this.destRgbBlend = destRgbBlend;
this.sourceAlphaBlend = sourceAlphaBlend;
this.destAlphaBlend = destAlphaBlend;
return this;
}
/** Sets a texture uniform. */
public Shader setTexture(String name, Texture texture) {
// Special handling for Textures. If replacing an existing texture uniform, reuse the texture
// unit.
int location = getUniformLocation(name);
Uniform uniform = uniforms.get(location);
int textureUnit;
if (!(uniform instanceof UniformTexture)) {
textureUnit = maxTextureUnit++;
} else {
UniformTexture uniformTexture = (UniformTexture) uniform;
textureUnit = uniformTexture.getTextureUnit();
}
uniforms.put(location, new UniformTexture(textureUnit, texture));
return this;
}
/** Sets a {@code bool} uniform. */
public Shader setBool(String name, boolean v0) {
int[] values = {v0 ? 1 : 0};
uniforms.put(getUniformLocation(name), new UniformInt(values));
return this;
}
/** Sets an {@code int} uniform. */
public Shader setInt(String name, int v0) {
int[] values = {v0};
uniforms.put(getUniformLocation(name), new UniformInt(values));
return this;
}
/** Sets a {@code float} uniform. */
public Shader setFloat(String name, float v0) {
float[] values = {v0};
uniforms.put(getUniformLocation(name), new Uniform1f(values));
return this;
}
/** Sets a {@code vec2} uniform. */
public Shader setVec2(String name, float[] values) {
if (values.length != 2) {
throw new IllegalArgumentException("Value array length must be 2");
}
uniforms.put(getUniformLocation(name), new Uniform2f(values.clone()));
return this;
}
/** Sets a {@code vec3} uniform. */
public Shader setVec3(String name, float[] values) {
if (values.length != 3) {
throw new IllegalArgumentException("Value array length must be 3");
}
uniforms.put(getUniformLocation(name), new Uniform3f(values.clone()));
return this;
}
/** Sets a {@code vec4} uniform. */
public Shader setVec4(String name, float[] values) {
if (values.length != 4) {
throw new IllegalArgumentException("Value array length must be 4");
}
uniforms.put(getUniformLocation(name), new Uniform4f(values.clone()));
return this;
}
/** Sets a {@code mat2} uniform. */
public Shader setMat2(String name, float[] values) {
if (values.length != 4) {
throw new IllegalArgumentException("Value array length must be 4 (2x2)");
}
uniforms.put(getUniformLocation(name), new UniformMatrix2f(values.clone()));
return this;
}
/** Sets a {@code mat3} uniform. */
public Shader setMat3(String name, float[] values) {
if (values.length != 9) {
throw new IllegalArgumentException("Value array length must be 9 (3x3)");
}
uniforms.put(getUniformLocation(name), new UniformMatrix3f(values.clone()));
return this;
}
/** Sets a {@code mat4} uniform. */
public Shader setMat4(String name, float[] values) {
if (values.length != 16) {
throw new IllegalArgumentException("Value array length must be 16 (4x4)");
}
uniforms.put(getUniformLocation(name), new UniformMatrix4f(values.clone()));
return this;
}
/** Sets a {@code bool} array uniform. */
public Shader setBoolArray(String name, boolean[] values) {
int[] intValues = new int[values.length];
for (int i = 0; i < values.length; ++i) {
intValues[i] = values[i] ? 1 : 0;
}
uniforms.put(getUniformLocation(name), new UniformInt(intValues));
return this;
}
/** Sets an {@code int} array uniform. */
public Shader setIntArray(String name, int[] values) {
uniforms.put(getUniformLocation(name), new UniformInt(values.clone()));
return this;
}
/** Sets a {@code float} array uniform. */
public Shader setFloatArray(String name, float[] values) {
uniforms.put(getUniformLocation(name), new Uniform1f(values.clone()));
return this;
}
/** Sets a {@code vec2} array uniform. */
public Shader setVec2Array(String name, float[] values) {
if (values.length % 2 != 0) {
throw new IllegalArgumentException("Value array length must be divisible by 2");
}
uniforms.put(getUniformLocation(name), new Uniform2f(values.clone()));
return this;
}
/** Sets a {@code vec3} array uniform. */
public Shader setVec3Array(String name, float[] values) {
if (values.length % 3 != 0) {
throw new IllegalArgumentException("Value array length must be divisible by 3");
}
uniforms.put(getUniformLocation(name), new Uniform3f(values.clone()));
return this;
}
/** Sets a {@code vec4} array uniform. */
public Shader setVec4Array(String name, float[] values) {
if (values.length % 4 != 0) {
throw new IllegalArgumentException("Value array length must be divisible by 4");
}
uniforms.put(getUniformLocation(name), new Uniform4f(values.clone()));
return this;
}
/** Sets a {@code mat2} array uniform. */
public Shader setMat2Array(String name, float[] values) {
if (values.length % 4 != 0) {
throw new IllegalArgumentException("Value array length must be divisible by 4 (2x2)");
}
uniforms.put(getUniformLocation(name), new UniformMatrix2f(values.clone()));
return this;
}
/** Sets a {@code mat3} array uniform. */
public Shader setMat3Array(String name, float[] values) {
if (values.length % 9 != 0) {
throw new IllegalArgumentException("Values array length must be divisible by 9 (3x3)");
}
uniforms.put(getUniformLocation(name), new UniformMatrix3f(values.clone()));
return this;
}
/** Sets a {@code mat4} uniform. */
public Shader setMat4Array(String name, float[] values) {
if (values.length % 16 != 0) {
throw new IllegalArgumentException("Value array length must be divisible by 16 (4x4)");
}
uniforms.put(getUniformLocation(name), new UniformMatrix4f(values.clone()));
return this;
}
/**
* Activates the shader. Don't call this directly unless you are doing low level OpenGL code;
* instead, prefer {@link SampleRender#draw}.
*/
public void lowLevelUse() {
// Make active shader/set uniforms
if (programId == 0) {
throw new IllegalStateException("Attempted to use freed shader");
}
GLES30.glUseProgram(programId);
GLError.maybeThrowGLException("Failed to use shader program", "glUseProgram");
GLES30.glBlendFuncSeparate(
sourceRgbBlend.glesEnum,
destRgbBlend.glesEnum,
sourceAlphaBlend.glesEnum,
destAlphaBlend.glesEnum);
GLError.maybeThrowGLException("Failed to set blend mode", "glBlendFuncSeparate");
GLES30.glDepthMask(depthWrite);
GLError.maybeThrowGLException("Failed to set depth write mask", "glDepthMask");
if (depthTest) {
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
GLError.maybeThrowGLException("Failed to enable depth test", "glEnable");
} else {
GLES30.glDisable(GLES30.GL_DEPTH_TEST);
GLError.maybeThrowGLException("Failed to disable depth test", "glDisable");
}
if (cullFace) {
GLES30.glEnable(GLES30.GL_CULL_FACE);
GLError.maybeThrowGLException("Failed to enable backface culling", "glEnable");
} else {
GLES30.glDisable(GLES30.GL_CULL_FACE);
GLError.maybeThrowGLException("Failed to disable backface culling", "glDisable");
}
try {
// Remove all non-texture uniforms from the map after setting them, since they're stored as
// part of the program.
ArrayList<Integer> obsoleteEntries = new ArrayList<>(uniforms.size());
for (Map.Entry<Integer, Uniform> entry : uniforms.entrySet()) {
try {
entry.getValue().use(entry.getKey());
if (!(entry.getValue() instanceof UniformTexture)) {
obsoleteEntries.add(entry.getKey());
}
} catch (GLException e) {
String name = uniformNames.get(entry.getKey());
throw new IllegalArgumentException("Error setting uniform `" + name + "'", e);
}
}
uniforms.keySet().removeAll(obsoleteEntries);
} finally {
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLError.maybeLogGLError(Log.WARN, TAG, "Failed to set active texture", "glActiveTexture");
}
}
private static interface Uniform {
public void use(int location);
}
private static class UniformTexture implements Uniform {
private final int textureUnit;
private final Texture texture;
public UniformTexture(int textureUnit, Texture texture) {
this.textureUnit = textureUnit;
this.texture = texture;
}
public int getTextureUnit() {
return textureUnit;
}
@Override
public void use(int location) {
if (texture.getTextureId() == 0) {
throw new IllegalStateException("Tried to draw with freed texture");
}
GLES30.glActiveTexture(GLES30.GL_TEXTURE0 + textureUnit);
GLError.maybeThrowGLException("Failed to set active texture", "glActiveTexture");
GLES30.glBindTexture(texture.getTarget().glesEnum, texture.getTextureId());
GLError.maybeThrowGLException("Failed to bind texture", "glBindTexture");
GLES30.glUniform1i(location, textureUnit);
GLError.maybeThrowGLException("Failed to set shader texture uniform", "glUniform1i");
}
}
private static class UniformInt implements Uniform {
private final int[] values;
public UniformInt(int[] values) {
this.values = values;
}
@Override
public void use(int location) {
GLES30.glUniform1iv(location, values.length, values, 0);
GLError.maybeThrowGLException("Failed to set shader uniform 1i", "glUniform1iv");
}
}
private static class Uniform1f implements Uniform {
private final float[] values;
public Uniform1f(float[] values) {
this.values = values;
}
@Override
public void use(int location) {
GLES30.glUniform1fv(location, values.length, values, 0);
GLError.maybeThrowGLException("Failed to set shader uniform 1f", "glUniform1fv");
}
}
private static class Uniform2f implements Uniform {
private final float[] values;
public Uniform2f(float[] values) {
this.values = values;
}
@Override
public void use(int location) {
GLES30.glUniform2fv(location, values.length / 2, values, 0);
GLError.maybeThrowGLException("Failed to set shader uniform 2f", "glUniform2fv");
}
}
private static class Uniform3f implements Uniform {
private final float[] values;
public Uniform3f(float[] values) {
this.values = values;
}
@Override
public void use(int location) {
GLES30.glUniform3fv(location, values.length / 3, values, 0);
GLError.maybeThrowGLException("Failed to set shader uniform 3f", "glUniform3fv");
}
}
private static class Uniform4f implements Uniform {
private final float[] values;
public Uniform4f(float[] values) {
this.values = values;
}
@Override
public void use(int location) {
GLES30.glUniform4fv(location, values.length / 4, values, 0);
GLError.maybeThrowGLException("Failed to set shader uniform 4f", "glUniform4fv");
}
}
private static class UniformMatrix2f implements Uniform {
private final float[] values;
public UniformMatrix2f(float[] values) {
this.values = values;
}
@Override
public void use(int location) {
GLES30.glUniformMatrix2fv(location, values.length / 4, /*transpose=*/ false, values, 0);
GLError.maybeThrowGLException("Failed to set shader uniform matrix 2f", "glUniformMatrix2fv");
}
}
private static class UniformMatrix3f implements Uniform {
private final float[] values;
public UniformMatrix3f(float[] values) {
this.values = values;
}
@Override
public void use(int location) {
GLES30.glUniformMatrix3fv(location, values.length / 9, /*transpose=*/ false, values, 0);
GLError.maybeThrowGLException("Failed to set shader uniform matrix 3f", "glUniformMatrix3fv");
}
}
private static class UniformMatrix4f implements Uniform {
private final float[] values;
public UniformMatrix4f(float[] values) {
this.values = values;
}
@Override
public void use(int location) {
GLES30.glUniformMatrix4fv(location, values.length / 16, /*transpose=*/ false, values, 0);
GLError.maybeThrowGLException("Failed to set shader uniform matrix 4f", "glUniformMatrix4fv");
}
}
private int getUniformLocation(String name) {
Integer locationObject = uniformLocations.get(name);
if (locationObject != null) {
return locationObject;
}
int location = GLES30.glGetUniformLocation(programId, name);
GLError.maybeThrowGLException("Failed to find uniform", "glGetUniformLocation");
if (location == -1) {
throw new IllegalArgumentException("Shader uniform does not exist: " + name);
}
uniformLocations.put(name, Integer.valueOf(location));
uniformNames.put(Integer.valueOf(location), name);
return location;
}
private static int createShader(int type, String code) {
int shaderId = GLES30.glCreateShader(type);
GLError.maybeThrowGLException("Shader creation failed", "glCreateShader");
GLES30.glShaderSource(shaderId, code);
GLError.maybeThrowGLException("Shader source failed", "glShaderSource");
GLES30.glCompileShader(shaderId);
GLError.maybeThrowGLException("Shader compilation failed", "glCompileShader");
final int[] compileStatus = new int[1];
GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == GLES30.GL_FALSE) {
String infoLog = GLES30.glGetShaderInfoLog(shaderId);
GLError.maybeLogGLError(
Log.WARN, TAG, "Failed to retrieve shader info log", "glGetShaderInfoLog");
GLES30.glDeleteShader(shaderId);
GLError.maybeLogGLError(Log.WARN, TAG, "Failed to free shader", "glDeleteShader");
throw new GLException(0, "Shader compilation failed: " + infoLog);
}
return shaderId;
}
private static String createShaderDefinesCode(Map<String, String> defines) {
if (defines == null) {
return "";
}
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, String> entry : defines.entrySet()) {
builder.append("#define " + entry.getKey() + " " + entry.getValue() + "\n");
}
return builder.toString();
}
private static String insertShaderDefinesCode(String sourceCode, String definesCode) {
String result =
sourceCode.replaceAll(
"(?m)^(\\s*#\\s*version\\s+.*)$", "$1\n" + Matcher.quoteReplacement(definesCode));
if (result.equals(sourceCode)) {
// No #version specified, so just prepend source
return definesCode + sourceCode;
}
return result;
}
private static String inputStreamToString(InputStream stream) throws IOException {
InputStreamReader reader = new InputStreamReader(stream, UTF_8.name());
char[] buffer = new char[1024 * 4];
StringBuilder builder = new StringBuilder();
int amount = 0;
while ((amount = reader.read(buffer)) != -1) {
builder.append(buffer, 0, amount);
}
reader.close();
return builder.toString();
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES11Ext;
import android.opengl.GLES30;
import android.util.Log;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
/** A GPU-side texture. */
public class Texture implements Closeable {
private static final String TAG = Texture.class.getSimpleName();
private final int[] textureId = {0};
private final Target target;
/**
* Describes the way the texture's edges are rendered.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexParameter.xhtml">GL_TEXTURE_WRAP_S</a>.
*/
public enum WrapMode {
CLAMP_TO_EDGE(GLES30.GL_CLAMP_TO_EDGE),
MIRRORED_REPEAT(GLES30.GL_MIRRORED_REPEAT),
REPEAT(GLES30.GL_REPEAT);
/* package-private */
final int glesEnum;
private WrapMode(int glesEnum) {
this.glesEnum = glesEnum;
}
}
/**
* Describes the target this texture is bound to.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBindTexture.xhtml">glBindTexture</a>.
*/
public enum Target {
TEXTURE_2D(GLES30.GL_TEXTURE_2D),
TEXTURE_EXTERNAL_OES(GLES11Ext.GL_TEXTURE_EXTERNAL_OES),
TEXTURE_CUBE_MAP(GLES30.GL_TEXTURE_CUBE_MAP);
final int glesEnum;
private Target(int glesEnum) {
this.glesEnum = glesEnum;
}
}
/**
* Describes the color format of the texture.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml">glTexImage2d</a>.
*/
public enum ColorFormat {
LINEAR(GLES30.GL_RGBA8),
SRGB(GLES30.GL_SRGB8_ALPHA8);
final int glesEnum;
private ColorFormat(int glesEnum) {
this.glesEnum = glesEnum;
}
}
/**
* Construct an empty {@link Texture}.
*
* <p>Since {@link Texture}s created in this way are not populated with data, this method is
* mostly only useful for creating {@link Target.TEXTURE_EXTERNAL_OES} textures. See {@link
* #createFromAsset} if you want a texture with data.
*/
public Texture(SampleRender render, Target target, WrapMode wrapMode) {
this(render, target, wrapMode, /*useMipmaps=*/ true);
}
public Texture(SampleRender render, Target target, WrapMode wrapMode, boolean useMipmaps) {
this.target = target;
GLES30.glGenTextures(1, textureId, 0);
GLError.maybeThrowGLException("Texture creation failed", "glGenTextures");
int minFilter = useMipmaps ? GLES30.GL_LINEAR_MIPMAP_LINEAR : GLES30.GL_LINEAR;
try {
GLES30.glBindTexture(target.glesEnum, textureId[0]);
GLError.maybeThrowGLException("Failed to bind texture", "glBindTexture");
GLES30.glTexParameteri(target.glesEnum, GLES30.GL_TEXTURE_MIN_FILTER, minFilter);
GLError.maybeThrowGLException("Failed to set texture parameter", "glTexParameteri");
GLES30.glTexParameteri(target.glesEnum, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLError.maybeThrowGLException("Failed to set texture parameter", "glTexParameteri");
GLES30.glTexParameteri(target.glesEnum, GLES30.GL_TEXTURE_WRAP_S, wrapMode.glesEnum);
GLError.maybeThrowGLException("Failed to set texture parameter", "glTexParameteri");
GLES30.glTexParameteri(target.glesEnum, GLES30.GL_TEXTURE_WRAP_T, wrapMode.glesEnum);
GLError.maybeThrowGLException("Failed to set texture parameter", "glTexParameteri");
} catch (Throwable t) {
close();
throw t;
}
}
/** Create a texture from the given asset file name. */
public static Texture createFromAsset(
SampleRender render, String assetFileName, WrapMode wrapMode, ColorFormat colorFormat)
throws IOException {
Texture texture = new Texture(render, Target.TEXTURE_2D, wrapMode);
Bitmap bitmap = null;
try {
// The following lines up to glTexImage2D could technically be replaced with
// GLUtils.texImage2d, but this method does not allow for loading sRGB images.
// Load and convert the bitmap and copy its contents to a direct ByteBuffer. Despite its name,
// the ARGB_8888 config is actually stored in RGBA order.
bitmap =
convertBitmapToConfig(
BitmapFactory.decodeStream(render.getAssets().open(assetFileName)),
Bitmap.Config.ARGB_8888);
ByteBuffer buffer = ByteBuffer.allocateDirect(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(buffer);
buffer.rewind();
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texture.getTextureId());
GLError.maybeThrowGLException("Failed to bind texture", "glBindTexture");
GLES30.glTexImage2D(
GLES30.GL_TEXTURE_2D,
/*level=*/ 0,
colorFormat.glesEnum,
bitmap.getWidth(),
bitmap.getHeight(),
/*border=*/ 0,
GLES30.GL_RGBA,
GLES30.GL_UNSIGNED_BYTE,
buffer);
GLError.maybeThrowGLException("Failed to populate texture data", "glTexImage2D");
GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);
GLError.maybeThrowGLException("Failed to generate mipmaps", "glGenerateMipmap");
} catch (Throwable t) {
texture.close();
throw t;
} finally {
if (bitmap != null) {
bitmap.recycle();
}
}
return texture;
}
@Override
public void close() {
if (textureId[0] != 0) {
GLES30.glDeleteTextures(1, textureId, 0);
GLError.maybeLogGLError(Log.WARN, TAG, "Failed to free texture", "glDeleteTextures");
textureId[0] = 0;
}
}
/** Retrieve the native texture ID. */
public int getTextureId() {
return textureId[0];
}
/* package-private */
Target getTarget() {
return target;
}
private static Bitmap convertBitmapToConfig(Bitmap bitmap, Bitmap.Config config) {
// We use this method instead of BitmapFactory.Options.outConfig to support a minimum of Android
// API level 24.
if (bitmap.getConfig() == config) {
return bitmap;
}
Bitmap result = bitmap.copy(config, /*isMutable=*/ false);
bitmap.recycle();
return result;
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender;
import android.opengl.GLES30;
import java.io.Closeable;
import java.nio.FloatBuffer;
/**
* A list of vertex attribute data stored GPU-side.
*
* <p>One or more {@link VertexBuffer}s are used when constructing a {@link Mesh} to describe vertex
* attribute data; for example, local coordinates, texture coordinates, vertex normals, etc.
*
* @see <a
* href="https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glVertexAttribPointer.xhtml">glVertexAttribPointer</a>
*/
public class VertexBuffer implements Closeable {
private final GpuBuffer buffer;
private final int numberOfEntriesPerVertex;
/**
* Construct a {@link VertexBuffer} populated with initial data.
*
* <p>The GPU buffer will be filled with the data in the <i>direct</i> buffer {@code entries},
* starting from the beginning of the buffer (not the current cursor position). The cursor will be
* left in an undefined position after this function returns.
*
* <p>The number of vertices in the buffer can be expressed as {@code entries.limit() /
* numberOfEntriesPerVertex}. Thus, The size of the buffer must be divisible by {@code
* numberOfEntriesPerVertex}.
*
* <p>The {@code entries} buffer may be null, in which case an empty buffer is constructed
* instead.
*/
public VertexBuffer(SampleRender render, int numberOfEntriesPerVertex, FloatBuffer entries) {
if (entries != null && entries.limit() % numberOfEntriesPerVertex != 0) {
throw new IllegalArgumentException(
"If non-null, vertex buffer data must be divisible by the number of data points per"
+ " vertex");
}
this.numberOfEntriesPerVertex = numberOfEntriesPerVertex;
buffer = new GpuBuffer(GLES30.GL_ARRAY_BUFFER, GpuBuffer.FLOAT_SIZE, entries);
}
/**
* Populate with new data.
*
* <p>The entire buffer is replaced by the contents of the <i>direct</i> buffer {@code entries}
* starting from the beginning of the buffer, not the current cursor position. The cursor will be
* left in an undefined position after this function returns.
*
* <p>The GPU buffer is reallocated automatically if necessary.
*
* <p>The {@code entries} buffer may be null, in which case the buffer will become empty.
* Otherwise, the size of {@code entries} must be divisible by the number of entries per vertex
* specified during construction.
*/
public void set(FloatBuffer entries) {
if (entries != null && entries.limit() % numberOfEntriesPerVertex != 0) {
throw new IllegalArgumentException(
"If non-null, vertex buffer data must be divisible by the number of data points per"
+ " vertex");
}
buffer.set(entries);
}
@Override
public void close() {
buffer.free();
}
/* package-private */
int getBufferId() {
return buffer.getBufferId();
}
/* package-private */
int getNumberOfEntriesPerVertex() {
return numberOfEntriesPerVertex;
}
/* package-private */
int getNumberOfVertices() {
return buffer.getSize() / numberOfEntriesPerVertex;
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender.arcore;
import android.media.Image;
import android.opengl.GLES30;
import com.google.ar.core.Coordinates2d;
import com.google.ar.core.Frame;
import com.example.thetrek.common.samplerender.Framebuffer;
import com.example.thetrek.common.samplerender.Mesh;
import com.example.thetrek.common.samplerender.SampleRender;
import com.example.thetrek.common.samplerender.Shader;
import com.example.thetrek.common.samplerender.Texture;
import com.example.thetrek.common.samplerender.VertexBuffer;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.HashMap;
/**
* This class both renders the AR camera background and composes the a scene foreground. The camera
* background can be rendered as either camera image data or camera depth data. The virtual scene
* can be composited with or without depth occlusion.
*/
public class BackgroundRenderer {
private static final String TAG = BackgroundRenderer.class.getSimpleName();
// components_per_vertex * number_of_vertices * float_size
private static final int COORDS_BUFFER_SIZE = 2 * 4 * 4;
private static final FloatBuffer NDC_QUAD_COORDS_BUFFER =
ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
private static final FloatBuffer VIRTUAL_SCENE_TEX_COORDS_BUFFER =
ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
static {
NDC_QUAD_COORDS_BUFFER.put(
new float[] {
/*0:*/ -1f, -1f, /*1:*/ +1f, -1f, /*2:*/ -1f, +1f, /*3:*/ +1f, +1f,
});
VIRTUAL_SCENE_TEX_COORDS_BUFFER.put(
new float[] {
/*0:*/ 0f, 0f, /*1:*/ 1f, 0f, /*2:*/ 0f, 1f, /*3:*/ 1f, 1f,
});
}
private final FloatBuffer cameraTexCoords =
ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
private final Mesh mesh;
private final VertexBuffer cameraTexCoordsVertexBuffer;
private Shader backgroundShader;
private Shader occlusionShader;
private final Texture cameraDepthTexture;
private final Texture cameraColorTexture;
private Texture depthColorPaletteTexture;
private boolean useDepthVisualization;
private boolean useOcclusion;
private float aspectRatio;
/**
* Allocates and initializes OpenGL resources needed by the background renderer. Must be called
* during a {@link SampleRender.Renderer} callback, typically in {@link
* SampleRender.Renderer#onSurfaceCreated()}.
*/
public BackgroundRenderer(SampleRender render) {
cameraColorTexture =
new Texture(
render,
Texture.Target.TEXTURE_EXTERNAL_OES,
Texture.WrapMode.CLAMP_TO_EDGE,
/*useMipmaps=*/ false);
cameraDepthTexture =
new Texture(
render,
Texture.Target.TEXTURE_2D,
Texture.WrapMode.CLAMP_TO_EDGE,
/*useMipmaps=*/ false);
// Create a Mesh with three vertex buffers: one for the screen coordinates (normalized device
// coordinates), one for the camera texture coordinates (to be populated with proper data later
// before drawing), and one for the virtual scene texture coordinates (unit texture quad)
VertexBuffer screenCoordsVertexBuffer =
new VertexBuffer(render, /* numberOfEntriesPerVertex=*/ 2, NDC_QUAD_COORDS_BUFFER);
cameraTexCoordsVertexBuffer =
new VertexBuffer(render, /*numberOfEntriesPerVertex=*/ 2, /*entries=*/ null);
VertexBuffer virtualSceneTexCoordsVertexBuffer =
new VertexBuffer(render, /* numberOfEntriesPerVertex=*/ 2, VIRTUAL_SCENE_TEX_COORDS_BUFFER);
VertexBuffer[] vertexBuffers = {
screenCoordsVertexBuffer, cameraTexCoordsVertexBuffer, virtualSceneTexCoordsVertexBuffer,
};
mesh =
new Mesh(render, Mesh.PrimitiveMode.TRIANGLE_STRIP, /*indexBuffer=*/ null, vertexBuffers);
}
/**
* Sets whether the background camera image should be replaced with a depth visualization instead.
* This reloads the corresponding shader code, and must be called on the GL thread.
*/
public void setUseDepthVisualization(SampleRender render, boolean useDepthVisualization)
throws IOException {
if (backgroundShader != null) {
if (this.useDepthVisualization == useDepthVisualization) {
return;
}
backgroundShader.close();
backgroundShader = null;
this.useDepthVisualization = useDepthVisualization;
}
if (useDepthVisualization) {
depthColorPaletteTexture =
Texture.createFromAsset(
render,
"models/depth_color_palette.png",
Texture.WrapMode.CLAMP_TO_EDGE,
Texture.ColorFormat.LINEAR);
backgroundShader =
Shader.createFromAssets(
render,
"shaders/background_show_depth_color_visualization.vert",
"shaders/background_show_depth_color_visualization.frag",
/*defines=*/ null)
.setTexture("u_CameraDepthTexture", cameraDepthTexture)
.setTexture("u_ColorMap", depthColorPaletteTexture)
.setDepthTest(false)
.setDepthWrite(false);
} else {
backgroundShader =
Shader.createFromAssets(
render,
"shaders/background_show_camera.vert",
"shaders/background_show_camera.frag",
/*defines=*/ null)
.setTexture("u_CameraColorTexture", cameraColorTexture)
.setDepthTest(false)
.setDepthWrite(false);
}
}
/**
* Sets whether to use depth for occlusion. This reloads the shader code with new {@code
* #define}s, and must be called on the GL thread.
*/
public void setUseOcclusion(SampleRender render, boolean useOcclusion) throws IOException {
if (occlusionShader != null) {
if (this.useOcclusion == useOcclusion) {
return;
}
occlusionShader.close();
occlusionShader = null;
this.useOcclusion = useOcclusion;
}
HashMap<String, String> defines = new HashMap<>();
defines.put("USE_OCCLUSION", useOcclusion ? "1" : "0");
occlusionShader =
Shader.createFromAssets(render, "shaders/occlusion.vert", "shaders/occlusion.frag", defines)
.setDepthTest(false)
.setDepthWrite(false)
.setBlend(Shader.BlendFactor.SRC_ALPHA, Shader.BlendFactor.ONE_MINUS_SRC_ALPHA);
if (useOcclusion) {
occlusionShader
.setTexture("u_CameraDepthTexture", cameraDepthTexture)
.setFloat("u_DepthAspectRatio", aspectRatio);
}
}
/**
* Updates the display geometry. This must be called every frame before calling either of
* BackgroundRenderer's draw methods.
*
* @param frame The current {@code Frame} as returned by {@link Session#update()}.
*/
public void updateDisplayGeometry(Frame frame) {
if (frame.hasDisplayGeometryChanged()) {
// If display rotation changed (also includes view size change), we need to re-query the UV
// coordinates for the screen rect, as they may have changed as well.
frame.transformCoordinates2d(
Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
NDC_QUAD_COORDS_BUFFER,
Coordinates2d.TEXTURE_NORMALIZED,
cameraTexCoords);
cameraTexCoordsVertexBuffer.set(cameraTexCoords);
}
}
/** Update depth texture with Image contents. */
public void updateCameraDepthTexture(Image image) {
// SampleRender abstraction leaks here
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, cameraDepthTexture.getTextureId());
GLES30.glTexImage2D(
GLES30.GL_TEXTURE_2D,
0,
GLES30.GL_RG8,
image.getWidth(),
image.getHeight(),
0,
GLES30.GL_RG,
GLES30.GL_UNSIGNED_BYTE,
image.getPlanes()[0].getBuffer());
if (useOcclusion) {
aspectRatio = (float) image.getWidth() / (float) image.getHeight();
occlusionShader.setFloat("u_DepthAspectRatio", aspectRatio);
}
}
/**
* Draws the AR background image. The image will be drawn such that virtual content rendered with
* the matrices provided by {@link com.google.ar.core.Camera#getViewMatrix(float[], int)} and
* {@link com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)} will
* accurately follow static physical objects.
*/
public void drawBackground(SampleRender render) {
render.draw(mesh, backgroundShader);
}
/**
* Draws the virtual scene. Any objects rendered in the given {@link Framebuffer} will be drawn
* given the previously specified {@link OcclusionMode}.
*
* <p>Virtual content should be rendered using the matrices provided by {@link
* com.google.ar.core.Camera#getViewMatrix(float[], int)} and {@link
* com.google.ar.core.Camera#getProjectionMatrix(float[], int, float, float)}.
*/
public void drawVirtualScene(
SampleRender render, Framebuffer virtualSceneFramebuffer, float zNear, float zFar) {
occlusionShader.setTexture(
"u_VirtualSceneColorTexture", virtualSceneFramebuffer.getColorTexture());
if (useOcclusion) {
occlusionShader
.setTexture("u_VirtualSceneDepthTexture", virtualSceneFramebuffer.getDepthTexture())
.setFloat("u_ZNear", zNear)
.setFloat("u_ZFar", zFar);
}
render.draw(mesh, occlusionShader);
}
/** Return the camera color texture generated by this object. */
public Texture getCameraColorTexture() {
return cameraColorTexture;
}
/** Return the camera depth texture generated by this object. */
public Texture getCameraDepthTexture() {
return cameraDepthTexture;
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender.arcore;
import android.opengl.Matrix;
import com.example.thetrek.common.samplerender.IndexBuffer;
import com.example.thetrek.common.samplerender.Mesh;
import com.example.thetrek.common.samplerender.SampleRender;
import com.example.thetrek.common.samplerender.Shader;
import com.example.thetrek.common.samplerender.Texture;
import com.example.thetrek.common.samplerender.VertexBuffer;
import com.google.ar.core.Camera;
import com.google.ar.core.Plane;
import com.google.ar.core.Pose;
import com.google.ar.core.TrackingState;
import com.example.thetrek.common.samplerender.IndexBuffer;
import com.example.thetrek.common.samplerender.Mesh;
import com.example.thetrek.common.samplerender.SampleRender;
import com.example.thetrek.common.samplerender.Shader;
import com.example.thetrek.common.samplerender.Shader.BlendFactor;
import com.example.thetrek.common.samplerender.Texture;
import com.example.thetrek.common.samplerender.VertexBuffer;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Renders the detected AR planes. */
public class PlaneRenderer {
private static final String TAG = PlaneRenderer.class.getSimpleName();
// Shader names.
private static final String VERTEX_SHADER_NAME = "shaders/plane.vert";
private static final String FRAGMENT_SHADER_NAME = "shaders/plane.frag";
private static final String TEXTURE_NAME = "models/trigrid.png";
private static final int BYTES_PER_FLOAT = Float.SIZE / 8;
private static final int BYTES_PER_INT = Integer.SIZE / 8;
private static final int COORDS_PER_VERTEX = 3; // x, z, alpha
private static final int VERTS_PER_BOUNDARY_VERT = 2;
private static final int INDICES_PER_BOUNDARY_VERT = 3;
private static final int INITIAL_BUFFER_BOUNDARY_VERTS = 64;
private static final int INITIAL_VERTEX_BUFFER_SIZE_BYTES =
BYTES_PER_FLOAT * COORDS_PER_VERTEX * VERTS_PER_BOUNDARY_VERT * INITIAL_BUFFER_BOUNDARY_VERTS;
private static final int INITIAL_INDEX_BUFFER_SIZE_BYTES =
BYTES_PER_INT
* INDICES_PER_BOUNDARY_VERT
* INDICES_PER_BOUNDARY_VERT
* INITIAL_BUFFER_BOUNDARY_VERTS;
private static final float FADE_RADIUS_M = 0.25f;
private static final float DOTS_PER_METER = 10.0f;
private static final float EQUILATERAL_TRIANGLE_SCALE = (float) (1 / Math.sqrt(3));
// Using the "signed distance field" approach to render sharp lines and circles.
// {dotThreshold, lineThreshold, lineFadeSpeed, occlusionScale}
// dotThreshold/lineThreshold: red/green intensity above which dots/lines are present
// lineFadeShrink: lines will fade in between alpha = 1-(1/lineFadeShrink) and 1.0
// occlusionShrink: occluded planes will fade out between alpha = 0 and 1/occlusionShrink
private static final float[] GRID_CONTROL = {0.2f, 0.4f, 2.0f, 1.5f};
private final Mesh mesh;
private final IndexBuffer indexBufferObject;
private final VertexBuffer vertexBufferObject;
private final Shader shader;
private FloatBuffer vertexBuffer =
ByteBuffer.allocateDirect(INITIAL_VERTEX_BUFFER_SIZE_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
private IntBuffer indexBuffer =
ByteBuffer.allocateDirect(INITIAL_INDEX_BUFFER_SIZE_BYTES)
.order(ByteOrder.nativeOrder())
.asIntBuffer();
// Temporary lists/matrices allocated here to reduce number of allocations for each frame.
private final float[] viewMatrix = new float[16];
private final float[] modelMatrix = new float[16];
private final float[] modelViewMatrix = new float[16];
private final float[] modelViewProjectionMatrix = new float[16];
private final float[] planeAngleUvMatrix =
new float[4]; // 2x2 rotation matrix applied to uv coords.
private final float[] normalVector = new float[3];
private final Map<Plane, Integer> planeIndexMap = new HashMap<>();
/**
* Allocates and initializes OpenGL resources needed by the plane renderer. Must be called during
* a {@link SampleRender.Renderer} callback, typically in {@link
* SampleRender.Renderer#onSurfaceCreated}.
*/
public PlaneRenderer(SampleRender render) throws IOException {
Texture texture =
Texture.createFromAsset(
render, TEXTURE_NAME, Texture.WrapMode.REPEAT, Texture.ColorFormat.LINEAR);
shader =
Shader.createFromAssets(render, VERTEX_SHADER_NAME, FRAGMENT_SHADER_NAME, /*defines=*/ null)
.setTexture("u_Texture", texture)
.setVec4("u_GridControl", GRID_CONTROL)
.setBlend(
BlendFactor.DST_ALPHA, // RGB (src)
BlendFactor.ONE, // RGB (dest)
BlendFactor.ZERO, // ALPHA (src)
BlendFactor.ONE_MINUS_SRC_ALPHA) // ALPHA (dest)
.setDepthWrite(false);
indexBufferObject = new IndexBuffer(render, /*entries=*/ null);
vertexBufferObject = new VertexBuffer(render, COORDS_PER_VERTEX, /*entries=*/ null);
VertexBuffer[] vertexBuffers = {vertexBufferObject};
mesh = new Mesh(render, Mesh.PrimitiveMode.TRIANGLE_STRIP, indexBufferObject, vertexBuffers);
}
/** Updates the plane model transform matrix and extents. */
private void updatePlaneParameters(
float[] planeMatrix, float extentX, float extentZ, FloatBuffer boundary) {
System.arraycopy(planeMatrix, 0, modelMatrix, 0, 16);
if (boundary == null) {
vertexBuffer.limit(0);
indexBuffer.limit(0);
return;
}
// Generate a new set of vertices and a corresponding triangle strip index set so that
// the plane boundary polygon has a fading edge. This is done by making a copy of the
// boundary polygon vertices and scaling it down around center to push it inwards. Then
// the index buffer is setup accordingly.
boundary.rewind();
int boundaryVertices = boundary.limit() / 2;
int numVertices;
int numIndices;
numVertices = boundaryVertices * VERTS_PER_BOUNDARY_VERT;
// drawn as GL_TRIANGLE_STRIP with 3n-2 triangles (n-2 for fill, 2n for perimeter).
numIndices = boundaryVertices * INDICES_PER_BOUNDARY_VERT;
if (vertexBuffer.capacity() < numVertices * COORDS_PER_VERTEX) {
int size = vertexBuffer.capacity();
while (size < numVertices * COORDS_PER_VERTEX) {
size *= 2;
}
vertexBuffer =
ByteBuffer.allocateDirect(BYTES_PER_FLOAT * size)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
}
vertexBuffer.rewind();
vertexBuffer.limit(numVertices * COORDS_PER_VERTEX);
if (indexBuffer.capacity() < numIndices) {
int size = indexBuffer.capacity();
while (size < numIndices) {
size *= 2;
}
indexBuffer =
ByteBuffer.allocateDirect(BYTES_PER_INT * size)
.order(ByteOrder.nativeOrder())
.asIntBuffer();
}
indexBuffer.rewind();
indexBuffer.limit(numIndices);
// Note: when either dimension of the bounding box is smaller than 2*FADE_RADIUS_M we
// generate a bunch of 0-area triangles. These don't get rendered though so it works
// out ok.
float xScale = Math.max((extentX - 2 * FADE_RADIUS_M) / extentX, 0.0f);
float zScale = Math.max((extentZ - 2 * FADE_RADIUS_M) / extentZ, 0.0f);
while (boundary.hasRemaining()) {
float x = boundary.get();
float z = boundary.get();
vertexBuffer.put(x);
vertexBuffer.put(z);
vertexBuffer.put(0.0f);
vertexBuffer.put(x * xScale);
vertexBuffer.put(z * zScale);
vertexBuffer.put(1.0f);
}
// step 1, perimeter
indexBuffer.put((short) ((boundaryVertices - 1) * 2));
for (int i = 0; i < boundaryVertices; ++i) {
indexBuffer.put((short) (i * 2));
indexBuffer.put((short) (i * 2 + 1));
}
indexBuffer.put((short) 1);
// This leaves us on the interior edge of the perimeter between the inset vertices
// for boundary verts n-1 and 0.
// step 2, interior:
for (int i = 1; i < boundaryVertices / 2; ++i) {
indexBuffer.put((short) ((boundaryVertices - 1 - i) * 2 + 1));
indexBuffer.put((short) (i * 2 + 1));
}
if (boundaryVertices % 2 != 0) {
indexBuffer.put((short) ((boundaryVertices / 2) * 2 + 1));
}
}
/**
* Draws the collection of tracked planes, with closer planes hiding more distant ones.
*
* @param allPlanes The collection of planes to draw.
* @param cameraPose The pose of the camera, as returned by {@link Camera#getPose()}
* @param cameraProjection The projection matrix, as returned by {@link
* Camera#getProjectionMatrix(float[], int, float, float)}
*/
public void drawPlanes(
SampleRender render, Collection<Plane> allPlanes, Pose cameraPose, float[] cameraProjection) {
// Planes must be sorted by distance from camera so that we draw closer planes first, and
// they occlude the farther planes.
List<SortablePlane> sortedPlanes = new ArrayList<>();
for (Plane plane : allPlanes) {
if (plane.getTrackingState() != TrackingState.TRACKING || plane.getSubsumedBy() != null) {
continue;
}
float distance = calculateDistanceToPlane(plane.getCenterPose(), cameraPose);
if (distance < 0) { // Plane is back-facing.
continue;
}
sortedPlanes.add(new SortablePlane(distance, plane));
}
Collections.sort(
sortedPlanes,
new Comparator<SortablePlane>() {
@Override
public int compare(SortablePlane a, SortablePlane b) {
return Float.compare(b.distance, a.distance);
}
});
cameraPose.inverse().toMatrix(viewMatrix, 0);
for (SortablePlane sortedPlane : sortedPlanes) {
Plane plane = sortedPlane.plane;
float[] planeMatrix = new float[16];
plane.getCenterPose().toMatrix(planeMatrix, 0);
// Get transformed Y axis of plane's coordinate system.
plane.getCenterPose().getTransformedAxis(1, 1.0f, normalVector, 0);
updatePlaneParameters(
planeMatrix, plane.getExtentX(), plane.getExtentZ(), plane.getPolygon());
// Get plane index. Keep a map to assign same indices to same planes.
Integer planeIndex = planeIndexMap.get(plane);
if (planeIndex == null) {
planeIndex = planeIndexMap.size();
planeIndexMap.put(plane, planeIndex);
}
// Each plane will have its own angle offset from others, to make them easier to
// distinguish. Compute a 2x2 rotation matrix from the angle.
float angleRadians = planeIndex * 0.144f;
float uScale = DOTS_PER_METER;
float vScale = DOTS_PER_METER * EQUILATERAL_TRIANGLE_SCALE;
planeAngleUvMatrix[0] = +(float) Math.cos(angleRadians) * uScale;
planeAngleUvMatrix[1] = -(float) Math.sin(angleRadians) * vScale;
planeAngleUvMatrix[2] = +(float) Math.sin(angleRadians) * uScale;
planeAngleUvMatrix[3] = +(float) Math.cos(angleRadians) * vScale;
// Build the ModelView and ModelViewProjection matrices
// for calculating cube position and light.
Matrix.multiplyMM(modelViewMatrix, 0, viewMatrix, 0, modelMatrix, 0);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, cameraProjection, 0, modelViewMatrix, 0);
// Populate the shader uniforms for this frame.
shader.setMat4("u_Model", modelMatrix);
shader.setMat4("u_ModelViewProjection", modelViewProjectionMatrix);
shader.setMat2("u_PlaneUvMatrix", planeAngleUvMatrix);
shader.setVec3("u_Normal", normalVector);
// Set the position of the plane
vertexBufferObject.set(vertexBuffer);
indexBufferObject.set(indexBuffer);
render.draw(mesh, shader);
}
}
private static class SortablePlane {
final float distance;
final Plane plane;
SortablePlane(float distance, Plane plane) {
this.distance = distance;
this.plane = plane;
}
}
// Calculate the normal distance to plane from cameraPose, the given planePose should have y axis
// parallel to plane's normal, for example plane's center pose or hit test pose.
public static float calculateDistanceToPlane(Pose planePose, Pose cameraPose) {
float[] normal = new float[3];
float cameraX = cameraPose.tx();
float cameraY = cameraPose.ty();
float cameraZ = cameraPose.tz();
// Get transformed Y axis of plane's coordinate system.
planePose.getTransformedAxis(1, 1.0f, normal, 0);
// Compute dot product of plane's normal with vector from camera to plane center.
return (cameraX - planePose.tx()) * normal[0]
+ (cameraY - planePose.ty()) * normal[1]
+ (cameraZ - planePose.tz()) * normal[2];
}
}
/*
* Copyright 2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.common.samplerender.arcore;
import static java.lang.Math.max;
import static java.lang.Math.min;
import android.media.Image;
import android.opengl.GLES30;
import android.util.Log;
import com.google.ar.core.ImageFormat;
import com.example.thetrek.common.samplerender.GLError;
import com.example.thetrek.common.samplerender.Mesh;
import com.example.thetrek.common.samplerender.SampleRender;
import com.example.thetrek.common.samplerender.Shader;
import com.example.thetrek.common.samplerender.Texture;
import com.example.thetrek.common.samplerender.VertexBuffer;
import com.example.thetrek.R;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
/**
* Filters a provided cubemap into a cubemap lookup texture which is a function of the direction of
* a reflected ray of light and material roughness, i.e. the LD term of the specular IBL
* calculation.
*
* <p>See https://google.github.io/filament/Filament.md.html#lighting/imagebasedlights for a more
* detailed explanation.
*/
public class SpecularCubemapFilter implements Closeable {
private static final String TAG = SpecularCubemapFilter.class.getSimpleName();
private static final int COMPONENTS_PER_VERTEX = 2;
private static final int NUMBER_OF_VERTICES = 4;
private static final int FLOAT_SIZE = 4;
private static final int COORDS_BUFFER_SIZE =
COMPONENTS_PER_VERTEX * NUMBER_OF_VERTICES * FLOAT_SIZE;
private static final int NUMBER_OF_CUBE_FACES = 6;
private static final FloatBuffer COORDS_BUFFER =
ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
static {
COORDS_BUFFER.put(
new float[] {
/*0:*/ -1f, -1f, /*1:*/ +1f, -1f, /*2:*/ -1f, +1f, /*3:*/ +1f, +1f,
});
}
private static final String[] ATTACHMENT_LOCATION_DEFINES = {
"PX_LOCATION", "NX_LOCATION", "PY_LOCATION", "NY_LOCATION", "PZ_LOCATION", "NZ_LOCATION",
};
private static final int[] ATTACHMENT_ENUMS = {
GLES30.GL_COLOR_ATTACHMENT0,
GLES30.GL_COLOR_ATTACHMENT1,
GLES30.GL_COLOR_ATTACHMENT2,
GLES30.GL_COLOR_ATTACHMENT3,
GLES30.GL_COLOR_ATTACHMENT4,
GLES30.GL_COLOR_ATTACHMENT5,
};
// We need to create enough shaders and framebuffers to encompass every face of the cubemap. Each
// color attachment is used by the framebuffer to render to a different face of the cubemap, so we
// use "chunks" which define as many color attachments as possible for each face. For example, if
// we have a maximum of 3 color attachments, we must create two shaders with the following color
// attachments:
//
// layout(location = 0) out vec4 o_FragColorPX;
// layout(location = 1) out vec4 o_FragColorNX;
// layout(location = 2) out vec4 o_FragColorPY;
//
// and
//
// layout(location = 0) out vec4 o_FragColorNY;
// layout(location = 1) out vec4 o_FragColorPZ;
// layout(location = 2) out vec4 o_FragColorNZ;
private static class Chunk {
public final int chunkIndex;
public final int chunkSize;
public final int firstFaceIndex;
public Chunk(int chunkIndex, int maxChunkSize) {
this.chunkIndex = chunkIndex;
this.firstFaceIndex = chunkIndex * maxChunkSize;
this.chunkSize = min(maxChunkSize, NUMBER_OF_CUBE_FACES - this.firstFaceIndex);
}
}
private static class ChunkIterable implements Iterable<Chunk> {
public final int maxChunkSize;
public final int numberOfChunks;
public ChunkIterable(int maxNumberOfColorAttachments) {
this.maxChunkSize = min(maxNumberOfColorAttachments, NUMBER_OF_CUBE_FACES);
int numberOfChunks = NUMBER_OF_CUBE_FACES / this.maxChunkSize;
if (NUMBER_OF_CUBE_FACES % this.maxChunkSize != 0) {
numberOfChunks++;
}
this.numberOfChunks = numberOfChunks;
}
@Override
public Iterator<Chunk> iterator() {
return new Iterator<Chunk>() {
private Chunk chunk = new Chunk(/*chunkIndex=*/ 0, maxChunkSize);
@Override
public boolean hasNext() {
return chunk.chunkIndex < numberOfChunks;
}
@Override
public Chunk next() {
Chunk result = this.chunk;
this.chunk = new Chunk(result.chunkIndex + 1, maxChunkSize);
return result;
}
};
}
}
private static class ImportanceSampleCacheEntry {
public float[] direction;
public float contribution;
public float level;
}
private final int resolution;
private final int numberOfImportanceSamples;
private final int numberOfMipmapLevels;
private final Texture radianceCubemap;
private final Texture ldCubemap;
// Indexed by attachment chunk.
private final Shader[] shaders;
private final Mesh mesh;
// Using OpenGL directly here since cubemap framebuffers are very involved. Indexed by
// [mipmapLevel][attachmentChunk].
private final int[][] framebuffers;
/**
* Constructs a {@link SpecularCubemapFilter}.
*
* <p>The provided resolution refers to both the width and height of the input resolution and the
* resolution of the highest mipmap level of the filtered cubemap texture.
*
* <p>Ideally, the cubemap would need to be filtered by computing a function of every sample over
* the hemisphere for every texel. Since this is not practical to compute, a limited, discrete
* number of importance samples are selected instead. A larger number of importance samples will
* generally provide more accurate results, but in the case of ARCore, the cubemap estimations are
* already very low resolution, and higher values provide rapidly diminishing returns.
*/
public SpecularCubemapFilter(SampleRender render, int resolution, int numberOfImportanceSamples)
throws IOException {
this.resolution = resolution;
this.numberOfImportanceSamples = numberOfImportanceSamples;
this.numberOfMipmapLevels = log2(resolution) + 1;
try {
radianceCubemap =
new Texture(render, Texture.Target.TEXTURE_CUBE_MAP, Texture.WrapMode.CLAMP_TO_EDGE);
ldCubemap =
new Texture(render, Texture.Target.TEXTURE_CUBE_MAP, Texture.WrapMode.CLAMP_TO_EDGE);
ChunkIterable chunks = new ChunkIterable(getMaxColorAttachments());
initializeLdCubemap();
shaders = createShaders(render, chunks);
framebuffers = createFramebuffers(chunks);
// Create the quad mesh that encompasses the entire view.
VertexBuffer coordsBuffer = new VertexBuffer(render, COMPONENTS_PER_VERTEX, COORDS_BUFFER);
mesh =
new Mesh(
render,
Mesh.PrimitiveMode.TRIANGLE_STRIP,
/*indexBuffer=*/ null,
new VertexBuffer[] {coordsBuffer});
} catch (Throwable t) {
close();
throw t;
}
}
@Override
public void close() {
if (framebuffers != null) {
for (int[] framebufferChunks : framebuffers) {
GLES30.glDeleteFramebuffers(framebufferChunks.length, framebufferChunks, 0);
GLError.maybeLogGLError(
Log.WARN, TAG, "Failed to free framebuffers", "glDeleteFramebuffers");
}
}
if (radianceCubemap != null) {
radianceCubemap.close();
}
if (ldCubemap != null) {
ldCubemap.close();
}
if (shaders != null) {
for (Shader shader : shaders) {
shader.close();
}
}
}
/**
* Updates and filters the provided cubemap textures from ARCore.
*
* <p>This method should be called every frame with the result of {@link
* com.google.ar.core.LightEstimate.acquireEnvironmentalHdrCubeMap()} to update the filtered
* cubemap texture, accessible via {@link getFilteredCubemapTexture()}.
*
* <p>The given {@link Image}s will be closed by this method, even if an exception occurs.
*/
public void update(Image[] images) {
try {
GLES30.glBindTexture(GLES30.GL_TEXTURE_CUBE_MAP, radianceCubemap.getTextureId());
GLError.maybeThrowGLException("Failed to bind radiance cubemap texture", "glBindTexture");
if (images.length != NUMBER_OF_CUBE_FACES) {
throw new IllegalArgumentException(
"Number of images differs from the number of sides of a cube.");
}
for (int i = 0; i < NUMBER_OF_CUBE_FACES; ++i) {
Image image = images[i];
// Sanity check for the format of the cubemap.
if (image.getFormat() != ImageFormat.RGBA_FP16) {
throw new IllegalArgumentException(
"Unexpected image format for cubemap: " + image.getFormat());
}
if (image.getHeight() != image.getWidth()) {
throw new IllegalArgumentException("Cubemap face is not square.");
}
if (image.getHeight() != resolution) {
throw new IllegalArgumentException(
"Cubemap face resolution ("
+ image.getHeight()
+ ") does not match expected value ("
+ resolution
+ ").");
}
GLES30.glTexImage2D(
GLES30.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
/*level=*/ 0,
GLES30.GL_RGBA16F,
/*width=*/ resolution,
/*height=*/ resolution,
/*border=*/ 0,
GLES30.GL_RGBA,
GLES30.GL_HALF_FLOAT,
image.getPlanes()[0].getBuffer());
GLError.maybeThrowGLException("Failed to populate cubemap face", "glTexImage2D");
}
GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_CUBE_MAP);
GLError.maybeThrowGLException("Failed to generate cubemap mipmaps", "glGenerateMipmap");
// Do the filtering operation, filling the mipmaps of ldTexture with the roughness filtered
// cubemap.
for (int level = 0; level < numberOfMipmapLevels; ++level) {
int mipmapResolution = resolution >> level;
GLES30.glViewport(0, 0, mipmapResolution, mipmapResolution);
GLError.maybeThrowGLException("Failed to set viewport dimensions", "glViewport");
for (int chunkIndex = 0; chunkIndex < shaders.length; ++chunkIndex) {
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, framebuffers[level][chunkIndex]);
GLError.maybeThrowGLException("Failed to bind cubemap framebuffer", "glBindFramebuffer");
shaders[chunkIndex].setInt("u_RoughnessLevel", level);
shaders[chunkIndex].lowLevelUse();
mesh.lowLevelDraw();
}
}
} finally {
for (Image image : images) {
image.close();
}
}
}
/** Returns the number of mipmap levels in the filtered cubemap texture. */
public int getNumberOfMipmapLevels() {
return numberOfMipmapLevels;
}
/**
* Returns the filtered cubemap texture whose contents are updated with each call to {@link
* #update(Image[])}.
*/
public Texture getFilteredCubemapTexture() {
return ldCubemap;
}
private void initializeLdCubemap() {
// Initialize mipmap levels of LD cubemap.
GLES30.glBindTexture(GLES30.GL_TEXTURE_CUBE_MAP, ldCubemap.getTextureId());
GLError.maybeThrowGLException("Could not bind LD cubemap texture", "glBindTexture");
for (int level = 0; level < numberOfMipmapLevels; ++level) {
int mipmapResolution = resolution >> level;
for (int face = 0; face < NUMBER_OF_CUBE_FACES; ++face) {
GLES30.glTexImage2D(
GLES30.GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
level,
GLES30.GL_RGB16F,
/*width=*/ mipmapResolution,
/*height=*/ mipmapResolution,
/*border=*/ 0,
GLES30.GL_RGB,
GLES30.GL_HALF_FLOAT,
/*data=*/ null);
GLError.maybeThrowGLException("Could not initialize LD cubemap mipmap", "glTexImage2D");
}
}
}
private Shader[] createShaders(SampleRender render, ChunkIterable chunks) throws IOException {
ImportanceSampleCacheEntry[][] importanceSampleCaches = generateImportanceSampleCaches();
HashMap<String, String> commonDefines = new HashMap<>();
commonDefines.put("NUMBER_OF_IMPORTANCE_SAMPLES", Integer.toString(numberOfImportanceSamples));
commonDefines.put("NUMBER_OF_MIPMAP_LEVELS", Integer.toString(numberOfMipmapLevels));
Shader[] shaders = new Shader[chunks.numberOfChunks];
for (Chunk chunk : chunks) {
HashMap<String, String> defines = new HashMap<>(commonDefines);
for (int location = 0; location < chunk.chunkSize; ++location) {
defines.put(
ATTACHMENT_LOCATION_DEFINES[chunk.firstFaceIndex + location],
Integer.toString(location));
}
// Create the shader and populate its uniforms with the importance sample cache entries.
shaders[chunk.chunkIndex] =
Shader.createFromAssets(
render, "shaders/cubemap_filter.vert", "shaders/cubemap_filter.frag", defines)
.setTexture("u_Cubemap", radianceCubemap)
.setDepthTest(false)
.setDepthWrite(false);
}
for (Shader shader : shaders) {
for (int i = 0; i < importanceSampleCaches.length; ++i) {
ImportanceSampleCacheEntry[] cache = importanceSampleCaches[i];
String cacheName = "u_ImportanceSampleCaches[" + i + "]";
shader.setInt(cacheName + ".number_of_entries", cache.length);
for (int j = 0; j < cache.length; ++j) {
ImportanceSampleCacheEntry entry = cache[j];
String entryName = cacheName + ".entries[" + j + "]";
shader
.setVec3(entryName + ".direction", entry.direction)
.setFloat(entryName + ".contribution", entry.contribution)
.setFloat(entryName + ".level", entry.level);
}
}
}
return shaders;
}
private int[][] createFramebuffers(ChunkIterable chunks) {
// Create the framebuffers for each mipmap level.
int[][] framebuffers = new int[numberOfMipmapLevels][];
for (int level = 0; level < numberOfMipmapLevels; ++level) {
int[] framebufferChunks = new int[chunks.numberOfChunks];
GLES30.glGenFramebuffers(framebufferChunks.length, framebufferChunks, 0);
GLError.maybeThrowGLException("Could not create cubemap framebuffers", "glGenFramebuffers");
for (Chunk chunk : chunks) {
// Set the drawbuffers
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, framebufferChunks[chunk.chunkIndex]);
GLError.maybeThrowGLException("Could not bind framebuffer", "glBindFramebuffer");
GLES30.glDrawBuffers(chunk.chunkSize, ATTACHMENT_ENUMS, 0);
GLError.maybeThrowGLException("Could not bind draw buffers", "glDrawBuffers");
// Since GLES doesn't support glFramebufferTexture, we will use each cubemap face as a
// different color attachment.
for (int attachment = 0; attachment < chunk.chunkSize; ++attachment) {
GLES30.glFramebufferTexture2D(
GLES30.GL_FRAMEBUFFER,
GLES30.GL_COLOR_ATTACHMENT0 + attachment,
GLES30.GL_TEXTURE_CUBE_MAP_POSITIVE_X + chunk.firstFaceIndex + attachment,
ldCubemap.getTextureId(),
level);
GLError.maybeThrowGLException(
"Could not attach LD cubemap mipmap to framebuffer", "glFramebufferTexture");
}
}
framebuffers[level] = framebufferChunks;
}
return framebuffers;
}
/**
* Generate a cache of importance sampling terms in tangent space, indexed by {@code
* [roughnessLevel-1][sampleIndex]}.
*/
private ImportanceSampleCacheEntry[][] generateImportanceSampleCaches() {
ImportanceSampleCacheEntry[][] result =
new ImportanceSampleCacheEntry[numberOfMipmapLevels - 1][];
for (int i = 0; i < numberOfMipmapLevels - 1; ++i) {
int mipmapLevel = i + 1;
float perceptualRoughness = mipmapLevel / (float) (numberOfMipmapLevels - 1);
float roughness = perceptualRoughness * perceptualRoughness;
int resolution = this.resolution >> mipmapLevel;
float log4omegaP = log4((4.0f * PI_F) / (6 * resolution * resolution));
float inverseNumberOfSamples = 1f / numberOfImportanceSamples;
ArrayList<ImportanceSampleCacheEntry> cache = new ArrayList<>(numberOfImportanceSamples);
float weight = 0f;
for (int sampleIndex = 0; sampleIndex < numberOfImportanceSamples; ++sampleIndex) {
float[] u = hammersley(sampleIndex, inverseNumberOfSamples);
float[] h = hemisphereImportanceSampleDggx(u, roughness);
float noh = h[2];
float noh2 = noh * noh;
float nol = 2f * noh2 - 1f;
if (nol > 0) {
ImportanceSampleCacheEntry entry = new ImportanceSampleCacheEntry();
entry.direction = new float[] {2f * noh * h[0], 2 * noh * h[1], nol};
float pdf = distributionGgx(noh, roughness) / 4f;
float log4omegaS = log4(1f / (numberOfImportanceSamples * pdf));
// K is a LOD bias that allows a bit of overlapping between samples
float log4K = 1f; // K = 4
float l = log4omegaS - log4omegaP + log4K;
entry.level = min(max(l, 0f), (float) (numberOfMipmapLevels - 1));
entry.contribution = nol;
cache.add(entry);
weight += nol;
}
}
for (ImportanceSampleCacheEntry entry : cache) {
entry.contribution /= weight;
}
result[i] = new ImportanceSampleCacheEntry[cache.size()];
cache.toArray(result[i]);
}
return result;
}
private static int getMaxColorAttachments() {
int[] result = new int[1];
GLES30.glGetIntegerv(GLES30.GL_MAX_COLOR_ATTACHMENTS, result, 0);
GLError.maybeThrowGLException("Failed to get max color attachments", "glGetIntegerv");
return result[0];
}
// Math!
private static final float PI_F = (float) Math.PI;
private static int log2(int value) {
if (value <= 0) {
throw new IllegalArgumentException("value must be positive");
}
value >>= 1;
int result = 0;
while (value != 0) {
++result;
value >>= 1;
}
return result;
}
private static float log4(float value) {
return (float) (Math.log((double) value) / Math.log(4.0));
}
private static float sqrt(float value) {
return (float) Math.sqrt((double) value);
}
private static float sin(float value) {
return (float) Math.sin((double) value);
}
private static float cos(float value) {
return (float) Math.cos((double) value);
}
private static float[] hammersley(int i, float iN) {
float tof = 0.5f / 0x80000000L;
long bits = i;
bits = (bits << 16) | (bits >>> 16);
bits = ((bits & 0x55555555L) << 1) | ((bits & 0xAAAAAAAAL) >>> 1);
bits = ((bits & 0x33333333L) << 2) | ((bits & 0xCCCCCCCCL) >>> 2);
bits = ((bits & 0x0F0F0F0FL) << 4) | ((bits & 0xF0F0F0F0L) >>> 4);
bits = ((bits & 0x00FF00FFL) << 8) | ((bits & 0xFF00FF00L) >>> 8);
return new float[] {i * iN, bits * tof};
}
private static float[] hemisphereImportanceSampleDggx(float[] u, float a) {
// GGX - Trowbridge-Reitz importance sampling
float phi = 2.0f * PI_F * u[0];
// NOTE: (aa-1) == (a-1)(a+1) produces better fp accuracy
float cosTheta2 = (1f - u[1]) / (1f + (a + 1f) * ((a - 1f) * u[1]));
float cosTheta = sqrt(cosTheta2);
float sinTheta = sqrt(1f - cosTheta2);
return new float[] {sinTheta * cos(phi), sinTheta * sin(phi), cosTheta};
}
private static float distributionGgx(float noh, float a) {
// NOTE: (aa-1) == (a-1)(a+1) produces better fp accuracy
float f = (a - 1f) * ((a + 1f) * (noh * noh)) + 1f;
return (a * a) / (PI_F * f * f);
}
}
package com.example.thetrek.db;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class LocationDAO {
private SQLiteDatabase database;
private LocationsDbHelper dbHelper;
public LocationDAO(Context context) {
dbHelper = new LocationsDbHelper(context);
}
public void open() {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
public long insertLocation(String name, double latitude, double longitude, double elevation, boolean isVisited) {
ContentValues values = new ContentValues();
values.put(LocationsDbHelper.COLUMN_NAME, name);
values.put(LocationsDbHelper.COLUMN_LATITUDE, latitude);
values.put(LocationsDbHelper.COLUMN_LONGITUDE, longitude);
values.put(LocationsDbHelper.COLUMN_ELEVATION, elevation);
values.put(LocationsDbHelper.COLUMN_IS_VISITED, isVisited ? 1 : 0);
return database.insert(LocationsDbHelper.TABLE_NAME, null, values);
}
public void updateLocation(String name, int id, double latitude, double longitude, double elevation, boolean isVisited) {
ContentValues values = new ContentValues();
values.put(LocationsDbHelper.COLUMN_NAME, name);
values.put(LocationsDbHelper.COLUMN_LATITUDE, latitude);
values.put(LocationsDbHelper.COLUMN_LONGITUDE, longitude);
values.put(LocationsDbHelper.COLUMN_ELEVATION, elevation);
values.put(LocationsDbHelper.COLUMN_IS_VISITED, isVisited ? 1 : 0);
String whereClause = LocationsDbHelper.COLUMN_ID + " = ?";
String[] whereArgs = {String.valueOf(id)};
database.update(LocationsDbHelper.TABLE_NAME, values, whereClause, whereArgs);
}
public void deleteLocation(int id) {
String whereClause = LocationsDbHelper.COLUMN_ID + " = ?";
String[] whereArgs = {String.valueOf(id)};
database.delete(LocationsDbHelper.TABLE_NAME, whereClause, whereArgs);
}
public Cursor getAllLocations() {
String[] projection = {LocationsDbHelper.COLUMN_NAME, LocationsDbHelper.COLUMN_ID, LocationsDbHelper.COLUMN_LATITUDE, LocationsDbHelper.COLUMN_LONGITUDE, LocationsDbHelper.COLUMN_ELEVATION, LocationsDbHelper.COLUMN_IS_VISITED};
return database.query(LocationsDbHelper.TABLE_NAME, projection, null, null, null, null, null);
}
}
package com.example.thetrek.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class LocationsDbHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "location.db";
public static final int DATABASE_VERSION = 1;
public static final String TABLE_NAME = "locations";
public static final String COLUMN_ID = "id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_LATITUDE = "latitude";
public static final String COLUMN_LONGITUDE = "longitude";
public static final String COLUMN_ELEVATION = "elevation";
public static final String COLUMN_IS_VISITED = "is_visited";
public LocationsDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createTableQuery = "CREATE TABLE " + TABLE_NAME + "(" +
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_NAME + " TEXT, " +
COLUMN_LATITUDE + " REAL, " +
COLUMN_LONGITUDE + " REAL, " +
COLUMN_ELEVATION + " REAL, " +
COLUMN_IS_VISITED + " INTEGER)";
db.execSQL(createTableQuery);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String dropTableQuery = "DROP TABLE IF EXISTS " + TABLE_NAME;
db.execSQL(dropTableQuery);
onCreate(db);
}
}
package com.example.thetrek.db;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.example.thetrek.screens.rewards.Reward;
import java.util.ArrayList;
import java.util.List;
public class RewardDbHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "reward_database";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_REWARDS = "rewards";
private static final String COLUMN_ID = "id";
private static final String COLUMN_REWARD_NAME = "reward_name";
public RewardDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createTableQuery = "CREATE TABLE " + TABLE_REWARDS + " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_REWARD_NAME + " TEXT)";
db.execSQL(createTableQuery);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Implement if needed
}
// CRUD operations
public int addReward(String rewardName) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_REWARD_NAME, rewardName);
long id = db.insert(TABLE_REWARDS, null, values);
db.close();
return (int) id;
}
public List<Reward> getAllRewards() {
List<Reward> rewardList = new ArrayList<>();
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_REWARDS, null, null, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int idColumnIndex = cursor.getColumnIndex(COLUMN_ID);
int rewardNameColumnIndex = cursor.getColumnIndex(COLUMN_REWARD_NAME);
do {
int id = cursor.getInt(idColumnIndex);
String rewardName = cursor.getString(rewardNameColumnIndex);
Reward reward = new Reward(id, rewardName);
rewardList.add(reward);
} while (cursor.moveToNext());
}
if (cursor != null) {
cursor.close();
}
db.close();
return rewardList;
}
public int updateReward(Reward reward) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_REWARD_NAME, reward.getRewardName());
int rowsAffected = db.update(TABLE_REWARDS, values, COLUMN_ID + " = ?",
new String[]{String.valueOf(reward.getId())});
db.close();
return rowsAffected;
}
public int deleteReward(Reward reward) {
SQLiteDatabase db = getWritableDatabase();
int rowsAffected = db.delete(TABLE_REWARDS, COLUMN_ID + " = ?",
new String[]{String.valueOf(reward.getId())});
db.close();
return rowsAffected;
}
}
package com.example.thetrek.db;
public class RewardStrings {
private static final String[] progressArray = {
"You got this",
"WOW",
"Keep going!",
"You're on the right track",
"Amazing job!",
"Making progress",
"One step at a time",
"Great effort!",
"Halfway there",
"Stay focused",
"Almost done!",
"Believe in yourself",
"You're getting closer",
"Impressive work!",
"Keep pushing forward",
"Don't give up",
"You can do it",
"Just a little more",
"Incredible!",
"Keep it up",
"You're doing great",
"Fantastic!",
"Getting better",
"Stay determined",
"Inspiring!",
"Onward and upward",
"Bravo!",
"You're unstoppable",
"Persevere!",
"Getting closer to your goal",
"Well done",
"Making it happen",
"You're awesome!",
"Keep the momentum",
"Just a few more steps",
"You're making a difference",
"Excellent progress",
"You're a star",
"Almost there",
"You're doing fantastic",
"Keep on going",
"You're incredible",
"You're unstoppable",
"You've got this",
"Keep up the good work",
"Success is near",
"You're on fire",
"No stopping you",
"Keep striving",
"Unbelievable progress",
"Keep climbing",
"You're a champion",
"You're a rockstar",
"The finish line is in sight",
"Way to go!",
"You're a winner",
"Keep the faith",
"You're on the right path",
"You're a shining star",
"Just keep swimming",
"You're an inspiration",
"Amazing effort",
"You're doing splendidly",
"Don't stop now",
"You're making waves",
"You're doing extraordinary",
"Keep aiming higher",
"You're on a roll",
"Spectacular job",
"You're doing remarkable",
"Never give up",
"You're making it happen",
"You're on your way",
"You're phenomenal",
"You're crushing it",
"You're outstanding",
"Keep chasing your dreams",
"You're doing beautifully",
"You're on the right road",
"You're on the verge of success",
"You're exceeding expectations",
"You're on the cusp of greatness",
"You're on the right course",
"You're on the brink of victory",
"You're reaching new heights",
"You're making history",
"You're on the right trajectory",
"You're on the path to greatness",
"You're approaching the pinnacle",
"You're ascending to new levels",
"You're on the road to triumph",
"You're on the threshold of glory",
"You're conquering challenges",
"You're on the journey to success",
"You're on the track to victory",
"You're on the precipice of achievement",
"You're advancing with determination",
"You're on the brink of success",
"You're on the verge of triumph",
"You're making significant strides",
"You're on the verge of a breakthrough",
"You're on the brink of accomplishment",
"You're progressing with purpose"
};
public static String[] getProgressArray() {
return progressArray;
}
}
package com.example.thetrek.geospatial;
public class Anchorr {
private double latitude;
private double longitude;
private double altitude;
public Anchorr(double latitude, double longitude, double altitude) {
this.latitude = latitude;
this.longitude = longitude;
this.altitude = altitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getAltitude() {
return altitude;
}
public void setAltitude(double altitude) {
this.altitude = altitude;
}
}
/*
* Copyright 2022 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.geospatial;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.PopupMenu;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.DialogFragment;
import com.example.thetrek.R;
import com.example.thetrek.db.LocationDAO;
import com.example.thetrek.db.LocationsDbHelper;
import com.example.thetrek.screens.findPlaces.FindPlacesActivity;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.ar.core.Anchor;
import com.google.ar.core.Anchor.RooftopAnchorState;
import com.google.ar.core.Anchor.TerrainAnchorState;
import com.google.ar.core.ArCoreApk;
import com.google.ar.core.Camera;
import com.google.ar.core.Config;
import com.google.ar.core.Earth;
import com.google.ar.core.Frame;
import com.google.ar.core.GeospatialPose;
import com.google.ar.core.HitResult;
import com.google.ar.core.Plane;
import com.google.ar.core.Point;
import com.google.ar.core.Point.OrientationMode;
import com.google.ar.core.PointCloud;
import com.google.ar.core.Pose;
import com.google.ar.core.ResolveAnchorOnRooftopFuture;
import com.google.ar.core.ResolveAnchorOnTerrainFuture;
import com.google.ar.core.Session;
import com.google.ar.core.StreetscapeGeometry;
import com.google.ar.core.Trackable;
import com.google.ar.core.TrackingState;
import com.google.ar.core.VpsAvailability;
import com.google.ar.core.VpsAvailabilityFuture;
import com.example.thetrek.common.helpers.CameraPermissionHelper;
import com.example.thetrek.common.helpers.DisplayRotationHelper;
import com.example.thetrek.common.helpers.FullScreenHelper;
import com.example.thetrek.common.helpers.LocationPermissionHelper;
import com.example.thetrek.common.helpers.SnackbarHelper;
import com.example.thetrek.common.helpers.TrackingStateHelper;
import com.example.thetrek.common.samplerender.Framebuffer;
import com.example.thetrek.common.samplerender.IndexBuffer;
import com.example.thetrek.common.samplerender.Mesh;
import com.example.thetrek.common.samplerender.SampleRender;
import com.example.thetrek.common.samplerender.Shader;
import com.example.thetrek.common.samplerender.Shader.BlendFactor;
import com.example.thetrek.common.samplerender.Texture;
import com.example.thetrek.common.samplerender.VertexBuffer;
import com.example.thetrek.common.samplerender.arcore.BackgroundRenderer;
import com.example.thetrek.common.samplerender.arcore.PlaneRenderer;
import com.google.ar.core.exceptions.CameraNotAvailableException;
import com.google.ar.core.exceptions.FineLocationPermissionNotGrantedException;
import com.google.ar.core.exceptions.GooglePlayServicesLocationLibraryNotLinkedException;
import com.google.ar.core.exceptions.UnavailableApkTooOldException;
import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException;
import com.google.ar.core.exceptions.UnavailableDeviceNotCompatibleException;
import com.google.ar.core.exceptions.UnavailableSdkTooOldException;
import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationException;
import com.google.ar.core.exceptions.UnsupportedConfigurationException;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Main activity for the Geospatial API example.
*
* <p>This example shows how to use the Geospatial APIs. Once the device is localized, anchors can
* be created at the device's geospatial location. Anchor locations are persisted across sessions
* and will be recreated once localized.
*/
public class GeospatialActivity extends AppCompatActivity
implements SampleRender.Renderer,
VpsAvailabilityNoticeDialogFragment.NoticeDialogListener,
PrivacyNoticeDialogFragment.NoticeDialogListener, LocationListener {
private static final String TAG = GeospatialActivity.class.getSimpleName();
private static final String SHARED_PREFERENCES_SAVED_ANCHORS = "SHARED_PREFERENCES_SAVED_ANCHORS";
private static final String ALLOW_GEOSPATIAL_ACCESS_KEY = "ALLOW_GEOSPATIAL_ACCESS";
private static final String ANCHOR_MODE = "ANCHOR_MODE";
private static final float Z_NEAR = 0.1f;
private static final float Z_FAR = 1000f;
// The thresholds that are required for horizontal and orientation accuracies before entering into
// the LOCALIZED state. Once the accuracies are equal or less than these values, the app will
// allow the user to place anchors.
private static final double LOCALIZING_HORIZONTAL_ACCURACY_THRESHOLD_METERS = 10;
private static final double LOCALIZING_ORIENTATION_YAW_ACCURACY_THRESHOLD_DEGREES = 15;
// Once in the LOCALIZED state, if either accuracies degrade beyond these amounts, the app will
// revert back to the LOCALIZING state.
private static final double LOCALIZED_HORIZONTAL_ACCURACY_HYSTERESIS_METERS = 10;
private static final double LOCALIZED_ORIENTATION_YAW_ACCURACY_HYSTERESIS_DEGREES = 10;
private static final int LOCALIZING_TIMEOUT_SECONDS = 180;
private static final int MAXIMUM_ANCHORS = 20;
private static final long DURATION_FOR_NO_TERRAIN_ANCHOR_RESULT_MS = 10000;
// Rendering. The Renderers are created here, and initialized when the GL surface is created.
private GLSurfaceView surfaceView;
private boolean installRequested;
private Integer clearedAnchorsAmount = null;
/** Timer to keep track of how much time has passed since localizing has started. */
private long localizingStartTimestamp;
/** Deadline for showing resolving terrain anchors no result yet message. */
private long deadlineForMessageMillis;
double latitude = 7.924029;
double longitude = 81.017482;
double altitude = -30.62;
private Anchorr anchorr = new Anchorr(latitude,longitude,altitude);
private Anchorr anchorr_two = new Anchorr(latitude+0.000005,longitude,altitude);
private ArrayList<Anchorr> anchorss = new ArrayList<>();
private LocationDAO locationDAO;
private Button getRewardsButton;
List<com.example.thetrek.screens.locationsHandler.Location> locationList;
LocationManager locationManager;
enum State {
/** The Geospatial API has not yet been initialized. */
UNINITIALIZED,
/** The Geospatial API is not supported. */
UNSUPPORTED,
/** The Geospatial API has encountered an unrecoverable error. */
EARTH_STATE_ERROR,
/** The Session has started, but {@link Earth} isn't {@link TrackingState.TRACKING} yet. */
PRETRACKING,
/**
* {@link Earth} is {@link TrackingState.TRACKING}, but the desired positioning confidence
* hasn't been reached yet.
*/
LOCALIZING,
/** The desired positioning confidence wasn't reached in time. */
LOCALIZING_FAILED,
/**
* {@link Earth} is {@link TrackingState.TRACKING} and the desired positioning confidence has
* been reached.
*/
LOCALIZED
}
private State state = State.UNINITIALIZED;
enum AnchorType {
// Set WGS84 anchor.
GEOSPATIAL,
// Set Terrain anchor.
TERRAIN,
// Set Rooftop anchor.
ROOFTOP
}
private AnchorType anchorType = AnchorType.GEOSPATIAL;
private Session session;
private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper();
private DisplayRotationHelper displayRotationHelper;
private final TrackingStateHelper trackingStateHelper = new TrackingStateHelper(this);
private SampleRender render;
private SharedPreferences sharedPreferences;
private String lastStatusText;
private TextView geospatialPoseTextView;
private TextView statusTextView;
private TextView tapScreenTextView;
private Button setAnchorButton;
private Button clearAnchorsButton;
private Switch streetscapeGeometrySwitch;
private PlaneRenderer planeRenderer;
private BackgroundRenderer backgroundRenderer;
private Framebuffer virtualSceneFramebuffer;
private boolean hasSetTextureNames = false;
// Set rendering Streetscape Geometry.
private boolean isRenderStreetscapeGeometry = false;
// Virtual object (ARCore geospatial)
private Mesh virtualObjectMesh;
private Shader geospatialAnchorVirtualObjectShader;
// Virtual object (ARCore geospatial terrain)
private Shader terrainAnchorVirtualObjectShader;
private final Object anchorsLock = new Object();
@GuardedBy("anchorsLock")
private final List<Anchor> anchors = new ArrayList<>();
private final Set<Anchor> terrainAnchors = new HashSet<>();
private final Set<Anchor> rooftopAnchors = new HashSet<>();
// Temporary matrix allocated here to reduce number of allocations for each frame.
private final float[] modelMatrix = new float[16];
private final float[] viewMatrix = new float[16];
private final float[] projectionMatrix = new float[16];
private final float[] modelViewMatrix = new float[16]; // view x model
private final float[] modelViewProjectionMatrix = new float[16]; // projection x view x model
private final float[] identityQuaternion = {0, 0, 0, 1};
// Locks needed for synchronization
private final Object singleTapLock = new Object();
@GuardedBy("singleTapLock")
private MotionEvent queuedSingleTap;
// Tap handling and UI.
private GestureDetector gestureDetector;
// Point Cloud
private VertexBuffer pointCloudVertexBuffer;
private Mesh pointCloudMesh;
private Shader pointCloudShader;
// Keep track of the last point cloud rendered to avoid updating the VBO if point cloud
// was not changed. Do this using the timestamp since we can't compare PointCloud objects.
private long lastPointCloudTimestamp = 0;
// Provides device location.
private FusedLocationProviderClient fusedLocationClient;
// Streetscape geometry.
private final ArrayList<float[]> wallsColor = new ArrayList<float[]>();
private Shader streetscapeGeometryTerrainShader;
private Shader streetscapeGeometryBuildingShader;
// A set of planes representing building outlines and floors.
private final Map<StreetscapeGeometry, Mesh> streetscapeGeometryToMeshes = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedPreferences = getPreferences(Context.MODE_PRIVATE);
anchorss.add(anchorr);
anchorss.add(anchorr_two);
locationDAO = new LocationDAO(this);
locationDAO.open();
locationList = getLocationDataFromDatabase();
setContentView(R.layout.activity_main);
surfaceView = findViewById(R.id.surfaceview);
geospatialPoseTextView = findViewById(R.id.geospatial_pose_view);
statusTextView = findViewById(R.id.status_text_view);
tapScreenTextView = findViewById(R.id.tap_screen_text_view);
setAnchorButton = findViewById(R.id.set_anchor_button);
clearAnchorsButton = findViewById(R.id.clear_anchors_button);
getRewardsButton = findViewById(R.id.geo_get_reward_btn);
//Runtime permissions
if (ContextCompat.checkSelfPermission(GeospatialActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(GeospatialActivity.this,new String[]{
Manifest.permission.ACCESS_FINE_LOCATION
},100);
}
getRewardsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//create method
getLocation();
}
});
// Earth earth = session.getEarth();
// if (earth != null) {
// updateGeospatialState(earth);
// }
// double latitude = 7.924117;
// double longitude = 81.017504;
// double altitude = -27.62;
// float qx,qy,qz,qw;
// qx=0f;
// qy=0f;
// qz=0f;
// qw=0f;
//
//
//
// if (earth != null && earth.getTrackingState() == TrackingState.TRACKING) {
// Anchor anchor =
// earth.createAnchor(
// /* Location values */
// latitude,
// longitude,
// altitude,
// /* Rotational pose values */
// qx,
// qy,
// qz,
// qw);
//
// // Attach content to the anchor specified by geodetic location and pose.
// }
setAnchorButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
PopupMenu popup = new PopupMenu(GeospatialActivity.this, v);
popup.setOnMenuItemClickListener(GeospatialActivity.this::settingsMenuClick);
popup.inflate(R.menu.setting_menu);
popup.show();
popup
.getMenu()
.findItem(sharedPreferences.getInt(ANCHOR_MODE, R.id.geospatial))
.setChecked(true);
}
});
clearAnchorsButton.setOnClickListener(view -> handleClearAnchorsButton());
streetscapeGeometrySwitch = findViewById(R.id.streetscape_geometry_switch);
// Initial terrain anchor mode is DISABLED.
streetscapeGeometrySwitch.setChecked(false);
streetscapeGeometrySwitch.setOnCheckedChangeListener(this::onRenderStreetscapeGeometryChanged);
displayRotationHelper = new DisplayRotationHelper(/* activity= */ this);
// Set up renderer.
render = new SampleRender(surfaceView, this, getAssets());
installRequested = false;
clearedAnchorsAmount = null;
// Set up touch listener.
gestureDetector =
new GestureDetector(
this,
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
synchronized (singleTapLock) {
queuedSingleTap = e;
}
return true;
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
});
surfaceView.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
fusedLocationClient = LocationServices.getFusedLocationProviderClient(/* context= */ this);
}
@Override
public void onLocationChanged(@NonNull Location location) {
Toast.makeText(this, ""+location.getLatitude()+","+location.getLongitude() + location.getAltitude(), Toast.LENGTH_SHORT).show();
for(com.example.thetrek.screens.locationsHandler.Location existingLocation:locationList){
Location targetLocation = new Location("");
targetLocation.setLatitude(existingLocation.getLatitude());
targetLocation.setLongitude(existingLocation.getLongitude());
// targetLocation.setAltitude(existingLocation.getElevation());
float distance = location.distanceTo(targetLocation);
Log.d("Distance","Distance "+ distance);
Toast.makeText(this, "Distance :" +distance, Toast.LENGTH_SHORT).show();
if(distance<=10){
locationDAO.updateLocation(existingLocation.getLocationName(),existingLocation.getId(),existingLocation.getLatitude(),existingLocation.getLongitude(),existingLocation.getElevation(),true);
finish();
Intent intent = new Intent(this.getApplicationContext(), RewardingScreenActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
this.getApplicationContext().startActivity(intent);
}
}
// Check whether the current location is in threshold distance to any of the anchor locations
}
@Override
public void onLocationChanged(@NonNull List<Location> locations) {
LocationListener.super.onLocationChanged(locations);
}
@Override
public void onFlushComplete(int requestCode) {
LocationListener.super.onFlushComplete(requestCode);
}
@Override
public void onProviderEnabled(@NonNull String provider) {
LocationListener.super.onProviderEnabled(provider);
}
@Override
public void onProviderDisabled(@NonNull String provider) {
LocationListener.super.onProviderDisabled(provider);
}
@SuppressLint("MissingPermission")
private void getLocation() {
try {
locationManager = (LocationManager) getApplicationContext().getSystemService(LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000,5,GeospatialActivity.this);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
if (session != null) {
// Explicitly close ARCore Session to release native resources.
// Review the API reference for important considerations before calling close() in apps with
// more complicated lifecycle requirements:
// https://developers.google.com/ar/reference/java/arcore/reference/com/google/ar/core/Session#close()
session.close();
session = null;
}
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
if (sharedPreferences.getBoolean(ALLOW_GEOSPATIAL_ACCESS_KEY, /* defValue= */ false)) {
createSession();
} else {
showPrivacyNoticeDialog();
}
surfaceView.onResume();
displayRotationHelper.onResume();
}
private void showPrivacyNoticeDialog() {
DialogFragment dialog = PrivacyNoticeDialogFragment.createDialog();
dialog.show(getSupportFragmentManager(), PrivacyNoticeDialogFragment.class.getName());
}
private void createSession() {
Exception exception = null;
String message = null;
if (session == null) {
try {
switch (ArCoreApk.getInstance().requestInstall(this, !installRequested)) {
case INSTALL_REQUESTED:
installRequested = true;
return;
case INSTALLED:
break;
}
// ARCore requires camera permissions to operate. If we did not yet obtain runtime
// permission on Android M and above, now is a good time to ask the user for it.
if (!CameraPermissionHelper.hasCameraPermission(this)) {
CameraPermissionHelper.requestCameraPermission(this);
return;
}
if (!LocationPermissionHelper.hasFineLocationPermission(this)) {
LocationPermissionHelper.requestFineLocationPermission(this);
return;
}
// Create the session.
// Plane finding mode is default on, which will help the dynamic alignment of terrain
// anchors on ground.
session = new Session(/* context= */ this);
} catch (UnavailableArcoreNotInstalledException
| UnavailableUserDeclinedInstallationException e) {
message = "Please install ARCore";
exception = e;
} catch (UnavailableApkTooOldException e) {
message = "Please update ARCore";
exception = e;
} catch (UnavailableSdkTooOldException e) {
message = "Please update this app";
exception = e;
} catch (UnavailableDeviceNotCompatibleException e) {
message = "This device does not support AR";
exception = e;
} catch (Exception e) {
message = "Failed to create AR session";
exception = e;
}
if (message != null) {
messageSnackbarHelper.showError(this, message);
Log.e(TAG, "Exception creating session", exception);
return;
}
}
// Check VPS availability before configure and resume session.
if (session != null) {
getLastLocation();
}
// Note that order matters - see the note in onPause(), the reverse applies here.
try {
configureSession();
// To record a live camera session for later playback, call
// `session.startRecording(recordingConfig)` at anytime. To playback a previously recorded AR
// session instead of using the live camera feed, call
// `session.setPlaybackDatasetUri(Uri)` before calling `session.resume()`. To
// learn more about recording and playback, see:
// https://developers.google.com/ar/develop/java/recording-and-playback
session.resume();
} catch (CameraNotAvailableException e) {
message = "Camera not available. Try restarting the app.";
exception = e;
} catch (GooglePlayServicesLocationLibraryNotLinkedException e) {
message = "Google Play Services location library not linked or obfuscated with Proguard.";
exception = e;
} catch (FineLocationPermissionNotGrantedException e) {
message = "The Android permission ACCESS_FINE_LOCATION was not granted.";
exception = e;
} catch (UnsupportedConfigurationException e) {
message = "This device does not support GeospatialMode.ENABLED.";
exception = e;
} catch (SecurityException e) {
message = "Camera failure or the internet permission has not been granted.";
exception = e;
}
if (message != null) {
session = null;
messageSnackbarHelper.showError(this, message);
Log.e(TAG, "Exception configuring and resuming the session", exception);
return;
}
}
private void getLastLocation() {
try {
fusedLocationClient
.getLastLocation()
.addOnSuccessListener(
new OnSuccessListener<Location>() {
@Override
public void onSuccess(Location location) {
double latitude = 0;
double longitude = 0;
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
} else {
Log.e(TAG, "Error location is null");
}
checkVpsAvailability(latitude, longitude);
}
});
} catch (SecurityException e) {
Log.e(TAG, "No location permissions granted by User!");
}
}
private void checkVpsAvailability(double latitude, double longitude) {
final VpsAvailabilityFuture future =
session.checkVpsAvailabilityAsync(
latitude,
longitude,
availability -> {
if (availability != VpsAvailability.AVAILABLE) {
showVpsNotAvailabilityNoticeDialog();
}
});
}
private void showVpsNotAvailabilityNoticeDialog() {
DialogFragment dialog = VpsAvailabilityNoticeDialogFragment.createDialog();
dialog.show(getSupportFragmentManager(), VpsAvailabilityNoticeDialogFragment.class.getName());
}
@Override
public void onPause() {
super.onPause();
if (session != null) {
// Note that the order matters - GLSurfaceView is paused first so that it does not try
// to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may
// still call session.update() and get a SessionPausedException.
displayRotationHelper.onPause();
surfaceView.onPause();
session.pause();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
super.onRequestPermissionsResult(requestCode, permissions, results);
if (!CameraPermissionHelper.hasCameraPermission(this)) {
// Use toast instead of snackbar here since the activity will exit.
Toast.makeText(this, "Camera permission is needed to run this application", Toast.LENGTH_LONG)
.show();
if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) {
// Permission denied with checking "Do not ask again".
CameraPermissionHelper.launchPermissionSettings(this);
}
finish();
}
// Check if this result pertains to the location permission.
if (LocationPermissionHelper.hasFineLocationPermissionsResponseInResult(permissions)
&& !LocationPermissionHelper.hasFineLocationPermission(this)) {
// Use toast instead of snackbar here since the activity will exit.
Toast.makeText(
this,
"Precise location permission is needed to run this application",
Toast.LENGTH_LONG)
.show();
if (!LocationPermissionHelper.shouldShowRequestPermissionRationale(this)) {
// Permission denied with checking "Do not ask again".
LocationPermissionHelper.launchPermissionSettings(this);
}
finish();
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
FullScreenHelper.setFullScreenOnWindowFocusChanged(this, hasFocus);
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
super.onPointerCaptureChanged(hasCapture);
}
@Override
public void onSurfaceCreated(SampleRender render) {
// Prepare the rendering objects. This involves reading shaders and 3D model files, so may throw
// an IOException.
try {
planeRenderer = new PlaneRenderer(render);
backgroundRenderer = new BackgroundRenderer(render);
virtualSceneFramebuffer = new Framebuffer(render, /* width= */ 1, /* height= */ 1);
// Virtual object to render (ARCore geospatial)
Texture virtualObjectTexture =
Texture.createFromAsset(
render,
"models/spatial_marker_baked.png",
Texture.WrapMode.CLAMP_TO_EDGE,
Texture.ColorFormat.SRGB);
virtualObjectMesh = Mesh.createFromAsset(render, "models/geospatial_marker.obj");
geospatialAnchorVirtualObjectShader =
Shader.createFromAssets(
render,
"shaders/ar_unlit_object.vert",
"shaders/ar_unlit_object.frag",
/* defines= */ null)
.setTexture("u_Texture", virtualObjectTexture);
// Virtual object to render (Terrain anchor marker)
Texture terrainAnchorVirtualObjectTexture =
Texture.createFromAsset(
render,
"models/spatial_marker_yellow.png",
Texture.WrapMode.CLAMP_TO_EDGE,
Texture.ColorFormat.SRGB);
terrainAnchorVirtualObjectShader =
Shader.createFromAssets(
render,
"shaders/ar_unlit_object.vert",
"shaders/ar_unlit_object.frag",
/* defines= */ null)
.setTexture("u_Texture", terrainAnchorVirtualObjectTexture);
backgroundRenderer.setUseDepthVisualization(render, false);
backgroundRenderer.setUseOcclusion(render, false);
// Point cloud
pointCloudShader =
Shader.createFromAssets(
render,
"shaders/point_cloud.vert",
"shaders/point_cloud.frag",
/* defines= */ null)
.setVec4(
"u_Color", new float[] {31.0f / 255.0f, 188.0f / 255.0f, 210.0f / 255.0f, 1.0f})
.setFloat("u_PointSize", 5.0f);
// four entries per vertex: X, Y, Z, confidence
pointCloudVertexBuffer =
new VertexBuffer(render, /* numberOfEntriesPerVertex= */ 4, /* entries= */ null);
final VertexBuffer[] pointCloudVertexBuffers = {pointCloudVertexBuffer};
pointCloudMesh =
new Mesh(
render, Mesh.PrimitiveMode.POINTS, /* indexBuffer= */ null, pointCloudVertexBuffers);
streetscapeGeometryBuildingShader =
Shader.createFromAssets(
render,
"shaders/streetscape_geometry.vert",
"shaders/streetscape_geometry.frag",
/* defines= */ null)
.setBlend(
BlendFactor.DST_ALPHA, // RGB (src)
BlendFactor.ONE); // ALPHA (dest)
streetscapeGeometryTerrainShader =
Shader.createFromAssets(
render,
"shaders/streetscape_geometry.vert",
"shaders/streetscape_geometry.frag",
/* defines= */ null)
.setBlend(
BlendFactor.DST_ALPHA, // RGB (src)
BlendFactor.ONE); // ALPHA (dest)
wallsColor.add(new float[] {0.5f, 0.0f, 0.5f, 0.3f});
wallsColor.add(new float[] {0.5f, 0.5f, 0.0f, 0.3f});
wallsColor.add(new float[] {0.0f, 0.5f, 0.5f, 0.3f});
} catch (IOException e) {
Log.e(TAG, "Failed to read a required asset file", e);
messageSnackbarHelper.showError(this, "Failed to read a required asset file: " + e);
}
}
@Override
public void onSurfaceChanged(SampleRender render, int width, int height) {
displayRotationHelper.onSurfaceChanged(width, height);
virtualSceneFramebuffer.resize(width, height);
}
@Override
public void onDrawFrame(SampleRender render) {
if (session == null) {
return;
}
// Texture names should only be set once on a GL thread unless they change. This is done during
// onDrawFrame rather than onSurfaceCreated since the session is not guaranteed to have been
// initialized during the execution of onSurfaceCreated.
if (!hasSetTextureNames) {
session.setCameraTextureNames(
new int[] {backgroundRenderer.getCameraColorTexture().getTextureId()});
hasSetTextureNames = true;
}
// -- Update per-frame state
// Notify ARCore session that the view size changed so that the perspective matrix and
// the video background can be properly adjusted.
displayRotationHelper.updateSessionIfNeeded(session);
updateStreetscapeGeometries(session.getAllTrackables(StreetscapeGeometry.class));
// Obtain the current frame from ARSession. When the configuration is set to
// UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the
// camera framerate.
Frame frame;
try {
frame = session.update();
} catch (CameraNotAvailableException e) {
Log.e(TAG, "Camera not available during onDrawFrame", e);
messageSnackbarHelper.showError(this, "Camera not available. Try restarting the app.");
return;
}
Camera camera = frame.getCamera();
// BackgroundRenderer.updateDisplayGeometry must be called every frame to update the coordinates
// used to draw the background camera image.
backgroundRenderer.updateDisplayGeometry(frame);
// Keep the screen unlocked while tracking, but allow it to lock when tracking stops.
trackingStateHelper.updateKeepScreenOnFlag(camera.getTrackingState());
Earth earth = session.getEarth();
if (earth != null) {
updateGeospatialState(earth);
}
// Show a message based on whether tracking has failed, if planes are detected, and if the user
// has placed any objects.
String message = null;
switch (state) {
case UNINITIALIZED:
break;
case UNSUPPORTED:
message = getResources().getString(R.string.status_unsupported);
break;
case PRETRACKING:
message = getResources().getString(R.string.status_pretracking);
break;
case EARTH_STATE_ERROR:
message = getResources().getString(R.string.status_earth_state_error);
break;
case LOCALIZING:
message = getResources().getString(R.string.status_localize_hint);
break;
case LOCALIZING_FAILED:
message = getResources().getString(R.string.status_localize_timeout);
break;
case LOCALIZED:
if (lastStatusText.equals(getResources().getString(R.string.status_localize_hint))) {
message = getResources().getString(R.string.status_localize_complete);
}
break;
}
if (message != null && lastStatusText != message) {
lastStatusText = message;
runOnUiThread(
() -> {
statusTextView.setVisibility(View.VISIBLE);
statusTextView.setText(lastStatusText);
});
}
synchronized (anchorsLock) {
if (anchors.size() >= MAXIMUM_ANCHORS) {
runOnUiThread(
() -> {
setAnchorButton.setVisibility(View.INVISIBLE);
tapScreenTextView.setVisibility(View.INVISIBLE);
});
}
}
// Handle user input.
handleTap(frame, camera.getTrackingState());
// -- Draw background
if (frame.getTimestamp() != 0) {
// Suppress rendering if the camera did not produce the first frame yet. This is to avoid
// drawing possible leftover data from previous sessions if the texture is reused.
backgroundRenderer.drawBackground(render);
}
// If not tracking, don't draw 3D objects.
if (camera.getTrackingState() != TrackingState.TRACKING || state != State.LOCALIZED) {
return;
}
// -- Draw virtual objects
// Get projection matrix.
camera.getProjectionMatrix(projectionMatrix, 0, Z_NEAR, Z_FAR);
// Get camera matrix and draw.
camera.getViewMatrix(viewMatrix, 0);
// Visualize tracked points.
// Use try-with-resources to automatically release the point cloud.
try (PointCloud pointCloud = frame.acquirePointCloud()) {
if (pointCloud.getTimestamp() > lastPointCloudTimestamp) {
pointCloudVertexBuffer.set(pointCloud.getPoints());
lastPointCloudTimestamp = pointCloud.getTimestamp();
}
Matrix.multiplyMM(modelViewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
pointCloudShader.setMat4("u_ModelViewProjection", modelViewProjectionMatrix);
render.draw(pointCloudMesh, pointCloudShader);
}
// Visualize planes.
planeRenderer.drawPlanes(
render,
session.getAllTrackables(Plane.class),
camera.getDisplayOrientedPose(),
projectionMatrix);
// Visualize anchors created by touch.
render.clear(virtualSceneFramebuffer, 0f, 0f, 0f, 0f);
// -- Draw Streetscape Geometries.
if (isRenderStreetscapeGeometry) {
int index = 0;
for (Map.Entry<StreetscapeGeometry, Mesh> set : streetscapeGeometryToMeshes.entrySet()) {
StreetscapeGeometry streetscapeGeometry = set.getKey();
if (streetscapeGeometry.getTrackingState() != TrackingState.TRACKING) {
continue;
}
Mesh mesh = set.getValue();
Pose pose = streetscapeGeometry.getMeshPose();
pose.toMatrix(modelMatrix, 0);
// Calculate model/view/projection matrices
Matrix.multiplyMM(modelViewMatrix, 0, viewMatrix, 0, modelMatrix, 0);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, projectionMatrix, 0, modelViewMatrix, 0);
if (streetscapeGeometry.getType() == StreetscapeGeometry.Type.BUILDING) {
float[] color = wallsColor.get(index % wallsColor.size());
index += 1;
streetscapeGeometryBuildingShader
.setVec4(
"u_Color",
new float[] {/* r= */ color[0], /* g= */ color[1], /* b= */ color[2], color[3]})
.setMat4("u_ModelViewProjection", modelViewProjectionMatrix);
render.draw(mesh, streetscapeGeometryBuildingShader);
} else if (streetscapeGeometry.getType() == StreetscapeGeometry.Type.TERRAIN) {
streetscapeGeometryTerrainShader
.setVec4("u_Color", new float[] {/* r= */ 0f, /* g= */ .5f, /* b= */ 0f, 0.3f})
.setMat4("u_ModelViewProjection", modelViewProjectionMatrix);
render.draw(mesh, streetscapeGeometryTerrainShader);
}
}
}
render.clear(virtualSceneFramebuffer, 0f, 0f, 0f, 0f);
synchronized (anchorsLock) {
for (Anchor anchor : anchors) {
// Get the current pose of an Anchor in world space. The Anchor pose is updated
// during calls to session.update() as ARCore refines its estimate of the world.
// Only render resolved Terrain & Rooftop anchors and Geospatial anchors.
if (anchor.getTrackingState() != TrackingState.TRACKING) {
continue;
}
anchor.getPose().toMatrix(modelMatrix, 0);
float[] scaleMatrix = new float[16];
Matrix.setIdentityM(scaleMatrix, 0);
float scale = getScale(anchor.getPose(), camera.getDisplayOrientedPose());
scaleMatrix[0] = scale;
scaleMatrix[5] = scale;
scaleMatrix[10] = scale;
Matrix.multiplyMM(modelMatrix, 0, modelMatrix, 0, scaleMatrix, 0);
// Rotate the virtual object 180 degrees around the Y axis to make the object face the GL
// camera -Z axis, since camera Z axis faces toward users.
float[] rotationMatrix = new float[16];
Matrix.setRotateM(rotationMatrix, 0, 180, 0.0f, 1.0f, 0.0f);
float[] rotationModelMatrix = new float[16];
Matrix.multiplyMM(rotationModelMatrix, 0, modelMatrix, 0, rotationMatrix, 0);
// Calculate model/view/projection matrices
Matrix.multiplyMM(modelViewMatrix, 0, viewMatrix, 0, rotationModelMatrix, 0);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, projectionMatrix, 0, modelViewMatrix, 0);
// Update shader properties and draw
if (terrainAnchors.contains(anchor) || rooftopAnchors.contains(anchor)) {
terrainAnchorVirtualObjectShader.setMat4(
"u_ModelViewProjection", modelViewProjectionMatrix);
render.draw(virtualObjectMesh, terrainAnchorVirtualObjectShader, virtualSceneFramebuffer);
} else {
geospatialAnchorVirtualObjectShader.setMat4(
"u_ModelViewProjection", modelViewProjectionMatrix);
render.draw(
virtualObjectMesh, geospatialAnchorVirtualObjectShader, virtualSceneFramebuffer);
}
}
if (anchors.size() > 0) {
String anchorMessage =
getResources()
.getQuantityString(
R.plurals.status_anchors_set, anchors.size(), anchors.size(), MAXIMUM_ANCHORS);
runOnUiThread(
() -> {
statusTextView.setVisibility(View.VISIBLE);
statusTextView.setText(anchorMessage);
});
}
}
// Compose the virtual scene with the background.
backgroundRenderer.drawVirtualScene(render, virtualSceneFramebuffer, Z_NEAR, Z_FAR);
}
/**
* Updates all the StreetscapeGeometries. Existing StreetscapeGeometries will have pose updated,
* and non-existing StreetscapeGeometries will be removed from the scene.
*/
private void updateStreetscapeGeometries(Collection<StreetscapeGeometry> streetscapeGeometries) {
for (StreetscapeGeometry streetscapeGeometry : streetscapeGeometries) {
// If the Streetscape Geometry node is already added to the scene, then we'll simply update
// the pose.
if (streetscapeGeometryToMeshes.containsKey(streetscapeGeometry)) {
} else {
// Otherwise, we create a StreetscapeGeometry mesh and add it to the scene.
Mesh mesh = getSampleRenderMesh(streetscapeGeometry);
streetscapeGeometryToMeshes.put(streetscapeGeometry, mesh);
}
}
}
private Mesh getSampleRenderMesh(StreetscapeGeometry streetscapeGeometry) {
FloatBuffer streetscapeGeometryBuffer = streetscapeGeometry.getMesh().getVertexList();
streetscapeGeometryBuffer.rewind();
VertexBuffer meshVertexBuffer =
new VertexBuffer(
render, /* numberOfEntriesPerVertex= */ 3, /* entries= */ streetscapeGeometryBuffer);
IndexBuffer meshIndexBuffer =
new IndexBuffer(render, streetscapeGeometry.getMesh().getIndexList());
final VertexBuffer[] meshVertexBuffers = {meshVertexBuffer};
return new Mesh(
render,
Mesh.PrimitiveMode.TRIANGLES,
/* indexBuffer= */ meshIndexBuffer,
meshVertexBuffers);
}
/** Configures the session with feature settings. */
private void configureSession() {
// Earth mode may not be supported on this device due to insufficient sensor quality.
if (!session.isGeospatialModeSupported(Config.GeospatialMode.ENABLED)) {
state = State.UNSUPPORTED;
return;
}
Config config = session.getConfig();
config =
config
.setGeospatialMode(Config.GeospatialMode.ENABLED)
.setStreetscapeGeometryMode(Config.StreetscapeGeometryMode.ENABLED);
session.configure(config);
state = State.PRETRACKING;
localizingStartTimestamp = System.currentTimeMillis();
}
/** Change behavior depending on the current {@link State} of the application. */
private void updateGeospatialState(Earth earth) {
if (earth.getEarthState() != Earth.EarthState.ENABLED) {
state = State.EARTH_STATE_ERROR;
return;
}
if (earth.getTrackingState() != TrackingState.TRACKING) {
state = State.PRETRACKING;
return;
}
if (state == State.PRETRACKING) {
updatePretrackingState(earth);
} else if (state == State.LOCALIZING) {
updateLocalizingState(earth);
} else if (state == State.LOCALIZED) {
updateLocalizedState(earth);
}
}
/**
* Handles the updating for {@link State.PRETRACKING}. In this state, wait for {@link Earth} to
* have {@link TrackingState.TRACKING}. If it hasn't been enabled by now, then we've encountered
* an unrecoverable {@link State.EARTH_STATE_ERROR}.
*/
private void updatePretrackingState(Earth earth) {
if (earth.getTrackingState() == TrackingState.TRACKING) {
state = State.LOCALIZING;
return;
}
runOnUiThread(() -> geospatialPoseTextView.setText(R.string.geospatial_pose_not_tracking));
}
/**
* Handles the updating for {@link State.LOCALIZING}. In this state, wait for the horizontal and
* orientation threshold to improve until it reaches your threshold.
*
* <p>If it takes too long for the threshold to be reached, this could mean that GPS data isn't
* accurate enough, or that the user is in an area that can't be localized with StreetView.
*/
private void updateLocalizingState(Earth earth) {
GeospatialPose geospatialPose = earth.getCameraGeospatialPose();
if (geospatialPose.getHorizontalAccuracy() <= LOCALIZING_HORIZONTAL_ACCURACY_THRESHOLD_METERS
&& geospatialPose.getOrientationYawAccuracy()
<= LOCALIZING_ORIENTATION_YAW_ACCURACY_THRESHOLD_DEGREES) {
state = State.LOCALIZED;
synchronized (anchorsLock) {
final int anchorNum = anchors.size();
if (anchorNum == 0) {
createAnchorFromSharedPreferences(earth);
}
if (anchorNum < MAXIMUM_ANCHORS) {
runOnUiThread(
() -> {
setAnchorButton.setVisibility(View.VISIBLE);
tapScreenTextView.setVisibility(View.VISIBLE);
if (anchorNum > 0) {
clearAnchorsButton.setVisibility(View.VISIBLE);
}
});
}
}
return;
}
if (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - localizingStartTimestamp)
> LOCALIZING_TIMEOUT_SECONDS) {
state = State.LOCALIZING_FAILED;
return;
}
updateGeospatialPoseText(geospatialPose);
}
/**
* Handles the updating for {@link State.LOCALIZED}. In this state, check the accuracy for
* degradation and return to {@link State.LOCALIZING} if the position accuracies have dropped too
* low.
*/
private void updateLocalizedState(Earth earth) {
GeospatialPose geospatialPose = earth.getCameraGeospatialPose();
// Check if either accuracy has degraded to the point we should enter back into the LOCALIZING
// state.
if (geospatialPose.getHorizontalAccuracy()
> LOCALIZING_HORIZONTAL_ACCURACY_THRESHOLD_METERS
+ LOCALIZED_HORIZONTAL_ACCURACY_HYSTERESIS_METERS
|| geospatialPose.getOrientationYawAccuracy()
> LOCALIZING_ORIENTATION_YAW_ACCURACY_THRESHOLD_DEGREES
+ LOCALIZED_ORIENTATION_YAW_ACCURACY_HYSTERESIS_DEGREES) {
// Accuracies have degenerated, return to the localizing state.
state = State.LOCALIZING;
localizingStartTimestamp = System.currentTimeMillis();
runOnUiThread(
() -> {
setAnchorButton.setVisibility(View.INVISIBLE);
tapScreenTextView.setVisibility(View.INVISIBLE);
clearAnchorsButton.setVisibility(View.INVISIBLE);
});
return;
}
updateGeospatialPoseText(geospatialPose);
}
private void updateGeospatialPoseText(GeospatialPose geospatialPose) {
float[] quaternion = geospatialPose.getEastUpSouthQuaternion();
String poseText =
getResources()
.getString(
R.string.geospatial_pose,
geospatialPose.getLatitude(),
geospatialPose.getLongitude(),
geospatialPose.getHorizontalAccuracy(),
geospatialPose.getAltitude(),
geospatialPose.getVerticalAccuracy(),
quaternion[0],
quaternion[1],
quaternion[2],
quaternion[3],
geospatialPose.getOrientationYawAccuracy());
runOnUiThread(
() -> {
geospatialPoseTextView.setText(poseText);
});
}
// Return the scale in range [1, 2] after mapping a distance between camera and anchor to [2, 20].
private float getScale(Pose anchorPose, Pose cameraPose) {
double distance =
Math.sqrt(
Math.pow(anchorPose.tx() - cameraPose.tx(), 2.0)
+ Math.pow(anchorPose.ty() - cameraPose.ty(), 2.0)
+ Math.pow(anchorPose.tz() - cameraPose.tz(), 2.0));
double mapDistance = Math.min(Math.max(2, distance), 20);
return (float) (mapDistance - 2) / (20 - 2) + 1;
}
/**
* Handles the button that creates an anchor.
*
* <p>Ensure Earth is in the proper state, then create the anchor. Persist the parameters used to
* create the anchors so that the anchors will be loaded next time the app is launched.
*/
private void handleSetAnchorButton() {}
/** Menu button to choose anchor type. */
protected boolean settingsMenuClick(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.anchor_reset) {
return true;
}
item.setChecked(!item.isChecked());
sharedPreferences.edit().putInt(ANCHOR_MODE, itemId).commit();
if (itemId == R.id.geospatial) {
anchorType = AnchorType.GEOSPATIAL;
return true;
} else if (itemId == R.id.terrain) {
anchorType = AnchorType.TERRAIN;
return true;
} else if (itemId == R.id.rooftop) {
anchorType = AnchorType.ROOFTOP;
return true;
}
return false;
}
/** Creates anchor with the provided GeospatialPose, either from camera or HitResult. */
private void createAnchorWithGeospatialPose(Earth earth, GeospatialPose geospatialPose) {
double latitude = geospatialPose.getLatitude();
double longitude = geospatialPose.getLongitude();
double altitude = geospatialPose.getAltitude();
float[] quaternion = geospatialPose.getEastUpSouthQuaternion();
switch (anchorType) {
case TERRAIN:
createTerrainAnchor(earth, latitude, longitude, identityQuaternion);
storeAnchorParameters(latitude, longitude, 0, identityQuaternion);
break;
case GEOSPATIAL:
createAnchor(earth, latitude, longitude, altitude, quaternion);
storeAnchorParameters(latitude, longitude, altitude, quaternion);
break;
case ROOFTOP:
createRooftopAnchor(earth, latitude, longitude, identityQuaternion);
storeAnchorParameters(latitude, longitude, 0, identityQuaternion);
break;
}
runOnUiThread(
() -> {
clearAnchorsButton.setVisibility(View.VISIBLE);
});
if (clearedAnchorsAmount != null) {
clearedAnchorsAmount = null;
}
}
private void handleClearAnchorsButton() {
synchronized (anchorsLock) {
clearedAnchorsAmount = anchors.size();
String message =
getResources()
.getQuantityString(
R.plurals.status_anchors_cleared, clearedAnchorsAmount, clearedAnchorsAmount);
statusTextView.setVisibility(View.VISIBLE);
statusTextView.setText(message);
for (Anchor anchor : anchors) {
anchor.detach();
}
anchors.clear();
}
clearAnchorsFromSharedPreferences();
clearAnchorsButton.setVisibility(View.INVISIBLE);
setAnchorButton.setVisibility(View.VISIBLE);
tapScreenTextView.setVisibility(View.VISIBLE);
}
/** Create an anchor at a specific geodetic location using a EUS quaternion. */
private void createAnchor(
Earth earth, double latitude, double longitude, double altitude, float[] quaternion) {
// latitude = 7.924029;
// longitude = 81.017482;
// altitude = -30.62;
for (com.example.thetrek.screens.locationsHandler.Location an : locationList) {
latitude = an.getLatitude();
longitude = an.getLongitude();
altitude = an.getElevation();
Anchor anchor =
earth.createAnchor(
latitude,
longitude,
altitude,
quaternion[0],
quaternion[1],
quaternion[2],
quaternion[3]);
synchronized (anchorsLock) {
anchors.add(anchor);
}
}
// latitude = anchorr.getLatitude();
// longitude = anchorr.getLongitude();
// altitude = anchorr.getAltitude();
//
// Anchor anchor =
// earth.createAnchor(
// latitude,
// longitude,
// altitude,
// quaternion[0],
// quaternion[1],
// quaternion[2],
// quaternion[3]);
// synchronized (anchorsLock) {
// anchors.add(anchor);
// }
}
/** Create a terrain anchor at a specific geodetic location using a EUS quaternion. */
private void createTerrainAnchor(
Earth earth, double latitude, double longitude, float[] quaternion) {
final ResolveAnchorOnTerrainFuture future =
earth.resolveAnchorOnTerrainAsync(
latitude,
longitude,
/* altitudeAboveTerrain= */ 0.0f,
quaternion[0],
quaternion[1],
quaternion[2],
quaternion[3],
(anchor, state) -> {
if (state == TerrainAnchorState.SUCCESS) {
synchronized (anchorsLock) {
anchors.add(anchor);
terrainAnchors.add(anchor);
}
} else {
statusTextView.setVisibility(View.VISIBLE);
statusTextView.setText(getString(R.string.status_terrain_anchor, state));
}
});
}
/** Create a rooftop anchor at a specific geodetic location using a EUS quaternion. */
private void createRooftopAnchor(
Earth earth, double latitude, double longitude, float[] quaternion) {
final ResolveAnchorOnRooftopFuture future =
earth.resolveAnchorOnRooftopAsync(
latitude,
longitude,
/* altitudeAboveRooftop= */ 0.0f,
quaternion[0],
quaternion[1],
quaternion[2],
quaternion[3],
(anchor, state) -> {
if (state == RooftopAnchorState.SUCCESS) {
synchronized (anchorsLock) {
anchors.add(anchor);
rooftopAnchors.add(anchor);
}
} else {
statusTextView.setVisibility(View.VISIBLE);
statusTextView.setText(getString(R.string.status_rooftop_anchor, state));
}
});
}
/**
* Helper function to store the parameters used in anchor creation in {@link SharedPreferences}.
*/
private void storeAnchorParameters(
double latitude, double longitude, double altitude, float[] quaternion) {
Set<String> anchorParameterSet =
sharedPreferences.getStringSet(SHARED_PREFERENCES_SAVED_ANCHORS, new HashSet<>());
HashSet<String> newAnchorParameterSet = new HashSet<>(anchorParameterSet);
SharedPreferences.Editor editor = sharedPreferences.edit();
String type = "";
switch (anchorType) {
case TERRAIN:
type = "Terrain";
break;
case ROOFTOP:
type = "Rooftop";
break;
default:
type = "";
break;
}
newAnchorParameterSet.add(
String.format(
type + "%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f",
latitude,
longitude,
altitude,
quaternion[0],
quaternion[1],
quaternion[2],
quaternion[3]));
editor.putStringSet(SHARED_PREFERENCES_SAVED_ANCHORS, newAnchorParameterSet);
editor.commit();
}
private void clearAnchorsFromSharedPreferences() {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putStringSet(SHARED_PREFERENCES_SAVED_ANCHORS, null);
editor.commit();
}
/** Creates all anchors that were stored in the {@link SharedPreferences}. */
private void createAnchorFromSharedPreferences(Earth earth) {
Set<String> anchorParameterSet =
sharedPreferences.getStringSet(SHARED_PREFERENCES_SAVED_ANCHORS, null);
if (anchorParameterSet == null) {
return;
}
for (String anchorParameters : anchorParameterSet) {
AnchorType type = AnchorType.GEOSPATIAL;
if (anchorParameters.contains("Terrain")) {
type = AnchorType.TERRAIN;
anchorParameters = anchorParameters.replace("Terrain", "");
} else if (anchorParameters.contains("Rooftop")) {
type = AnchorType.ROOFTOP;
anchorParameters = anchorParameters.replace("Rooftop", "");
}
String[] parameters = anchorParameters.split(",");
if (parameters.length != 7) {
Log.d(
TAG, "Invalid number of anchor parameters. Expected four, found " + parameters.length);
continue;
}
double latitude = Double.parseDouble(parameters[0]);
double longitude = Double.parseDouble(parameters[1]);
double altitude = Double.parseDouble(parameters[2]);
float[] quaternion =
new float[] {
Float.parseFloat(parameters[3]),
Float.parseFloat(parameters[4]),
Float.parseFloat(parameters[5]),
Float.parseFloat(parameters[6])
};
switch (type) {
case TERRAIN:
createTerrainAnchor(earth, latitude, longitude, quaternion);
break;
case ROOFTOP:
createRooftopAnchor(earth, latitude, longitude, quaternion);
break;
default:
createAnchor(earth, latitude, longitude, altitude, quaternion);
break;
}
}
runOnUiThread(() -> clearAnchorsButton.setVisibility(View.VISIBLE));
}
@Override
public void onDialogPositiveClick(DialogFragment dialog) {
if (!sharedPreferences.edit().putBoolean(ALLOW_GEOSPATIAL_ACCESS_KEY, true).commit()) {
throw new AssertionError("Could not save the user preference to SharedPreferences!");
}
createSession();
}
@Override
public void onDialogContinueClick(DialogFragment dialog) {
dialog.dismiss();
}
private void onRenderStreetscapeGeometryChanged(CompoundButton button, boolean isChecked) {
if (session == null) {
return;
}
isRenderStreetscapeGeometry = isChecked;
}
/**
* Handles the most recent user tap.
*
* <p>We only ever handle one tap at a time, since this app only allows for a single anchor.
*
* @param frame the current AR frame
* @param cameraTrackingState the current camera tracking state
*/
private void handleTap(Frame frame, TrackingState cameraTrackingState) {
// Handle taps. Handling only one tap per frame, as taps are usually low frequency
// compared to frame rate.
synchronized (singleTapLock) {
synchronized (anchorsLock) {
if (queuedSingleTap == null
|| anchors.size() >= MAXIMUM_ANCHORS
|| cameraTrackingState != TrackingState.TRACKING) {
queuedSingleTap = null;
return;
}
}
Earth earth = session.getEarth();
if (earth == null || earth.getTrackingState() != TrackingState.TRACKING) {
queuedSingleTap = null;
return;
}
for (HitResult hit : frame.hitTest(queuedSingleTap)) {
if (shouldCreateAnchorWithHit(hit)) {
Pose hitPose = hit.getHitPose();
GeospatialPose geospatialPose = earth.getGeospatialPose(hitPose);
createAnchorWithGeospatialPose(earth, geospatialPose);
break; // Only handle the first valid hit.
}
}
queuedSingleTap = null;
}
}
/** Returns {@code true} if and only if the hit can be used to create an Anchor reliably. */
private boolean shouldCreateAnchorWithHit(HitResult hit) {
Trackable trackable = hit.getTrackable();
if (isRenderStreetscapeGeometry) {
if (trackable instanceof StreetscapeGeometry) {
return true;
}
}
if (trackable instanceof Plane) {
// Check if the hit was within the plane's polygon.
return ((Plane) trackable).isPoseInPolygon(hit.getHitPose());
} else if (trackable instanceof Point) {
// Check if the hit was against an oriented point.
return ((Point) trackable).getOrientationMode() == OrientationMode.ESTIMATED_SURFACE_NORMAL;
}
return false;
}
private List<com.example.thetrek.screens.locationsHandler.Location> getLocationDataFromDatabase() {
List<com.example.thetrek.screens.locationsHandler.Location> locationList = new ArrayList<>();
Cursor cursor = locationDAO.getAllLocations();
if (cursor != null && cursor.moveToFirst()) {
int idIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_ID);
int latitudeIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_LATITUDE);
int longitudeIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_LONGITUDE);
int isVisitedIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_IS_VISITED);
int locationNameIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_NAME);
int elevationIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_ELEVATION);
do {
int id = cursor.getInt(idIndex);
double latitude = cursor.getDouble(latitudeIndex);
double longitude = cursor.getDouble(longitudeIndex);
boolean isVisited = cursor.getInt(isVisitedIndex) == 1;
String locationName = cursor.getString(locationNameIndex);
double elevation = cursor.getDouble(elevationIndex);
com.example.thetrek.screens.locationsHandler.Location location = new com.example.thetrek.screens.locationsHandler.Location(id, latitude, longitude, isVisited, locationName, elevation);
locationList.add(location);
} while (cursor.moveToNext());
}
if (cursor != null) {
cursor.close();
}
return locationList;
}
}
/*
* Copyright 2022 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.geospatial;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import com.example.thetrek.R;
/** A DialogFragment for the Privacy Notice Dialog Box. */
public class PrivacyNoticeDialogFragment extends DialogFragment {
/** Listener for a privacy notice response. */
public interface NoticeDialogListener {
/** Invoked when the user accepts sharing experience. */
void onDialogPositiveClick(DialogFragment dialog);
}
NoticeDialogListener noticeDialogListener;
static PrivacyNoticeDialogFragment createDialog() {
PrivacyNoticeDialogFragment dialogFragment = new PrivacyNoticeDialogFragment();
return dialogFragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
// Verify that the host activity implements the callback interface
try {
noticeDialogListener = (NoticeDialogListener) context;
} catch (ClassCastException e) {
throw new AssertionError("Must implement NoticeDialogListener", e);
}
}
@Override
public void onDetach() {
super.onDetach();
noticeDialogListener = null;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AlertDialogCustom);
builder
.setTitle(R.string.share_experience_title)
.setMessage(R.string.share_experience_message)
.setPositiveButton(
R.string.agree_to_share,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// Send the positive button event back to the host activity
noticeDialogListener.onDialogPositiveClick(PrivacyNoticeDialogFragment.this);
}
})
.setNegativeButton(
R.string.learn_more,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
Intent browserIntent =
new Intent(
Intent.ACTION_VIEW, Uri.parse(getString(R.string.learn_more_url)));
getActivity().startActivity(browserIntent);
}
});
return builder.create();
}
}
package com.example.thetrek.geospatial;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.example.thetrek.HomeActivity;
import com.example.thetrek.R;
import com.example.thetrek.db.RewardDbHelper;
import com.example.thetrek.db.RewardStrings;
import com.example.thetrek.screens.findPlaces.FindPlacesActivity;
import com.example.thetrek.screens.rewards.Reward;
public class RewardingScreenActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rewarding_screen);
getSupportActionBar().hide();
Button findPlacesButton = (Button) findViewById(R.id.btn_claim_reward);
findPlacesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RewardDbHelper dbHelper = new RewardDbHelper(v.getContext());
int id = dbHelper.addReward("New Reward");
dbHelper.updateReward(new Reward(id, RewardStrings.getProgressArray()[id]));
finish();
Intent intent = new Intent(v.getContext(), HomeActivity.class);
v.getContext().startActivity(intent);
}
});
}
}
\ No newline at end of file
/*
* Copyright 2022 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.thetrek.geospatial;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import com.example.thetrek.R;
/** A DialogFragment for the VPS availability Notice Dialog Box. */
public class VpsAvailabilityNoticeDialogFragment extends DialogFragment {
/** Listener for a VPS availability notice response. */
public interface NoticeDialogListener {
/** Invoked when the user accepts sharing experience. */
void onDialogContinueClick(DialogFragment dialog);
}
NoticeDialogListener noticeDialogListener;
static VpsAvailabilityNoticeDialogFragment createDialog() {
VpsAvailabilityNoticeDialogFragment dialogFragment = new VpsAvailabilityNoticeDialogFragment();
return dialogFragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
// Verify that the host activity implements the callback interface
try {
noticeDialogListener = (NoticeDialogListener) context;
} catch (ClassCastException e) {
throw new AssertionError("Must implement NoticeDialogListener", e);
}
}
@Override
public void onDetach() {
super.onDetach();
noticeDialogListener = null;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AlertDialogCustom);
builder
.setTitle(R.string.vps_unavailable_title)
.setMessage(R.string.vps_unavailable_message)
.setCancelable(false)
.setPositiveButton(
R.string.continue_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// Send the positive button event back to the host activity
noticeDialogListener.onDialogContinueClick(
VpsAvailabilityNoticeDialogFragment.this);
}
});
Dialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
}
package com.example.thetrek.screens.findPlaces;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
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.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.PlaceLikelihood;
import com.google.android.libraries.places.api.model.RectangularBounds;
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
import com.google.android.libraries.places.api.net.PlacesClient;
import java.util.Arrays;
import java.util.List;
import com.example.thetrek.R;
public class FindPlacesActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final int PERMISSION_REQUEST_ACCESS_FINE_LOCATION = 100;
private PlacesClient placesClient;
private GoogleMap googleMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_places);
getSupportActionBar().hide();
// Initialize Places API
Places.initialize(getApplicationContext(), getString(R.string.google_maps_key));
placesClient = Places.createClient(this);
// Set up UI components
Button btnFindPlaces = findViewById(R.id.btnFindPlaces);
btnFindPlaces.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
checkLocationPermission();
}
});
// Obtain the SupportMapFragment and get notified when the map is ready to be used
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.mapFragment);
mapFragment.getMapAsync(this);
}
private void checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
getCurrentLocation();
Toast.makeText(this, "Finding places near you", Toast.LENGTH_SHORT).show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_REQUEST_ACCESS_FINE_LOCATION);
}
}
private void getCurrentLocation() {
FusedLocationProviderClient locationClient = LocationServices.getFusedLocationProviderClient(this);
LocationRequest locationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setNumUpdates(1);
LocationCallback locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
Location location = locationResult.getLastLocation();
if (location != null) {
findNearbyPlaces(location);
moveMapCamera(location);
} else {
Toast.makeText(FindPlacesActivity.this, "Unable to get current location", Toast.LENGTH_SHORT).show();
}
}
};
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
locationClient.requestLocationUpdates(locationRequest, locationCallback, null);
}
private void findNearbyPlaces(Location location) {
List<Place.Field> placeFields = Arrays.asList(Place.Field.NAME, Place.Field.ADDRESS, Place.Field.LAT_LNG);
PlacesClient placesClient = Places.createClient(this);
FindCurrentPlaceRequest request = FindCurrentPlaceRequest.builder(placeFields).build();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
placesClient.findCurrentPlace(request)
.addOnSuccessListener((response) -> {
for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {
Place place = placeLikelihood.getPlace();
String name = place.getName();
String address = place.getAddress();
LatLng latLng = place.getLatLng();
if (latLng != null && googleMap != null) {
googleMap.addMarker(new MarkerOptions().position(latLng).title(name).snippet(address));
}
}
})
.addOnFailureListener((exception) -> {
Toast.makeText(FindPlacesActivity.this, "Failed to find nearby places: " + exception.getMessage(), Toast.LENGTH_SHORT).show();
});
}
private void moveMapCamera(Location location) {
if (googleMap != null) {
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15f));
}
}
@Override
public void onMapReady(GoogleMap googleMap) {
this.googleMap = googleMap;
// Enable location tracking
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
googleMap.setMyLocationEnabled(true);
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_REQUEST_ACCESS_FINE_LOCATION);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_ACCESS_FINE_LOCATION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getCurrentLocation();
if (googleMap != null) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
googleMap.setMyLocationEnabled(true);
}
} else {
Toast.makeText(this, "Location permission denied", Toast.LENGTH_SHORT).show();
}
}
}
}
\ No newline at end of file
package com.example.thetrek.screens.getPhysical;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.example.thetrek.R;
public class GetPhysicalActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_physical);
getSupportActionBar().hide();
}
}
\ No newline at end of file
package com.example.thetrek.screens.locationsHandler;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.example.thetrek.R;
import com.example.thetrek.db.LocationDAO;
public class AddLocations extends AppCompatActivity {
private EditText editTextLatitude;
private EditText editTextLongitude;
private EditText editTextLocationName;
private EditText editTextElevation;
private Button buttonAddLocation;
private LocationDAO locationDAO;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_locations);
getSupportActionBar().hide();
editTextLatitude = findViewById(R.id.txt_addLocation_latitude);
editTextLongitude = findViewById(R.id.txt_addLocation_longitude);
editTextLocationName = findViewById(R.id.txt_addLocation_name);
editTextElevation = findViewById(R.id.txt_addLocation_elevation);
buttonAddLocation = findViewById(R.id.btn_addLocationActivity_addLocation);
locationDAO = new LocationDAO(this);
locationDAO.open();
buttonAddLocation.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String name = editTextLocationName.getText().toString();
double latitude = Double.parseDouble(editTextLatitude.getText().toString());
double longitude = Double.parseDouble(editTextLongitude.getText().toString());
double elevation = Double.parseDouble(editTextElevation.getText().toString());
boolean isVisited = false; // Set the initial value for isVisited as needed
long insertedId = locationDAO.insertLocation(name, latitude, longitude, elevation, isVisited);
if (insertedId != -1) {
Toast.makeText(AddLocations.this, "Location added to the database", Toast.LENGTH_SHORT).show();
editTextLatitude.setText("");
editTextLongitude.setText("");
editTextLocationName.setText("");
editTextElevation.setText("");
} else {
Toast.makeText(AddLocations.this, "Failed to add location to the database", Toast.LENGTH_SHORT).show();
}
finish();
Intent intent = new Intent(v.getContext(), LocationsCrud.class);
v.getContext().startActivity(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
locationDAO.close();
}
}
\ No newline at end of file
package com.example.thetrek.screens.locationsHandler;
public class Location {
private int id;
private double latitude;
private double longitude;
private boolean isVisited;
private String locationName;
private double elevation;
public Location(int id, double latitude, double longitude, boolean isVisited, String locationName, double elevation) {
this.id = id;
this.latitude = latitude;
this.longitude = longitude;
this.isVisited = isVisited;
this.locationName = locationName;
this.elevation = elevation;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public boolean isVisited() {
return isVisited;
}
public void setVisited(boolean visited) {
isVisited = visited;
}
public String getLocationName() {
return locationName;
}
public void setLocationName(String locationName) {
this.locationName = locationName;
}
public double getElevation() {
return elevation;
}
public void setElevation(double elevation) {
this.elevation = elevation;
}
}
package com.example.thetrek.screens.locationsHandler;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.thetrek.R;
import com.example.thetrek.db.LocationDAO;
import java.util.List;
public class LocationAdapter extends RecyclerView.Adapter<LocationAdapter.ViewHolder> {
private List<Location> locationList;
private LocationDAO locationDAO;
public LocationAdapter(List<Location> locationList, LocationDAO locationDAO) {
this.locationDAO = locationDAO;
this.locationList = locationList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.location_details_item, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Location location = locationList.get(position);
holder.textViewName.setText(String.valueOf(location.getLocationName()));
holder.textViewLatitude.setText(String.valueOf(location.getLatitude()));
holder.textViewLongitude.setText(String.valueOf(location.getLongitude()));
holder.textViewElevation.setText(String.valueOf(location.getElevation()));
holder.textViewVisited.setText(location.isVisited() ? "Visited" : "Not Visited");
holder.buttonDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
locationDAO.deleteLocation(location.getId());
locationList.remove(location);
notifyDataSetChanged();
}
});
}
@Override
public int getItemCount() {
return locationList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView textViewLatitude;
public TextView textViewLongitude;
public TextView textViewVisited;
public TextView textViewName;
public TextView textViewElevation;
public Button buttonDelete;
public ViewHolder(@NonNull View itemView) {
super(itemView);
textViewLatitude = itemView.findViewById(R.id.lbl_locationCrud_latitude);
textViewLongitude = itemView.findViewById(R.id.lbl_locationCrud_longitude);
textViewVisited = itemView.findViewById(R.id.lbl_locationCrud_isVisited);
textViewName = itemView.findViewById(R.id.lbl_locationCrud_locationName);
textViewElevation = itemView.findViewById(R.id.lbl_locationCrud_elevation);
buttonDelete = itemView.findViewById(R.id.btn_locationCrud_deleteLocation);
}
}
}
package com.example.thetrek.screens.locationsHandler;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.example.thetrek.R;
import com.example.thetrek.db.LocationDAO;
import com.example.thetrek.db.LocationsDbHelper;
import java.util.ArrayList;
import java.util.List;
public class LocationsCrud extends AppCompatActivity {
private RecyclerView recyclerView;
private LocationAdapter locationAdapter;
private TextView textViewNoLocations;
private LocationDAO locationDAO;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_locations_crud);
getSupportActionBar().hide();
recyclerView = findViewById(R.id.recyclerView_locationsCrud);
textViewNoLocations = findViewById(R.id.lbl_locationsCrud_noLocationMessage);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
locationDAO = new LocationDAO(this);
locationDAO.open();
List<Location> locationList = getLocationDataFromDatabase();
if (locationList.isEmpty()) {
recyclerView.setVisibility(View.GONE);
textViewNoLocations.setVisibility(View.VISIBLE);
} else {
recyclerView.setVisibility(View.VISIBLE);
textViewNoLocations.setVisibility(View.GONE);
locationAdapter = new LocationAdapter(locationList, locationDAO);
recyclerView.setAdapter(locationAdapter);
}
Button addLocations = (Button) findViewById(R.id.btn_locationsCrudActivity_addLocations);
addLocations.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
Intent intent = new Intent(v.getContext(), AddLocations.class);
v.getContext().startActivity(intent);
}
});
}
private List<Location> getLocationDataFromDatabase() {
List<Location> locationList = new ArrayList<>();
Cursor cursor = locationDAO.getAllLocations();
if (cursor != null && cursor.moveToFirst()) {
int idIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_ID);
int latitudeIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_LATITUDE);
int longitudeIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_LONGITUDE);
int isVisitedIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_IS_VISITED);
int locationNameIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_NAME);
int elevationIndex = cursor.getColumnIndex(LocationsDbHelper.COLUMN_ELEVATION);
do {
int id = cursor.getInt(idIndex);
double latitude = cursor.getDouble(latitudeIndex);
double longitude = cursor.getDouble(longitudeIndex);
boolean isVisited = cursor.getInt(isVisitedIndex) == 1;
String locationName = cursor.getString(locationNameIndex);
double elevation = cursor.getDouble(elevationIndex);
Location location = new Location(id, latitude, longitude, isVisited, locationName, elevation);
locationList.add(location);
} while (cursor.moveToNext());
}
if (cursor != null) {
cursor.close();
}
return locationList;
}
}
\ No newline at end of file
package com.example.thetrek.screens.mobileUsage;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.SystemClock;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import com.example.thetrek.MainActivityLogin;
import com.example.thetrek.R;
public class AlarmHelper {
private static final int NOTIFICATION_ID = 1;
private static final int HOUR_IN_MILLIS = 60 * 1000; // 1 hour in milliseconds
private static final String CHANNEL_ID = "screen_monitoring_channel";
public static void scheduleNotificationAlarm(Context context) {
// Create a pending intent to start the notification service
Intent serviceIntent = new Intent(context, NotificationService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the alarm manager
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// Calculate the alarm time
long triggerTime = SystemClock.elapsedRealtime() + HOUR_IN_MILLIS;
// Schedule the alarm
if (alarmManager != null) {
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, pendingIntent);
}
}
public static void cancelNotificationAlarm(Context context) {
// Create a pending intent to cancel the notification service
Intent serviceIntent = new Intent(context, NotificationService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the alarm manager
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// Cancel the alarm
if (alarmManager != null) {
alarmManager.cancel(pendingIntent);
}
}
public static void showNotification(Context context) {
// Create a pending intent to open the MainActivity when the notification is clicked
Intent intent = new Intent(context, MainActivityLogin.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Create the notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Screen Awake Notification")
.setContentText("Your screen has been awake for over 1 hour.")
.setSmallIcon(R.drawable.ic_baseline_notifications_24)
.setColor(ContextCompat.getColor(context, R.color.black))
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true);
// Show the notification
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Screen Monitoring", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
package com.example.thetrek.screens.mobileUsage;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.thetrek.R;
import java.util.List;
public class AppUsageAdapter extends RecyclerView.Adapter<AppUsageAdapter.AppUsageViewHolder> {
private List<AppUsageStats> appUsageStatsList;
public void setAppUsageStatsList(List<AppUsageStats> appUsageStatsList) {
this.appUsageStatsList = appUsageStatsList;
notifyDataSetChanged();
}
@NonNull
@Override
public AppUsageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.mobile_usage_stat_item, parent, false);
return new AppUsageViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull AppUsageViewHolder holder, int position) {
AppUsageStats appUsageStats = appUsageStatsList.get(position);
holder.bind(appUsageStats);
}
@Override
public int getItemCount() {
return appUsageStatsList != null ? appUsageStatsList.size() : 0;
}
static class AppUsageViewHolder extends RecyclerView.ViewHolder {
private TextView appNameTextView;
private TextView usageTimeTextView;
AppUsageViewHolder(@NonNull View itemView) {
super(itemView);
appNameTextView = itemView.findViewById(R.id.mobileUsage_appName_item);
usageTimeTextView = itemView.findViewById(R.id.mobileUsage_appTime_item);
}
void bind(AppUsageStats appUsageStats) {
appNameTextView.setText(appUsageStats.getAppName());
long usageTime = appUsageStats.getUsageTime() / (1000 * 60); // Convert to seconds
usageTimeTextView.setText(String.format("%d Minutes", usageTime));
}
}
}
package com.example.thetrek.screens.mobileUsage;
public class AppUsageStats {
private String appName;
private long usageTime;
public AppUsageStats() {
}
public AppUsageStats(String appName, long usageTime) {
this.appName = appName;
this.usageTime = usageTime;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public long getUsageTime() {
return usageTime;
}
public void setUsageTime(long usageTime) {
this.usageTime = usageTime;
}
}
package com.example.thetrek.screens.mobileUsage;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.animation.ValueAnimator;
import android.app.AppOpsManager;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.animation.DecelerateInterpolator;
import com.example.thetrek.R;
import com.github.mikephil.charting.animation.Easing;
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry;
import com.github.mikephil.charting.utils.ColorTemplate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class MobileUsageActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private AppUsageAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mobile_usage);
getSupportActionBar().hide();
PieChart pieChart = findViewById(R.id.pieChart);
AppOpsManager appOps = (AppOpsManager)
getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(), getPackageName());
if(mode == AppOpsManager.MODE_ALLOWED){
List<AppUsageStats> appUsageStats = getAppUsageStats();
logAppUsageStats(appUsageStats);
recyclerView = findViewById(R.id.mobileUsage_recylclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new AppUsageAdapter();
adapter.setAppUsageStatsList(appUsageStats);
recyclerView.setAdapter(adapter);
List<PieEntry> pieEntries = new ArrayList<>();
for (AppUsageStats appUsageStat : appUsageStats) {
pieEntries.add(new PieEntry(appUsageStat.getUsageTime()/(1000*60), appUsageStat.getAppName()));
}
PieDataSet dataSet = new PieDataSet(pieEntries.subList(0, 5), "App Usage");
dataSet.setColors(ColorTemplate.COLORFUL_COLORS);
dataSet.setValueTextSize(12f);
PieData pieData = new PieData(dataSet);
pieChart.setData(pieData);
pieChart.setDrawEntryLabels(true);
pieChart.setEntryLabelColor(android.R.color.black);
pieChart.setEntryLabelTextSize(12f);
pieChart.getDescription().setEnabled(false);
pieChart.getLegend().setEnabled(false);
pieChart.getLegend().setEnabled(true);
pieChart.setDrawEntryLabels(true);
pieChart.animateXY(1000, 1000, Easing.EaseInOutQuad);
pieChart.spin(2000, pieChart.getRotationAngle(), pieChart.getRotationAngle() + 360, Easing.EaseInOutQuad);
// Animate the hole radius using a custom animation
int startHoleRadius = 0; // Initial hole radius
int endHoleRadius = 50; // Final hole radius
int animationDuration = 1500; // Animation duration in milliseconds
ValueAnimator animator = ValueAnimator.ofInt(startHoleRadius, endHoleRadius);
animator.setDuration(animationDuration);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
pieChart.setHoleRadius(animatedValue);
pieChart.invalidate();
}
});
animator.start();
pieChart.invalidate();
}else {
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
}
}
private List<AppUsageStats> getAppUsageStats() {
UsageStatsManager usageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
Calendar calendar = Calendar.getInstance();
long endTime = calendar.getTimeInMillis();
calendar.add(Calendar.DAY_OF_MONTH, -1);
long startTime = calendar.getTimeInMillis();
List<UsageStats> statsList = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, startTime, endTime);
List<AppUsageStats> appUsageStatsList = new ArrayList<>();
for (UsageStats usageStats : statsList) {
String appName = usageStats.getPackageName();
int lastDotIndex = appName.lastIndexOf(".");
if (lastDotIndex != -1 && lastDotIndex < appName.length() - 1) {
appName = appName.substring(lastDotIndex + 1);
}
appName = capitalizeFirstLetter(appName);
long usageTime = usageStats.getTotalTimeInForeground();
AppUsageStats appUsageStats = new AppUsageStats(appName, usageTime);
appUsageStatsList.add(appUsageStats);
}
Collections.sort(appUsageStatsList, new Comparator<AppUsageStats>() {
@Override
public int compare(AppUsageStats appUsageStats1, AppUsageStats appUsageStats2) {
long usageTime1 = appUsageStats1.getUsageTime();
long usageTime2 = appUsageStats2.getUsageTime();
// Sort in descending order
return Long.compare(usageTime2, usageTime1);
}
});
return appUsageStatsList;
}
private String capitalizeFirstLetter(String input) {
if (input == null || input.isEmpty()) {
return input;
}
return input.substring(0, 1).toUpperCase() + input.substring(1);
}
private void logAppUsageStats(List<AppUsageStats> appUsageStatsList) {
for (AppUsageStats appUsageStats : appUsageStatsList) {
String appName = appUsageStats.getAppName();
long usageTime = appUsageStats.getUsageTime();
String logMessage = "App Name: " + appName + ", Usage Time: " + usageTime + " milliseconds";
Log.d("AppUsageStats", logMessage);
}
}
}
\ No newline at end of file
package com.example.thetrek.screens.mobileUsage;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class NotificationService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Show the notification
AlarmHelper.showNotification(this);
// Stop the service
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
package com.example.thetrek.screens.mobileUsage;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import com.example.thetrek.R;
import com.example.thetrek.screens.findPlaces.FindPlacesActivity;
public class ScreenMonitoringService extends Service {
private static final String CHANNEL_ID = "screen_monitoring_channel";
private static final int NOTIFICATION_ID = 1;
private static final int HOUR_IN_MILLIS = 60 * 60 * 1000; // 1 hour in milliseconds
private ScreenReceiver screenReceiver;
private Handler handler;
private Runnable notificationRunnable;
@Override
public void onCreate() {
super.onCreate();
handler = new Handler();
notificationRunnable = new Runnable() {
@Override
public void run() {
sendNotification();
}
};
screenReceiver = new ScreenReceiver();
registerReceiver(screenReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
registerReceiver(screenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (powerManager.isInteractive()) {
// If the phone is already awake, start the timer immediately
startNotificationTimer();
}
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(screenReceiver);
stopNotificationTimer();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void startNotificationTimer() {
handler.postDelayed(notificationRunnable, HOUR_IN_MILLIS);
}
private void stopNotificationTimer() {
handler.removeCallbacks(notificationRunnable);
}
private void sendNotification() {
// Create a pending intent to open the MainActivity when the notification is clicked
Intent intent = new Intent(this, FindPlacesActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
// Create the notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Too long on the phone")
.setContentText("Hey there! You've been on your phone for an hour, and while it's great to stay connected, how about exploring some nearby places that can boost your well-being?")
.setSmallIcon(R.drawable.ic_baseline_notifications_24)
.setColor(ContextCompat.getColor(this, R.color.black))
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true);
// Show the notification
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Screen Monitoring", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
private class ScreenReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
startNotificationTimer();
} else if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
stopNotificationTimer();
}
}
}
}
package com.example.thetrek.screens.mobileUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
public class ScreenOnReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// Schedule an alarm after 1 hour
AlarmHelper.scheduleNotificationAlarm(context);
}
}
}
package com.example.thetrek.screens.rewards;
public class Reward {
private int id;
private String rewardName;
public Reward(int id, String rewardName) {
this.id = id;
this.rewardName = rewardName;
}
public int getId() {
return id;
}
public String getRewardName() {
return rewardName;
}
}
package com.example.thetrek.screens.rewards;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.thetrek.R;
import java.util.List;
public class RewardAdapter extends RecyclerView.Adapter<RewardAdapter.RewardViewHolder> {
private List<Reward> rewardList;
public RewardAdapter(List<Reward> rewardList) {
this.rewardList = rewardList;
}
public static class RewardViewHolder extends RecyclerView.ViewHolder {
TextView textViewRewardName;
public RewardViewHolder(View itemView) {
super(itemView);
textViewRewardName = itemView.findViewById(R.id.txt_rewards_rewardName);
}
}
@NonNull
@Override
public RewardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.reward_item, parent, false);
return new RewardViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RewardViewHolder holder, int position) {
Reward reward = rewardList.get(position);
holder.textViewRewardName.setText(reward.getRewardName());
}
@Override
public int getItemCount() {
return rewardList.size();
}
}
package com.example.thetrek.screens.rewards;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import com.example.thetrek.R;
import com.example.thetrek.db.RewardDbHelper;
import java.util.ArrayList;
import java.util.List;
public class RewardsActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private RewardAdapter rewardAdapter;
private List<Reward> rewardList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rewards);
getSupportActionBar().hide();
recyclerView = findViewById(R.id.recyclerView_rewards);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
rewardList = new ArrayList<>(); // Ensure proper initialization here
rewardAdapter = new RewardAdapter(rewardList);
recyclerView.setAdapter(rewardAdapter);
loadRewardData();
}
private void loadRewardData() {
// Retrieve the reward data from SQLite and update the reward list
// For example, you can use your RewardDbHelper class to fetch the data
RewardDbHelper dbHelper = new RewardDbHelper(this);
rewardList.clear();
rewardList.addAll(dbHelper.getAllRewards());
rewardAdapter.notifyDataSetChanged();
}
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient android:angle="90" android:startColor="#A443E9" android:endColor="#38f9d7" android:type="linear"/>
</shape>
</item>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true">
<shape android:shape="rectangle">
<corners android:radius="10dp"/>
<gradient android:startColor="#A443E9" android:endColor="#38f9d7" android:type="linear"/>
</shape>
</item>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 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
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#039be5"/>
<corners android:radius="10dp"/>
<padding
android:left="2dp"
android:right="2dp"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 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
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid
android:color="@android:color/white"/>
<corners
android:radius="10dp" />
<padding
android:left="5dp"
android:top="10dp"
android:right="5dp"
android:bottom="10dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:state_focused="true">
<shape android:shape="rectangle">
<gradient android:startColor="#A443E9" android:endColor="#38f9d7" android:type="linear"/>
<corners android:radius="10dp"/>
</shape>
</item>
</selector>
\ No newline at end of file
<vector android:height="24dp" android:tint="#7C7C7C"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
<path android:fillColor="@android:color/white" android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
</vector>
<vector android:height="24dp" android:tint="#2195F2"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/>
</vector>
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,6h-2.18c0.11,-0.31 0.18,-0.65 0.18,-1 0,-1.66 -1.34,-3 -3,-3 -1.05,0 -1.96,0.54 -2.5,1.35l-0.5,0.67 -0.5,-0.68C10.96,2.54 10.05,2 9,2 7.34,2 6,3.34 6,5c0,0.35 0.07,0.69 0.18,1L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM15,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM9,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM20,19L4,19v-2h16v2zM20,14L4,14L4,8h5.08L7,10.83 8.62,12 11,8.76l1,-1.36 1,1.36L15.38,12 17,10.83 14.92,8L20,8v6z"/>
</vector>
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M9,11.75c-0.69,0 -1.25,0.56 -1.25,1.25s0.56,1.25 1.25,1.25 1.25,-0.56 1.25,-1.25 -0.56,-1.25 -1.25,-1.25zM15,11.75c-0.69,0 -1.25,0.56 -1.25,1.25s0.56,1.25 1.25,1.25 1.25,-0.56 1.25,-1.25 -0.56,-1.25 -1.25,-1.25zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8 0,-0.29 0.02,-0.58 0.05,-0.86 2.36,-1.05 4.23,-2.98 5.21,-5.37C11.07,8.33 14.05,10 17.42,10c0.78,0 1.53,-0.09 2.25,-0.26 0.21,0.71 0.33,1.47 0.33,2.26 0,4.41 -3.59,8 -8,8z"/>
</vector>
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
</vector>
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#FFFFFF" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M14,10H3v2h11V10zM14,6H3v2h11V6zM18,14v-4h-2v4h-4v2h4v4h2v-4h4v-2H18zM3,16h7v-2H3V16z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
</vector>
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
</vector>
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M18.41,5.8L17.2,4.59c-0.78,-0.78 -2.05,-0.78 -2.83,0l-2.68,2.68L3,15.96V20h4.04l8.74,-8.74 2.63,-2.63c0.79,-0.78 0.79,-2.05 0,-2.83zM6.21,18H5v-1.21l8.66,-8.66 1.21,1.21L6.21,18zM11,20l4,-4h6v4H11z"/>
</vector>
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".screens.locationsHandler.AddLocations">
<LinearLayout
android:layout_width="375dp"
android:layout_height="684dp"
android:layout_marginStart="16dp"
android:layout_marginTop="23dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="24dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textView6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Locations"
android:textSize="24sp"
android:textStyle="bold" />
<EditText
android:id="@+id/txt_addLocation_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Location Name"
android:inputType="textPersonName" />
<EditText
android:id="@+id/txt_addLocation_latitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Latitude"
android:inputType="textPersonName" />
<EditText
android:id="@+id/txt_addLocation_longitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Longitude"
android:inputType="textPersonName" />
<EditText
android:id="@+id/txt_addLocation_elevation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Elevation"
android:inputType="textPersonName" />
<Button
android:id="@+id/btn_addLocationActivity_addLocation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Location" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/mapFragment"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="?android:attr/preferenceLayoutChild" />
<Button
android:id="@+id/btnFindPlaces"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp"
android:text="Find Places" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".screens.getPhysical.GetPhysicalActivity">
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="311dp"
android:text="Get Physical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeActivity">
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="352dp"
android:layout_height="677dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="16dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="70dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="272dp"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Good Morning," />
<TextView
android:id="@+id/textView5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="paramee"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
<ImageView
android:id="@+id/imageView4"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:layout_weight="1"
app:srcCompat="@drawable/profileuser" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="25dp" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="match_parent"
android:layout_height="150dp"
app:srcCompat="@drawable/logo" />
<Button
android:id="@+id/btn_homeActivity_findPlaces"
android:layout_width="match_parent"
android:layout_height="65dp"
android:backgroundTint="#5D71C9"
android:text="Find Places" />
<Button
android:id="@+id/btn_homeActivity_mobileUsage"
android:layout_width="match_parent"
android:layout_height="65dp"
android:backgroundTint="#5D71C9"
android:text="Mobile Usage" />
<Button
android:id="@+id/btn_homeActivity_getPhysical"
android:layout_width="match_parent"
android:layout_height="65dp"
android:backgroundTint="#5D71C9"
android:text="Get Physical" />
<Button
android:id="@+id/btn_predictor"
android:layout_width="match_parent"
android:layout_height="65dp"
android:backgroundTint="#5D71C9"
android:text="Suggestion Predictor" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:orientation="horizontal">
<Button
android:id="@+id/btn_homeActivity_locationsHandler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="#404040"
android:text="Locations"
app:icon="@drawable/ic_baseline_location_on_24" />
<Space
android:layout_width="wrap_content"
android:layout_height="26dp"
android:layout_weight="0.2" />
<Button
android:id="@+id/btn_homeActivity_rewards"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="#404040"
android:text="Rewards"
app:icon="@drawable/ic_baseline_card_giftcard_24" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:orientation="horizontal">
<Button
android:id="@+id/btn_activityRecognition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="#404040"
android:text="Activity"
app:icon="@drawable/activity" />
<Space
android:layout_width="wrap_content"
android:layout_height="26dp"
android:layout_weight="0.2" />
<Button
android:id="@+id/btn_recorder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="#404040"
android:text="Recorder"
app:icon="@drawable/recorder" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".screens.locationsHandler.LocationsCrud">
<LinearLayout
android:layout_width="380dp"
android:layout_height="679dp"
android:layout_marginStart="15dp"
android:layout_marginTop="26dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="26dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textView14"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Locations"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/lbl_locationsCrud_noLocationMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="No Locations Saved" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView_locationsCrud"
android:layout_width="match_parent"
android:layout_height="590dp" />
<Button
android:id="@+id/btn_locationsCrudActivity_addLocations"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Locations"
app:icon="@drawable/ic_baseline_playlist_add_24" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivityLogin">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="24dp"
android:layout_marginTop="35dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="35dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="263dp"
app:srcCompat="@drawable/logo" />
<Space
android:layout_width="match_parent"
android:layout_height="27dp" />
<TextView
android:id="@+id/user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="E-mail Address / Username"
android:textSize="16sp"
android:textStyle="bold"
android:drawableStart="@drawable/ic_mail"/>
<EditText
android:id="@+id/editTextTextPersonName2"
android:layout_width="match_parent"
android:layout_height="46dp"
android:ems="10"
android:inputType="textPersonName" />
<TextView
android:id="@+id/text_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Password"
android:textSize="16sp"
android:textStyle="bold"
android:drawableStart="@drawable/ic_lock"/>
<EditText
android:id="@+id/editTextTextPersonName"
android:layout_width="match_parent"
android:layout_height="46dp"
android:ems="10"
android:inputType="textPersonName" />
<Space
android:layout_width="match_parent"
android:layout_height="26dp" />
<Button
android:id="@+id/btnSubmit_login"
android:layout_width="match_parent"
android:layout_height="65dp"
android:backgroundTint="#5D71C9"
android:text="Sign in" />
<Space
android:layout_width="match_parent"
android:layout_height="28dp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2022 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
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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".geospatial.GeospatialActivity">
<android.opengl.GLSurfaceView
android:id="@+id/surfaceview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="top"/>
<TextView
android:id="@+id/status_text_view"
android:layout_width="fill_parent"
android:layout_height="150dp"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:padding="20dp"
android:textColor="#ffffff"
android:background="#bf323232"/>
<TextView
android:id="@+id/geospatial_pose_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:padding="20dp"
android:textColor="#ffffff"
android:background="#bf323232"/>
<TextView
android:id="@+id/tap_screen_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/status_text_view"
android:layout_centerInParent="true"
android:visibility="gone"
android:text="@string/tap_screen_text"
android:textColor="#ffffff"/>
<Button
android:id="@+id/set_anchor_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/streetscape_geometry_switch"
android:text="@string/button_text_set_anchor"
android:drawableRight="@drawable/ic_settings_white"
android:layout_alignParentRight="true"
android:visibility="invisible"/>
<Switch
android:id="@+id/streetscape_geometry_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/switch_render_streetscaope_geometry"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:padding="10dp"
android:textOff="Off"
android:textOn="On" />
<Button
android:id="@+id/clear_anchors_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_text_clear_anchors"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:visibility="invisible"/>
<Button
android:id="@+id/geo_get_reward_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="27dp"
android:text="Get reward" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="24dp"
android:layout_marginTop="35dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="35dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="263dp"
app:srcCompat="@drawable/logo" />
<Space
android:layout_width="match_parent"
android:layout_height="27dp" />
<Button
android:id="@+id/btnSubmit_login"
android:layout_width="match_parent"
android:layout_height="65dp"
android:backgroundTint="#5D71C9"
android:text="Sign in" />
<Space
android:layout_width="match_parent"
android:layout_height="28dp" />
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="or"
android:textAlignment="center" />
<Button
android:id="@+id/createAcc"
android:layout_width="match_parent"
android:layout_height="65dp"
android:backgroundTint="#F4F4F4"
android:text="Create an Account"
android:textColor="#5D71C9" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivityLogin">
<Button
android:id="@+id/record_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:onClick="onRecordButtonClickListener"
android:text="@string/record"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/status_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:text="@string/status_stopped"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/label_spinner" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:text="@string/refresh_rate"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2">
<RadioButton
android:id="@+id/normal_radio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:onClick="onRadioButtonClicked"
android:paddingEnd="8dp"
android:text="@string/speed_normal" />
<RadioButton
android:id="@+id/fast_radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:checked="false"
android:onClick="onRadioButtonClicked"
android:paddingEnd="8dp"
android:text="@string/speed_fast" />
<RadioButton
android:id="@+id/fastest_radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:onClick="onRadioButtonClicked"
android:paddingEnd="8dp"
android:text="@string/speed_fastest" />
</RadioGroup>
<Spinner
android:id="@+id/label_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/radioGroup" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ededed"
android:padding="0dp"
tools:context="io.github.introml.activityrecognition.MainActivity">
<TableRow
android:id="@+id/title_row"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:textAlignment="center"
android:background="@android:color/holo_orange_dark">
<TextView
android:id="@+id/title"
android:layout_weight="1"
android:text="@string/activity"
android:textAlignment="center"
android:textSize="20sp"
android:textStyle="bold"
android:fontFamily="sans-serif-medium"
/>
<TextView
android:id="@+id/textView"
android:layout_weight="1"
android:text="@string/probability"
android:textAlignment="center"
android:textSize="20sp"
android:textStyle="bold"
android:fontFamily="sans-serif-medium"/>
</TableRow>
<TableRow
android:id="@+id/downstairs_row"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dp">
<TextView
android:id="@+id/downstairs_title"
android:layout_weight="1"
android:text="@string/downstairs"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/downstairs_prob"
android:layout_weight="1"
android:textAlignment="center"
android:textSize="18sp" />
</TableRow>
<TableRow
android:id="@+id/jogging_row"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dp">
<TextView
android:id="@+id/jogging_title"
android:layout_weight="1"
android:text="@string/jogging"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/jogging_prob"
android:layout_weight="1"
android:textAlignment="center"
android:textSize="18sp" />
</TableRow>
<TableRow
android:id="@+id/sitting_row"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dp">
<TextView
android:id="@+id/sitting_title"
android:layout_weight="1"
android:text="@string/sitting"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/sitting_prob"
android:layout_weight="1"
android:textAlignment="center"
android:textSize="18sp" />
</TableRow>
<TableRow
android:id="@+id/standing_row"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dp">
<TextView
android:id="@+id/standing_title"
android:layout_weight="1"
android:text="@string/standing"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/standing_prob"
android:layout_weight="1"
android:textAlignment="center"
android:textSize="18sp" />
</TableRow>
<TableRow
android:id="@+id/upstairs_row"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp">
<TextView
android:id="@+id/upstairs_title"
android:layout_weight="1"
android:text="@string/upstairs"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/upstairs_prob"
android:layout_weight="1"
android:textAlignment="center"
android:textSize="18sp" />
</TableRow>
<TableRow
android:id="@+id/walking_row"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp">
<TextView
android:id="@+id/walking_title"
android:layout_weight="1"
android:text="@string/walking"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/walking_prob"
android:layout_weight="1"
android:textAlignment="center"
android:textSize="18sp" />
</TableRow>
<TableRow
android:id="@+id/biking_row"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp">
<TextView
android:id="@+id/biking_title"
android:layout_weight="1"
android:text="@string/biking"
android:textAlignment="center"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/biking_prob"
android:layout_weight="1"
android:textAlignment="center"
android:textSize="18sp" />
</TableRow>
</TableLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".screens.mobileUsage.MobileUsageActivity">
<LinearLayout
android:layout_width="383dp"
android:layout_height="703dp"
android:layout_marginStart="29dp"
android:layout_marginTop="27dp"
android:layout_marginEnd="29dp"
android:layout_marginBottom="28dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textView8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Mobile App Usage"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/textView9"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Top Apps"
android:textSize="16sp" />
<com.github.mikephil.charting.charts.PieChart
android:id="@+id/pieChart"
android:layout_width="match_parent"
android:layout_height="300dp" />
<Space
android:layout_width="match_parent"
android:layout_height="11dp" />
<TextView
android:id="@+id/textView10"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="All Apps"
android:textSize="16sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mobileUsage_recylclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".geospatial.RewardingScreenActivity">
<LinearLayout
android:layout_width="371dp"
android:layout_height="670dp"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="31dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<TextView
android:id="@+id/textView17"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Congratulations"
android:textAlignment="center"
android:textSize="24sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/imageView6"
android:layout_width="match_parent"
android:layout_height="393dp"
android:adjustViewBounds="false"
app:srcCompat="@drawable/rewards" />
<Space
android:layout_width="match_parent"
android:layout_height="46dp" />
<Button
android:id="@+id/btn_claim_reward"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Claim Reward" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".screens.rewards.RewardsActivity">
<LinearLayout
android:layout_width="367dp"
android:layout_height="674dp"
android:layout_marginStart="22dp"
android:layout_marginTop="28dp"
android:layout_marginEnd="22dp"
android:layout_marginBottom="29dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textView16"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Rewards"
android:textSize="24sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView_rewards"
android:layout_width="match_parent"
android:layout_height="579dp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SignUp"
android:scrollbars="vertical">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="MissingConstraints"
android:layout_gravity="center"
android:gravity="center"
android:scrollbars="vertical">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="263dp"
app:srcCompat="@drawable/logo" />
<EditText
android:id="@+id/textName"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/edit_text_box"
android:layout_marginBottom="10dp"
android:inputType="textPersonName"
android:hint="Name"
android:gravity="center"
android:padding="20dp"
android:drawableStart="@drawable/ic_name"
/>
<EditText
android:id="@+id/textNumber"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/edit_text_box"
android:layout_marginBottom="10dp"
android:inputType="number"
android:hint="Phone Number"
android:gravity="center"
android:padding="20dp"
android:drawableStart="@drawable/ic_phone"
/>
<EditText
android:id="@+id/textEmail"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/edit_text_box"
android:layout_marginBottom="10dp"
android:inputType="textEmailAddress"
android:hint="Email"
android:gravity="center"
android:padding="20dp"
android:drawableStart="@drawable/ic_mail"
/>
<EditText
android:id="@+id/textPass"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/edit_text_box"
android:layout_marginBottom="10dp"
android:inputType="numberPassword"
android:hint="Password"
android:gravity="center"
android:padding="20dp"
android:drawableStart="@drawable/ic_lock"
/>
<Button
android:id="@+id/btnSignUpAcc"
android:backgroundTint="#5D71C9"
android:text="Sign up"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:padding="10dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<View
android:layout_width="100dp"
android:layout_height="2dp"
android:background="@android:color/black"/>
<TextView
android:layout_width="25dp"
android:layout_height="25dp"
android:textSize="20dp"
android:text="Or"
android:gravity="center"/>
<View
android:layout_width="100dp"
android:layout_height="2dp"
android:background="@android:color/black"/>
</LinearLayout>
<TextView
android:id="@+id/loginAcc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login"
android:clickable="true"
android:textStyle="bold"
android:textSize="20dp"
/>
</LinearLayout>
</ScrollView>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SuggestActivity"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Suggession Predictor"
android:textSize="24dp"
android:textAlignment="center"
android:layout_marginTop="10dp"
android:textStyle="bold"
android:textColor="@color/cardview_dark_background"></TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="🏃‍♂️ Are your muscles itching for action, or is it a day to take it easy? Discover now! 🌳"
android:textSize="13dp"
android:textColor="@color/design_default_color_primary"
android:textAlignment="center"
android:layout_marginTop="10dp"
android:textStyle="bold"></TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="🌈 This isn't your typical form; it's your direct connection to the universe of activity wisdom. Just complete the form, and our cosmic algorithm will decode the stars to let you know if it's time to engage in some physical activity or savor some well-deserved relaxation.🌟💪"
android:textColor="@color/cardview_dark_background"
android:textSize="12dp"
android:textAlignment="center"
android:layout_marginTop="10dp"
android:textStyle="bold"></TextView>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Age"
android:id="@+id/age"
android:textStyle="bold"
android:layout_marginTop="10dp"
></EditText>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Gender (0 – Male, 1 – Female)"
android:id="@+id/gender"
android:textStyle="bold"
android:layout_marginTop="10dp"
></EditText>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Weather (0 – Rainy, 1 – Sunny)"
android:id="@+id/weather"
android:textStyle="bold"
android:layout_marginTop="10dp"
></EditText>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Place Change (0- No, 1-Yes)"
android:id="@+id/Place_change"
android:textStyle="bold"
android:layout_marginTop="10dp"
></EditText>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Time (Hours)"
android:id="@+id/Time"
android:textStyle="bold"
android:layout_marginTop="10dp"
></EditText>
<Button
android:layout_width="match_parent"
android:layout_height="65dp"
android:text="Predict"
android:layout_marginTop="20dp"
android:textStyle="bold"
android:id="@+id/predict"
android:backgroundTint="#5D71C9"></Button>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""
android:textSize="20dp"
android:textAlignment="center"
android:layout_marginTop="25dp"
android:textStyle="bold"
android:id="@+id/result"
android:textColor="@color/cardview_dark_background"></TextView>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="384dp"
android:layout_height="252dp"
android:layout_marginStart="11dp"
android:layout_marginTop="31dp"
android:layout_marginEnd="11dp"
android:background="#E4E4E4"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="248dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView2"
android:layout_width="192dp"
android:layout_height="93dp"
android:layout_weight="1"
app:srcCompat="@drawable/place" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/textView12"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Name"
android:textStyle="bold" />
<TextView
android:id="@+id/lbl_locationCrud_locationName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Location Name" />
<TextView
android:id="@+id/textView11"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Latitude"
android:textStyle="bold" />
<TextView
android:id="@+id/lbl_locationCrud_latitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Location Latitude" />
<TextView
android:id="@+id/textView15"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Longitude"
android:textStyle="bold" />
<TextView
android:id="@+id/lbl_locationCrud_longitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Location Longitude" />
<TextView
android:id="@+id/lbl_locationCrud_elevation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Elevation"
android:textStyle="bold" />
<TextView
android:id="@+id/textView18"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Location Elevation" />
<TextView
android:id="@+id/textView13"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Visited?"
android:textStyle="bold" />
<TextView
android:id="@+id/lbl_locationCrud_isVisited"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Yes" />
<Button
android:id="@+id/btn_locationCrud_deleteLocation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#FF3030"
android:text="Delete" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="388dp"
android:layout_height="40dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="11dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Space
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="37dp"
android:orientation="horizontal">
<TextView
android:id="@+id/mobileUsage_appName_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableStart="@drawable/ic_baseline_apps_24"
android:text="WhatsApp"
android:textColor="#2196F3"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/mobileUsage_appTime_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableStart="@drawable/ic_baseline_access_time_24"
android:text="1234"
android:textStyle="italic" />
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="376dp"
android:layout_height="85dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="19dp"
android:background="#E3E3E3"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView5"
android:layout_width="10dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
app:srcCompat="@drawable/rewards" />
<TextView
android:id="@+id/txt_rewards_rewardName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="You are awesome!"
android:textSize="16sp"
android:textStyle="bold|italic" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_label"
android:icon="@android:drawable/ic_menu_add"
android:title="@string/add_label"
android:visible="true"
app:showAsAction="always" />
</menu>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2023 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
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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<item android:id="@+id/anchor_reset" android:title="Anchor Setting" app:showAsAction="ifRoom"/>
<group android:checkableBehavior="single" >
<item android:id="@+id/geospatial" android:title="Geospatial" app:showAsAction=" never"/>
<item android:id="@+id/terrain" android:title="Terrain" app:showAsAction=" never"/>
<item android:id="@+id/rooftop" android:title="Rooftop" app:showAsAction=" never"/>
</group>
</menu>
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.TheTrek" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="colorTransparent">#00000000</color>
<color name="colorBlue">#33B5E5</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2022 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
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.
-->
<resources>
<dimen name="dialog_btn_padding">8dp</dimen>
</resources>
<resources>
<string name="app_name">The Trek</string>
<string name="button_text_set_anchor" translatable="false">ANCHOR SETTING</string>
<string name="switch_render_streetscaope_geometry" translatable="false">SHOW GEOMETRY</string>
<string name="button_text_clear_anchors" translatable="false">CLEAR ALL ANCHORS</string>
<string name="tap_screen_text" translatable="false">TAP ON SCREEN TO CREATE ANCHOR</string>
<string name="status_unsupported" translatable="false">This device is not supported.</string>
<string name="status_pretracking" translatable="false">Localizing your device to set anchor.</string>
<string name="status_earth_state_error" translatable="false">There was an error state error, check that you have a valid authentication for the ARCore API.</string>
<string name="status_localize_hint" translatable="false">Point your camera at buildings, stores, and signs near you.</string>
<string name="status_localize_timeout" translatable="false">Localization not possible.\nClose and open the app to restart session.</string>
<string name="status_localize_complete" translatable="false">Localization complete.</string>
<string name="status_terrain_anchor" translatable="false">Terrain anchor state: %1$s</string>
<string name="status_rooftop_anchor" translatable="false">Rooftop anchor state: %1$s</string>
<string name="terrain_anchor_no_result_yet" translatable="false">Still resolving the terrain anchor. Please make sure you\'re in an area that has VPS coverage.</string>
<string name="terrain_anchor_resource_exhausted" translatable="false">Too many terrain anchors have already been held. Clear all anchors to create new ones.</string>
<plurals name="status_anchors_set">
<item quantity="one">%d / %d Anchor set</item>
<item quantity="other">%d / %d Anchors set</item>
</plurals>
<plurals name="status_anchors_cleared">
<item quantity="one">%d Anchor cleared</item>
<item quantity="other">%d Anchors cleared</item>
</plurals>
<string name="geospatial_pose_not_tracking" translatable="false">GEOSPATIAL POSE: Not tracking</string>
<string name="geospatial_pose" translatable="false">LAT/LNG: %.6f˚, %.6f˚\n\t\t\tACCURACY: %.2fm\nALTITUDE: %.2fm\n\t\t\tACCURACY: %.2fm\nORIENTATION: [%.1f, %.1f, %.1f, %.1f]\n\t\t\tYAW ACCURACY: %.1f˚</string>
<!-- Strings used in privacy notice dialog messages. [CHAR_LIMIT=NONE] -->
<string name="share_experience_title">AR in the real world</string>
<!-- Privacy notice message. [CHAR_LIMIT=NONE] -->
<string name="share_experience_message">To power this session, Google will process visual data from your camera.</string>
<!-- Privacy notice accept. [CHAR_LIMIT=NONE] -->
<string name="agree_to_share">Get started</string>
<!-- Privacy notice learn more. [CHAR_LIMIT=NONE] -->
<string name="learn_more">Learn more</string>
<!-- Geospatial privacy website. [CHAR_LIMIT=NONE] -->
<string name="learn_more_url" translatable="false">https://developers.google.com/ar/data-privacy</string>
<!-- Strings used in VPS unavailable notice dialog messages. [CHAR_LIMIT=NONE] -->
<string name="vps_unavailable_title">VPS not available</string>
<!-- VPS unavailable notice message. [CHAR_LIMIT=NONE] -->
<string name="vps_unavailable_message">Your current location does not have VPS coverage. Your session will be using your GPS signal only if VPS is not available.</string>
<!-- VPS unavailable notice continue. [CHAR_LIMIT=NONE] -->
<string name="continue_button">Continue</string>
<string name="google_maps_key">AIzaSyBKMivuX-s-BvbGQOxiFbo7Q0_ZyrjePdo</string>
<string name="record">Record</string>
<string name="recording">Recording</string>
<string name="accelerometer">Accelerometer</string>
<string name="gyro">Gyroscope</string>
<string name="linear_acc">Linear Acceleration</string>
<string name="status_stopped">Hit record button to start</string>
<string name="refresh_rate">Refresh rate</string>
<string name="speed_normal">Normal</string>
<string name="speed_fast">Fast</string>
<string name="speed_fastest">Fastest</string>
<string name="rw_permission_string">Kindly allow read/write permission to save recorded dataset into external storage</string>
<string name="recording_stopped">Recording Stopped</string>
<string name="add_label">Add Label</string>
<string name="activity">Activity</string>
<string name="probability">Probability</string>
<string name="downstairs">Downstairs</string>
<string name="jogging">Jogging</string>
<string name="sitting">Sitting</string>
<string name="standing">Standing</string>
<string name="upstairs">Upstairs</string>
<string name="walking">Walking</string>
<string name="biking">Biking</string>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2022 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
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.
-->
<resources>
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
<style name="AlertDialogCustom" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:gravity">center</item>
<item name="android:windowBackground">@drawable/dialog_bg</item>
<item name="android:buttonBarNegativeButtonStyle">@style/NegativeButtonStyle</item>
<item name="android:buttonBarPositiveButtonStyle">@style/PositiveButtonStyle</item>
</style>
<style name="NegativeButtonStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
<item name="android:textColor">@android:color/darker_gray</item>
<item name="android:paddingLeft">@dimen/dialog_btn_padding</item>
<item name="android:paddingRight">@dimen/dialog_btn_padding</item>
</style>
<style name="PositiveButtonStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
<item name="android:textColor">@android:color/white</item>
<item name="android:background">@drawable/button_bg</item>
<item name="android:paddingLeft">@dimen/dialog_btn_padding</item>
<item name="android:paddingRight">@dimen/dialog_btn_padding</item>
</style>
<!-- Base application theme. -->
<style name="AppTheme1" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.TheTrek" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>
\ No newline at end of file
package com.example.thetrek;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' apply false
}
\ No newline at end of file
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
\ No newline at end of file
#Tue May 30 23:03:01 IST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# 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.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
rootProject.name = "The Trek"
include ':app'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment