feat: add targeting list
This commit is contained in:
parent
be86e353d1
commit
2da9616b2b
|
@ -155,4 +155,4 @@
|
||||||
<option name="WRAP_ON_TYPING" value="0" />
|
<option name="WRAP_ON_TYPING" value="0" />
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="1.8" />
|
<bytecodeTargetLevel target="11" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<component name="RunConfigurationProducerService">
|
<component name="RunConfigurationProducerService">
|
||||||
<option name="ignoredProducers">
|
<option name="ignoredProducers">
|
||||||
<set>
|
<set>
|
||||||
|
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||||
|
|
|
@ -58,16 +58,16 @@ dependencies {
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
// default kotlin android stuff
|
// default kotlin android stuff
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.6.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.4.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||||
// preference ui objects
|
// preference ui objects
|
||||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||||
// libsodium for android
|
// libsodium for android
|
||||||
|
@ -100,11 +100,11 @@ dependencies {
|
||||||
implementation 'com.leinardi.android:speed-dial:3.1.1'
|
implementation 'com.leinardi.android:speed-dial:3.1.1'
|
||||||
// tests
|
// tests
|
||||||
testImplementation 'junit:junit:4.13'
|
testImplementation 'junit:junit:4.13'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
// desugaring
|
// desugaring
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
// sentry
|
// sentry
|
||||||
implementation 'io.sentry:sentry-android:4.0.0'
|
implementation 'io.sentry:sentry-android:4.0.0'
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ import io.annaclemens.xivchat.model.message.ClientCatchUp
|
||||||
import io.annaclemens.xivchat.model.message.ClientChannel
|
import io.annaclemens.xivchat.model.message.ClientChannel
|
||||||
import io.annaclemens.xivchat.model.message.ClientMessage
|
import io.annaclemens.xivchat.model.message.ClientMessage
|
||||||
import io.annaclemens.xivchat.model.message.ClientPlayerList
|
import io.annaclemens.xivchat.model.message.ClientPlayerList
|
||||||
|
import io.annaclemens.xivchat.model.message.ClientPreference
|
||||||
|
import io.annaclemens.xivchat.model.message.ClientPreferences
|
||||||
import io.annaclemens.xivchat.model.message.ClientShutdown
|
import io.annaclemens.xivchat.model.message.ClientShutdown
|
||||||
import io.annaclemens.xivchat.model.message.InputChannel
|
import io.annaclemens.xivchat.model.message.InputChannel
|
||||||
import io.annaclemens.xivchat.model.message.Ping
|
import io.annaclemens.xivchat.model.message.Ping
|
||||||
|
@ -49,6 +51,8 @@ import io.annaclemens.xivchat.model.message.SuccessMessage
|
||||||
import io.annaclemens.xivchat.ui.friends.FriendListViewModel
|
import io.annaclemens.xivchat.ui.friends.FriendListViewModel
|
||||||
import io.annaclemens.xivchat.ui.messages.MessagesViewModel
|
import io.annaclemens.xivchat.ui.messages.MessagesViewModel
|
||||||
import io.annaclemens.xivchat.ui.servers.ServersViewModel
|
import io.annaclemens.xivchat.ui.servers.ServersViewModel
|
||||||
|
import io.annaclemens.xivchat.ui.targeting.TargetingListViewModel
|
||||||
|
import io.annaclemens.xivchat.ui.targeting.TargetingPlayer
|
||||||
import io.annaclemens.xivchat.util.AlertDialogFragment
|
import io.annaclemens.xivchat.util.AlertDialogFragment
|
||||||
import io.annaclemens.xivchat.util.AppKeyExchange
|
import io.annaclemens.xivchat.util.AppKeyExchange
|
||||||
import io.annaclemens.xivchat.util.CharacterSearchResults
|
import io.annaclemens.xivchat.util.CharacterSearchResults
|
||||||
|
@ -83,6 +87,8 @@ import kotlinx.coroutines.withTimeout
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
class ConnectionService : Service() {
|
class ConnectionService : Service() {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -174,7 +180,7 @@ class ConnectionService : Service() {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? {
|
override fun onBind(intent: Intent?): IBinder {
|
||||||
return this.binder
|
return this.binder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,6 +323,9 @@ class ConnectionService : Service() {
|
||||||
this.service.app.findNavController(R.id.nav_host_fragment).navigate(R.id.nav_messages_tabs)
|
this.service.app.findNavController(R.id.nav_host_fragment).navigate(R.id.nav_messages_tabs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send protocol preferences
|
||||||
|
this.sendPreferences(txStream, tx)
|
||||||
|
|
||||||
// request catch-up or backlog
|
// request catch-up or backlog
|
||||||
this.requestBacklog(txStream, tx)
|
this.requestBacklog(txStream, tx)
|
||||||
|
|
||||||
|
@ -504,6 +513,13 @@ class ConnectionService : Service() {
|
||||||
SecretMessage.writeSecretMessage(this@ConnectionRunner.service.app.sodium, txStream, tx, bytes)
|
SecretMessage.writeSecretMessage(this@ConnectionRunner.service.app.sodium, txStream, tx, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun sendPreferences(txStream: ByteWriteChannel, tx: Key) {
|
||||||
|
val prefs = ClientPreferences(mapOf(
|
||||||
|
ClientPreference.TargetingListSupport to true,
|
||||||
|
))
|
||||||
|
SecretMessage.writeSecretMessage(this.service.app.sodium, txStream, tx, prefs.encode())
|
||||||
|
}
|
||||||
|
|
||||||
private class EventLoop(private val runner: ConnectionRunner) {
|
private class EventLoop(private val runner: ConnectionRunner) {
|
||||||
private var loop = true
|
private var loop = true
|
||||||
|
|
||||||
|
@ -652,6 +668,34 @@ class ConnectionService : Service() {
|
||||||
vm.friends.value = list.players.toList()
|
vm.friends.value = list.players.toList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PlayerListType.Targeting -> {
|
||||||
|
val vm: TargetingListViewModel by this.runner.service.app.viewModels()
|
||||||
|
this.runner.service.handler.post {
|
||||||
|
val now = Date()
|
||||||
|
val data = list.players
|
||||||
|
val oldTargeting = vm.targeting.value.orEmpty()
|
||||||
|
|
||||||
|
val newTargeting = data
|
||||||
|
.asSequence()
|
||||||
|
.filter { new -> oldTargeting.all { current -> current.player.name != new.name && current.player.homeWorld != new.homeWorld } }
|
||||||
|
.map { TargetingPlayer(it, true, now) }
|
||||||
|
val finalTargeting = oldTargeting
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
var timestamp = it.timestamp
|
||||||
|
val current = data.any { dataPlayer -> dataPlayer.name == it.player.name && dataPlayer.homeWorld == it.player.homeWorld }
|
||||||
|
if (current || it.current) {
|
||||||
|
timestamp = now
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetingPlayer(it.player, current, timestamp)
|
||||||
|
}
|
||||||
|
.plus(newTargeting)
|
||||||
|
.sortedByDescending { it.current }
|
||||||
|
.sortedByDescending { it.timestamp }
|
||||||
|
vm.targeting.value = finalTargeting.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// TODO: other player list types
|
// TODO: other player list types
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,6 +204,7 @@ class MainActivity : AppCompatActivity(), AlertDialogFragment.DialogHost, EditTe
|
||||||
R.id.nav_servers,
|
R.id.nav_servers,
|
||||||
R.id.nav_messages_tabs,
|
R.id.nav_messages_tabs,
|
||||||
R.id.nav_friend_list,
|
R.id.nav_friend_list,
|
||||||
|
R.id.nav_targeting_list,
|
||||||
R.id.nav_settings,
|
R.id.nav_settings,
|
||||||
R.id.nav_about,
|
R.id.nav_about,
|
||||||
),
|
),
|
||||||
|
|
|
@ -104,3 +104,33 @@ class ClientChannel(private val channel: InputChannel) {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ClientPreferences(private val preferences: Map<ClientPreference, Any>) {
|
||||||
|
fun encode(): ByteArray {
|
||||||
|
val packer = MessagePack.newDefaultBufferPacker()
|
||||||
|
packer.packArrayHeader(1)
|
||||||
|
packer.packMapHeader(this.preferences.size)
|
||||||
|
for (entry in this.preferences) {
|
||||||
|
packer.packByte(entry.key.code.toByte())
|
||||||
|
packer.packBoolean(entry.value == true) // TODO: fix
|
||||||
|
}
|
||||||
|
packer.close()
|
||||||
|
|
||||||
|
val bytes = packer.toByteArray()
|
||||||
|
val buf = ByteArray(1 + bytes.size)
|
||||||
|
buf[0] = ClientOperation.Preferences.code
|
||||||
|
bytes.copyInto(buf, 1)
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ClientPreference(val code: UByte) {
|
||||||
|
BacklogNewestMessagesFirst(0u),
|
||||||
|
TargetingListSupport(1u);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val map = values().associateBy(ClientPreference::code)
|
||||||
|
fun fromCode(code: UByte) = this.map[code]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ enum class PlayerListType(val code: Byte) {
|
||||||
Party(1),
|
Party(1),
|
||||||
Friend(2),
|
Friend(2),
|
||||||
Linkshell(3),
|
Linkshell(3),
|
||||||
CrossLinkshell(4);
|
CrossLinkshell(4),
|
||||||
|
Targeting(5);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val map = values().associateBy(PlayerListType::code)
|
private val map = values().associateBy(PlayerListType::code)
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package io.annaclemens.xivchat.ui.targeting
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import io.annaclemens.xivchat.R
|
||||||
|
import io.annaclemens.xivchat.databinding.TargetingListElementBinding
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class TargetingListAdapter(private val context: Context) : RecyclerView.Adapter<TargetingListAdapter.ViewHolder>() {
|
||||||
|
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
private val binding = TargetingListElementBinding.bind(itemView)
|
||||||
|
|
||||||
|
val name = this.binding.targetingName
|
||||||
|
val timestamp = this.binding.targetingTime
|
||||||
|
}
|
||||||
|
|
||||||
|
private val inflater = LayoutInflater.from(this.context)
|
||||||
|
private var playerList: List<TargetingPlayer> = emptyList()
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val itemView = this.inflater.inflate(R.layout.targeting_list_element, parent, false)
|
||||||
|
return this.ViewHolder(itemView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val targeting = this.playerList[position]
|
||||||
|
|
||||||
|
// TODO: add send tell context menu?
|
||||||
|
|
||||||
|
holder.name.text = targeting.player.name ?: this.context.getString(R.string.friend_list_could_not_retrieve)
|
||||||
|
holder.timestamp.text = if (targeting.current) {
|
||||||
|
this.context.getString(R.string.targeting_now)
|
||||||
|
} else {
|
||||||
|
if (Instant.now().epochSecond - targeting.timestamp.toInstant().epochSecond > 24 * 60 * 60) {
|
||||||
|
SimpleDateFormat("dd/MM", Locale.getDefault()).format(targeting.timestamp)
|
||||||
|
} else {
|
||||||
|
DateFormat.getTimeInstance(DateFormat.SHORT).format(targeting.timestamp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.name.isEnabled = targeting.current
|
||||||
|
holder.timestamp.isEnabled = targeting.current
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = this.playerList.size
|
||||||
|
|
||||||
|
fun setTargetingList(list: List<TargetingPlayer>) {
|
||||||
|
this.playerList = list
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package io.annaclemens.xivchat.ui.targeting
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import io.annaclemens.xivchat.databinding.FragmentTargetingBinding
|
||||||
|
import io.annaclemens.xivchat.util.getApp
|
||||||
|
|
||||||
|
class TargetingListFragment : Fragment() {
|
||||||
|
private lateinit var viewAdapter: TargetingListAdapter
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
val vm: TargetingListViewModel by this.getApp().viewModels()
|
||||||
|
val binding = FragmentTargetingBinding.inflate(inflater)
|
||||||
|
|
||||||
|
this.viewAdapter = TargetingListAdapter(this.requireContext())
|
||||||
|
binding.targetingList.apply {
|
||||||
|
this.setHasFixedSize(true)
|
||||||
|
this.layoutManager = LinearLayoutManager(this.context)
|
||||||
|
this.adapter = this@TargetingListFragment.viewAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateEmptyText(binding, vm.targeting.value.orEmpty().isEmpty())
|
||||||
|
|
||||||
|
vm.targeting.observe(this.viewLifecycleOwner) {
|
||||||
|
this.updateEmptyText(binding, it.orEmpty().isEmpty())
|
||||||
|
|
||||||
|
this.viewAdapter.setTargetingList(it.orEmpty())
|
||||||
|
this.viewAdapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateEmptyText(binding: FragmentTargetingBinding, visible: Boolean) {
|
||||||
|
binding.textView.visibility = if (visible) {
|
||||||
|
View.VISIBLE
|
||||||
|
} else {
|
||||||
|
View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.annaclemens.xivchat.ui.targeting
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class TargetingListViewModel : ViewModel() {
|
||||||
|
val targeting: MutableLiveData<List<TargetingPlayer>?> = MutableLiveData()
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package io.annaclemens.xivchat.ui.targeting
|
||||||
|
|
||||||
|
import io.annaclemens.xivchat.model.message.Player
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class TargetingPlayer(
|
||||||
|
val player: Player,
|
||||||
|
val current: Boolean,
|
||||||
|
val timestamp: Date,
|
||||||
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M12,6c3.79,0 7.17,2.13 8.82,5.5C19.17,14.87 15.79,17 12,17s-7.17,-2.13 -8.82,-5.5C4.83,8.13 8.21,6 12,6m0,-2C7,4 2.73,7.11 1,11.5 2.73,15.89 7,19 12,19s9.27,-3.11 11,-7.5C21.27,7.11 17,4 12,4zM12,9c1.38,0 2.5,1.12 2.5,2.5S13.38,14 12,14s-2.5,-1.12 -2.5,-2.5S10.62,9 12,9m0,-2c-2.48,0 -4.5,2.02 -4.5,4.5S9.52,16 12,16s4.5,-2.02 4.5,-4.5S14.48,7 12,7z" />
|
||||||
|
</vector>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/targeting_empty" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/targetingList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/targetingName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||||
|
tools:text="Goat Goatington" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/targetingTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textAlignment="textEnd"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||||
|
tools:text="04:45" />
|
||||||
|
</LinearLayout>
|
|
@ -20,6 +20,10 @@
|
||||||
android:id="@+id/nav_friend_list"
|
android:id="@+id/nav_friend_list"
|
||||||
android:icon="@drawable/people_black_24dp"
|
android:icon="@drawable/people_black_24dp"
|
||||||
android:title="@string/menu_friend_list" />
|
android:title="@string/menu_friend_list" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_targeting_list"
|
||||||
|
android:icon="@drawable/visibility_black_24dp"
|
||||||
|
android:title="@string/menu_targeting" />
|
||||||
</group>
|
</group>
|
||||||
<group android:id="@+id/menuSettings">
|
<group android:id="@+id/menuSettings">
|
||||||
<item
|
<item
|
||||||
|
|
|
@ -89,6 +89,13 @@
|
||||||
android:name="playerWorld"
|
android:name="playerWorld"
|
||||||
app:argType="integer" />
|
app:argType="integer" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/nav_targeting_list"
|
||||||
|
android:name="io.annaclemens.xivchat.ui.targeting.TargetingListFragment"
|
||||||
|
android:label="@string/menu_targeting"
|
||||||
|
tools:layout="@layout/fragment_targeting" />
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/nav_scanned_servers"
|
android:id="@+id/nav_scanned_servers"
|
||||||
android:name="io.annaclemens.xivchat.ui.servers.scan.ScannedServersFragment"
|
android:name="io.annaclemens.xivchat.ui.servers.scan.ScannedServersFragment"
|
||||||
|
|
|
@ -208,4 +208,7 @@
|
||||||
<string name="servers_scan_searching">Searching for servers…</string>
|
<string name="servers_scan_searching">Searching for servers…</string>
|
||||||
<string name="servers_scan_instructions">Tap on a server below to add it.</string>
|
<string name="servers_scan_instructions">Tap on a server below to add it.</string>
|
||||||
<string name="friend_list_could_not_retrieve">(Could not retrieve)</string>
|
<string name="friend_list_could_not_retrieve">(Could not retrieve)</string>
|
||||||
|
<string name="menu_targeting">Targeting</string>
|
||||||
|
<string name="targeting_now">Now</string>
|
||||||
|
<string name="targeting_empty">No one has targeted you since connecting.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = "1.4.30"
|
ext.kotlin_version = "1.5.20"
|
||||||
ext.ktor_version = '1.4.0'
|
ext.ktor_version = '1.4.0'
|
||||||
ext.room_version = '2.2.6'
|
ext.room_version = '2.3.0'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.3"
|
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||||
|
|
Loading…
Reference in New Issue