2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-30 05:37:43 +00:00

Main activity responsive layout

This commit is contained in:
Dmitry Yudin 2023-04-13 17:54:21 +02:00 committed by Albert Vaca Cintora
parent e2dbc39e3a
commit 8aeefded7c
19 changed files with 299 additions and 253 deletions

View File

@ -0,0 +1,30 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_drawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:layout_height="match_parent"
android:layout_width="match_parent"
tools:context="org.kde.kdeconnect.UserInterface.MainActivity">
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout" />
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

View File

@ -1,44 +1,51 @@
<LinearLayout <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
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="match_parent">
android:orientation="@integer/activity_device_orientation"
tools:context="org.kde.kdeconnect.UserInterface.DeviceFragment">
<!-- Layout shown when device is reachable but not yet paired --> <androidx.core.widget.NestedScrollView
<include android:layout_width="match_parent"
android:id="@+id/pair_request" android:layout_height="match_parent"
layout="@layout/view_pair_request" android:fillViewport="true">
tools:visibility="gone"/>
<!-- Layout shown when we can't pair with device or device is not reachable --> <LinearLayout
<include android:layout_width="match_parent"
android:id="@+id/pair_error" android:layout_height="wrap_content"
layout="@layout/view_pair_error" android:orientation="vertical"
tools:visibility="gone"/> tools:context="org.kde.kdeconnect.UserInterface.DeviceFragment">
<!-- Layouts shown when device is paired and reachable --> <!-- Layout shown when device is reachable but not yet paired -->
<GridView <include
android:id="@+id/plugins_list" android:id="@+id/pair_request"
android:layout_width="wrap_content" layout="@layout/view_pair_request"
android:layout_height="wrap_content" tools:visibility="gone"/>
android:layout_weight="@integer/plugins_list_weight"
android:numColumns="@integer/plugins_columns"
android:horizontalSpacing="8dp"
android:verticalSpacing="8dp"
android:layout_margin="@dimen/activity_vertical_margin"
tools:listitem="@layout/list_plugin_entry"
tools:layout_height="300dp" />
<ListView <!-- Layout shown when we can't pair with device or device is not reachable -->
android:id="@+id/buttons_list" <include
android:layout_width="match_parent" android:id="@+id/pair_error"
android:layout_height="wrap_content" layout="@layout/view_pair_error"
android:layout_weight="@integer/buttons_list_weight" tools:visibility="gone"/>
android:divider="@null"
android:dividerHeight="0dp" <!-- Layouts shown when device is paired and reachable -->
tools:context=".DeviceActivity" <androidx.recyclerview.widget.RecyclerView
tools:listitem="@layout/list_item_with_icon_entry" android:id="@+id/plugins_list"
tools:layout_height="300dp" /> android:layout_width="match_parent"
</LinearLayout> android:layout_height="wrap_content"
android:layout_margin="12dp"
android:nestedScrollingEnabled="false"
tools:listitem="@layout/list_plugin_entry"
tools:layout_height="300dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/permissions_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
tools:context=".DeviceActivity"
tools:listitem="@layout/list_item_plugin_header"
tools:layout_height="300dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</FrameLayout>

View File

@ -1,32 +1,37 @@
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
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:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"> <!-- fitSystemWindows to make the drawer slide below the Lollipop transparent status bar -->
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:layout_height="match_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
tools:context="org.kde.kdeconnect.UserInterface.MainActivity"> android:layout_height="match_parent">
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout" /> <androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/> android:fitsSystemWindows="true"> <!-- fitSystemWindows to make the drawer slide below the Lollipop transparent status bar -->
</androidx.coordinatorlayout.widget.CoordinatorLayout> <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:layout_height="match_parent"
android:layout_width="match_parent"
tools:context="org.kde.kdeconnect.UserInterface.MainActivity">
<com.google.android.material.navigation.NavigationView <include layout="@layout/toolbar" android:id="@+id/toolbar_layout"/>
android:id="@+id/navigation_drawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header" />
</androidx.drawerlayout.widget.DrawerLayout> <FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_drawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"/>
</androidx.drawerlayout.widget.DrawerLayout>
</FrameLayout>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <TextView xmlns:android="http://schemas.android.com/apk/res/android"
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:background="?selectableItemBackground"
android:paddingLeft="16dp" android:padding="@dimen/view_default_padding"
android:paddingTop="28dp" tools:background="@android:color/darker_gray"
android:paddingRight="16dp" tools:text="@tools:sample/lorem"/>
android:paddingBottom="8dp" />

