From ae24cd6ca846aeb594398223f53024d2910f6622 Mon Sep 17 00:00:00 2001 From: Mash Kyrielight Date: Tue, 31 Dec 2024 18:00:39 +0000 Subject: [PATCH] Fix insets for android 15 Apps are edge-to-edge by default on devices running Android 15 https://developer.android.com/develop/ui/views/layout/edge-to-edge Fix: https://bugs.kde.org/show_bug.cgi?id=495999 --- res/layout/activity_about_kde.xml | 2 + res/layout/activity_custom_devices.xml | 1 + res/layout/activity_device.xml | 3 +- res/layout/activity_licenses.xml | 3 +- res/layout/activity_mpris.xml | 3 +- res/layout/activity_sendkeystrokes.xml | 1 + res/layout/devices_list.xml | 1 + res/layout/fragment_about.xml | 4 +- .../kde/kdeconnect/Helpers/WindowHelper.kt | 25 ++++++++ .../BigscreenPlugin/BigscreenActivity.java | 39 +++++++----- .../FindMyPhoneActivity.java | 23 ++++--- .../MousePadPlugin/ComposeSendActivity.kt | 2 + .../MousePadPlugin/MousePadActivity.java | 35 +++++++---- .../SendKeystrokesToHostActivity.java | 39 ++++++++---- .../Plugins/MprisPlugin/MprisActivity.kt | 21 +++---- .../NotificationFilterActivity.java | 32 ++++++---- .../PresenterPlugin/PresenterActivity.kt | 6 +- .../RunCommandPlugin/RunCommandActivity.java | 39 +++++++----- .../Plugins/SharePlugin/ShareActivity.java | 42 +++++++++---- .../UserInterface/About/AboutFragment.kt | 39 +++++++----- .../UserInterface/About/AboutKDEActivity.kt | 16 +++-- .../UserInterface/About/LicensesActivity.kt | 15 +++-- .../UserInterface/CustomDevicesActivity.java | 34 +++++++--- .../UserInterface/DeviceFragment.kt | 6 ++ .../UserInterface/PairingFragment.java | 2 + .../UserInterface/PluginSettingsActivity.java | 19 ++++-- .../UserInterface/SettingsFragment.kt | 6 ++ .../UserInterface/TrustedNetworksActivity.kt | 10 ++- src/org/kde/kdeconnect/base/BaseActivity.kt | 45 +++++++++++++ .../kde/kdeconnect/extensions/ViewBinding.kt | 15 +++++ src/org/kde/kdeconnect/extensions/Window.kt | 63 +++++++++++++++++++ 31 files changed, 444 insertions(+), 147 deletions(-) create mode 100644 src/org/kde/kdeconnect/Helpers/WindowHelper.kt create mode 100644 src/org/kde/kdeconnect/base/BaseActivity.kt create mode 100644 src/org/kde/kdeconnect/extensions/ViewBinding.kt create mode 100644 src/org/kde/kdeconnect/extensions/Window.kt diff --git a/res/layout/activity_about_kde.xml b/res/layout/activity_about_kde.xml index 2426d32b..e0fa2107 100644 --- a/res/layout/activity_about_kde.xml +++ b/res/layout/activity_about_kde.xml @@ -21,6 +21,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" + android:clipToPadding="false" + android:id="@+id/scroll_view" app:layout_behavior="@string/appbar_scrolling_view_behavior"> + android:layout_height="match_parent" + android:clipToPadding="false"> + android:layout_height="match_parent" + android:clipToPadding="false"/> diff --git a/res/layout/activity_mpris.xml b/res/layout/activity_mpris.xml index 45fed849..69723542 100644 --- a/res/layout/activity_mpris.xml +++ b/res/layout/activity_mpris.xml @@ -17,7 +17,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted + android:layout_height="wrap_content" + android:fitsSystemWindows="true"> diff --git a/res/layout/devices_list.xml b/res/layout/devices_list.xml index 4d983db3..b590ccb6 100644 --- a/res/layout/devices_list.xml +++ b/res/layout/devices_list.xml @@ -22,6 +22,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted android:descendantFocusability="afterDescendants" android:dividerHeight="12dp" android:orientation="vertical" + android:clipToPadding="false" tools:listitem="@layout/list_card_entry" tools:context=".MainActivity" /> diff --git a/res/layout/fragment_about.xml b/res/layout/fragment_about.xml index 152b038e..4c8cb694 100644 --- a/res/layout/fragment_about.xml +++ b/res/layout/fragment_about.xml @@ -11,7 +11,9 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" - android:fillViewport="true"> + android:fillViewport="true" + android:clipToPadding="false" + android:id="@+id/scroll_view"> + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ +package org.kde.kdeconnect.Helpers + +import android.view.View +import org.kde.kdeconnect.extensions.setupBottomMargin +import org.kde.kdeconnect.extensions.setupBottomPadding + +object WindowHelper { + + // for java only + @JvmStatic + fun setupBottomPadding(view: View) { + view.setupBottomPadding() + } + + // for java only + @JvmStatic + fun setupBottomMargin(view: View) { + view.setupBottomMargin() + } +} \ No newline at end of file diff --git a/src/org/kde/kdeconnect/Plugins/BigscreenPlugin/BigscreenActivity.java b/src/org/kde/kdeconnect/Plugins/BigscreenPlugin/BigscreenActivity.java index 5b324f6f..3ce20443 100644 --- a/src/org/kde/kdeconnect/Plugins/BigscreenPlugin/BigscreenActivity.java +++ b/src/org/kde/kdeconnect/Plugins/BigscreenPlugin/BigscreenActivity.java @@ -15,37 +15,46 @@ import android.speech.RecognizerIntent; import android.speech.SpeechRecognizer; import android.view.View; -import androidx.appcompat.app.AppCompatActivity; +import androidx.annotation.NonNull; import org.kde.kdeconnect.KdeConnect; import org.kde.kdeconnect.UserInterface.MainActivity; import org.kde.kdeconnect.UserInterface.PermissionsAlertDialogFragment; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.databinding.ActivityBigscreenBinding; import java.util.ArrayList; import java.util.Objects; -public class BigscreenActivity extends AppCompatActivity { +import kotlin.Lazy; +import kotlin.LazyKt; + +public class BigscreenActivity extends BaseActivity { private static final int REQUEST_SPEECH = 100; + + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivityBigscreenBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + protected ActivityBigscreenBinding getBinding() { + return lazyBinding.getValue(); + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final ActivityBigscreenBinding binding = ActivityBigscreenBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - setSupportActionBar(binding.toolbarLayout.toolbar); + setSupportActionBar(getBinding().toolbarLayout.toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); final String deviceId = getIntent().getStringExtra("deviceId"); if (!SpeechRecognizer.isRecognitionAvailable(this)) { - binding.micButton.setEnabled(false); - binding.micButton.setVisibility(View.INVISIBLE); + getBinding().micButton.setEnabled(false); + getBinding().micButton.setVisibility(View.INVISIBLE); } BigscreenPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, BigscreenPlugin.class); @@ -54,13 +63,13 @@ public class BigscreenActivity extends AppCompatActivity { return; } - binding.leftButton.setOnClickListener(v -> plugin.sendLeft()); - binding.rightButton.setOnClickListener(v -> plugin.sendRight()); - binding.upButton.setOnClickListener(v -> plugin.sendUp()); - binding.downButton.setOnClickListener(v -> plugin.sendDown()); - binding.selectButton.setOnClickListener(v -> plugin.sendSelect()); - binding.homeButton.setOnClickListener(v -> plugin.sendHome()); - binding.micButton.setOnClickListener(v -> { + getBinding().leftButton.setOnClickListener(v -> plugin.sendLeft()); + getBinding().rightButton.setOnClickListener(v -> plugin.sendRight()); + getBinding().upButton.setOnClickListener(v -> plugin.sendUp()); + getBinding().downButton.setOnClickListener(v -> plugin.sendDown()); + getBinding().selectButton.setOnClickListener(v -> plugin.sendSelect()); + getBinding().homeButton.setOnClickListener(v -> plugin.sendHome()); + getBinding().micButton.setOnClickListener(v -> { if (plugin.hasMicPermission()) { activateSTT(); } else { diff --git a/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneActivity.java b/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneActivity.java index a344f99d..6c6a1087 100644 --- a/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneActivity.java +++ b/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneActivity.java @@ -10,26 +10,35 @@ import android.util.Log; import android.view.Window; import android.view.WindowManager; -import androidx.appcompat.app.AppCompatActivity; +import androidx.annotation.NonNull; import org.kde.kdeconnect.KdeConnect; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.databinding.ActivityFindMyPhoneBinding; import java.util.Objects; -public class FindMyPhoneActivity extends AppCompatActivity { +import kotlin.Lazy; +import kotlin.LazyKt; + +public class FindMyPhoneActivity extends BaseActivity { static final String EXTRA_DEVICE_ID = "deviceId"; String deviceId; + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivityFindMyPhoneBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + protected ActivityFindMyPhoneBinding getBinding() { + return lazyBinding.getValue(); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final ActivityFindMyPhoneBinding binding = ActivityFindMyPhoneBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - setSupportActionBar(binding.toolbarLayout.toolbar); + setSupportActionBar(getBinding().toolbarLayout.toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); @@ -47,7 +56,7 @@ public class FindMyPhoneActivity extends AppCompatActivity { WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ); - binding.bFindMyPhone.setOnClickListener(view -> finish()); + getBinding().bFindMyPhone.setOnClickListener(view -> finish()); } @Override diff --git a/src/org/kde/kdeconnect/Plugins/MousePadPlugin/ComposeSendActivity.kt b/src/org/kde/kdeconnect/Plugins/MousePadPlugin/ComposeSendActivity.kt index 60480811..961d4aa5 100644 --- a/src/org/kde/kdeconnect/Plugins/MousePadPlugin/ComposeSendActivity.kt +++ b/src/org/kde/kdeconnect/Plugins/MousePadPlugin/ComposeSendActivity.kt @@ -30,6 +30,7 @@ import org.kde.kdeconnect.UserInterface.compose.KdeTextButton import org.kde.kdeconnect.UserInterface.compose.KdeTextField import org.kde.kdeconnect.UserInterface.compose.KdeTheme import org.kde.kdeconnect.UserInterface.compose.KdeTopAppBar +import org.kde.kdeconnect.extensions.safeDrawPadding import org.kde.kdeconnect_tp.R private const val INPUT_CACHE_KEY = "compose_send_input_cache" @@ -91,6 +92,7 @@ class ComposeSendActivity : AppCompatActivity() { private fun ComposeSendScreen() { KdeTheme(this) { Scaffold( + modifier = Modifier.safeDrawPadding(), topBar = { KdeTopAppBar( title = stringResource(R.string.compose_send_title), diff --git a/src/org/kde/kdeconnect/Plugins/MousePadPlugin/MousePadActivity.java b/src/org/kde/kdeconnect/Plugins/MousePadPlugin/MousePadActivity.java index 5102afc4..41b2043b 100644 --- a/src/org/kde/kdeconnect/Plugins/MousePadPlugin/MousePadActivity.java +++ b/src/org/kde/kdeconnect/Plugins/MousePadPlugin/MousePadActivity.java @@ -13,7 +13,6 @@ import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; -import android.util.Log; import android.view.GestureDetector; import android.view.HapticFeedbackConstants; import android.view.Menu; @@ -24,18 +23,23 @@ import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; -import androidx.appcompat.app.AppCompatActivity; +import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; import org.kde.kdeconnect.KdeConnect; import org.kde.kdeconnect.UserInterface.PluginSettingsActivity; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.R; +import org.kde.kdeconnect_tp.databinding.ActivityMousepadBinding; import java.util.Objects; +import kotlin.Lazy; +import kotlin.LazyKt; + public class MousePadActivity - extends AppCompatActivity + extends BaseActivity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, MousePadGestureDetector.OnGestureListener, @@ -74,6 +78,14 @@ public class MousePadActivity private boolean prefsApplied = false; + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivityMousepadBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + protected ActivityMousepadBinding getBinding() { + return lazyBinding.getValue(); + } + enum ClickType { LEFT, RIGHT, MIDDLE, NONE; @@ -131,15 +143,12 @@ public class MousePadActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_mousepad); - - setSupportActionBar(findViewById(R.id.toolbar)); + setSupportActionBar(getBinding().toolbarLayout.toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); - - findViewById(R.id.mouse_click_left).setOnClickListener(v -> sendLeftClick()); - findViewById(R.id.mouse_click_middle).setOnClickListener(v -> sendMiddleClick()); - findViewById(R.id.mouse_click_right).setOnClickListener(v -> sendRightClick()); + getBinding().mouseClickLeft.setOnClickListener(v -> sendLeftClick()); + getBinding().mouseClickMiddle.setOnClickListener(v -> sendMiddleClick()); + getBinding().mouseClickRight.setOnClickListener(v -> sendRightClick()); deviceId = getIntent().getStringExtra("deviceId"); @@ -150,7 +159,7 @@ public class MousePadActivity mDetector.setOnDoubleTapListener(this); mSensorManager = ContextCompat.getSystemService(this, SensorManager.class); - keyListenerView = findViewById(R.id.keyListener); + keyListenerView = getBinding().keyListener; keyListenerView.setDeviceId(deviceId); prefs = PreferenceManager.getDefaultSharedPreferences(this); @@ -582,9 +591,9 @@ public class MousePadActivity } if (prefs.getBoolean(getString(R.string.mousepad_mouse_buttons_enabled_pref), true)) { - findViewById(R.id.mouse_buttons).setVisibility(View.VISIBLE); + getBinding().mouseButtons.setVisibility(View.VISIBLE); } else { - findViewById(R.id.mouse_buttons).setVisibility(View.GONE); + getBinding().mouseButtons.setVisibility(View.GONE); } doubleTapDragEnabled = prefs.getBoolean(getString(R.string.mousepad_doubletap_drag_enabled_pref), true); diff --git a/src/org/kde/kdeconnect/Plugins/MousePadPlugin/SendKeystrokesToHostActivity.java b/src/org/kde/kdeconnect/Plugins/MousePadPlugin/SendKeystrokesToHostActivity.java index 7fee2c83..7e10c031 100644 --- a/src/org/kde/kdeconnect/Plugins/MousePadPlugin/SendKeystrokesToHostActivity.java +++ b/src/org/kde/kdeconnect/Plugins/MousePadPlugin/SendKeystrokesToHostActivity.java @@ -13,17 +13,18 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AppCompatActivity; - import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Helpers.SafeTextChecker; +import org.kde.kdeconnect.Helpers.WindowHelper; import org.kde.kdeconnect.KdeConnect; import org.kde.kdeconnect.NetworkPacket; import org.kde.kdeconnect.UserInterface.List.EntryItemWithIcon; import org.kde.kdeconnect.UserInterface.List.ListAdapter; import org.kde.kdeconnect.UserInterface.List.SectionItem; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.databinding.ActivitySendkeystrokesBinding; @@ -33,7 +34,10 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -public class SendKeystrokesToHostActivity extends AppCompatActivity { +import kotlin.Lazy; +import kotlin.LazyKt; + +public class SendKeystrokesToHostActivity extends BaseActivity { // text with these length and content can be send without user confirmation. // more or less chosen arbitrarily, so that we allow short PINS and TANS without interruption (if only one device is connected) @@ -42,19 +46,30 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity { public static final String SAFE_CHARS = "1234567890"; - private ActivitySendkeystrokesBinding binding; private boolean contentIsOkay; + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivitySendkeystrokesBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + public ActivitySendkeystrokesBinding getBinding() { + return lazyBinding.getValue(); + } + + @Override + public boolean isScrollable() { + return true; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = ActivitySendkeystrokesBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - setSupportActionBar(binding.toolbarLayout.toolbar); + setSupportActionBar(getBinding().toolbarLayout.toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); + + WindowHelper.setupBottomPadding(getBinding().devicesList); } @@ -73,7 +88,7 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity { if ("text/x-keystrokes".equals(type)) { String toSend = intent.getStringExtra(Intent.EXTRA_TEXT); - binding.textToSend.setText(toSend); + getBinding().textToSend.setText(toSend); // if the preference send_safe_text_immediately is true, we will check if exactly one // device is connected and send the text to it without user confirmation, to make sending of @@ -121,7 +136,7 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity { private void sendKeys(Device deviceId) { String toSend; - if (binding.textToSend.getText() != null && (toSend = binding.textToSend.getText().toString().trim()).length() > 0) { + if (getBinding().textToSend.getText() != null && (toSend = getBinding().textToSend.getText().toString().trim()).length() > 0) { final NetworkPacket np = new NetworkPacket(MousePadPlugin.PACKET_TYPE_MOUSEPAD_REQUEST); np.set("key", toSend); MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId.getDeviceId(), MousePadPlugin.class); @@ -156,8 +171,8 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity { } } - binding.devicesList.setAdapter(new ListAdapter(SendKeystrokesToHostActivity.this, items)); - binding.devicesList.setOnItemClickListener((adapterView, view, i, l) -> { + getBinding().devicesList.setAdapter(new ListAdapter(SendKeystrokesToHostActivity.this, items)); + getBinding().devicesList.setOnItemClickListener((adapterView, view, i, l) -> { Device device = devicesList.get(i - 1); // NOTE: -1 because of the title! sendKeys(device); this.finish(); // close the activity diff --git a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.kt b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.kt index 81afd169..167ba28d 100644 --- a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.kt +++ b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.kt @@ -8,22 +8,25 @@ package org.kde.kdeconnect.Plugins.MprisPlugin import android.os.Bundle import android.view.KeyEvent import androidx.annotation.StringRes -import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import com.google.android.material.tabs.TabLayoutMediator import org.kde.kdeconnect.Plugins.SystemVolumePlugin.SystemVolumeFragment +import org.kde.kdeconnect.base.BaseActivity +import org.kde.kdeconnect.extensions.viewBinding import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.databinding.ActivityMprisBinding -class MprisActivity : AppCompatActivity() { - private lateinit var activityMprisBinding: ActivityMprisBinding +class MprisActivity : BaseActivity() { + + override val binding: ActivityMprisBinding by viewBinding(ActivityMprisBinding::inflate) + private lateinit var mprisPagerAdapter: MprisPagerAdapter override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { return when (keyCode) { KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> { - val pagePosition = activityMprisBinding.mprisTabs.selectedTabPosition + val pagePosition = binding.mprisTabs.selectedTabPosition if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { mprisPagerAdapter.onVolumeUp(pagePosition) } else { @@ -46,17 +49,13 @@ class MprisActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - activityMprisBinding = ActivityMprisBinding.inflate(layoutInflater) - - setContentView(activityMprisBinding.root) - val deviceId = intent.getStringExtra(MprisPlugin.DEVICE_ID_KEY) mprisPagerAdapter = MprisPagerAdapter(this, deviceId) - activityMprisBinding.mprisPager.adapter = mprisPagerAdapter + binding.mprisPager.adapter = mprisPagerAdapter val tabLayoutMediator = TabLayoutMediator( - activityMprisBinding.mprisTabs, activityMprisBinding.mprisPager + binding.mprisTabs, binding.mprisPager ) { tab, position -> tab.setText( mprisPagerAdapter.getTitle(position) @@ -65,7 +64,7 @@ class MprisActivity : AppCompatActivity() { tabLayoutMediator.attach() - setSupportActionBar(activityMprisBinding.toolbar) + setSupportActionBar(binding.toolbar) supportActionBar!!.setDisplayHomeAsUpEnabled(true) } diff --git a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java index fa0034c6..858ceaaf 100644 --- a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java +++ b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java @@ -27,14 +27,15 @@ import android.widget.CheckBox; import android.widget.CheckedTextView; import android.widget.ListView; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.SearchView; import androidx.core.widget.TextViewCompat; import com.google.android.material.materialswitch.MaterialSwitch; import org.kde.kdeconnect.Helpers.ThreadHelper; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.databinding.ActivityNotificationFilterBinding; @@ -43,9 +44,12 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import kotlin.Lazy; +import kotlin.LazyKt; + //TODO: Turn this into a PluginSettingsFragment -public class NotificationFilterActivity extends AppCompatActivity { - private ActivityNotificationFilterBinding binding; +public class NotificationFilterActivity extends BaseActivity { + private AppDatabase appDatabase; private String prefKey; @@ -60,6 +64,14 @@ public class NotificationFilterActivity extends AppCompatActivity { // This variable stores all app information and serves as a data source for filtering. private List mAllApps; private List apps; // Filtered data. + + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivityNotificationFilterBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + protected ActivityNotificationFilterBinding getBinding() { + return lazyBinding.getValue(); + } class AppListAdapter extends BaseAdapter { @@ -87,13 +99,13 @@ public class NotificationFilterActivity extends AppCompatActivity { if (position == 0) { checkedTextView.setText(R.string.all); TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(checkedTextView, null, null, null, null); - binding.lvFilterApps.setItemChecked(position, appDatabase.getAllEnabled()); + getBinding().lvFilterApps.setItemChecked(position, appDatabase.getAllEnabled()); } else { final AppListInfo info = apps.get(position - 1); checkedTextView.setText(info.name); TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(checkedTextView, info.icon, null, null, null); checkedTextView.setCompoundDrawablePadding((int) (8 * getResources().getDisplayMetrics().density)); - binding.lvFilterApps.setItemChecked(position, info.isEnabled); + getBinding().lvFilterApps.setItemChecked(position, info.isEnabled); } return view; @@ -105,14 +117,12 @@ public class NotificationFilterActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = ActivityNotificationFilterBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); appDatabase = new AppDatabase(NotificationFilterActivity.this, false); if (getIntent()!= null){ prefKey = getIntent().getStringExtra(NotificationsPlugin.getPrefKey()); } - setSupportActionBar(binding.toolbarLayout.toolbar); + setSupportActionBar(getBinding().toolbarLayout.toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); SharedPreferences preferences = this.getSharedPreferences(prefKey, Context.MODE_PRIVATE); @@ -152,7 +162,7 @@ public class NotificationFilterActivity extends AppCompatActivity { } private void displayAppList() { - final ListView listView = binding.lvFilterApps; + final ListView listView = getBinding().lvFilterApps; AppListAdapter adapter = new AppListAdapter(); listView.setAdapter(adapter); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); @@ -233,7 +243,7 @@ public class NotificationFilterActivity extends AppCompatActivity { } listView.setVisibility(View.VISIBLE); - binding.spinner.setVisibility(View.GONE); + getBinding().spinner.setVisibility(View.GONE); } private Drawable resizeIcon(Drawable icon, int maxSize) { @@ -284,7 +294,7 @@ public class NotificationFilterActivity extends AppCompatActivity { } } - ((AppListAdapter) binding.lvFilterApps.getAdapter()).notifyDataSetChanged(); + ((AppListAdapter) getBinding().lvFilterApps.getAdapter()).notifyDataSetChanged(); return true; } }); diff --git a/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterActivity.kt b/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterActivity.kt index 98ed4bf0..66166229 100644 --- a/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterActivity.kt +++ b/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterActivity.kt @@ -37,6 +37,7 @@ import org.kde.kdeconnect.KdeConnect import org.kde.kdeconnect.UserInterface.compose.KdeButton import org.kde.kdeconnect.UserInterface.compose.KdeTheme import org.kde.kdeconnect.UserInterface.compose.KdeTopAppBar +import org.kde.kdeconnect.extensions.safeDrawPadding import org.kde.kdeconnect_tp.R private const val VOLUME_UP = 1 @@ -116,7 +117,10 @@ class PresenterActivity : AppCompatActivity(), SensorEventListener { val sensorManager = LocalContext.current.getSystemService(SENSOR_SERVICE) as? SensorManager KdeTheme(this) { - Scaffold(topBar = { PresenterAppBar() }) { + Scaffold( + modifier = Modifier.safeDrawPadding(), + topBar = { PresenterAppBar() } + ) { Column( modifier = Modifier.fillMaxSize().padding(it).padding(16.dp), verticalArrangement = Arrangement.spacedBy(20.dp), diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandActivity.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandActivity.java index 4ff50eff..25a89137 100644 --- a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandActivity.java +++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandActivity.java @@ -17,8 +17,8 @@ import android.view.View; import android.widget.AdapterView; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import org.json.JSONException; @@ -26,6 +26,7 @@ import org.json.JSONObject; import org.kde.kdeconnect.Device; import org.kde.kdeconnect.KdeConnect; import org.kde.kdeconnect.UserInterface.List.ListAdapter; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.databinding.ActivityRunCommandBinding; @@ -35,8 +36,19 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; -public class RunCommandActivity extends AppCompatActivity { - private ActivityRunCommandBinding binding; +import kotlin.Lazy; +import kotlin.LazyKt; + +public class RunCommandActivity extends BaseActivity { + + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivityRunCommandBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + protected ActivityRunCommandBinding getBinding() { + return lazyBinding.getValue(); + } + private String deviceId; private final RunCommandPlugin.CommandsChangedCallback commandsChangedCallback = () -> runOnUiThread(this::updateView); private List commandItems; @@ -48,7 +60,7 @@ public class RunCommandActivity extends AppCompatActivity { return; } - registerForContextMenu(binding.runCommandsList); + registerForContextMenu(getBinding().runCommandsList); commandItems = new ArrayList<>(); for (JSONObject obj : plugin.getCommandList()) { @@ -63,26 +75,23 @@ public class RunCommandActivity extends AppCompatActivity { ListAdapter adapter = new ListAdapter(RunCommandActivity.this, commandItems); - binding.runCommandsList.setAdapter(adapter); - binding.runCommandsList.setOnItemClickListener((adapterView, view1, i, l) -> + getBinding().runCommandsList.setAdapter(adapter); + getBinding().runCommandsList.setOnItemClickListener((adapterView, view1, i, l) -> plugin.runCommand(commandItems.get(i).getKey())); String text = getString(R.string.addcommand_explanation); if (!plugin.canAddCommand()) { text += "\n" + getString(R.string.addcommand_explanation2); } - binding.addCommandExplanation.setText(text); - binding.addCommandExplanation.setVisibility(commandItems.isEmpty() ? View.VISIBLE : View.GONE); + getBinding().addCommandExplanation.setText(text); + getBinding().addCommandExplanation.setVisibility(commandItems.isEmpty() ? View.VISIBLE : View.GONE); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = ActivityRunCommandBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - setSupportActionBar(binding.toolbarLayout.toolbar); + setSupportActionBar(getBinding().toolbarLayout.toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); @@ -93,11 +102,11 @@ public class RunCommandActivity extends AppCompatActivity { RunCommandPlugin plugin = device.getPlugin(RunCommandPlugin.class); if (plugin != null) { if (plugin.canAddCommand()) { - binding.addCommandButton.show(); + getBinding().addCommandButton.show(); } else { - binding.addCommandButton.hide(); + getBinding().addCommandButton.hide(); } - binding.addCommandButton.setOnClickListener(v -> { + getBinding().addCommandButton.setOnClickListener(v -> { plugin.sendSetupPacket(); new AlertDialog.Builder(RunCommandActivity.this) .setTitle(R.string.add_command) diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareActivity.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareActivity.java index 188fa354..7bfa1ecb 100644 --- a/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareActivity.java +++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareActivity.java @@ -15,16 +15,19 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.webkit.URLUtil; +import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.preference.PreferenceManager; import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.Device; +import org.kde.kdeconnect.Helpers.WindowHelper; import org.kde.kdeconnect.KdeConnect; import org.kde.kdeconnect.UserInterface.List.EntryItemWithIcon; import org.kde.kdeconnect.UserInterface.List.ListAdapter; import org.kde.kdeconnect.UserInterface.List.SectionItem; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.databinding.ActivityShareBinding; @@ -34,11 +37,27 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; -public class ShareActivity extends AppCompatActivity { +import kotlin.Lazy; +import kotlin.LazyKt; + +public class ShareActivity extends BaseActivity { private static final String KEY_UNREACHABLE_URL_LIST = "key_unreachable_url_list"; - private ActivityShareBinding binding; + private SharedPreferences mSharedPrefs; + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivityShareBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + public ActivityShareBinding getBinding() { + return lazyBinding.getValue(); + } + + @Override + public boolean isScrollable() { + return true; + } + @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -59,9 +78,9 @@ public class ShareActivity extends AppCompatActivity { private void refreshDevicesAction() { BackgroundService.ForceRefreshConnections(this); - binding.devicesListLayout.refreshListLayout.setRefreshing(true); - binding.devicesListLayout.refreshListLayout.postDelayed(() -> { - binding.devicesListLayout.refreshListLayout.setRefreshing(false); + getBinding().devicesListLayout.refreshListLayout.setRefreshing(true); + getBinding().devicesListLayout.refreshListLayout.postDelayed(() -> { + getBinding().devicesListLayout.refreshListLayout.setRefreshing(false); }, 1500); } @@ -100,8 +119,8 @@ public class ShareActivity extends AppCompatActivity { } } - binding.devicesListLayout.devicesList.setAdapter(new ListAdapter(ShareActivity.this, items)); - binding.devicesListLayout.devicesList.setOnItemClickListener((adapterView, view, i, l) -> { + getBinding().devicesListLayout.devicesList.setAdapter(new ListAdapter(ShareActivity.this, items)); + getBinding().devicesListLayout.devicesList.setOnItemClickListener((adapterView, view, i, l) -> { Device device = devicesList.get(i - 1); //NOTE: -1 because of the title! SharePlugin plugin = KdeConnect.getInstance().getDevicePlugin(device.getDeviceId(), SharePlugin.class); if (intentHasUrl && !device.isReachable()) { @@ -140,20 +159,19 @@ public class ShareActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = ActivityShareBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - mSharedPrefs = PreferenceManager.getDefaultSharedPreferences (this); - setSupportActionBar(binding.toolbarLayout.toolbar); + setSupportActionBar(getBinding().toolbarLayout.toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); ActionBar actionBar = getSupportActionBar(); - binding.devicesListLayout.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction); + getBinding().devicesListLayout.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction); if (actionBar != null) { actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM); } + + WindowHelper.setupBottomPadding(getBinding().devicesListLayout.devicesList); } @Override diff --git a/src/org/kde/kdeconnect/UserInterface/About/AboutFragment.kt b/src/org/kde/kdeconnect/UserInterface/About/AboutFragment.kt index 3e5da530..24aaa4dc 100644 --- a/src/org/kde/kdeconnect/UserInterface/About/AboutFragment.kt +++ b/src/org/kde/kdeconnect/UserInterface/About/AboutFragment.kt @@ -18,11 +18,13 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import org.kde.kdeconnect.UserInterface.List.ListAdapter import org.kde.kdeconnect.UserInterface.MainActivity +import org.kde.kdeconnect.extensions.setupBottomPadding import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.databinding.FragmentAboutBinding class AboutFragment : Fragment() { - private var binding: FragmentAboutBinding? = null + private var _binding: FragmentAboutBinding? = null + private val binding get() = _binding!! private lateinit var aboutData: AboutData private var tapCount = 0 private var firstTapMillis: Long? = null @@ -46,23 +48,28 @@ class AboutFragment : Fragment() { } aboutData = requireArguments().getParcelable("ABOUT_DATA")!! - binding = FragmentAboutBinding.inflate(inflater, container, false) + _binding = FragmentAboutBinding.inflate(inflater, container, false) updateData() - return binding!!.root + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.scrollView.setupBottomPadding() } @SuppressLint("SetTextI18n") fun updateData() { // Update general info - binding!!.appName.text = aboutData.name - binding!!.appIcon.setImageDrawable(this.context?.let { ContextCompat.getDrawable(it, aboutData.icon) }) - binding!!.appVersion.text = this.context?.getString(R.string.version, aboutData.versionName) + binding.appName.text = aboutData.name + binding.appIcon.setImageDrawable(this.context?.let { ContextCompat.getDrawable(it, aboutData.icon) }) + binding.appVersion.text = this.context?.getString(R.string.version, aboutData.versionName) // Setup Easter Egg onClickListener - binding!!.generalInfoCard.setOnClickListener { + binding.generalInfoCard.setOnClickListener { if (firstTapMillis == null) { firstTapMillis = System.currentTimeMillis() } @@ -80,24 +87,24 @@ class AboutFragment : Fragment() { // Update button onClickListeners - setupInfoButton(aboutData.bugURL, binding!!.reportBugButton) - setupInfoButton(aboutData.donateURL, binding!!.donateButton) - setupInfoButton(aboutData.sourceCodeURL, binding!!.sourceCodeButton) + setupInfoButton(aboutData.bugURL, binding.reportBugButton) + setupInfoButton(aboutData.donateURL, binding.donateButton) + setupInfoButton(aboutData.sourceCodeURL, binding.sourceCodeButton) - binding!!.licensesButton.setOnClickListener { + binding.licensesButton.setOnClickListener { startActivity(Intent(context, LicensesActivity::class.java)) } - binding!!.aboutKdeButton.setOnClickListener { + binding.aboutKdeButton.setOnClickListener { startActivity(Intent(context, AboutKDEActivity::class.java)) } - setupInfoButton(aboutData.websiteURL, binding!!.websiteButton) + setupInfoButton(aboutData.websiteURL, binding.websiteButton) // Update authors - binding!!.authorsList.adapter = ListAdapter(this.requireContext(), aboutData.authors.map { AboutPersonEntryItem(it) }, false) + binding.authorsList.adapter = ListAdapter(this.requireContext(), aboutData.authors.map { AboutPersonEntryItem(it) }, false) if (aboutData.authorsFooterText != null) { - binding!!.authorsFooterText.text = context?.getString(aboutData.authorsFooterText!!) + binding.authorsFooterText.text = context?.getString(aboutData.authorsFooterText!!) } } @@ -113,6 +120,6 @@ class AboutFragment : Fragment() { override fun onDestroyView() { super.onDestroyView() - binding = null + _binding = null } } \ No newline at end of file diff --git a/src/org/kde/kdeconnect/UserInterface/About/AboutKDEActivity.kt b/src/org/kde/kdeconnect/UserInterface/About/AboutKDEActivity.kt index 63e87982..68c29d12 100644 --- a/src/org/kde/kdeconnect/UserInterface/About/AboutKDEActivity.kt +++ b/src/org/kde/kdeconnect/UserInterface/About/AboutKDEActivity.kt @@ -10,17 +10,21 @@ import android.os.Bundle import android.text.Html import android.text.Spanned import android.text.method.LinkMovementMethod -import androidx.appcompat.app.AppCompatActivity +import org.kde.kdeconnect.base.BaseActivity +import org.kde.kdeconnect.extensions.setupBottomPadding +import org.kde.kdeconnect.extensions.viewBinding import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.databinding.ActivityAboutKdeBinding -class AboutKDEActivity : AppCompatActivity() { +class AboutKDEActivity : BaseActivity() { + + override val binding: ActivityAboutKdeBinding by viewBinding(ActivityAboutKdeBinding::inflate) + + override val isScrollable: Boolean = true + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val binding = ActivityAboutKdeBinding.inflate(layoutInflater) - setContentView(binding.root) - setSupportActionBar(binding.toolbarLayout.toolbar) supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportActionBar!!.setDisplayShowHomeEnabled(true) @@ -34,6 +38,8 @@ class AboutKDEActivity : AppCompatActivity() { binding.reportBugsOrWishesTextView.movementMethod = LinkMovementMethod.getInstance() binding.joinKdeTextView.movementMethod = LinkMovementMethod.getInstance() binding.supportKdeTextView.movementMethod = LinkMovementMethod.getInstance() + + binding.scrollView.setupBottomPadding() } private fun fromHtml(html: String): Spanned { diff --git a/src/org/kde/kdeconnect/UserInterface/About/LicensesActivity.kt b/src/org/kde/kdeconnect/UserInterface/About/LicensesActivity.kt index 407f99ab..c086808b 100644 --- a/src/org/kde/kdeconnect/UserInterface/About/LicensesActivity.kt +++ b/src/org/kde/kdeconnect/UserInterface/About/LicensesActivity.kt @@ -10,23 +10,26 @@ import android.os.Bundle import android.util.DisplayMetrics import android.view.Menu import android.view.MenuItem -import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller import org.apache.commons.io.IOUtils +import org.kde.kdeconnect.base.BaseActivity +import org.kde.kdeconnect.extensions.setupBottomPadding +import org.kde.kdeconnect.extensions.viewBinding import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.databinding.ActivityLicensesBinding import java.nio.charset.Charset -class LicensesActivity : AppCompatActivity() { - private lateinit var binding: ActivityLicensesBinding +class LicensesActivity : BaseActivity() { + + override val binding: ActivityLicensesBinding by viewBinding(ActivityLicensesBinding::inflate) + + override val isScrollable: Boolean = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityLicensesBinding.inflate(layoutInflater) - setContentView(binding.root) - + binding.licensesText.setupBottomPadding() setSupportActionBar(binding.toolbarLayout.toolbar) supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportActionBar!!.setDisplayShowHomeEnabled(true) diff --git a/src/org/kde/kdeconnect/UserInterface/CustomDevicesActivity.java b/src/org/kde/kdeconnect/UserInterface/CustomDevicesActivity.java index 8e416b44..f26cd729 100644 --- a/src/org/kde/kdeconnect/UserInterface/CustomDevicesActivity.java +++ b/src/org/kde/kdeconnect/UserInterface/CustomDevicesActivity.java @@ -17,7 +17,6 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.TooltipCompat; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; @@ -28,6 +27,8 @@ import com.google.android.material.snackbar.BaseTransientBottomBar; import com.google.android.material.snackbar.Snackbar; import org.kde.kdeconnect.DeviceHost; +import org.kde.kdeconnect.Helpers.WindowHelper; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.databinding.ActivityCustomDevicesBinding; @@ -35,9 +36,11 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.Objects; +import kotlin.Lazy; +import kotlin.LazyKt; import kotlin.Unit; -public class CustomDevicesActivity extends AppCompatActivity implements CustomDevicesAdapter.Callback { +public class CustomDevicesActivity extends BaseActivity implements CustomDevicesAdapter.Callback { private static final String TAG_ADD_DEVICE_DIALOG = "AddDeviceDialog"; private static final String KEY_CUSTOM_DEVLIST_PREFERENCE = "device_list_preference"; @@ -53,18 +56,23 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe private DeletedCustomDevice lastDeletedCustomDevice; private int editingDeviceAtPosition; + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivityCustomDevicesBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + protected ActivityCustomDevicesBinding getBinding() { + return lazyBinding.getValue(); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final ActivityCustomDevicesBinding binding = ActivityCustomDevicesBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); + recyclerView = getBinding().recyclerView; + emptyListMessage = getBinding().emptyListMessage; + final FloatingActionButton fab = getBinding().floatingActionButton; - recyclerView = binding.recyclerView; - emptyListMessage = binding.emptyListMessage; - final FloatingActionButton fab = binding.floatingActionButton; - - setSupportActionBar(binding.toolbarLayout.toolbar); + setSupportActionBar(getBinding().toolbarLayout.toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); @@ -86,6 +94,9 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); recyclerView.setAdapter(customDevicesAdapter); + WindowHelper.setupBottomPadding(recyclerView); + WindowHelper.setupBottomMargin(getBinding().floatingActionButton); + addDeviceDialog = (EditTextAlertDialogFragment) getSupportFragmentManager().findFragmentByTag(TAG_ADD_DEVICE_DIALOG); if (addDeviceDialog != null) { addDeviceDialog.setCallback(new AddDeviceDialogCallback()); @@ -269,4 +280,9 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe super.onBackPressed(); return true; } + + @Override + public boolean isScrollable() { + return true; + } } diff --git a/src/org/kde/kdeconnect/UserInterface/DeviceFragment.kt b/src/org/kde/kdeconnect/UserInterface/DeviceFragment.kt index 688df5d4..4dd0124e 100644 --- a/src/org/kde/kdeconnect/UserInterface/DeviceFragment.kt +++ b/src/org/kde/kdeconnect/UserInterface/DeviceFragment.kt @@ -44,6 +44,7 @@ import org.kde.kdeconnect.Plugins.Plugin import org.kde.kdeconnect.Plugins.PresenterPlugin.PresenterPlugin import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin import org.kde.kdeconnect.UserInterface.compose.KdeTheme +import org.kde.kdeconnect.extensions.setupBottomPadding import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.databinding.ActivityDeviceBinding import org.kde.kdeconnect_tp.databinding.ViewPairErrorBinding @@ -156,6 +157,11 @@ class DeviceFragment : Fragment() { return deviceBinding.root } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + deviceBinding?.deviceView?.setupBottomPadding() + } + private fun refreshDevicesAction() { BackgroundService.ForceRefreshConnections(requireContext()) requireErrorBinding().errorMessageContainer.isRefreshing = true diff --git a/src/org/kde/kdeconnect/UserInterface/PairingFragment.java b/src/org/kde/kdeconnect/UserInterface/PairingFragment.java index 6881cc1b..484b289f 100644 --- a/src/org/kde/kdeconnect/UserInterface/PairingFragment.java +++ b/src/org/kde/kdeconnect/UserInterface/PairingFragment.java @@ -32,6 +32,7 @@ import androidx.fragment.app.Fragment; import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Helpers.TrustedNetworkHelper; +import org.kde.kdeconnect.Helpers.WindowHelper; import org.kde.kdeconnect.KdeConnect; import org.kde.kdeconnect.UserInterface.List.ListAdapter; import org.kde.kdeconnect.UserInterface.List.PairingDeviceItem; @@ -118,6 +119,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb // Configure focus order for Accessibility, for touchpads, and for TV remotes // (allow focus of items in the device list) devicesListBinding.devicesList.setItemsCanFocus(true); + WindowHelper.setupBottomPadding(devicesListBinding.devicesList); } @Override diff --git a/src/org/kde/kdeconnect/UserInterface/PluginSettingsActivity.java b/src/org/kde/kdeconnect/UserInterface/PluginSettingsActivity.java index 901350e7..6c94c3db 100644 --- a/src/org/kde/kdeconnect/UserInterface/PluginSettingsActivity.java +++ b/src/org/kde/kdeconnect/UserInterface/PluginSettingsActivity.java @@ -13,8 +13,8 @@ import android.view.MenuItem; import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -24,12 +24,17 @@ import org.kde.kdeconnect.Device; import org.kde.kdeconnect.DeviceStats; import org.kde.kdeconnect.KdeConnect; import org.kde.kdeconnect.Plugins.Plugin; +import org.kde.kdeconnect.base.BaseActivity; import org.kde.kdeconnect_tp.R; +import org.kde.kdeconnect_tp.databinding.ActivityPluginSettingsBinding; import java.util.Objects; +import kotlin.Lazy; +import kotlin.LazyKt; + public class PluginSettingsActivity - extends AppCompatActivity + extends BaseActivity implements PluginPreference.PluginPreferenceCallback { public static final String EXTRA_DEVICE_ID = "deviceId"; @@ -38,12 +43,18 @@ public class PluginSettingsActivity //TODO: Save/restore state static private String deviceId; //Static because if we get here by using the back button in the action bar, the extra deviceId will not be set. + private final Lazy lazyBinding = LazyKt.lazy(() -> ActivityPluginSettingsBinding.inflate(getLayoutInflater())); + + @NonNull + @Override + protected ActivityPluginSettingsBinding getBinding() { + return lazyBinding.getValue(); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_plugin_settings); - setSupportActionBar(findViewById(R.id.toolbar)); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); diff --git a/src/org/kde/kdeconnect/UserInterface/SettingsFragment.kt b/src/org/kde/kdeconnect/UserInterface/SettingsFragment.kt index e2b1d2fe..8d11d8aa 100644 --- a/src/org/kde/kdeconnect/UserInterface/SettingsFragment.kt +++ b/src/org/kde/kdeconnect/UserInterface/SettingsFragment.kt @@ -34,6 +34,7 @@ import org.kde.kdeconnect.Helpers.DeviceHelper.filterName import org.kde.kdeconnect.Helpers.DeviceHelper.getDeviceName import org.kde.kdeconnect.Helpers.NotificationHelper import org.kde.kdeconnect.UserInterface.ThemeUtil.applyTheme +import org.kde.kdeconnect.extensions.setupBottomPadding import org.kde.kdeconnect_tp.R class SettingsFragment : PreferenceFragmentCompat() { @@ -62,6 +63,11 @@ class SettingsFragment : PreferenceFragmentCompat() { preferenceScreen = screen } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + listView.setupBottomPadding() + } + private fun deviceNamePref(context: Context) = EditTextPreference(context).apply { key = DeviceHelper.KEY_DEVICE_NAME_PREFERENCE isSelectable = true diff --git a/src/org/kde/kdeconnect/UserInterface/TrustedNetworksActivity.kt b/src/org/kde/kdeconnect/UserInterface/TrustedNetworksActivity.kt index e4dd0fea..fa013197 100644 --- a/src/org/kde/kdeconnect/UserInterface/TrustedNetworksActivity.kt +++ b/src/org/kde/kdeconnect/UserInterface/TrustedNetworksActivity.kt @@ -12,13 +12,14 @@ import android.view.View import android.widget.* import android.widget.AdapterView.OnItemClickListener import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity import org.kde.kdeconnect.Helpers.TrustedNetworkHelper +import org.kde.kdeconnect.base.BaseActivity +import org.kde.kdeconnect.extensions.viewBinding import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.databinding.TrustedNetworkListBinding -class TrustedNetworksActivity : AppCompatActivity() { - lateinit var binding: TrustedNetworkListBinding +class TrustedNetworksActivity : BaseActivity() { + override val binding: TrustedNetworkListBinding by viewBinding(TrustedNetworkListBinding::inflate) private val trustedNetworks: MutableList = mutableListOf() private val trustedNetworksView: ListView @@ -39,9 +40,6 @@ class TrustedNetworksActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = TrustedNetworkListBinding.inflate(layoutInflater) - setContentView(binding.root) - setSupportActionBar(binding.toolbarLayout.toolbar) supportActionBar!!.apply { setDisplayHomeAsUpEnabled(true) diff --git a/src/org/kde/kdeconnect/base/BaseActivity.kt b/src/org/kde/kdeconnect/base/BaseActivity.kt new file mode 100644 index 00000000..76b6f294 --- /dev/null +++ b/src/org/kde/kdeconnect/base/BaseActivity.kt @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2024 Mash Kyrielight + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ +package org.kde.kdeconnect.base + +import android.os.Build +import android.os.Bundle +import android.view.View +import android.view.ViewGroup.MarginLayoutParams +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams +import androidx.viewbinding.ViewBinding +import org.kde.kdeconnect.extensions.getSafeDrawInsets +import org.kde.kdeconnect.extensions.setOnApplyWindowInsetsListenerCompat + +abstract class BaseActivity : AppCompatActivity() { + + protected abstract val binding: VB + + open val isScrollable = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + binding.root.setOnApplyWindowInsetsListenerCompat { view, insets -> + onWindowInsetsChanged(view, insets) + insets + } + } + } + + open fun onWindowInsetsChanged(view: View, insets: WindowInsetsCompat) { + val safeDrawInsets = insets.getSafeDrawInsets() + view.updateLayoutParams { + bottomMargin = if (isScrollable) 0 else safeDrawInsets.bottom + leftMargin = safeDrawInsets.left + rightMargin = safeDrawInsets.right + } + } + +} \ No newline at end of file diff --git a/src/org/kde/kdeconnect/extensions/ViewBinding.kt b/src/org/kde/kdeconnect/extensions/ViewBinding.kt new file mode 100644 index 00000000..d971050d --- /dev/null +++ b/src/org/kde/kdeconnect/extensions/ViewBinding.kt @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2024 Mash Kyrielight + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ +package org.kde.kdeconnect.extensions + +import android.view.LayoutInflater +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding + +inline fun AppCompatActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) = + lazy(LazyThreadSafetyMode.NONE) { + bindingInflater.invoke(layoutInflater) + } \ No newline at end of file diff --git a/src/org/kde/kdeconnect/extensions/Window.kt b/src/org/kde/kdeconnect/extensions/Window.kt new file mode 100644 index 00000000..bb6f98fb --- /dev/null +++ b/src/org/kde/kdeconnect/extensions/Window.kt @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2024 Mash Kyrielight + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ +package org.kde.kdeconnect.extensions + +import android.os.Build +import android.view.View +import android.view.ViewGroup.MarginLayoutParams +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.ui.Modifier +import androidx.core.graphics.Insets +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams +import androidx.core.view.updatePadding + +fun View.setOnApplyWindowInsetsListenerCompat(listener: (v: View, insets: WindowInsetsCompat) -> WindowInsetsCompat) { + ViewCompat.setOnApplyWindowInsetsListener(this, listener) +} + +fun WindowInsetsCompat.getSafeDrawInsets(): Insets { + return getInsets( + WindowInsetsCompat.Type.systemBars() + or WindowInsetsCompat.Type.displayCutout() + or WindowInsetsCompat.Type.ime() + ) +} + +fun View.setupBottomPadding() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + return + } + val originalBottomPadding = paddingBottom + setOnApplyWindowInsetsListenerCompat { _, insets -> + val safeInsets = insets.getSafeDrawInsets() + updatePadding(bottom = originalBottomPadding + safeInsets.bottom) + insets + } +} + +fun View.setupBottomMargin() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + return + } + val originalBottomMargin = (layoutParams as MarginLayoutParams).bottomMargin + setOnApplyWindowInsetsListenerCompat { _, insets -> + val safeInsets = insets.getSafeDrawInsets() + updateLayoutParams { + bottomMargin = originalBottomMargin + safeInsets.bottom + } + insets + } +} + +fun Modifier.safeDrawPadding(): Modifier { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + safeDrawingPadding() + } else { + Modifier + } +} \ No newline at end of file