master
Zhengyu Peng 3 years ago
parent 756fee013c
commit eda367239a

@ -12,6 +12,10 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Hexapod"> android:theme="@style/Theme.Hexapod">
<activity
android:name=".DeviceListActivity"
android:exported="false"
android:theme="@style/Theme.MaterialComponents.DayNight.Dialog"/>
<activity <activity
android:name=".ControlActivity" android:name=".ControlActivity"
android:exported="false" android:exported="false"

@ -0,0 +1,184 @@
package com.rookiedev.hexapod
import android.annotation.SuppressLint
import android.app.Activity
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.view.View
import android.view.Window
import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.ListView
import android.widget.TextView
/**
* This Activity appears as a dialog. It lists any paired devices and
* devices detected in the area after discovery. When a device is chosen
* by the user, the MAC address of the device is sent back to the parent
* Activity in the result Intent.
*/
class DeviceListActivity : Activity() {
/**
* Member fields
*/
private var mBtAdapter: BluetoothAdapter? = null
private var mContext: Context? = null
private var bluetoothManager: BluetoothManager? = null
/**
* Newly discovered devices
*/
private var mNewDevicesArrayAdapter: ArrayAdapter<String>? = null
@SuppressLint("MissingPermission")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Setup the window
// requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS)
setContentView(R.layout.activity_device_list)
mContext = applicationContext
// Set result CANCELED in case the user backs out
setResult(RESULT_CANCELED)
// Initialize array adapters. One for already paired devices and
// one for newly discovered devices
val pairedDevicesArrayAdapter = ArrayAdapter<String>(this, R.layout.device_name)
mNewDevicesArrayAdapter = ArrayAdapter(this, R.layout.device_name)
// Find and set up the ListView for paired devices
val pairedListView: ListView = findViewById<ListView>(R.id.paired_devices)
pairedListView.adapter = pairedDevicesArrayAdapter
pairedListView.onItemClickListener = mDeviceClickListener
// Find and set up the ListView for newly discovered devices
// val newDevicesListView: ListView = findViewById(R.id.new_devices)
// newDevicesListView.adapter = mNewDevicesArrayAdapter
// newDevicesListView.onItemClickListener = mDeviceClickListener
// Register for broadcasts when a device is discovered
// var filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
// this.registerReceiver(mReceiver, filter)
// Register for broadcasts when discovery has finished
// filter = IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
// this.registerReceiver(mReceiver, filter)
// Get the local Bluetooth adapter
bluetoothManager =
mContext!!.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager!!.adapter
// Get a set of currently paired devices
val pairedDevices: Set<BluetoothDevice> = bluetoothManager!!.adapter.bondedDevices
// If there are paired devices, add each one to the ArrayAdapter
if (pairedDevices.isNotEmpty()) {
findViewById<View>(R.id.title_paired_devices).visibility = View.VISIBLE
for (device in pairedDevices) {
pairedDevicesArrayAdapter.add(
"""
${device.name}
${device.address}
""".trimIndent()
)
}
} else {
val noDevices = "No device"
pairedDevicesArrayAdapter.add(noDevices)
}
}
@SuppressLint("MissingPermission")
override fun onDestroy() {
super.onDestroy()
// Make sure we're not doing discovery anymore
// if (mBtAdapter != null) {
// mBtAdapter!!.cancelDiscovery()
// }
// bluetoothManager!!.adapter.cancelDiscovery()
// Unregister broadcast listeners
// unregisterReceiver(mReceiver)
}
/**
* The on-click listener for all devices in the ListViews
*/
@SuppressLint("MissingPermission")
private val mDeviceClickListener =
OnItemClickListener { av, v, arg2, arg3 -> // Cancel discovery because it's costly and we're about to connect
// bluetoothManager!!.adapter.cancelDiscovery()
// Get the device MAC address, which is the last 17 chars in the View
val info = (v as TextView).text.toString()
val address = info.substring(info.length - 17)
// Create the result Intent and include the MAC address
val intent = Intent()
intent.putExtra(EXTRA_DEVICE_ADDRESS, address)
// Set result and finish this Activity
setResult(RESULT_OK, intent)
finish()
}
/**
* The BroadcastReceiver that listens for discovered devices and changes the title when
* discovery is finished
*/
private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() {
@SuppressLint("MissingPermission")
override fun onReceive(context: Context?, intent: Intent) {
val action = intent.action
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND == action) {
// Get the BluetoothDevice object from the Intent
val device =
intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
// If it's already paired, skip it, because it's been listed already
if (device != null && device.bondState != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter!!.add(
"""
${device.name}
${device.address}
""".trimIndent()
)
}
// When discovery is finished, change the Activity title
// } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) {
// setProgressBarIndeterminateVisibility(false)
// setTitle(R.string.select_device)
// if (mNewDevicesArrayAdapter!!.count == 0) {
// val noDevices = resources.getText(R.string.none_found).toString()
// mNewDevicesArrayAdapter!!.add(noDevices)
// }
}
}
}
companion object {
/**
* Tag for Log
*/
private const val TAG = "DeviceListActivity"
/**
* Return Intent extra
*/
var EXTRA_DEVICE_ADDRESS = "device_address"
}
}

