diff --git a/app/build.gradle b/app/build.gradle index 0fd069c..2982665 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { // 24 is the minimum since ARCore only works with 24 and higher. minSdkVersion 24 targetSdkVersion 28 - versionCode 3 - versionName '0.2 - prototype' + versionCode 5 + versionName '0.4 - prototype' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' @@ -75,15 +75,15 @@ dependencies { implementation 'androidx.cardview:cardview:1.0.0' implementation "androidx.constraintlayout:constraintlayout:2.0.0-alpha3" // implementation "androidx.core:core-ktx:1.0.1" - implementation 'androidx.fragment:fragment:1.1.0-alpha03' + implementation 'androidx.fragment:fragment:1.1.0-alpha04' implementation "androidx.lifecycle:lifecycle-common-java8:$androidx_lifecycle" implementation "androidx.lifecycle:lifecycle-extensions:$androidx_lifecycle" implementation "androidx.lifecycle:lifecycle-reactivestreams:$androidx_lifecycle" kapt "androidx.lifecycle:lifecycle-compiler:$androidx_lifecycle" - implementation 'androidx.preference:preference:1.1.0-alpha02' - implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha01' + implementation 'androidx.preference:preference:1.1.0-alpha03' + implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha02' implementation "androidx.room:room-runtime:$androidx_room" implementation "androidx.room:room-migration:$androidx_room" @@ -95,12 +95,12 @@ dependencies { implementation 'com.android.volley:volley:1.1.1' - implementation('com.github.bumptech.glide:glide:4.8.0') { + implementation('com.github.bumptech.glide:glide:4.9.0') { exclude group: 'com.android.support' } - implementation 'com.google.android.material:material:1.1.0-alpha02' -// implementation 'com.google.android.play:core:1.3.6' + implementation 'com.google.android.material:material:1.1.0-alpha03' + implementation 'com.google.android.play:core:1.3.7' implementation "com.google.ar.sceneform:core:$sceneform_version" implementation "com.google.ar.sceneform.ux:sceneform-ux:$sceneform_version" @@ -109,27 +109,27 @@ dependencies { exclude group: 'com.github.bumptech.glide' } - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1' implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version" implementation "org.jetbrains.anko:anko-commons:0.10.8" - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.21" implementation "uk.co.samuelwall:material-tap-target-prompt:2.14.0" - testImplementation 'junit:junit:4.13-beta-1' + testImplementation 'junit:junit:4.13-beta-2' testImplementation 'org.hamcrest:hamcrest-all:1.3' - testImplementation 'io.mockk:mockk:1.9.kotlin12' + testImplementation 'io.mockk:mockk:1.9.1.kotlin12' - androidTestImplementation "androidx.test:core:1.1.0" - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test:rules:1.1.1' - androidTestUtil 'androidx.test:orchestrator:1.1.1' + androidTestImplementation 'androidx.test:core:1.1.1-alpha01' + androidTestImplementation 'androidx.test:runner:1.1.2-alpha01' + androidTestImplementation 'androidx.test:rules:1.1.2-alpha01' + androidTestUtil 'androidx.test:orchestrator:1.1.2-alpha01' - androidTestImplementation "androidx.test.ext:junit:1.1.0" - androidTestImplementation 'androidx.annotation:annotation:1.0.1' + androidTestImplementation 'androidx.test.ext:junit:1.1.1-alpha01' + androidTestImplementation 'androidx.annotation:annotation:1.1.0-alpha01' androidTestImplementation 'androidx.arch.core:core-testing:2.0.0' androidTestImplementation "androidx.room:room-testing:$androidx_room" diff --git a/app/src/main/assets/locations.json b/app/src/main/assets/locations.json index 54da2ef..aa8b3c1 100644 --- a/app/src/main/assets/locations.json +++ b/app/src/main/assets/locations.json @@ -10,45 +10,63 @@ }, { "name": "Iwami Ginzan", - "description": "World Heritage silver mine area", + "description": "The Iwami Ginzan (石見銀山) was an underground silver mine in the city of Ōda, in Shimane Prefecture on the main island of Honshu, Japan.[1] It was the largest silver mine in Japanese history. It was active for almost four hundred years, from its discovery in 1526 to its closing in 1923.", "module_id": "location_iwamiginzan", - "content_size": "1234MB", + "content_size": "234 MB", "thumb_path": "/module_asset/iwami_ginzan/iwamiginzanthumb.webp", "intro_html_path": "/module_asset/iwami_ginzan/site/iwamiginzan.html", "is_active": true }, { "name": "Izumo Taisha", - "description": "Japanese Grand Shrine", + "description": "Izumo-taisha (出雲大社, Izumo Grand Shrine), officially Izumo Ōyashiro, is one of the most ancient and important Shinto shrines in Japan. No record gives the date of establishment. Located in Izumo, Shimane Prefecture, it is believed by many to be the oldest Shinto shrine in Japan, even predating the Ise Grand Shrine.", "module_id": "location_izumotaisha", - "content_size": "1234MB", + "content_size": "234 MB", "thumb_path": "/module_asset/izumo_taisha/images/izumotaishathumb.webp", "intro_html_path": "/module_asset/izumo_taisha/izumotaisha.html", "is_active": true }, { "name": "Nara", - "description": "", + "description": "Nara (奈良市 Nara-shi, Japanese: [naꜜɾa]) is the capital city of Nara Prefecture located in the Kansai region of Japan. The city occupies the northern part of Nara Prefecture, bordering Kyoto Prefecture. During 710 CE - 784 CE, Nara was the capital of Japan, and the Emperor (天皇) lived there before moving the capital to Kyoto.", "module_id": "location_nara", - "content_size": "1234MB", + "content_size": "234 MB", "thumb_path": "", "intro_html_path": "", "is_active": false }, { "name": "Hiroshima", - "description": "", + "description": "Hiroshima (広島市 Hiroshima-shi, Japanese: [çiɾoɕima]) is the capital of Hiroshima Prefecture and the largest city in the Chūgoku region of western Honshu, the largest island of Japan. On April 1, 1980, Hiroshima became a designated city. Hiroshima was the first city targeted by a nuclear weapon, when the United States Army Air Forces (USAAF) dropped an atomic bomb on the city at 8:15 a.m. on August 6, 1945, near the end of World War II.", "module_id": "location_hiroshima", - "content_size": "1234MB", + "content_size": "234 MB", "thumb_path": "", "intro_html_path": "", "is_active": false }, { - "name": "Niigata", - "description": "", + "name": "Sado Island", + "description": "Sado (佐渡市 Sado-shi) is a city located on Sado Island (佐渡ヶ島 Sadogashima) in Niigata Prefecture, Japan. Sado experienced a sudden economic boom during the Edo period when gold was found in 1601 at Aikawa (相川). A major source of revenue for the Tokugawa shogunate.", "module_id": "location_niigata", - "content_size": "1234MB", + "content_size": "234 MB", + "thumb_path": "", + "intro_html_path": "", + "is_active": false + }, + { + "name": "Germany", + "description": "", + "module_id": "", + "is_title": true, + "thumb_path": "", + "intro_html_path": "", + "is_active": false + }, + { + "name": "Kloster Hirsau", + "description": "Hirsau Abbey, formerly known as Hirschau Abbey, was once one of the most important Benedictine abbeys of Germany. It is located in the Hirsau borough of Calw on the northern slopes of the Black Forest mountain range. In the 11th and 12th century, the monastery was a centre of the Cluniac Reforms, implemented as \"Hirsau\" Reforms in the German lands. The complex was devastated during the War of the Palatine Succession in 1692 and not rebuilt.", + "module_id": "location_kloster_hirsau", + "content_size": "234 MB", "thumb_path": "", "intro_html_path": "", "is_active": false @@ -64,9 +82,9 @@ }, { "name": "Machu Picchu", - "description": "", + "description": "Machu Picchu is a 15th-century Inca citadel, located in the Eastern Cordillera of southern Peru, on a mountain ridge 2,430 metres above sea level. Most archaeologists believe that Machu Picchu was constructed as an estate for the Inca emperor Pachacuti (1438–1472).", "module_id": "location_machupicchu", - "content_size": "1234MB", + "content_size": "234 MB", "thumb_path": "", "intro_html_path": "", "is_active": false diff --git a/app/src/main/java/eu/michaelvogt/ar/author/data/Location.kt b/app/src/main/java/eu/michaelvogt/ar/author/data/Location.kt index b882fb0..cf6ad69 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/data/Location.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/data/Location.kt @@ -99,7 +99,7 @@ class Location( companion object { fun getDefaultLocation(): Location { return Location( - "My location", + "Current location", "Your current location", "", "/module_asset/location/images/mylocationthumb.webp", diff --git a/app/src/main/java/eu/michaelvogt/ar/author/data/utils/Json.kt b/app/src/main/java/eu/michaelvogt/ar/author/data/utils/Json.kt new file mode 100644 index 0000000..60187e1 --- /dev/null +++ b/app/src/main/java/eu/michaelvogt/ar/author/data/utils/Json.kt @@ -0,0 +1,101 @@ +/* + ARTester - AR for tourists by tourists + Copyright (C) 2018, 2019 Michael Vogt + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package eu.michaelvogt.ar.author.data.utils + +import android.content.Context +import android.util.Log +import com.google.ar.sceneform.math.Vector3 +import eu.michaelvogt.ar.author.data.* +import org.json.JSONObject + +class Json { + companion object { + private val TAG = Json::class.java.simpleName + + fun importLocation(context: Context?, viewModel: AuthorViewModel, location: Location, onFinished: () -> Unit) { + val assetManager = context?.assets + + if (assetManager != null) { + val moduleId = location.moduleId.also { + when (it) { + null, "" -> { + Log.e(TAG, "Module ID of location ${location.name} is missing. Required to access content") + } + } + } + + val reader = assetManager.open("$moduleId.json").bufferedReader() + val contentString = reader.readText() + reader.close() + + if (location.isLoaded == false) { + // Insert content + val locationObject = JSONObject(contentString).getJSONObject("location") + val groupArray = locationObject.getJSONArray("groups") + + for (groupIndex in 0 until groupArray.length()) { + val groupObject = groupArray.getJSONObject(groupIndex) + + viewModel.insertTitleGroup(TitleGroup(groupObject.getString("name"))).thenAccept { groupId -> + val markersArray = groupObject.getJSONArray("markers") + + for (markerIndex in 0 until markersArray.length()) { + val markerObject = markersArray.getJSONObject(markerIndex) + + // TODO: Load complete Marker into DB + viewModel.insertMarker(Marker(location.uId, groupId, + markerObject.getString("title"), + zeroPoint = Converters().vector3FromString(markerObject.getString("zero_point")) + ?: Vector3.zero()) + ).thenAccept { markerId -> + val areasArray = markerObject.getJSONArray("areas") + + for (areaIndex in 0 until areasArray.length()) { + val areaObject = areasArray.getJSONObject(areaIndex) + + // TODO: Load complete Area into DB + viewModel.insertArea(Area(areaObject.getString("title"))).thenAccept { areaId -> + viewModel.insertMarkerArea(MarkerArea(markerId, areaId)) + }.exceptionally { + Log.e(TAG, "Unable to insert area.", it) + null + } + } + }.exceptionally { + Log.e(TAG, "Unable to insert marker.", it) + null + } + } + }.exceptionally { + Log.e(TAG, "Unable to insert group", it) + null + } + } + + location.isLoaded = true + viewModel.updateLocation(location).thenAccept { + onFinished() + } + } else { + // TODO: Update content + } + } + } + } +} diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/AppFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/AppFragment.kt index b53edd9..5f0fec2 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/AppFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/AppFragment.kt @@ -32,8 +32,9 @@ open class AppFragment : Fragment() { viewModel = ViewModelProviders.of(activity!!).get(AuthorViewModel::class.java) } - fun setupFab(@DrawableRes iconRes: Int, listener: View.OnClickListener) { - if (!Preferences.getPreference(context, R.string.allow_edit_pref, false)) { + fun setupFab(@DrawableRes iconRes: Int, visibility: FabVisibility, listener: View.OnClickListener) { + if (visibility != FabVisibility.ALWAYS + && !Preferences.getPreference(context, R.string.allow_edit_pref, false)) { hideFab() return } @@ -72,4 +73,10 @@ open class AppFragment : Fragment() { fun hideBottomBar() { activity!!.bottom_nav.visibility = View.GONE } + + companion object { + enum class FabVisibility { + ALWAYS, EDITING + } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/AreaEditFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/AreaEditFragment.kt index 50bbb64..0232a9a 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/AreaEditFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/AreaEditFragment.kt @@ -88,7 +88,8 @@ class AreaEditFragment : AppFragment(), AreaCardEditHandler { fun onResume() { super.onResume() - setupFab(android.R.drawable.ic_menu_save, View.OnClickListener { + setupFab(android.R.drawable.ic_menu_save, + AppFragment.Companion.FabVisibility.EDITING, View.OnClickListener { // TODO: Save complete AreaVisual if (areaVisual.area.uId != 0L) { viewModel.updateArea(areaVisual.area).thenAccept { diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/CropFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/CropFragment.kt index 5f5671f..83f14de 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/CropFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/CropFragment.kt @@ -49,7 +49,7 @@ class CropFragment : AppFragment() { fun onResume() { super.onResume() - setupFab(android.R.drawable.ic_menu_save, View.OnClickListener { + setupFab(android.R.drawable.ic_menu_save, Companion.FabVisibility.EDITING, View.OnClickListener { FileUtils.saveImageToExternalStorage(crop_view.crop()!!, imagePath) navController.popBackStack() }) diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/ImagePreviewFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/ImagePreviewFragment.kt index 7d880fc..a3bd88e 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/ImagePreviewFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/ImagePreviewFragment.kt @@ -71,10 +71,11 @@ class ImagePreviewFragment : PreviewFragment() { arFragment.onUpdate(frameTime) val frame = arFragment.arSceneView.arFrame - val updatedAugmentedImages = frame.getUpdatedTrackables(AugmentedImage::class.java) + val updatedAugmentedImages = frame?.getUpdatedTrackables(AugmentedImage::class.java) + ?: emptyList() for (image in updatedAugmentedImages) { - val trackingState = arFragment.arSceneView.arFrame.camera.trackingState + val trackingState = arFragment.arSceneView.arFrame?.camera?.trackingState if (trackingState == TrackingState.TRACKING && !handledImages.contains(image.name)) { handledImages.plus(image.name) val anchor: Anchor? diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationEditFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationEditFragment.kt index bcba945..502172d 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationEditFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationEditFragment.kt @@ -99,7 +99,7 @@ class LocationEditFragment : AppFragment() { override fun onResume() { super.onResume() - setupFab(android.R.drawable.ic_menu_save, fabListener) + setupFab(android.R.drawable.ic_menu_save, AppFragment.Companion.FabVisibility.EDITING, fabListener) setupBottomNav(R.menu.actionbar_locationedit_menu, Toolbar.OnMenuItemClickListener { when (it.itemId) { R.id.actionbar_location_delete -> { diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationSearchFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationSearchFragment.kt index d5faef7..05a93be 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationSearchFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationSearchFragment.kt @@ -54,7 +54,7 @@ class LocationSearchFragment : AppFragment(), CardLinkListener { fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binder.locationList.setHasFixedSize(true) + binder.locationList.setHasFixedSize(false) adapter = LocationSearchAdapter(context, this) binder.locationList.adapter = adapter @@ -84,7 +84,7 @@ class LocationSearchFragment : AppFragment(), CardLinkListener { override fun onResume() { super.onResume() - setupFab(android.R.drawable.ic_input_add, View.OnClickListener { + setupFab(android.R.drawable.ic_input_add, AppFragment.Companion.FabVisibility.EDITING, View.OnClickListener { navController.navigate(LocationSearchFragmentDirections.actionToLocationEdit()) }) diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationlistFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationlistFragment.kt index ff48b46..8143b4d 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationlistFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/LocationlistFragment.kt @@ -27,15 +27,14 @@ import androidx.appcompat.widget.Toolbar import androidx.lifecycle.Observer import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.NavigationUI -import com.google.ar.sceneform.math.Vector3 import eu.michaelvogt.ar.author.R import eu.michaelvogt.ar.author.data.* -import eu.michaelvogt.ar.author.data.utils.Converters +import eu.michaelvogt.ar.author.data.utils.Json import eu.michaelvogt.ar.author.databinding.FragmentLocationlistBinding import eu.michaelvogt.ar.author.fragments.adapters.LocationListAdapter +import eu.michaelvogt.ar.author.modules.Module import eu.michaelvogt.ar.author.utils.* import kotlinx.android.synthetic.main.fragment_locationlist.* -import org.json.JSONObject /** * View to list the [Location]s provided by the view model. @@ -43,7 +42,7 @@ import org.json.JSONObject * Tab on a list item navigates to the web view, which loads the content of the URL stored in * [Location.introHtmlPath]. */ -class LocationlistFragment : AppFragment(), CardMenuListener { +class LocationlistFragment : AppFragment(), CardEventListener { private lateinit var adapter: LocationListAdapter private lateinit var binder: FragmentLocationlistBinding @@ -62,6 +61,7 @@ class LocationlistFragment : AppFragment(), CardMenuListener { adapter = LocationListAdapter(context, this) binder.locationList.adapter = adapter + binder.mylocation.handler = adapter val appBarConfiguration = AppBarConfiguration.Builder(R.id.location_list_fragment).build() NavigationUI.setupWithNavController(top_toolbar, navController, appBarConfiguration) @@ -75,7 +75,7 @@ class LocationlistFragment : AppFragment(), CardMenuListener { override fun onResume() { super.onResume() - setupFab(R.drawable.ic_search_black_24dp, View.OnClickListener { + setupFab(R.drawable.ic_search_black_24dp, AppFragment.Companion.FabVisibility.ALWAYS, View.OnClickListener { viewModel.currentLocationId = NEW_CURRENT_LOCATION navController.navigate(LocationlistFragmentDirections.actionToLocationSearch()) }) @@ -102,112 +102,9 @@ class LocationlistFragment : AppFragment(), CardMenuListener { override fun onDownloadClicked(location: Location) { -/* - // Can't be used currently, because of an incompatibility of play.core and sceneform - // https://github.com/google-ar/sceneform-android-sdk/issues/507 - - val splitManager = SplitInstallManagerFactory.create(context) - val splitRequest = SplitInstallRequest.newBuilder().addModule(moduleId).build() - - splitManager.startInstall(splitRequest) - .addOnSuccessListener { - println("Module isLoaded. Session: $it") - } - .addOnFailureListener { - println("LocationListFragment error: $it") - } - - splitManager.registerListener { state -> - state.moduleNames().forEach { - when (state.status()) { - SplitInstallSessionStatus.PENDING -> println("pending") - SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> { - println("confirmation") - activity?.startIntentSender( - state.resolutionIntent().getIntentSender(), - null, 0, 0, 0) - } - SplitInstallSessionStatus.DOWNLOADING -> println("downloading") - SplitInstallSessionStatus.INSTALLING -> println("installing") - SplitInstallSessionStatus.INSTALLED -> println("installed") - SplitInstallSessionStatus.FAILED -> println("failed") - SplitInstallSessionStatus.CANCELING -> println("canceling") - SplitInstallSessionStatus.CANCELED -> println("canceled") - } - } - } -*/ - // Module is already isLoaded and active until Sceneform bug is fixed - - // import module json file - // save in database - // reload location list - - // Todo: Refactor - - val assetManager = activity?.assets - - if (assetManager != null) { - val moduleId = location.moduleId.also { - when (it) { - null, "" -> { - Log.e(TAG, "Module ID of location ${location.name} is missing. Required to access content") - } - } - } - - val reader = assetManager.open("$moduleId.json").bufferedReader() - val contentString = reader.readText() - reader.close() - - if (location.isLoaded != null && location.isLoaded == false) { - // Insert content - val locationObject = JSONObject(contentString).getJSONObject("location") - val groupArray = locationObject.getJSONArray("groups") - - for (groupIndex in 0 until groupArray.length()) { - val groupObject = groupArray.getJSONObject(groupIndex) - - viewModel.insertTitleGroup(TitleGroup(groupObject.getString("name"))).thenAccept { groupId -> - val markersArray = groupObject.getJSONArray("markers") - - for (markerIndex in 0 until markersArray.length()) { - val markerObject = markersArray.getJSONObject(markerIndex) - - viewModel.insertMarker(Marker(location.uId, groupId, - markerObject.getString("title"), - zeroPoint = Converters().vector3FromString(markerObject.getString("zero_point")) - ?: Vector3.zero()) - ).thenAccept { markerId -> - val areasArray = markerObject.getJSONArray("areas") - - for (areaIndex in 0 until areasArray.length()) { - val areaObject = areasArray.getJSONObject(areaIndex) - - viewModel.insertArea(Area(areaObject.getString("title"))).thenAccept { areaId -> - viewModel.insertMarkerArea(MarkerArea(markerId, areaId)) - }.exceptionally { - Log.e(TAG, "Unable to insert area.", it) - null - } - } - }.exceptionally { - Log.e(TAG, "Unable to insert marker.", it) - null - } - } - }.exceptionally { - Log.e(TAG, "Unable to insert group", it) - null - } - } - - location.isLoaded = true - viewModel.updateLocation(location).thenAccept { - setLocations() - } - } else { - // TODO: Update content + Module.load(context, location.moduleId) { + Json.importLocation(context, viewModel, location) { + setLocations() } } } diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/MarkerEditFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/MarkerEditFragment.kt index 37b5abf..33e6cf4 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/MarkerEditFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/MarkerEditFragment.kt @@ -83,7 +83,7 @@ class MarkerEditFragment : AppFragment() { override fun onResume() { super.onResume() - setupFab(android.R.drawable.ic_menu_save, View.OnClickListener { + setupFab(android.R.drawable.ic_menu_save, AppFragment.Companion.FabVisibility.EDITING, View.OnClickListener { // TODO: Decide on correct validation strategy if (editMarker.locationId <= 0) { Snackbar.make(view!!, diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/MarkerListFragment.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/MarkerListFragment.kt index 5ae61ce..69133b3 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/MarkerListFragment.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/MarkerListFragment.kt @@ -92,7 +92,7 @@ class MarkerListFragment : AppFragment(), ItemClickListener { } }) - setupFab(android.R.drawable.ic_input_add, View.OnClickListener { + setupFab(android.R.drawable.ic_input_add, AppFragment.Companion.FabVisibility.EDITING, View.OnClickListener { viewModel.currentMarkerId = NEW_CURRENT_MARKER navController.navigate(MarkerListFragmentDirections.actionEditMarker()) }) diff --git a/app/src/main/java/eu/michaelvogt/ar/author/fragments/adapters/LocationListAdapter.kt b/app/src/main/java/eu/michaelvogt/ar/author/fragments/adapters/LocationListAdapter.kt index 5a36349..a329b2b 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/fragments/adapters/LocationListAdapter.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/fragments/adapters/LocationListAdapter.kt @@ -26,7 +26,7 @@ import androidx.recyclerview.widget.RecyclerView import eu.michaelvogt.ar.author.R import eu.michaelvogt.ar.author.data.Location import eu.michaelvogt.ar.author.databinding.CardLocationBinding -import eu.michaelvogt.ar.author.utils.CardMenuListener +import eu.michaelvogt.ar.author.utils.CardEventListener import eu.michaelvogt.ar.author.utils.Preferences /** @@ -34,7 +34,7 @@ import eu.michaelvogt.ar.author.utils.Preferences */ class LocationListAdapter( private val context: Context?, - private val listener: CardMenuListener) + private val listener: CardEventListener) : RecyclerView.Adapter() { private var locations: List = emptyList() diff --git a/app/src/main/java/eu/michaelvogt/ar/author/modules/Module.kt b/app/src/main/java/eu/michaelvogt/ar/author/modules/Module.kt new file mode 100644 index 0000000..383749a --- /dev/null +++ b/app/src/main/java/eu/michaelvogt/ar/author/modules/Module.kt @@ -0,0 +1,65 @@ +/* + ARTester - AR for tourists by tourists + Copyright (C) 2018, 2019 Michael Vogt + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package eu.michaelvogt.ar.author.modules + +import android.content.Context + +class Module { + companion object { + fun load(context: Context?, moduleId: String?, callback: () -> Unit) { + // Can't be used currently, because of an incompatibility of play.core and sceneform + // https://github.com/google-ar/sceneform-android-sdk/issues/507 -- + // TODO: Bug was just fixed, change for next release + callback() + +/* + val splitManager = SplitInstallManagerFactory.create(context) + val splitRequest = SplitInstallRequest.newBuilder().addModule(moduleId).build() + + splitManager.startInstall(splitRequest) + .addOnSuccessListener { + println("Module isLoaded. Session: $it") + } + .addOnFailureListener { + println("LocationListFragment error: $it") + } + + splitManager.registerListener { state -> + state.moduleNames().forEach { + when (state.status()) { + SplitInstallSessionStatus.PENDING -> println("pending") + SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> { + println("confirmation") + context.startIntentSender( + state.resolutionIntent().getIntentSender(), + null, 0, 0, 0) + } + SplitInstallSessionStatus.DOWNLOADING -> println("downloading") + SplitInstallSessionStatus.INSTALLING -> println("installing") + SplitInstallSessionStatus.INSTALLED -> println("installed") + SplitInstallSessionStatus.FAILED -> println("failed") + SplitInstallSessionStatus.CANCELING -> println("canceling") + SplitInstallSessionStatus.CANCELED -> println("canceled") + } + } + } +*/ + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/michaelvogt/ar/author/nodes/AreaNodeBuilder.kt b/app/src/main/java/eu/michaelvogt/ar/author/nodes/AreaNodeBuilder.kt index fd5b786..6f8ab80 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/nodes/AreaNodeBuilder.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/nodes/AreaNodeBuilder.kt @@ -34,7 +34,7 @@ import java.util.concurrent.CompletionStage class AreaNodeBuilder private constructor(private val context: Context, private val areaVisual: AreaVisual) { private var scene: Scene? = null - fun setScene(scene: Scene): AreaNodeBuilder { + fun setScene(scene: Scene?): AreaNodeBuilder { this.scene = scene return this } diff --git a/app/src/main/java/eu/michaelvogt/ar/author/utils/AppWebViewJs.kt b/app/src/main/java/eu/michaelvogt/ar/author/utils/AppWebViewJs.kt index 997d4a4..2991d69 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/utils/AppWebViewJs.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/utils/AppWebViewJs.kt @@ -39,9 +39,11 @@ class AppWebViewJs( fun openArView() { val importMarkersPref = Preferences.getPreference(activity, R.string.import_marker_images_pref, false) - if (importMarkersPref && viewModel.markersCache.isNotEmpty()) - navController.navigate(R.id.image_preview_fragment) - else - navController.navigate(R.id.touch_preview_fragment) + activity?.runOnUiThread { + if (importMarkersPref && viewModel.markersCache.isNotEmpty()) + navController.navigate(R.id.image_preview_fragment) + else + navController.navigate(R.id.touch_preview_fragment) + } } } diff --git a/app/src/main/java/eu/michaelvogt/ar/author/utils/Listeners.kt b/app/src/main/java/eu/michaelvogt/ar/author/utils/Listeners.kt index 4933a4b..73daabd 100644 --- a/app/src/main/java/eu/michaelvogt/ar/author/utils/Listeners.kt +++ b/app/src/main/java/eu/michaelvogt/ar/author/utils/Listeners.kt @@ -26,7 +26,7 @@ interface CardLinkListener { fun onTextClicked(searchLocation: SearchLocation) } -interface CardMenuListener { +interface CardEventListener { fun onMenuClick(view: View, location: Location) fun onItemClicked(uId: Long) fun onDownloadClicked(location: Location) diff --git a/app/src/main/res/layout/card_location.xml b/app/src/main/res/layout/card_location.xml index 93062b7..4773590 100644 --- a/app/src/main/res/layout/card_location.xml +++ b/app/src/main/res/layout/card_location.xml @@ -100,17 +100,47 @@ + + + + diff --git a/app/src/main/res/layout/card_location_search.xml b/app/src/main/res/layout/card_location_search.xml index 96e8f98..813195c 100644 --- a/app/src/main/res/layout/card_location_search.xml +++ b/app/src/main/res/layout/card_location_search.xml @@ -33,38 +33,58 @@ type="eu.michaelvogt.ar.author.data.tuples.SearchLocation" /> - + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + app:cardCornerRadius="2dp" + app:cardElevation="2dp"> - + android:layout_height="wrap_content"> - + - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4fd8941..1f9d3f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -137,11 +137,13 @@ More or less just an organisational structure to combine several AR scenes together for easy access. This can be a statue in your neighbourhood or a tour through a historical town.\n\n - You can create your own location through the + button below. + You can import additional locations through the search button below. Search Location - location search info - Button to open info about locations + Here you can select a location to import into the + location list. From there you can download the related media files needed for this location. + \n\nThe list loads kinda slow right now. Will fix with an upcoming version. + related location already imported /module_asset/location/images/placeholderthumb.webp diff --git a/build.gradle b/build.gradle index cd6d9c9..018c490 100644 --- a/build.gradle +++ b/build.gradle @@ -18,16 +18,16 @@ buildscript { ext.kotlin_version = '1.3.20' - ext.serialization_version = '0.9.1' + ext.serialization_version = '0.10.0' - ext.androidx_lifecycle = '2.1.0-alpha01' - ext.androidx_navigation = '1.0.0-alpha11' + ext.androidx_lifecycle = '2.1.0-alpha02' + ext.androidx_navigation = '1.0.0-beta02' ext.androidx_room = '2.1.0-alpha04' ext.google_support_version = '28.0.0' ext.google_lifecycle_version = '1.1.1' - ext.sceneform_version = '1.6.0' + ext.sceneform_version = '1.7.0' repositories { google() diff --git a/location_iwamiginzan/src/main/AndroidManifest.xml b/location_iwamiginzan/src/main/AndroidManifest.xml index a4246dc..4d386e4 100644 --- a/location_iwamiginzan/src/main/AndroidManifest.xml +++ b/location_iwamiginzan/src/main/AndroidManifest.xml @@ -4,7 +4,6 @@ package="eu.michaelvogt.locations.iwamiginzan"> diff --git a/location_izumotaisha/src/main/AndroidManifest.xml b/location_izumotaisha/src/main/AndroidManifest.xml index 5ba20cd..28b6641 100644 --- a/location_izumotaisha/src/main/AndroidManifest.xml +++ b/location_izumotaisha/src/main/AndroidManifest.xml @@ -4,7 +4,6 @@ package="eu.michaelvogt.locations.izumotaisha">