Iam and AR android app. I can load my .sfb files from asset. i want to load from direct server in order to secure my assets. Its loading from asset folder. not from direct server. iam using below code pls help me to solve this.
package com.example.a320_ar
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import com.google.ar.core.Anchor
import com.google.ar.core.Plane
import com.google.ar.sceneform.AnchorNode
import com.google.ar.sceneform.HitTestResult
import com.google.ar.sceneform.SkeletonNode
import com.google.ar.sceneform.animation.ModelAnimator
import com.google.ar.sceneform.rendering.ModelRenderable
import com.google.ar.sceneform.ux.ArFragment
import com.google.ar.sceneform.ux.TransformableNode
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
class MainActivity : AppCompatActivity() {
lateinit var arFragment: ArFragment
private lateinit var model: Uri
private var rendarable: ModelRenderable?=null
private var animator: ModelAnimator? = null
//private var modellink:String = "A320_Anim.sfb"
private var modellink:String = "http://10.0.0.193:90/fbx/A320_Anim.sfb"
#RequiresApi(Build.VERSION_CODES.N)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
arFragment = sceneform_fragment as ArFragment
arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->
if (plane.type != Plane.Type.HORIZONTAL_UPWARD_FACING) {
return#setOnTapArPlaneListener
}
var anchor = hitResult.createAnchor()
btnStart.setOnClickListener {
placeObject(
arFragment,
anchor,
Uri.parse(modellink)
)
}
}
}
#RequiresApi(Build.VERSION_CODES.N)
private fun animateModel(name: String) {
animator?.let { it->
if(it.isRunning){
it.end()
}
}
rendarable?.let { modelRenderable ->
val data = modelRenderable.getAnimationData(name)
animator = ModelAnimator(data,modelRenderable)
animator?.start()
}
}
#RequiresApi(Build.VERSION_CODES.N)
private fun placeObject(arFragment: ArFragment, anchor: Anchor?, model: Uri?) {
ModelRenderable.builder()
.setSource(arFragment.context,model)
.build()
.thenAccept{
rendarable = it
addtoScene(arFragment, anchor, it)
}
.exceptionally {
val builder = AlertDialog.Builder(this)
builder.setMessage( it.message).setTitle("Error")
val dialog = builder.create()
dialog.show()
return#exceptionally null
}
}
private fun addtoScene(arFragment: ArFragment, anchor: Anchor?, it: ModelRenderable?) {
val anchorNode = AnchorNode(anchor)
val skeletonNode = SkeletonNode()
skeletonNode.renderable = rendarable
Toast.makeText(this,"inside add scene",Toast.LENGTH_SHORT).show()
val node = TransformableNode(arFragment.transformationSystem)
node.addChild(skeletonNode)
node.setParent(anchorNode)
node.setOnTapListener { v: HitTestResult?, event: MotionEvent? ->
//msgText.text = "Tapped me...$anchorNode --- $anchor --- $skeletonNode"
// var bt = findViewById<Button>(R.id.btnDel)
//bt.visibility = View.VISIBLE
//removeAnchorNode(anchorNode)
//bt.setOnClickListener { removeAnchorNode(anchorNode) }
}
arFragment.arSceneView.scene.addChild(anchorNode)
}
}
its working fine no errors. but its not showing my object on fragment.
private var modellink:String = "http://10.0.0.193:90/fbx/A320_Anim.sfb" (not loading .sfb)
private var modellink:String = "A320_Anim.sfb" (Loading the .sfb- woring fine)
please help me to load the model directly from server. i used all the permissions correctly.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
Thanks in advance,
Syed Abdul Rahim
Are you still using the old 1.6 version of Sceneform? You could try the new maintained version and also discard the sfb format and switch to glTF. The maintained version is up to date related to android dependencies and ARCore/Filament.
Well to your second question, if you want to secure your assets you have to serve it from a password protected API-Endpoint, but you have to host your own server, maybe some simpler solutions exists. Strings or text files can be directly secured with on board libraries (https://developer.android.com/guide/topics/security/cryptography)
Related
when i build smaple project , app has been crashed
when i see logcat , i see this:
90-30676/io.agora.agora_android_uikit E/agora.io: jni_generator_helper.h: (line 131): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
what do i do ?
My code:
package io.agora.agora_android_uikit
import android.Manifest
import android.graphics.Color
import android.os.Bundle
import android.view.ViewGroup
import android.widget.Button
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import io.agora.agorauikit_android.AgoraButton
import io.agora.agorauikit_android.AgoraConnectionData
import io.agora.agorauikit_android.AgoraSettings
import io.agora.agorauikit_android.AgoraVideoViewer
import io.agora.agorauikit_android.requestPermission
import io.agora.rtc2.Constants
private const val PERMISSION_REQ_ID = 22
private val REQUESTED_PERMISSIONS = arrayOf<String>(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
#ExperimentalUnsignedTypes
class MainActivity : AppCompatActivity() {
var agView: AgoraVideoViewer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
try {
agView = AgoraVideoViewer(
this, AgoraConnectionData("*******"),
)
} catch (e: Exception) {
println("Could not initialise AgoraVideoViewer. Check your App ID is valid. ${e.message}")
return
}
val set = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
this.addContentView(agView, set)
if (AgoraVideoViewer.requestPermission(this)) {
agView!!.join("test", role = Constants.CLIENT_ROLE_BROADCASTER)
} else {
val joinButton = Button(this)
// this#MainActivity.runOnUiThread(java.lang.Runnable {
runOnUiThread {
Runnable { joinButton.text = "Allow Camera and Microphone, then click here" }
}
// })
joinButton.setOnClickListener {
if (AgoraVideoViewer.requestPermission(this)) {
// (joinButton.parent as ViewGroup).removeView(joinButton)
agView!!.join("test", role = Constants.CLIENT_ROLE_BROADCASTER)
}
}
// joinButton.setBackgroundColor(Color.GREEN)
// joinButton.setTextColor(Color.RED)
this.addContentView(
joinButton,
FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 300)
)
}
}
i want to solve my issue
Please wrap with function and make annotations there instead of mentioning in the main content view and wrap using UI thread.And refer sample github agora sdk github for reference - https://github.com/AgoraIO-Community/VideoUIKit-Android
I am making a specimen inventorying android app in Kotlin using Google Firebase. I use the firebase Realtime Datebase to store the specimen details & use firebase storage to store a picture of the specimen.
Eventually I will want to pull all of this data into the app to browse.
My Question: What is the best way to link the specimen's details in Realtime db to it's associated picture in Storage?
my code
RockEntry.kt
package com.inven.rock_stock
import android.util.Log
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import java.util.*
class RockEntry {
var name = ""
var purchDate = ""
var local = ""
var mine = ""
var weight = ""
var paid = ""
var asking = ""
var description = ""
var dimensions = ""
var specimenNumber = ""
var Uid = ""
var database = FirebaseDatabase.getInstance()
var ref = database.getReference("Rocks")
constructor(name:String,purchDate:String,local:String,mine:String,
weight:String,dimensions:String,paid:String,asking:String,
description:String,Uid:String){
this.name = name
this.purchDate = purchDate.toString()
this.local = local
this.mine = mine
this.weight = weight
this.dimensions = dimensions
this.paid = paid
this.asking = asking
this.description = description
this.Uid = UUID.randomUUID().toString()
}
MainActivity.kt
package com.inven.rock_stock
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.tasks.Continuation
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import kotlinx.android.synthetic.main.activity_main.*
import java.io.ByteArrayOutputStream
import java.io.File
import java.net.URI
var CAMERA_REQUEST_CODE = 0
var database = FirebaseDatabase.getInstance()
var ref = database.getReference("Rocks")
private var mStorageRef: StorageReference? = null
class MainActivity : AppCompatActivity() {
private val TAG = "MyActivity"
override fun onCreate(savedInstanceState: Bundle?) {
mStorageRef = FirebaseStorage.getInstance().getReference("ImagesBB")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
makeQuery()
}
imageBtn.setOnClickListener {
takePicture()
}
}
private fun makeQuery(){
var name = name.text.toString()
var purchDate = purchDate.toString()
var local = locality.text.toString()
var mine = mine.text.toString()
var weight = weight.text.toString()
var dimensions = dimensions.text.toString()
var paid = paid.text.toString()
var asking = asking.text.toString()
var description = description.text.toString()
if (!name.isBlank()) {
ref.child(name.toLowerCase()).setValue(
RockEntry(
name,
purchDate,
local,
mine,
weight,
paid,
asking,
dimensions,
description
)
)
}
else {
Toast.makeText(applicationContext, "Type in a name", Toast.LENGTH_LONG).show()
}
}
private fun takePicture() {
CAMERA_REQUEST_CODE = 222
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, CAMERA_REQUEST_CODE)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
CAMERA_REQUEST_CODE -> {
if (resultCode == Activity.RESULT_OK && data != null) {
val imageBitmap = data.extras?.get("data") as Bitmap
val baos = ByteArrayOutputStream()
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
val datar = baos.toByteArray()
mStorageRef!!.putBytes(datar)
}
}
}
}
}
'Linking' a FireBase Storage file to your Realtime Database can be accomplished by either
getting the location where a file lives in FireBase storage, via
toString() on the storage reference
or
getting a URL where you can download the file, via the .downloadUrl function.
Either of these options can then be stored as text into a Realtime Database 'entry'.
code example:
yourStorageBase?.putFile(element)?.addOnSuccessListener {
yourStorageBase?.downloadUrl?.addOnSuccessListener { downloadUri ->
// #1
var filepath = yourStorageBase.toString()
// #2
var downloadLink = downloadUri.toString()
}
}
Note:
If the following code is used within a function, you may 'prematurely' return to the caller before the picture has finished uploading, meaning you can't use the 'links' immediately. You can write a coroutine to ensure the IO has time to complete.
User RecyclerView to load all data in your app using live model class with data adapter.
User DataSnapShot in firebase to get all of the child nodes from a parent.
In Firebase DB, You have specimen which holds a number of Specimen, then you need to have one picture node as a child of each specimen node. When you upload the picture to Firebase Storage fetch its URL and insert it to the picture node under that particular Specimen.
I am trying to use mysql data in my basic Kotlin app and i use Android Studio. I've retrieved data and printed them into console with running only "Test.kt" file. But i want to use it in textView and stuff.
test.kt
When i want to run whole app, i'm facing "No suitable driver found" error.
MainActivity.kt
Here is my code:
MainActivity.kt
package com.example.dbtest
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
var list = ArrayList<DataModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
list = DBCon.connection()
for (i in 0 until list.size){
if (i == list.size){
textView.text = list[list.size].firstName + list[list.size].lastName
}
}
}
}
}
DBCon.kt
package com.example.dbtest
import java.sql.DriverManager
import java.sql.ResultSet
import java.sql.SQLException
object DBCon {
#JvmStatic
fun connection(): ArrayList<DataModel> {
var list = ArrayList<DataModel>()
try {
val connection = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/testdb",
"root",
""
)
val statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)
val result = statement.executeQuery("SELECT `id`, `firstName`, `lastName` FROM `testtable`")
while (result.next()){
list.add(DataModel(result.getInt("id"), result.getString("firstName"),result.getString("lastName")))
}
} catch (e: SQLException) {
e.printStackTrace()
}
return list
}
}
Test.kt
package com.example.dbtest
fun main(){
var list = ArrayList<DataModel>()
list = DBCon.connection()
for (i in 0 until list.size){
println(list[i].id.toString() + "- " + list[i].firstName + " " + list[i].lastName)
}
}
DataModel.kt
package com.example.dbtest
data class DataModel(val id: Int, val firstName: String, val lastName: String)
I'm new to Kotlin and i learned a lot new things to figure this out but i couldn't. Please lead my way to work this out. Thanks in advance.
P.S. Here is some things i did based on my search in order to solve this:
added jar file -> mysql-connector-java-8.0.21 and used as library // Done already
tried to add -> Class.forName("com.mysql.jdbc.Driver") // Not worked
tried to add -> Class.forName("com.mysql.cj.jdbc.Driver") // Not worked
added permission -> "android.permission.INTERNET" // Not worked
even tried to use -> AsyncTask // Not worked
I've written a unit test that use a mock location provider. The test passes when a location update is received, or fails when it times out.
The test passes fine running on emulated Pixel 3's, Android M through Android O. It times out on Android P and on Q. I also tested on a physical Pixel 3 with Q, still fails.
I've been beating my head against this for a while and can't figure out what's going on.
Test:
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.location.Location
import android.location.LocationManager
import androidx.test.rule.GrantPermissionRule
import design.inhale.androidtestutils.InstrumentedTest
import design.inhale.datasource.observer.NullDataSourceListener
import design.inhale.testutils.Latch
import design.inhale.utils.locationManager
import design.inhale.utils.mock
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.core.Is
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
class DeviceLocationSourceTest: InstrumentedTest() {
private val mockProviderName = "MockProvider"
private val locationManager: LocationManager
get() = appContext.locationManager
#get:Rule
val permissionRule: GrantPermissionRule = grant(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION)
#Before
fun init() {
addMockLocationProvider()
}
#After
fun deinit() {
instrumentation.waitForIdleSync()
removeMockLocationProvider()
}
#Test(timeout = 10_000)
fun receiveLocationUpdate() {
val latch = Latch()
val listener = object: LocationSourceListener {
override fun onDataUpdated(data: Location) {
with(data) {
assertThat(latitude, Is(equalTo(0.0)))
assertThat(longitude, Is(equalTo(0.0)))
}
latch.release()
}
}
mockLocationSource(listener).start()
instrumentation.waitForIdleSync() // in case we're hitting race conditions?
updateMockLocation(0.0, 0.0)
latch.await()
}
#Suppress("SameParameterValue")
private fun updateMockLocation(latitude: Double, longitude: Double) {
val location = Location(mockProviderName).mock(latitude, longitude)
locationManager.setTestProviderLocation(mockProviderName, location)
}
private fun mockLocationSource(listener: LocationSourceListener = NullDataSourceListener()) =
DeviceLocationSource(appContext, mockProviderName, listener)
private fun addMockLocationProvider() {
with(locationManager) {
try {
addTestProvider(
mockProviderName,
false,
false,
false,
false,
true,
true,
true,
0,
5)
} catch (e: IllegalArgumentException) {
// If this is caught, the mock provider already exists
}
setTestProviderEnabled(mockProviderName, true)
}
}
private fun removeMockLocationProvider() = locationManager.removeTestProvider(mockProviderName)
}
Manifest:
<manifest package="design.inhale.locationapi"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
androidTest/Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="design.inhale.locationapi"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
</manifest>
Location.mock():
#SuppressLint("ObsoleteSdkInt")
fun Location.mock(latitude: Double = 0.0, longitude: Double = 0.0, accuracy: Float = 0f): Location {
this.latitude = latitude
this.longitude = longitude
this.accuracy = accuracy
this.time = currentTimeMillis()
if (SDK_INT > JELLY_BEAN) this.elapsedRealtimeNanos = elapsedRealtimeNanos()
return this
}
Turns out I needed to add permission for background location access (I guess the test counts as running in the background?).
Added permission to the test manifest.
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>`
and changed the test to grant it:
val permissionRule: GrantPermissionRule = grant(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION)
Note that you'll want to make sure you don't add ACCESS_BACKGROUND_LOCATION to your production manifest. Google will pull your app if they find you're using this permission without a good reason.
I am able to tun my test but it fails. The problem is, mocked method is still returning wrong data. This is my method that I want to test it:
fun getTextByLanguage(list: List<TitleModel>) : String {
val deviceLanguage = Locale.getDefault().language
var default = ""
for (item in list) {
if (item.culture.contains(deviceLanguage, true)) return item.value
if (item.culture.contains("en", true)) default = item.value
}
return default
}
And this is how I am testing the method:
import junit.framework.TestCase.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.powermock.api.mockito.PowerMockito.*
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import java.util.Locale
#RunWith(PowerMockRunner::class)
#PrepareForTest(Locale::class)
class AppConfigUtilityByPowerMockTest {
#Test
fun `getTextByLanguage, test en`() {
mockStatic(Locale::class.java)
val mockedLocal = mock(Locale::class.java)
`when`(Locale.getDefault()).thenReturn(mockedLocal)
`when`(mockedLocal.language).thenReturn("en")
val list = listOf(TitleModel("en-ca", "Home"), TitleModel("fr-ca", "HomeFr"))
val actual = getTextByLanguage(list)
assertEquals("Home", actual)
}
#Test
fun `getTextByLanguage, test fr`() {
mockStatic(Locale::class.java)
val mockedLocal = mock(Locale::class.java)
`when`(Locale.getDefault()).thenReturn(mockedLocal)
`when`(mockedLocal.language).thenReturn("fr")
val list = listOf(TitleModel("en-ca", "Home"), TitleModel("fr-ca", "HomeFr"))
val actual = getTextByLanguage(list)
assertEquals("HomeFr", actual)
}
}
The first test cases without problem but the second one fails. This is the out put:
junit.framework.ComparisonFailure: expected:<Home[Fr]> but was:<Home[]>
Expected :HomeFr
Actual :Home
One way to answer the question is to get rid of Local class from the method and do stuff in Kotlin way. So, what I did is to change my original method like this:
fun getTextByLanguage(list: List<TitleModel>, deviceLanguage: String = getDeviceLanguage()) : String {
var default = ""
for (item in list) {
if (item.culture.contains(deviceLanguage, true)) return item.value
if (item.culture.contains("en", true)) default = item.value
}
return default
}
fun getDeviceLanguage(): String {
return Locale.getDefault().language
}
Now my test class looks like this:
import junit.framework.TestCase.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import java.util.Locale
#RunWith(PowerMockRunner::class)
#PrepareForTest(Locale::class)
class AppConfigUtilityByPowerMockTest {
#Test
fun `getTextByLanguage, test en`() {
val list = listOf(TitleModel("en-ca", "Home"), TitleModel("fr-ca", "HomeFr"))
val actual = getTextByLanguage(list, "en")
assertEquals("Home", actual)
}
#Test
fun `getTextByLanguage, test fr`() {
val list = listOf(TitleModel("en-ca", "Home"), TitleModel("fr-ca", "HomeFr"))
val actual = getTextByLanguage(list, "fr")
assertEquals("HomeFr", actual)
}
}
Although my test passes, I am still willing to see your recommendation and down to accept your answer to my question.