@ -1,8 +1,7 @@
package com.rookiedev.hexapod package com.rookiedev.hexapod
import android.Manifest import android.Manifest
import android.bluetooth.BluetoothDevice import android.app.Activity
import android.bluetooth.BluetoothManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -13,10 +12,15 @@ import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import android.widget.*
import android.widget.AdapterView.OnItemClickListener import android.widget.AdapterView.OnItemClickListener
import android.widget.Button
import android.widget.ListView
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
@ -28,6 +32,11 @@ class MainActivity : AppCompatActivity() {
private const val BLUETOOTH_PERMISSION_CODE = 100 private const val BLUETOOTH_PERMISSION_CODE = 100
private const val INTERNET_PERMISSION_CODE = 101 private const val INTERNET_PERMISSION_CODE = 101
} }
private val REQUEST_CONNECT_DEVICE_SECURE = 1
private val REQUEST_CONNECT_DEVICE_INSECURE = 2
private val REQUEST_ENABLE_BT = 3
private val SHAREDPREFSNAME = "com.rookiedev.hexapod_preferences" private val SHAREDPREFSNAME = "com.rookiedev.hexapod_preferences"
private val SHAREDPREFSIP = "IP" private val SHAREDPREFSIP = "IP"
private val SHAREDPREFSPORT = "PORT" private val SHAREDPREFSPORT = "PORT"
@ -51,6 +60,9 @@ class MainActivity : AppCompatActivity() {
val portLayout = findViewById<TextInputLayout>(R.id.port_input_layout) val portLayout = findViewById<TextInputLayout>(R.id.port_input_layout)
val deviceList = findViewById<ListView>(R.id.paired_devices) val deviceList = findViewById<ListView>(R.id.paired_devices)
val selectedDevice = findViewById<ConstraintLayout>(R.id.selected)
val deviceName = findViewById<TextView>(R.id.textView_device_name)
val deviceAddress = findViewById<TextView>(R.id.textView_device_address)
val sourceLink = findViewById<TextView>(R.id.textView_github) val sourceLink = findViewById<TextView>(R.id.textView_github)
sourceLink.movementMethod = LinkMovementMethod.getInstance() sourceLink.movementMethod = LinkMovementMethod.getInstance()
@ -65,72 +77,12 @@ class MainActivity : AppCompatActivity() {
ipLayout.visibility = View.VISIBLE ipLayout.visibility = View.VISIBLE
portLayout.visibility = View.VISIBLE portLayout.visibility = View.VISIBLE
deviceList.visibility = View.GONE deviceList.visibility = View.GONE
selectedDevice.visibility = View.GONE
} else if (tab.text == "Bluetooth") { } else if (tab.text == "Bluetooth") {
checkPermission(Manifest.permission.BLUETOOTH_CONNECT, BLUETOOTH_PERMISSION_CODE)
if (ActivityCompat.checkSelfPermission(
mContext!!,
Manifest.permission.BLUETOOTH_CONNECT
) != 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
}
ipLayout.visibility = View.GONE ipLayout.visibility = View.GONE
portLayout.visibility = View.GONE portLayout.visibility = View.GONE
deviceList.visibility = View.VISIBLE deviceList.visibility = View.VISIBLE
selectedDevice.visibility = View.VISIBLE
// Initialize array adapters. One for already paired devices and
// one for newly discovered devices
// Initialize array adapters. One for already paired devices and
// one for newly discovered devices
val pairedDevicesArrayAdapter =
ArrayAdapter<kotlin.String>(mContext!!, R.layout.device_name)
// Find and set up the ListView for paired devices
// Find and set up the ListView for paired devices
val pairedListView = findViewById<ListView>(R.id.paired_devices)
pairedListView.adapter = pairedDevicesArrayAdapter
pairedListView.onItemClickListener = mDeviceClickListener
// Get the local Bluetooth adapter
// Get the local Bluetooth adapter
// mBtAdapter = BluetoothAdapter.getDefaultAdapter()
val bluetoothManager = mContext!!.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.adapter
// Get a set of currently paired devices
// Get a set of currently paired devices
val pairedDevices: Set<BluetoothDevice> = bluetoothManager.adapter.bondedDevices
// If there are paired devices, add each one to the ArrayAdapter
// If there are paired devices, add each one to the ArrayAdapter
if (pairedDevices.isNotEmpty()) {
// findViewById<View>(R.id.title_paired_devices).visibility = View.VISIBLE
for (device in pairedDevices) {
pairedDevicesArrayAdapter.add(
"""
${device.name}
${device.address}
""".trimIndent()
)
}
pairedListView.layoutParams.height = 153*6
}
// else {
// val noDevices = resources.getText(R.string.none_paired).toString()
// pairedDevicesArrayAdapter.add(noDevices)
// }
} }
} }
@ -142,6 +94,13 @@ class MainActivity : AppCompatActivity() {
} }
) )
selectedDevice.setOnClickListener{
checkPermission(Manifest.permission.BLUETOOTH_CONNECT, BLUETOOTH_PERMISSION_CODE)
val serverIntent = Intent(this, DeviceListActivity::class.java)
resultLauncher.launch(serverIntent)
// startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE)
}
readSharedPref() readSharedPref()
buttonConnect.setOnClickListener { buttonConnect.setOnClickListener {
@ -184,6 +143,14 @@ class MainActivity : AppCompatActivity() {
}) })
} }
var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val data: Intent? = result.data
// doSomeOperations()
}
}
// Function to check and request permission. // Function to check and request permission.
private fun checkPermission(permission: String, requestCode: Int) { private fun checkPermission(permission: String, requestCode: Int) {
if (ActivityCompat.checkSelfPermission(this@MainActivity, permission) == PackageManager.PERMISSION_DENIED) { if (ActivityCompat.checkSelfPermission(this@MainActivity, permission) == PackageManager.PERMISSION_DENIED) {

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:id="@+id/title_paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="24dp"
android:textAppearance="?attr/textAppearanceHeadline6"
android:text="Select Device"
android:visibility="visible"
/>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<ListView
android:id="@+id/paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stackFromBottom="false"
/>
</LinearLayout>

@ -6,11 +6,13 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity"> tools:context=".MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="16dp"> android:paddingEnd="16dp"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView <ImageView
android:id="@+id/hexapod_logo" android:id="@+id/hexapod_logo"
@ -48,15 +50,15 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/ip_input_layout" android:id="@+id/ip_input_layout"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:visibility="visible"
app:errorEnabled="true" app:errorEnabled="true"
app:layout_constraintBottom_toTopOf="@id/port_input_layout" app:layout_constraintBottom_toTopOf="@id/port_input_layout"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tab" app:layout_constraintTop_toBottomOf="@+id/tab">
android:visibility="visible">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/ip_input" android:id="@+id/ip_input"
@ -70,15 +72,15 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/port_input_layout" android:id="@+id/port_input_layout"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:visibility="visible"
app:errorEnabled="true" app:errorEnabled="true"
app:layout_constraintBottom_toTopOf="@id/paired_devices" app:layout_constraintBottom_toTopOf="@id/paired_devices"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ip_input_layout" app:layout_constraintTop_toBottomOf="@id/ip_input_layout">
android:visibility="visible">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/port_input" android:id="@+id/port_input"
@ -100,27 +102,30 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/port_input_layout" app:layout_constraintTop_toBottomOf="@id/port_input_layout"
app:layout_constraintBottom_toTopOf="@id/button_connect" app:layout_constraintBottom_toTopOf="@id/selected"
android:visibility="visible"/> android:visibility="visible"/>
<!-- <include--> <include
<!-- layout="@layout/device_name"--> android:id="@+id/selected"
<!-- app:layout_constraintEnd_toEndOf="parent"--> android:visibility="visible"
<!-- app:layout_constraintHorizontal_bias="1.0"--> layout="@layout/selected_device"
<!-- app:layout_constraintStart_toStartOf="parent"--> app:layout_constraintBottom_toTopOf="@+id/button_connect"
<!-- app:layout_constraintTop_toBottomOf="@+id/paired_devices" />--> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/port_input_layout" />
<Button <Button
android:id="@+id/button_connect" android:id="@+id/button_connect"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:paddingTop="16dp" android:paddingTop="16dp"
android:paddingBottom="16dp" android:paddingBottom="16dp"
android:text="Connect" android:text="Connect"
app:layout_constraintBottom_toTopOf="@+id/textView_github"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/paired_devices" /> app:layout_constraintTop_toBottomOf="@id/selected" />
<TextView <TextView
android:id="@+id/textView_github" android:id="@+id/textView_github"
@ -133,5 +138,5 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_connect" /> app:layout_constraintTop_toBottomOf="@+id/button_connect" />
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>
</ScrollView> </ScrollView>

@ -3,44 +3,48 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackground">
<ImageView <ImageView
android:id="@+id/imageView" android:id="@+id/imageView"
android:layout_width="40dp" android:layout_width="64dp"
android:layout_height="40dp" android:layout_height="64dp"
android:padding="8dp" android:padding="16dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@+id/textView" app:layout_constraintEnd_toStartOf="@+id/textView_device_name"
app:srcCompat="@drawable/ic_baseline_bluetooth_24" /> app:srcCompat="@drawable/ic_baseline_bluetooth_24" />
<TextView <TextView
android:id="@+id/textView" android:id="@+id/textView_device_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceHeadline6" android:textAppearance="?attr/textAppearanceHeadline6"
android:text="TextView" android:text="TextView"
app:layout_constraintBottom_toTopOf="@+id/textView2" app:layout_constraintBottom_toTopOf="@+id/textView_device_address"
app:layout_constraintStart_toEndOf="@+id/imageView" app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" /> app:layout_constraintVertical_chainStyle="packed" />
<TextView <TextView
android:id="@+id/textView2" android:id="@+id/textView_device_address"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBody2" android:textAppearance="?attr/textAppearanceBody2"
android:text="TextView" android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageView" app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toBottomOf="@+id/textView" /> app:layout_constraintTop_toBottomOf="@+id/textView_device_name" />
<ImageView <ImageView
android:id="@+id/imageView2" android:id="@+id/imageView2"
android:layout_width="wrap_content" android:layout_width="64dp"
android:layout_height="wrap_content" android:layout_height="64dp"
android:padding="16dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"

Loading…
Cancel
Save