mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-31 06:05:12 +00:00
Use Jetpack Compose for the Device UI
This commit is contained in:
@@ -159,10 +159,13 @@ dependencies {
|
||||
|
||||
implementation 'androidx.compose.material3:material3:1.1.0'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview:1.4.3'
|
||||
implementation 'androidx.activity:activity-compose:1.7.1'
|
||||
implementation 'androidx.activity:activity-compose:1.7.2'
|
||||
implementation 'com.google.accompanist:accompanist-themeadapter-material3:0.31.0-alpha'
|
||||
implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.1'
|
||||
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview:1.4.3'
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling:1.4.3'
|
||||
|
||||
implementation 'androidx.media:media:1.6.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.core:core-ktx:1.10.1'
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<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">
|
||||
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"
|
||||
|
@@ -1,51 +1,33 @@
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.DeviceFragment">
|
||||
|
||||
<!-- Shown when device is reachable but not yet paired -->
|
||||
<include
|
||||
android:id="@+id/pair_request"
|
||||
layout="@layout/view_pair_request"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
<!-- Shown when the device is paired but not reachable -->
|
||||
<include
|
||||
android:id="@+id/pair_error"
|
||||
layout="@layout/view_pair_error"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/device_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- Shown when the device is paired and reachable -->
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/device_view_compose"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.DeviceFragment">
|
||||
|
||||
<!-- Layout shown when device is reachable but not yet paired -->
|
||||
<include
|
||||
android:id="@+id/pair_request"
|
||||
layout="@layout/view_pair_request"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
<!-- Layout shown when we can't pair with device or device is not reachable -->
|
||||
<include
|
||||
android:id="@+id/pair_error"
|
||||
layout="@layout/view_pair_error"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
<!-- Layouts shown when device is paired and reachable -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/plugins_list"
|
||||
android:layout_width="match_parent"
|
||||
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>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
|
@@ -1,37 +1,33 @@
|
||||
<FrameLayout 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"
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
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">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/coordinatorLayout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
tools:context="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
android:id="@+id/drawer_layout"
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
/>
|
||||
|
||||
<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"
|
||||
android:fitsSystemWindows="true">
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout"/>
|
||||
<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"/>
|
||||
|
||||
<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>
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
@@ -14,7 +14,7 @@
|
||||
android:divider="@null"
|
||||
android:dividerHeight="12dp"
|
||||
android:orientation="vertical"
|
||||
tools:listitem="@layout/list_plugin_entry"
|
||||
tools:listitem="@layout/list_card_entry"
|
||||
tools:context=".MainActivity" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
@@ -1,35 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/error_message_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/error_message_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:paddingEnd="8dip"
|
||||
android:paddingStart="0dip"
|
||||
android:src="@drawable/ic_error_outline_48dp"
|
||||
app:tint="?attr/colorHighContrast"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/not_reachable_message"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/unreachable_description"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/error_message_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:paddingEnd="8dip"
|
||||
android:paddingStart="0dip"
|
||||
android:src="@drawable/ic_error_outline_48dp"
|
||||
app:tint="?attr/colorHighContrast"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/not_reachable_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/unreachable_description"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
@@ -6,7 +6,7 @@
|
||||
android:id="@+id/pairing_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_gravity="top"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/activity_vertical_margin"
|
||||
android:visibility="gone"
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<integer name="plugins_columns">3</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_controls_weight">1</integer>
|
||||
|
@@ -14,11 +14,10 @@ import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
@@ -53,8 +52,8 @@ public class BigscreenPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.ic_presenter_24dp);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.ic_presenter_24dp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,7 +67,7 @@ public class BigscreenPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity(Context context) {
|
||||
public boolean displayAsButton(Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -7,17 +7,16 @@
|
||||
package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
|
||||
|
||||
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
@@ -130,14 +129,14 @@ public class ClipboardPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity(Context context) {
|
||||
public boolean displayAsButton(Context context) {
|
||||
return Build.VERSION.SDK_INT > Build.VERSION_CODES.P &&
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.ic_baseline_content_paste_24);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.ic_baseline_content_paste_24;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -9,10 +9,9 @@ package org.kde.kdeconnect.Plugins.MousePadPlugin;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
@@ -48,8 +47,8 @@ public class MousePadPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.touchpad_plugin_action_24dp);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.touchpad_plugin_action_24dp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +62,7 @@ public class MousePadPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity(Context context) {
|
||||
public boolean displayAsButton(Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -10,11 +10,10 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
@@ -250,8 +249,8 @@ public class MprisPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.mpris_plugin_action_24dp);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.mpris_plugin_action_24dp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -482,7 +481,7 @@ public class MprisPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity(Context context) {
|
||||
public boolean displayAsButton(Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -7,11 +7,10 @@
|
||||
package org.kde.kdeconnect.Plugins.PhotoPlugin;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.FilesHelper;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
@@ -62,8 +61,8 @@ public class PhotoPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.ic_camera);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.ic_camera;
|
||||
}
|
||||
|
||||
void sendCancel() {
|
||||
|
@@ -10,10 +10,10 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
@@ -109,11 +109,10 @@ public abstract class Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an icon associated to this plugin. This function can
|
||||
* access this.context to load the image from resources.
|
||||
* Return an icon associated to this plugin. Only needed if hasMainActivity() returns true and displayInContextMenu() returns false
|
||||
*/
|
||||
public @Nullable Drawable getIcon() {
|
||||
return null;
|
||||
public @DrawableRes int getIcon() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,16 +155,10 @@ public abstract class Plugin {
|
||||
/**
|
||||
* Return true if the plugin should display something in the Device main view
|
||||
*/
|
||||
public boolean hasMainActivity(Context context) {
|
||||
public boolean displayAsButton(Context context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement here what your plugin should do when clicked
|
||||
*/
|
||||
public void startMainActivity(Activity parentActivity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the entry for this app should appear in the context menu instead of the main view
|
||||
*/
|
||||
@@ -173,6 +166,12 @@ public abstract class Plugin {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement here what your plugin should do when clicked
|
||||
*/
|
||||
public void startMainActivity(Activity parentActivity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false when we should avoid loading this Plugin for {@link #device}.
|
||||
* <p>
|
||||
|
@@ -9,9 +9,9 @@ package org.kde.kdeconnect.Plugins;
|
||||
import static org.apache.commons.collections4.SetUtils.unmodifiableSet;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -38,7 +38,7 @@ public class PluginFactory {
|
||||
|
||||
public static class PluginInfo {
|
||||
|
||||
PluginInfo(@NonNull String displayName, @NonNull String description, @Nullable Drawable icon,
|
||||
PluginInfo(@NonNull String displayName, @NonNull String description, @DrawableRes int icon,
|
||||
boolean enabledByDefault, boolean hasSettings, boolean supportsDeviceSpecificSettings,
|
||||
boolean listenToUnpaired, @NonNull String[] supportedPacketTypes, @NonNull String[] outgoingPacketTypes,
|
||||
@NonNull Class<? extends Plugin> instantiableClass) {
|
||||
@@ -62,7 +62,7 @@ public class PluginFactory {
|
||||
return description;
|
||||
}
|
||||
|
||||
public @Nullable Drawable getIcon() {
|
||||
public @DrawableRes int getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class PluginFactory {
|
||||
|
||||
private final @NonNull String displayName;
|
||||
private final @NonNull String description;
|
||||
private final @Nullable Drawable icon;
|
||||
private final @DrawableRes int icon;
|
||||
private final boolean enabledByDefault;
|
||||
private final boolean hasSettings;
|
||||
private final boolean supportsDeviceSpecificSettings;
|
||||
|
@@ -12,11 +12,10 @@ import static org.kde.kdeconnect.Plugins.MousePadPlugin.KeyListenerView.SpecialK
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.kde.kdeconnect.Device;
|
||||
@@ -51,8 +50,8 @@ public class PresenterPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.ic_presenter_24dp);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.ic_presenter_24dp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,7 +60,7 @@ public class PresenterPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity(Context context) {
|
||||
public boolean displayAsButton(Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,6 @@ package org.kde.kdeconnect.Plugins.RemoteKeyboardPlugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.SystemClock;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
@@ -20,8 +19,8 @@ import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
@@ -153,8 +152,8 @@ public class RemoteKeyboardPlugin extends Plugin implements SharedPreferences.On
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.ic_action_keyboard_24dp);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.ic_action_keyboard_24dp;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -11,12 +11,11 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.json.JSONArray;
|
||||
@@ -78,8 +77,8 @@ public class RunCommandPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.run_command_plugin_icon_24dp);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.run_command_plugin_icon_24dp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,7 +172,7 @@ public class RunCommandPlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity(Context context) {
|
||||
public boolean displayAsButton(Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,6 @@ import android.app.Activity;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -20,6 +19,7 @@ import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.core.content.ContextCompat;
|
||||
@@ -84,8 +84,8 @@ public class SharePlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.share_plugin_action_24dp);
|
||||
public @DrawableRes int getIcon() {
|
||||
return R.drawable.share_plugin_action_24dp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,7 +94,7 @@ public class SharePlugin extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity(Context context) {
|
||||
public boolean displayAsButton(Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -14,24 +14,36 @@ import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.kde.kdeconnect.BackgroundService
|
||||
import org.kde.kdeconnect.Device
|
||||
import org.kde.kdeconnect.Device.PairingCallback
|
||||
import org.kde.kdeconnect.Device.PluginsChangedListener
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
|
||||
import org.kde.kdeconnect.KdeConnect
|
||||
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin
|
||||
import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin
|
||||
import org.kde.kdeconnect.Plugins.Plugin
|
||||
import org.kde.kdeconnect.UserInterface.List.PluginAdapter
|
||||
import org.kde.kdeconnect.UserInterface.List.PluginItem
|
||||
import org.kde.kdeconnect.Plugins.PresenterPlugin.PresenterPlugin
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin
|
||||
import org.kde.kdeconnect.UserInterface.compose.AppTheme
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityDeviceBinding
|
||||
import org.kde.kdeconnect_tp.databinding.ViewPairErrorBinding
|
||||
import org.kde.kdeconnect_tp.databinding.ViewPairRequestBinding
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Main view. Displays the current device and its plugins
|
||||
@@ -44,14 +56,8 @@ class DeviceFragment : Fragment() {
|
||||
private var device: Device? = null
|
||||
private val mActivity: MainActivity? by lazy { activity as MainActivity? }
|
||||
|
||||
//TODO use LinkedHashMap and delete irrelevant records when plugins changed
|
||||
private val pluginListItems: ArrayList<PluginItem> = ArrayList()
|
||||
private val permissionListItems: ArrayList<PluginItem> = ArrayList()
|
||||
|
||||
/**
|
||||
* Top-level ViewBinding for this fragment.
|
||||
*
|
||||
* Host for [.pluginListItems].
|
||||
*/
|
||||
private var deviceBinding: ActivityDeviceBinding? = null
|
||||
private fun requireDeviceBinding() = deviceBinding ?: throw IllegalStateException("deviceBinding is not set")
|
||||
@@ -100,6 +106,10 @@ class DeviceFragment : Fragment() {
|
||||
|
||||
device = KdeConnect.getInstance().getDevice(deviceId)
|
||||
|
||||
requireErrorBinding().errorMessageContainer.setOnRefreshListener {
|
||||
this.refreshDevicesAction()
|
||||
}
|
||||
|
||||
requirePairingBinding().pairButton.setOnClickListener {
|
||||
with(requirePairingBinding()) {
|
||||
pairButton.visibility = View.GONE
|
||||
@@ -128,10 +138,6 @@ class DeviceFragment : Fragment() {
|
||||
}
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
requireDeviceBinding().pluginsList.layoutManager =
|
||||
GridLayoutManager(requireContext(), resources.getInteger(R.integer.plugins_columns))
|
||||
requireDeviceBinding().permissionsList.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
device?.apply {
|
||||
mActivity?.supportActionBar?.title = name
|
||||
addPairingCallback(pairingCallback)
|
||||
@@ -146,7 +152,15 @@ class DeviceFragment : Fragment() {
|
||||
return deviceBinding.root
|
||||
}
|
||||
|
||||
private val pluginsChangedListener = PluginsChangedListener { refreshUI() }
|
||||
private fun refreshDevicesAction() {
|
||||
BackgroundService.ForceRefreshConnections(requireContext())
|
||||
requireErrorBinding().errorMessageContainer.isRefreshing = true
|
||||
requireErrorBinding().errorMessageContainer.postDelayed({
|
||||
errorBinding?.errorMessageContainer?.isRefreshing = false // check for null since the view might be destroyed by now
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
private val pluginsChangedListener = PluginsChangedListener { mActivity?.runOnUiThread { refreshUI() } }
|
||||
override fun onDestroyView() {
|
||||
device?.apply {
|
||||
removePluginsChangedListener(pluginsChangedListener)
|
||||
@@ -208,8 +222,8 @@ class DeviceFragment : Fragment() {
|
||||
}
|
||||
if (device.isPaired) {
|
||||
menu.add(R.string.device_menu_unpair).setOnMenuItemClickListener {
|
||||
// Remove listener so buttons don't show for an instant before changing the view
|
||||
device.apply {
|
||||
// Remove listener so buttons don't show for an instant before changing the view
|
||||
removePairingCallback(pairingCallback)
|
||||
removePluginsChangedListener(pluginsChangedListener)
|
||||
unpair()
|
||||
@@ -245,106 +259,54 @@ class DeviceFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun refreshUI() {
|
||||
val device = device ?: return
|
||||
//Once in-app, there is no point in keep displaying the notification if any
|
||||
device.hidePairingNotification()
|
||||
mActivity?.runOnUiThread(object : Runnable {
|
||||
override fun run() {
|
||||
if (device.isPairRequestedByPeer) {
|
||||
with (requirePairingBinding()) {
|
||||
pairMessage.setText(R.string.pair_requested)
|
||||
pairVerification.visibility = View.VISIBLE
|
||||
pairVerification.text = SslHelper.getVerificationKey(SslHelper.certificate, device.certificate)
|
||||
pairingButtons.visibility = View.VISIBLE
|
||||
pairProgress.visibility = View.GONE
|
||||
pairButton.visibility = View.GONE
|
||||
pairRequestButtons.visibility = View.VISIBLE
|
||||
}
|
||||
with (requireDeviceBinding()) {
|
||||
permissionsList.visibility = View.GONE
|
||||
pluginsList.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
val paired = device.isPaired
|
||||
val reachable = device.isReachable
|
||||
requirePairingBinding().pairingButtons.visibility = if (paired) View.GONE else View.VISIBLE
|
||||
if (paired && !reachable) {
|
||||
requireErrorBinding().errorMessageContainer.visibility = View.VISIBLE
|
||||
requireErrorBinding().notReachableMessage.visibility = View.VISIBLE
|
||||
requireDeviceBinding().permissionsList.visibility = View.GONE
|
||||
requireDeviceBinding().pluginsList.visibility = View.GONE
|
||||
} else if (paired) {
|
||||
requireErrorBinding().errorMessageContainer.visibility = View.GONE
|
||||
requireErrorBinding().notReachableMessage.visibility = View.GONE
|
||||
requireDeviceBinding().permissionsList.visibility = View.VISIBLE
|
||||
requireDeviceBinding().pluginsList.visibility = View.VISIBLE
|
||||
} else {
|
||||
requireDeviceBinding().permissionsList.visibility = View.GONE
|
||||
requireDeviceBinding().pluginsList.visibility = View.GONE
|
||||
}
|
||||
try {
|
||||
if (paired && reachable) {
|
||||
//Plugins button list
|
||||
val plugins: Collection<Plugin> = device.loadedPlugins.values
|
||||
|
||||
//TODO look for LinkedHashMap mention above
|
||||
pluginListItems.clear()
|
||||
permissionListItems.clear()
|
||||
|
||||
//Fill enabled plugins ArrayList
|
||||
for (p in plugins) {
|
||||
if (p.hasMainActivity(context) && !p.displayInContextMenu()) {
|
||||
pluginListItems.add(
|
||||
PluginItem(requireContext(), p, { p.startMainActivity(mActivity) })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Fill permissionListItems with permissions plugins
|
||||
createPermissionsList(
|
||||
device.pluginsWithoutPermissions,
|
||||
R.string.plugins_need_permission
|
||||
) { p: Plugin ->
|
||||
p.permissionExplanationDialog.show(childFragmentManager, null)
|
||||
}
|
||||
createPermissionsList(
|
||||
device.pluginsWithoutOptionalPermissions,
|
||||
R.string.plugins_need_optional_permission
|
||||
) { p: Plugin ->
|
||||
p.optionalPermissionExplanationDialog.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()
|
||||
}
|
||||
|
||||
mActivity?.invalidateOptionsMenu()
|
||||
} catch (e: IllegalStateException) {
|
||||
//Ignore: The activity was closed while we were trying to update it
|
||||
} catch (e: ConcurrentModificationException) {
|
||||
Log.e(TAG, "ConcurrentModificationException")
|
||||
this.run() //Try again
|
||||
}
|
||||
}
|
||||
if (device.isPairRequestedByPeer) {
|
||||
with (requirePairingBinding()) {
|
||||
pairMessage.setText(R.string.pair_requested)
|
||||
pairVerification.visibility = View.VISIBLE
|
||||
pairVerification.text = SslHelper.getVerificationKey(SslHelper.certificate, device.certificate)
|
||||
pairingButtons.visibility = View.VISIBLE
|
||||
pairProgress.visibility = View.GONE
|
||||
pairButton.visibility = View.GONE
|
||||
pairRequestButtons.visibility = View.VISIBLE
|
||||
}
|
||||
})
|
||||
requireDeviceBinding().deviceView.visibility = View.GONE
|
||||
} else {
|
||||
if (device.isPaired) {
|
||||
requirePairingBinding().pairingButtons.visibility = View.GONE
|
||||
if (device.isReachable) {
|
||||
requireErrorBinding().errorMessageContainer.visibility = View.GONE
|
||||
requireDeviceBinding().deviceView.visibility = View.VISIBLE
|
||||
requireDeviceBinding().deviceViewCompose.apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent { AppTheme { PluginList(device) } }
|
||||
}
|
||||
displayBatteryInfoIfPossible()
|
||||
} else {
|
||||
requireErrorBinding().errorMessageContainer.visibility = View.VISIBLE
|
||||
requireDeviceBinding().deviceView.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
requireErrorBinding().errorMessageContainer.visibility = View.GONE
|
||||
requireDeviceBinding().deviceView.visibility = View.GONE
|
||||
requirePairingBinding().pairingButtons.visibility = View.VISIBLE
|
||||
}
|
||||
mActivity?.invalidateOptionsMenu()
|
||||
}
|
||||
}
|
||||
|
||||
private val pairingCallback: PairingCallback = object : PairingCallback {
|
||||
override fun incomingRequest() {
|
||||
refreshUI()
|
||||
mActivity?.runOnUiThread { refreshUI() }
|
||||
}
|
||||
|
||||
override fun pairingSuccessful() {
|
||||
refreshUI()
|
||||
mActivity?.runOnUiThread { refreshUI() }
|
||||
}
|
||||
|
||||
override fun pairingFailed(error: String) {
|
||||
@@ -375,28 +337,6 @@ class DeviceFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPermissionsList(
|
||||
plugins: ConcurrentHashMap<String, Plugin>,
|
||||
@StringRes headerText: Int,
|
||||
action: (Plugin) -> Unit,
|
||||
) {
|
||||
if (plugins.isEmpty()) return
|
||||
val device = device ?: return
|
||||
permissionListItems.add(
|
||||
PluginItem(
|
||||
context = requireContext(),
|
||||
header = requireContext().getString(headerText),
|
||||
textStyleRes = com.google.android.material.R.style.TextAppearance_Material3_BodyMedium,
|
||||
)
|
||||
)
|
||||
for (plugin in plugins.values) {
|
||||
if (device.isPluginEnabled(plugin.pluginKey)) {
|
||||
permissionListItems.add(
|
||||
PluginItem(requireContext(), plugin, action, com.google.android.material.R.style.TextAppearance_Material3_LabelLarge)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tries to display battery info for the remote device. Includes
|
||||
@@ -429,4 +369,120 @@ class DeviceFragment : Fragment() {
|
||||
super.onDetach()
|
||||
mActivity?.supportActionBar?.subtitle = null
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun PreviewCompose() {
|
||||
val plugins = listOf(MprisPlugin(), RunCommandPlugin(), PresenterPlugin())
|
||||
plugins.forEach { it.setContext(LocalContext.current, null) }
|
||||
PluginButtons(plugins.iterator(), 2)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PluginButton(plugin : Plugin, modifier: Modifier) {
|
||||
Card(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
modifier = modifier.height(height = 120.dp),
|
||||
onClick = { plugin.startMainActivity(mActivity) }
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = Modifier.padding(horizontal=16.dp, vertical=10.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(plugin.icon),
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = plugin.actionName,
|
||||
maxLines = 2,
|
||||
fontSize = 18.sp,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PluginButtons(plugins: Iterator<Plugin>, numColumns: Int)
|
||||
{
|
||||
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
while (plugins.hasNext()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
repeat(numColumns) {
|
||||
if (plugins.hasNext()) {
|
||||
PluginButton(
|
||||
plugin = plugins.next(),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
} else {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PluginsWithoutPermissions(title : String, plugins: Collection<Plugin>, action : (plugin: Plugin) -> Unit)
|
||||
{
|
||||
Text(
|
||||
text = title,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
|
||||
)
|
||||
plugins.forEach { plugin ->
|
||||
Text(
|
||||
text = plugin.displayName,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { action(plugin) }
|
||||
.padding(start = 28.dp, end = 16.dp, top = 12.dp, bottom = 12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun PluginList(device : Device) {
|
||||
|
||||
val context = requireContext()
|
||||
|
||||
val pluginsWithButtons = device.loadedPlugins.values.filter { it.displayAsButton(context) }.iterator()
|
||||
val pluginsNeedPermissions = device.pluginsWithoutPermissions.values.filter { device.isPluginEnabled(it.pluginKey) }
|
||||
val pluginsNeedOptionalPermissions = device.pluginsWithoutOptionalPermissions.values.filter { device.isPluginEnabled(it.pluginKey) }
|
||||
|
||||
Surface {
|
||||
Column(modifier = Modifier.padding(top = 16.dp)) {
|
||||
|
||||
val numColumns = resources.getInteger(R.integer.plugins_columns)
|
||||
PluginButtons(pluginsWithButtons, numColumns)
|
||||
|
||||
Spacer(modifier = Modifier.padding(vertical=6.dp))
|
||||
|
||||
if (pluginsNeedPermissions.isNotEmpty()) {
|
||||
PluginsWithoutPermissions(
|
||||
title = getString(R.string.plugins_need_permission),
|
||||
plugins = pluginsNeedPermissions,
|
||||
action = { it.permissionExplanationDialog.show(childFragmentManager,null) }
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(vertical=2.dp))
|
||||
}
|
||||
|
||||
if (pluginsNeedOptionalPermissions.isNotEmpty()) {
|
||||
PluginsWithoutPermissions(
|
||||
title = getString(R.string.plugins_need_optional_permission),
|
||||
plugins = pluginsNeedOptionalPermissions,
|
||||
action = { it.optionalPermissionExplanationDialog.show(childFragmentManager,null) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.kde.kdeconnect_tp.databinding.ListPluginEntryBinding;
|
||||
import org.kde.kdeconnect_tp.databinding.ListCardEntryBinding;
|
||||
|
||||
public class EntryItemWithIcon implements ListAdapter.Item {
|
||||
protected final String title;
|
||||
@@ -26,7 +26,7 @@ public class EntryItemWithIcon implements ListAdapter.Item {
|
||||
@NonNull
|
||||
@Override
|
||||
public View inflateView(@NonNull LayoutInflater layoutInflater) {
|
||||
final ListPluginEntryBinding binding = ListPluginEntryBinding.inflate(layoutInflater);
|
||||
final ListCardEntryBinding binding = ListCardEntryBinding.inflate(layoutInflater);
|
||||
|
||||
binding.listItemEntryTitle.setText(title);
|
||||
binding.listItemEntryIcon.setImageDrawable(icon);
|
||||
|
@@ -1,53 +0,0 @@
|
||||
package org.kde.kdeconnect.UserInterface.List
|
||||
|
||||
import android.content.Context
|
||||
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>() {
|
||||
|
||||
private lateinit var context: Context
|
||||
|
||||
override fun onCreateViewHolder(viewGroup: ViewGroup, type: Int) : PluginViewHolder {
|
||||
context = viewGroup.context
|
||||
return PluginViewHolder(
|
||||
LayoutInflater.from(context).inflate(layoutRes, viewGroup, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemCount() = pluginList.size
|
||||
|
||||
override fun onBindViewHolder(holder: PluginViewHolder, position: Int) {
|
||||
pluginList[position].let { plugin ->
|
||||
holder.pluginTitle.text = plugin.header
|
||||
holder.pluginIcon?.setImageDrawable(plugin.icon)
|
||||
|
||||
// Remove context when we require API 23+
|
||||
plugin.textStyleRes?.let { holder.pluginTitle.setTextAppearance(context, 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)
|
||||
}
|
||||
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
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.actionName,
|
||||
textStyleRes = textStyleRes,
|
||||
) {
|
||||
this.action = { action(plugin) }
|
||||
this.icon = plugin.icon
|
||||
}
|
||||
}
|
@@ -44,7 +44,7 @@ private const val STATE_SELECTED_DEVICE = "selected_device" //Saved persistently
|
||||
class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
|
||||
private val mNavigationView: NavigationView by lazy { binding.navigationDrawer }
|
||||
private val mDrawerLayout: DrawerLayout? by lazy { binding.drawerLayout }
|
||||
private var mDrawerLayout: DrawerLayout? = null
|
||||
|
||||
private lateinit var mNavViewDeviceName: TextView
|
||||
|
||||
@@ -76,7 +76,9 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
super.onCreate(savedInstanceState)
|
||||
DeviceHelper.initializeDeviceId(this)
|
||||
|
||||
setContentView(binding.root)
|
||||
val root = binding.root
|
||||
setContentView(root)
|
||||
mDrawerLayout = if (root is DrawerLayout) root else null
|
||||
|
||||
val mDrawerHeader = mNavigationView.getHeaderView(0)
|
||||
mNavViewDeviceName = mDrawerHeader.findViewById(R.id.device_name)
|
||||
|
28
src/org/kde/kdeconnect/UserInterface/compose/AppTheme.kt
Normal file
28
src/org/kde/kdeconnect/UserInterface/compose/AppTheme.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package org.kde.kdeconnect.UserInterface.compose
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
@Composable
|
||||
fun AppTheme(
|
||||
content: @Composable () -> Unit) {
|
||||
val darkTheme = isSystemInDarkTheme()
|
||||
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
val colorScheme = when {
|
||||
dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current)
|
||||
dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current)
|
||||
darkTheme -> darkColorScheme()
|
||||
else -> lightColorScheme()
|
||||
}
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
content = content
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user