View File

@ -2,10 +2,11 @@
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
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="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="4dp"
style="@style/KdeConnectCardStyle.Filled" style="@style/KdeConnectCardStyle.Filled"
app:contentPadding="12dp" app:contentPadding="@dimen/view_default_padding"
tools:layout_width="240dp"> tools:layout_width="240dp">
<LinearLayout <LinearLayout
@ -17,7 +18,7 @@
android:minHeight="?android:attr/listPreferredItemHeight" android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="vertical"> android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView <ImageView
android:id="@+id/list_item_entry_icon" android:id="@+id/list_item_entry_icon"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -33,17 +33,14 @@
android:drawablePadding="5dp" android:drawablePadding="5dp"
android:layout_marginBottom="8dip" android:layout_marginBottom="8dip"
android:visibility="gone" android:visibility="gone"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
app:drawableStartCompat="@drawable/ic_key" /> app:drawableStartCompat="@drawable/ic_key" />
<Button <com.google.android.material.button.MaterialButton
android:id="@+id/pair_button" android:id="@+id/pair_button"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/button_round" android:text="@string/request_pairing" />
android:text="@string/request_pairing"
android:textColor="@android:color/white" />
<LinearLayout <LinearLayout
@ -51,27 +48,27 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:visibility="gone"> android:visibility="gone"
android:paddingVertical="4dp"
tools:visibility="visible">
<Button <com.google.android.material.button.MaterialButton
android:id="@+id/accept_button" android:id="@+id/accept_button"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="4dip"
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/button_round" android:text="@string/pairing_accept" />
android:text="@string/pairing_accept"
android:textColor="@android:color/white" />
<Button <android.widget.Space
android:layout_width="8dp"
android:layout_height="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/reject_button" android:id="@+id/reject_button"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="4dip"
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/button_round" android:text="@string/pairing_reject" />
android:text="@string/pairing_reject"
android:textColor="@android:color/white" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -1,11 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<integer name="activity_device_orientation">@integer/orientation_horizontal</integer>
<integer name="plugins_list_weight">4</integer>
<integer name="buttons_list_weight">6</integer>
<integer name="mpris_now_playing_orientation">@integer/orientation_horizontal</integer> <integer name="mpris_now_playing_orientation">@integer/orientation_horizontal</integer>
<integer name="mpris_now_playing_album_weight">1</integer> <integer name="mpris_now_playing_album_weight">1</integer>
<integer name="mpris_now_playing_controls_weight">1</integer> <integer name="mpris_now_playing_controls_weight">1</integer>

View File

@ -10,9 +10,6 @@
<item name="layout_wrap_content" type="dimen">-2</item> <item name="layout_wrap_content" type="dimen">-2</item>
<!--used in activity_device--> <!--used in activity_device-->
<integer name="activity_device_orientation">@integer/orientation_vertical</integer>
<integer name="plugins_list_weight">@null</integer>
<integer name="buttons_list_weight">@null</integer>
<integer name="plugins_columns">2</integer> <integer name="plugins_columns">2</integer>
<!--used in mpris_now_playing--> <!--used in mpris_now_playing-->

View File

@ -2,6 +2,7 @@
<!-- Default screen margins, per the Android Design guidelines. --> <!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="view_default_padding">16dp</dimen>
<dimen name="key_height">48dip</dimen> <dimen name="key_height">48dip</dimen>
<dimen name="fab_margin">16dp</dimen> <dimen name="fab_margin">16dp</dimen>
<dimen name="fab_elevation">6dp</dimen> <dimen name="fab_elevation">6dp</dimen>

View File

@ -162,7 +162,7 @@ public class RemoteKeyboardService
} }
} else { // != 1 instance of plugin -> show main activity view } else { // != 1 instance of plugin -> show main activity view
Intent intent = new Intent(this, MainActivity.class); Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("forceOverview", true); intent.putExtra(MainActivity.FLAG_FORCE_OVERVIEW, true);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); startActivity(intent);
if (instances.size() < 1) if (instances.size() < 1)

View File

@ -32,13 +32,11 @@ internal class SystemVolumeProvider private constructor(plugin: SystemVolumePlug
@JvmStatic @JvmStatic
fun fromPlugin(systemVolumePlugin: SystemVolumePlugin): SystemVolumeProvider { fun fromPlugin(systemVolumePlugin: SystemVolumePlugin): SystemVolumeProvider {
if (currentProvider == null) { val currentProvider = currentProvider ?: SystemVolumeProvider(systemVolumePlugin)
currentProvider = SystemVolumeProvider(systemVolumePlugin)
}
currentProvider!!.update(systemVolumePlugin) currentProvider.update(systemVolumePlugin)
return currentProvider!! return currentProvider
} }
private fun scale(value: Int, maxValue: Int, maxScaled: Int): Int { private fun scale(value: Int, maxValue: Int, maxScaled: Int): Int {

View File

@ -37,12 +37,10 @@ class AboutData(var name: String, var icon: Int, var versionName: String, var bu
parcel.writeString(sourceCodeURL) parcel.writeString(sourceCodeURL)
parcel.writeString(donateURL) parcel.writeString(donateURL)
if (authorsFooterText == null) { authorsFooterText?.let {
parcel.writeByte(0x00)
} else {
parcel.writeByte(0x01) parcel.writeByte(0x01)
parcel.writeInt(authorsFooterText!!) parcel.writeInt(it)
} } ?: parcel.writeByte(0x00)
} }
override fun describeContents(): Int = 0 override fun describeContents(): Int = 0

View File

@ -6,7 +6,6 @@
package org.kde.kdeconnect.UserInterface package org.kde.kdeconnect.UserInterface
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
@ -14,10 +13,10 @@ import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.kde.kdeconnect.BackgroundService import org.kde.kdeconnect.BackgroundService
import org.kde.kdeconnect.Device import org.kde.kdeconnect.Device
@ -26,10 +25,8 @@ import org.kde.kdeconnect.Device.PluginsChangedListener
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin
import org.kde.kdeconnect.Plugins.Plugin import org.kde.kdeconnect.Plugins.Plugin
import org.kde.kdeconnect.UserInterface.List.FailedPluginListItem import org.kde.kdeconnect.UserInterface.List.PluginAdapter
import org.kde.kdeconnect.UserInterface.List.ListAdapter
import org.kde.kdeconnect.UserInterface.List.PluginItem import org.kde.kdeconnect.UserInterface.List.PluginItem
import org.kde.kdeconnect.UserInterface.List.PluginListHeaderItem
import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.R
import org.kde.kdeconnect_tp.databinding.ActivityDeviceBinding import org.kde.kdeconnect_tp.databinding.ActivityDeviceBinding
import org.kde.kdeconnect_tp.databinding.ViewPairErrorBinding import org.kde.kdeconnect_tp.databinding.ViewPairErrorBinding
@ -48,8 +45,8 @@ class DeviceFragment : Fragment() {
private val mActivity: MainActivity? by lazy { activity as MainActivity? } private val mActivity: MainActivity? by lazy { activity as MainActivity? }
//TODO use LinkedHashMap and delete irrelevant records when plugins changed //TODO use LinkedHashMap and delete irrelevant records when plugins changed
private val pluginListItems: ArrayList<ListAdapter.Item> = ArrayList() private val pluginListItems: ArrayList<PluginItem> = ArrayList()
private val permissionListItems: ArrayList<ListAdapter.Item> = ArrayList() private val permissionListItems: ArrayList<PluginItem> = ArrayList()
/** /**
* Top-level ViewBinding for this fragment. * Top-level ViewBinding for this fragment.
@ -106,11 +103,13 @@ class DeviceFragment : Fragment() {
} }
requireBinding().pairButton.setOnClickListener { requireBinding().pairButton.setOnClickListener {
requireBinding().pairButton.visibility = View.GONE with(requireBinding()) {
requireBinding().pairMessage.text = null pairButton.visibility = View.GONE
requireBinding().pairVerification.visibility = View.VISIBLE pairMessage.text = null
requireBinding().pairVerification.text = SslHelper.getVerificationKey(SslHelper.certificate, device?.certificate) pairVerification.visibility = View.VISIBLE
requireBinding().pairProgress.visibility = View.VISIBLE pairVerification.text = SslHelper.getVerificationKey(SslHelper.certificate, device?.certificate)
pairProgress.visibility = View.VISIBLE
}
device?.requestPairing() device?.requestPairing()
} }
requireBinding().acceptButton.setOnClickListener { requireBinding().acceptButton.setOnClickListener {
@ -141,6 +140,10 @@ class DeviceFragment : Fragment() {
refreshUI() refreshUI()
} }
requireDeviceBinding().pluginsList.layoutManager =
GridLayoutManager(requireContext(), resources.getInteger(R.integer.plugins_columns))
requireDeviceBinding().permissionsList.layoutManager = LinearLayoutManager(requireContext())
return deviceBinding.root return deviceBinding.root
} }
@ -165,12 +168,11 @@ class DeviceFragment : Fragment() {
//Plugins button list //Plugins button list
val plugins: Collection<Plugin> = device.loadedPlugins.values val plugins: Collection<Plugin> = device.loadedPlugins.values
for (p in plugins) { for (p in plugins) {
if (!p.displayInContextMenu()) { if (p.displayInContextMenu()) {
continue menu.add(p.actionName).setOnMenuItemClickListener {
} p.startMainActivity(mActivity)
menu.add(p.actionName).setOnMenuItemClickListener { true
p.startMainActivity(mActivity) }
true
} }
} }
val intent = Intent(mActivity, PluginSettingsActivity::class.java) val intent = Intent(mActivity, PluginSettingsActivity::class.java)
@ -256,45 +258,57 @@ class DeviceFragment : Fragment() {
if (paired && !reachable) { if (paired && !reachable) {
requireErrorBinding().errorMessageContainer.visibility = View.VISIBLE requireErrorBinding().errorMessageContainer.visibility = View.VISIBLE
requireErrorBinding().notReachableMessage.visibility = View.VISIBLE requireErrorBinding().notReachableMessage.visibility = View.VISIBLE
requireDeviceBinding().permissionsList.visibility = View.GONE
requireDeviceBinding().pluginsList.visibility = View.GONE
} else { } else {
requireErrorBinding().errorMessageContainer.visibility = View.GONE requireErrorBinding().errorMessageContainer.visibility = View.GONE
requireErrorBinding().notReachableMessage.visibility = View.GONE requireErrorBinding().notReachableMessage.visibility = View.GONE
requireDeviceBinding().permissionsList.visibility = View.VISIBLE
requireDeviceBinding().pluginsList.visibility = View.VISIBLE
} }
try { try {
if (paired && reachable) { if (paired && reachable) {
//Plugins button list //Plugins button list
val plugins: Collection<Plugin> = device.loadedPlugins.values val plugins: Collection<Plugin> = device.loadedPlugins.values
//TODO look for LinkedHashMap mention above
pluginListItems.clear() pluginListItems.clear()
permissionListItems.clear() permissionListItems.clear()
//Fill enabled plugins ArrayList
for (p in plugins) { for (p in plugins) {
if (!p.hasMainActivity(context) || p.displayInContextMenu()) continue if (p.hasMainActivity(context) && !p.displayInContextMenu()) {
pluginListItems.add(PluginItem(p) { p.startMainActivity(mActivity) }) pluginListItems.add(
PluginItem(requireContext(), p, { p.startMainActivity(mActivity) })
)
}
} }
//Fill permissionListItems with permissions plugins
createPermissionsList( createPermissionsList(
device.pluginsWithoutPermissions, device.pluginsWithoutPermissions,
R.string.plugins_need_permission R.string.plugins_need_permission
) { plugin: Plugin -> ) { p: Plugin ->
val dialog = plugin.permissionExplanationDialog p.permissionExplanationDialog?.show(childFragmentManager, null)
dialog?.show(childFragmentManager, null)
} }
createPermissionsList( createPermissionsList(
device.pluginsWithoutOptionalPermissions, device.pluginsWithoutOptionalPermissions,
R.string.plugins_need_optional_permission R.string.plugins_need_optional_permission
) { plugin: Plugin -> ) { p: Plugin ->
val dialog: DialogFragment? = plugin.optionalPermissionExplanationDialog p.optionalPermissionExplanationDialog?.show(childFragmentManager, null)
dialog?.show(childFragmentManager, null)
} }
requireDeviceBinding().permissionsList.adapter =
PluginAdapter(permissionListItems, R.layout.list_item_plugin_header)
requireDeviceBinding().pluginsList.adapter =
PluginAdapter(pluginListItems, R.layout.list_plugin_entry)
requireDeviceBinding().permissionsList.adapter?.notifyDataSetChanged()
requireDeviceBinding().pluginsList.adapter?.notifyDataSetChanged()
displayBatteryInfoIfPossible() displayBatteryInfoIfPossible()
} }
requireDeviceBinding().pluginsList.adapter = ListAdapter(mActivity, pluginListItems)
//don't do unnecessary work when all permissions granted and remove view for landscape orientation
if (permissionListItems.isEmpty()) {
requireDeviceBinding().buttonsList.visibility = View.GONE
} else {
requireDeviceBinding().buttonsList.adapter = ListAdapter(mActivity, permissionListItems)
requireDeviceBinding().buttonsList.visibility = View.VISIBLE
}
mActivity?.invalidateOptionsMenu() mActivity?.invalidateOptionsMenu()
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
//Ignore: The activity was closed while we were trying to update it //Ignore: The activity was closed while we were trying to update it
@ -320,7 +334,7 @@ class DeviceFragment : Fragment() {
mActivity?.runOnUiThread { mActivity?.runOnUiThread {
with(requireBinding()) { with(requireBinding()) {
pairMessage.text = error pairMessage.text = error
pairVerification.text = "" pairVerification.text = null
pairVerification.visibility = View.GONE pairVerification.visibility = View.GONE
pairProgress.visibility = View.GONE pairProgress.visibility = View.GONE
pairButton.visibility = View.VISIBLE pairButton.visibility = View.VISIBLE
@ -346,17 +360,24 @@ class DeviceFragment : Fragment() {
private fun createPermissionsList( private fun createPermissionsList(
plugins: ConcurrentHashMap<String, Plugin>, plugins: ConcurrentHashMap<String, Plugin>,
headerText: Int, @StringRes headerText: Int,
action: FailedPluginListItem.Action action: (Plugin) -> Unit,
) { ) {
if (plugins.isEmpty()) return if (plugins.isEmpty()) return
val device = device ?: return val device = device ?: return
permissionListItems.add(PluginListHeaderItem(headerText)) permissionListItems.add(
PluginItem(
context = requireContext(),
header = requireContext().getString(headerText),
textStyleRes = R.style.TextAppearance_Material3_BodyMedium,
)
)
for (plugin in plugins.values) { for (plugin in plugins.values) {
if (!device.isPluginEnabled(plugin.pluginKey)) { if (device.isPluginEnabled(plugin.pluginKey)) {
continue permissionListItems.add(
PluginItem(requireContext(), plugin, action, R.style.TextAppearance_Material3_LabelLarge)
)
} }
permissionListItems.add(FailedPluginListItem(plugin, action))
} }
} }
@ -375,12 +396,10 @@ class DeviceFragment : Fragment() {
if (info != null) { if (info != null) {
@StringRes @StringRes
val resId: Int = if (info.isCharging) { val resId = when {
R.string.battery_status_charging_format info.isCharging -> R.string.battery_status_charging_format
} else if (BatteryPlugin.isLowBattery(info)) { BatteryPlugin.isLowBattery(info) -> R.string.battery_status_low_format
R.string.battery_status_low_format else -> R.string.battery_status_format
} else {
R.string.battery_status_format
} }
mActivity?.supportActionBar?.subtitle = mActivity?.getString(resId, info.currentCharge) mActivity?.supportActionBar?.subtitle = mActivity?.getString(resId, info.currentCharge)

View File

@ -1,19 +0,0 @@
/*
* SPDX-FileCopyrightText: 2018 Nicolas Fella <nicolas.fella@gmx.de>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.UserInterface.List;
import org.kde.kdeconnect.Plugins.Plugin;
public class FailedPluginListItem extends SmallEntryItem {
public interface Action {
void action(Plugin plugin);
}
public FailedPluginListItem(Plugin plugin, Action action) {
super(plugin.getDisplayName(), (view) -> action.action(plugin));
}
}

View File

@ -0,0 +1,47 @@
package org.kde.kdeconnect.UserInterface.List
import android.annotation.TargetApi
import android.os.Build
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import org.kde.kdeconnect_tp.R
/**
* Adapter for showing enabled plugins and permission requests
* can be used with following layouts:
* list_plugin_entry - card view with text and icon
* list_item_plugin_header - plain TextView
* Any other TextView layout
*/
class PluginAdapter(
private val pluginList: ArrayList<PluginItem>,
private val layoutRes: Int,
) : RecyclerView.Adapter<PluginAdapter.PluginViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, type: Int) =
PluginViewHolder(LayoutInflater.from(viewGroup.context).inflate(layoutRes, viewGroup, false))
override fun getItemCount() = pluginList.size
@TargetApi(Build.VERSION_CODES.M)
override fun onBindViewHolder(holder: PluginViewHolder, position: Int) {
pluginList[position].let { plugin ->
holder.pluginTitle.text = plugin.header
holder.pluginIcon?.setImageDrawable(plugin.icon)
plugin.textStyleRes?.let { holder.pluginTitle.setTextAppearance(it) }
plugin.action?.let { action -> holder.itemView.setOnClickListener { action.invoke() } }
}
}
class PluginViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val pluginTitle: TextView = view.findViewById(R.id.list_item_entry_title) ?: view as TextView
val pluginIcon: ImageView? = view.findViewById(R.id.list_item_entry_icon)
}
}

View File

@ -1,31 +0,0 @@
/*
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.UserInterface.List;
import android.view.LayoutInflater;
import android.view.View;
import androidx.annotation.NonNull;
import org.kde.kdeconnect.Plugins.Plugin;
public class PluginItem extends EntryItemWithIcon {
private final View.OnClickListener clickListener;
public PluginItem(Plugin p, View.OnClickListener clickListener) {
super(p.getActionName(), p.getIcon());
this.clickListener = clickListener;
}
@NonNull
@Override
public View inflateView(@NonNull LayoutInflater layoutInflater) {
final View root = super.inflateView(layoutInflater);
root.setOnClickListener(clickListener);
return root;
}
}

View File

@ -0,0 +1,29 @@
package org.kde.kdeconnect.UserInterface.List
import android.content.Context
import android.graphics.drawable.Drawable
import org.kde.kdeconnect.Plugins.Plugin
class PluginItem(
val context: Context,
val header: String,
val textStyleRes: Int? = null,
) {
var action: (() -> Unit)? = null
var icon: Drawable? = null
constructor(
context: Context,
plugin: Plugin,
action: (Plugin) -> Unit,
textStyleRes: Int? = null,
) : this(
context = context,
header = plugin.displayName,
textStyleRes = textStyleRes,
) {
this.action = { action(plugin) }
this.icon = plugin.icon
}
}

View File

@ -1,33 +0,0 @@
/*
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.UserInterface.List;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import org.kde.kdeconnect_tp.databinding.ListItemPluginHeaderBinding;
public class PluginListHeaderItem implements ListAdapter.Item {
private final int text;
public PluginListHeaderItem(int text) {
this.text = text;
}
@NonNull
@Override
public View inflateView(@NonNull LayoutInflater layoutInflater) {
TextView textView = ListItemPluginHeaderBinding.inflate(layoutInflater).getRoot();
textView.setText(text);
textView.setOnClickListener(null);
textView.setOnLongClickListener(null);
return textView;
}
}

View File

@ -63,6 +63,7 @@ public class MainActivity extends AppCompatActivity implements SharedPreferences
public static final String PAIRING_PENDING = "pending"; public static final String PAIRING_PENDING = "pending";
public static final String EXTRA_DEVICE_ID = "deviceId"; public static final String EXTRA_DEVICE_ID = "deviceId";
public static final String FLAG_FORCE_OVERVIEW = "forceOverview";
private NavigationView mNavigationView; private NavigationView mNavigationView;
private DrawerLayout mDrawerLayout; private DrawerLayout mDrawerLayout;
@ -95,22 +96,24 @@ public class MainActivity extends AppCompatActivity implements SharedPreferences
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
ActionBarDrawerToggle mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ if (mDrawerLayout != null) {
mDrawerLayout, /* DrawerLayout object */ ActionBarDrawerToggle mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
R.string.open, /* "open drawer" description */ mDrawerLayout, /* DrawerLayout object */
R.string.close /* "close drawer" description */ R.string.open, /* "open drawer" description */
); R.string.close /* "close drawer" description */
);
mDrawerLayout.addDrawerListener(mDrawerToggle); mDrawerLayout.addDrawerListener(mDrawerToggle);
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
}
mDrawerToggle.setDrawerIndicatorEnabled(true);
mDrawerToggle.syncState();
} }
mDrawerToggle.setDrawerIndicatorEnabled(true);
mDrawerToggle.syncState();
preferences = getSharedPreferences("stored_menu_selection", Context.MODE_PRIVATE); preferences = getSharedPreferences("stored_menu_selection", Context.MODE_PRIVATE);
// Note: The preference changed listener should be registered before getting the name, because getting // Note: The preference changed listener should be registered before getting the name, because getting
@ -144,14 +147,16 @@ public class MainActivity extends AppCompatActivity implements SharedPreferences
break; break;
} }
mDrawerLayout.closeDrawer(mNavigationView); if (mDrawerLayout != null) {
mDrawerLayout.closeDrawer(mNavigationView);
}
return true; return true;
}); });
// Decide which menu entry should be selected at start // Decide which menu entry should be selected at start
String savedDevice; String savedDevice;
int savedMenuEntry; int savedMenuEntry;
if (getIntent().hasExtra("forceOverview")) { if (getIntent().hasExtra(FLAG_FORCE_OVERVIEW)) {
Log.i("MainActivity", "Requested to start main overview"); Log.i("MainActivity", "Requested to start main overview");
savedDevice = null; savedDevice = null;
savedMenuEntry = MENU_ENTRY_ADD_DEVICE; savedMenuEntry = MENU_ENTRY_ADD_DEVICE;
@ -248,7 +253,7 @@ public class MainActivity extends AppCompatActivity implements SharedPreferences
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (mDrawerLayout.isDrawerOpen(mNavigationView)) { if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mNavigationView)) {
mDrawerLayout.closeDrawer(mNavigationView); mDrawerLayout.closeDrawer(mNavigationView);
} else if (mCurrentMenuEntry == MENU_ENTRY_SETTINGS || mCurrentMenuEntry == MENU_ENTRY_ABOUT) { } else if (mCurrentMenuEntry == MENU_ENTRY_SETTINGS || mCurrentMenuEntry == MENU_ENTRY_ABOUT) {
mCurrentMenuEntry = MENU_ENTRY_ADD_DEVICE; mCurrentMenuEntry = MENU_ENTRY_ADD_DEVICE;
@ -261,7 +266,7 @@ public class MainActivity extends AppCompatActivity implements SharedPreferences
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (mDrawerLayout != null && item.getItemId() == android.R.id.home) {
mDrawerLayout.openDrawer(mNavigationView); mDrawerLayout.openDrawer(mNavigationView);
return true; return true;
} else { } else {