From b6e252c4c164aeba61ec81186e5d23be9812bbae Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Sat, 22 Jul 2023 21:23:27 +0200 Subject: [PATCH] Switch UI to Material 3 and support dynamic colors --- app/build.gradle | 12 +- .../android/dslv/CheckableLinearLayout.java | 2 +- .../dslv/DragSortListPreferenceFragment.java | 9 +- .../gadgetbridge/GBApplication.java | 8 +- .../activities/AbstractGBActivity.java | 31 +- .../AbstractPreferenceFragment.java | 34 +-- .../activities/ActivitySummariesActivity.java | 4 +- .../activities/ActivitySummaryDetail.java | 10 +- .../activities/ConfigureContacts.java | 4 +- .../activities/ConfigureReminders.java | 4 +- .../activities/ConfigureWorldClocks.java | 5 +- .../activities/ControlCenterv2.java | 50 +++- .../activities/DataManagementActivity.java | 20 +- .../activities/DebugActivity.java | 18 +- .../activities/DiscoveryActivity.java | 9 +- .../activities/ReminderDetails.java | 4 +- .../activities/SettingsActivity.java | 21 +- ...SleepAlarmWidgetConfigurationActivity.java | 7 +- .../WidgetConfigurationActivity.java | 8 +- .../activities/WorldClockDetails.java | 4 +- .../AbstractAppManagerFragment.java | 17 +- .../charts/ActivityListingDashboard.java | 41 ++- .../charts/StepStreaksDashboard.java | 8 +- .../adapter/GBContactListAdapter.java | 5 +- .../adapter/GBDeviceAdapterv2.java | 21 +- .../adapter/GBReminderListAdapter.java | 5 +- .../adapter/GBWorldClockListAdapter.java | 6 +- .../qhybrid/CommuteActionsActivity.java | 18 +- .../devices/qhybrid/ConfigActivity.java | 12 +- .../qhybrid/FileManagementActivity.java | 5 +- .../devices/qhybrid/HRConfigActivity.java | 11 +- .../HybridHRWatchfaceDesignerActivity.java | 18 +- .../devices/qhybrid/TimePicker.java | 7 +- .../qhybrid/WidgetSettingsActivity.java | 6 +- .../SonyHeadphonesSettingsCustomizer.java | 11 +- .../gadgetbridge/util/AndroidUtils.java | 29 +- .../gadgetbridge/util/BondingUtil.java | 5 +- .../gadgetbridge/util/ChangeLog.java | 78 +++++ .../util/XTimePreferenceFragment.java | 5 +- .../util/dialogs/MaterialDialogFragment.java | 46 +++ ...erialEditTextPreferenceDialogFragment.java | 107 +++++++ .../MaterialListPreferenceDialogFragment.java | 117 ++++++++ ...ltiSelectListPreferenceDialogFragment.java | 136 +++++++++ .../MaterialPreferenceDialogFragment.java | 269 ++++++++++++++++++ app/src/main/res/color/alarm_dow.xml | 4 +- .../activity_controlcenterv2_app_bar_main.xml | 7 +- .../activity_controlcenterv2_content_main.xml | 1 - app/src/main/res/layout/activity_debug.xml | 3 - app/src/main/res/layout/device_itemv2.xml | 4 +- app/src/main/res/layout/item_alarm.xml | 10 +- .../res/layout/item_appmanager_watchapp.xml | 4 +- .../main/res/layout/list_item_checkable.xml | 22 +- app/src/main/res/layout/nav_header_main.xml | 8 +- .../sort_list_array_dialog_preference.xml | 4 +- app/src/main/res/menu/activity_list_menu.xml | 2 - .../menu/activity_take_screenshot_menu.xml | 1 - app/src/main/res/menu/menu_charts.xml | 3 +- app/src/main/res/values/arrays.xml | 2 + app/src/main/res/values/attrs.xml | 2 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/values/styles.xml | 121 +++++--- app/src/main/res/values/values.xml | 1 + 62 files changed, 1167 insertions(+), 281 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ChangeLog.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialDialogFragment.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialEditTextPreferenceDialogFragment.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialListPreferenceDialogFragment.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialMultiSelectListPreferenceDialogFragment.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialPreferenceDialogFragment.java diff --git a/app/build.gradle b/app/build.gradle index d0d2630d2..24d4124ab 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -213,22 +213,22 @@ dependencies { testImplementation "org.mockito:mockito-core:1.10.19" testImplementation "org.robolectric:robolectric:4.8.2" testImplementation "org.hamcrest:hamcrest-library:1.3" - testImplementation "com.google.code.gson:gson:2.8.6" + testImplementation "com.google.code.gson:gson:2.8.8" implementation fileTree(dir: "libs", include: ["*.jar"]) - implementation "androidx.appcompat:appcompat:1.3.1" + implementation "androidx.appcompat:appcompat:1.4.1" implementation "androidx.preference:preference:1.1.1" implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.recyclerview:recyclerview:1.2.1" implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.palette:palette:1.0.0" - implementation "androidx.activity:activity:1.4.0" - implementation "androidx.fragment:fragment:1.4.0" + implementation "androidx.activity:activity:1.5.1" + implementation "androidx.fragment:fragment:1.4.1" - implementation "com.google.android.material:material:1.4.0" + implementation "com.google.android.material:material:1.6.1" implementation 'com.google.android.flexbox:flexbox:3.0.0' - implementation "com.google.code.gson:gson:2.8.6" + implementation "com.google.code.gson:gson:2.8.8" implementation "no.nordicsemi.android:dfu:1.12.0" implementation("com.github.tony19:logback-android:2.0.0") { diff --git a/app/src/main/java/com/mobeta/android/dslv/CheckableLinearLayout.java b/app/src/main/java/com/mobeta/android/dslv/CheckableLinearLayout.java index b940c9041..1c3c71680 100644 --- a/app/src/main/java/com/mobeta/android/dslv/CheckableLinearLayout.java +++ b/app/src/main/java/com/mobeta/android/dslv/CheckableLinearLayout.java @@ -7,7 +7,7 @@ import android.widget.LinearLayout; public class CheckableLinearLayout extends LinearLayout implements Checkable { - private static final int CHECKABLE_CHILD_INDEX = 1; + private static final int CHECKABLE_CHILD_INDEX = 0; private Checkable child; public CheckableLinearLayout(Context context, AttributeSet attrs) { diff --git a/app/src/main/java/com/mobeta/android/dslv/DragSortListPreferenceFragment.java b/app/src/main/java/com/mobeta/android/dslv/DragSortListPreferenceFragment.java index 05604bcec..21f82bd94 100644 --- a/app/src/main/java/com/mobeta/android/dslv/DragSortListPreferenceFragment.java +++ b/app/src/main/java/com/mobeta/android/dslv/DragSortListPreferenceFragment.java @@ -49,18 +49,19 @@ import android.view.View; import android.widget.ArrayAdapter; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.preference.ListPreference; -import androidx.preference.ListPreferenceDialogFragmentCompat; import androidx.preference.Preference; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import java.util.ArrayList; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.util.dialogs.MaterialListPreferenceDialogFragment; -public class DragSortListPreferenceFragment extends ListPreferenceDialogFragmentCompat implements ListPreference.TargetFragment { +public class DragSortListPreferenceFragment extends MaterialListPreferenceDialogFragment implements ListPreference.TargetFragment { protected DragSortListView mListView; protected ArrayAdapter mAdapter; @@ -112,7 +113,7 @@ public class DragSortListPreferenceFragment extends ListPreferenceDialogFragment } @Override - protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { + protected void onPrepareDialogBuilder(MaterialAlertDialogBuilder builder) { // must be empty } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index f8f2b6ef2..1414d06d7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -132,6 +132,7 @@ public class GBApplication extends Application { public static final String ACTION_QUIT = "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit"; public static final String ACTION_LANGUAGE_CHANGE = "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.language_change"; + public static final String ACTION_THEME_CHANGE = "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.theme_change"; public static final String ACTION_NEW_DATA = "nodomain.freeyourgadget.gadgetbridge.action.new_data"; private static GBApplication app; @@ -1311,7 +1312,7 @@ public class GBApplication extends Application { Resources resources = context.getResources(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && - selectedTheme.equals(context.getString(R.string.pref_theme_value_system))) { + (selectedTheme.equals(context.getString(R.string.pref_theme_value_system)) || selectedTheme.equals(context.getString(R.string.pref_theme_value_dynamic)))) { return (resources.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; } else { return selectedTheme.equals(context.getString(R.string.pref_theme_value_dark)); @@ -1322,6 +1323,11 @@ public class GBApplication extends Application { return prefs.getBoolean("pref_key_theme_amoled_black", false); } + public static boolean areDynamicColorsEnabled() { + String selectedTheme = prefs.getString("pref_key_theme", context.getString(R.string.pref_theme_value_system)); + return selectedTheme.equals(context.getString(R.string.pref_theme_value_dynamic)); + } + public static int getTextColor(Context context) { TypedValue typedValue = new TypedValue(); Resources.Theme theme = context.getTheme(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java index 8d0ea2d51..08d626481 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java @@ -24,10 +24,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import java.util.Locale; - import androidx.appcompat.app.AppCompatActivity; import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import java.util.Locale; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; @@ -50,6 +51,9 @@ public abstract class AbstractGBActivity extends AppCompatActivity implements GB case GBApplication.ACTION_LANGUAGE_CHANGE: setLanguage(GBApplication.getLanguage(), true); break; + case GBApplication.ACTION_THEME_CHANGE: + recreate(); + break; case GBApplication.ACTION_QUIT: finish(); break; @@ -69,7 +73,27 @@ public abstract class AbstractGBActivity extends AppCompatActivity implements GB } public static void init(GBActivity activity, int flags) { - if (GBApplication.isDarkThemeEnabled()) { + if (GBApplication.areDynamicColorsEnabled()) { + if (GBApplication.isDarkThemeEnabled()) { + if ((flags & NO_ACTIONBAR) != 0) { + if (GBApplication.isAmoledBlackEnabled()) + activity.setTheme(R.style.GadgetbridgeThemeDynamicDarkAmoled_NoActionBar); + else + activity.setTheme(R.style.GadgetbridgeThemeDynamicDark_NoActionBar); + } else { + if (GBApplication.isAmoledBlackEnabled()) + activity.setTheme(R.style.GadgetbridgeThemeDynamicDarkAmoled); + else + activity.setTheme(R.style.GadgetbridgeThemeDynamicDark); + } + } else { + if ((flags & NO_ACTIONBAR) != 0) { + activity.setTheme(R.style.GadgetbridgeThemeDynamicLight_NoActionBar); + } else { + activity.setTheme(R.style.GadgetbridgeThemeDynamicLight); + } + } + } else if (GBApplication.isDarkThemeEnabled()) { if ((flags & NO_ACTIONBAR) != 0) { if (GBApplication.isAmoledBlackEnabled()) activity.setTheme(R.style.GadgetbridgeThemeBlack_NoActionBar); @@ -96,6 +120,7 @@ public abstract class AbstractGBActivity extends AppCompatActivity implements GB IntentFilter filterLocal = new IntentFilter(); filterLocal.addAction(GBApplication.ACTION_QUIT); filterLocal.addAction(GBApplication.ACTION_LANGUAGE_CHANGE); + filterLocal.addAction(GBApplication.ACTION_THEME_CHANGE); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal); init(this); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractPreferenceFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractPreferenceFragment.java index 07d979cfb..bf1e5ae2b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractPreferenceFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractPreferenceFragment.java @@ -22,6 +22,7 @@ import android.os.Bundle; import androidx.fragment.app.DialogFragment; import androidx.preference.EditTextPreference; import androidx.preference.ListPreference; +import androidx.preference.MultiSelectListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceFragmentCompat; @@ -40,10 +41,11 @@ import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.Set; -import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsActivity; import nodomain.freeyourgadget.gadgetbridge.util.XTimePreference; import nodomain.freeyourgadget.gadgetbridge.util.XTimePreferenceFragment; +import nodomain.freeyourgadget.gadgetbridge.util.dialogs.MaterialEditTextPreferenceDialogFragment; +import nodomain.freeyourgadget.gadgetbridge.util.dialogs.MaterialListPreferenceDialogFragment; +import nodomain.freeyourgadget.gadgetbridge.util.dialogs.MaterialMultiSelectListPreferenceDialogFragment; public abstract class AbstractPreferenceFragment extends PreferenceFragmentCompat { protected static final Logger LOG = LoggerFactory.getLogger(AbstractPreferenceFragment.class); @@ -92,24 +94,24 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragmentCompa DialogFragment dialogFragment; if (preference instanceof XTimePreference) { dialogFragment = new XTimePreferenceFragment(); - final Bundle bundle = new Bundle(1); - bundle.putString("key", preference.getKey()); - dialogFragment.setArguments(bundle); - dialogFragment.setTargetFragment(this, 0); - if (getFragmentManager() != null) { - dialogFragment.show(getFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG"); - } } else if (preference instanceof DragSortListPreference) { dialogFragment = new DragSortListPreferenceFragment(); - final Bundle bundle = new Bundle(1); - bundle.putString("key", preference.getKey()); - dialogFragment.setArguments(bundle); - dialogFragment.setTargetFragment(this, 0); - if (getFragmentManager() != null) { - dialogFragment.show(getFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG"); - } + } else if (preference instanceof EditTextPreference) { + dialogFragment = MaterialEditTextPreferenceDialogFragment.newInstance(preference.getKey()); + } else if (preference instanceof ListPreference) { + dialogFragment = MaterialListPreferenceDialogFragment.newInstance(preference.getKey()); + } else if (preference instanceof MultiSelectListPreference) { + dialogFragment = MaterialMultiSelectListPreferenceDialogFragment.newInstance(preference.getKey()); } else { super.onDisplayPreferenceDialog(preference); + return; + } + final Bundle bundle = new Bundle(1); + bundle.putString("key", preference.getKey()); + dialogFragment.setArguments(bundle); + dialogFragment.setTargetFragment(this, 0); + if (getFragmentManager() != null) { + dialogFragment.show(getFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG"); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java index b08f82d7d..416bc1868 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java @@ -17,7 +17,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.AlertDialog; import android.app.DatePickerDialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -44,6 +43,7 @@ import androidx.core.content.FileProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.slf4j.Logger; @@ -241,7 +241,7 @@ public class ActivitySummariesActivity extends AbstractListActivity directory_listing = new ArrayAdapter(ActivitySummaryDetail.this, android.R.layout.simple_list_item_1, filesGpxList); builder.setSingleChoiceItems(directory_listing, 0, new DialogInterface.OnClickListener() { @@ -306,7 +308,7 @@ public class ActivitySummaryDetail extends AbstractGBActivity { message = String.format("%s?", getString(R.string.activity_summary_detail_clear_gpx_track)); } - new AlertDialog.Builder(ActivitySummaryDetail.this) + new MaterialAlertDialogBuilder(ActivitySummaryDetail.this) .setCancelable(true) .setIcon(R.drawable.ic_warning) .setTitle(R.string.activity_summary_detail_editing_gpx_track) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureContacts.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureContacts.java index 1f59febee..83cc9028d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureContacts.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureContacts.java @@ -16,7 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.view.MenuItem; @@ -25,6 +24,7 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.slf4j.Logger; @@ -80,7 +80,7 @@ public class ConfigureContacts extends AbstractGBActivity { if (mGBContactListAdapter.getItemCount() >= deviceSlots) { // No more free slots - new AlertDialog.Builder(v.getContext()) + new MaterialAlertDialogBuilder(v.getContext()) .setTitle(R.string.reminder_no_free_slots_title) .setMessage(getBaseContext().getString(R.string.contact_no_free_slots_description, String.format(Locale.getDefault(), "%d", deviceSlots))) .setIcon(R.drawable.ic_warning) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureReminders.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureReminders.java index f736568a1..c20b82fdd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureReminders.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureReminders.java @@ -16,7 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; @@ -27,6 +26,7 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.slf4j.Logger; @@ -90,7 +90,7 @@ public class ConfigureReminders extends AbstractGBActivity { if (mGBReminderListAdapter.getItemCount() >= deviceSlots) { // No more free slots - new AlertDialog.Builder(v.getContext()) + new MaterialAlertDialogBuilder(v.getContext()) .setTitle(R.string.reminder_no_free_slots_title) .setMessage(getBaseContext().getString(R.string.reminder_no_free_slots_description, String.format(Locale.getDefault(), "%d", deviceSlots))) .setIcon(R.drawable.ic_warning) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureWorldClocks.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureWorldClocks.java index 5fd392edc..10e7d03fc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureWorldClocks.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureWorldClocks.java @@ -16,7 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -30,12 +29,12 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.text.SimpleDateFormat; import java.util.List; import java.util.Locale; import java.util.TimeZone; @@ -92,7 +91,7 @@ public class ConfigureWorldClocks extends AbstractGBActivity { if (mGBWorldClockListAdapter.getItemCount() >= deviceSlots) { // No more free slots - new AlertDialog.Builder(v.getContext()) + new MaterialAlertDialogBuilder(v.getContext()) .setTitle(R.string.world_clock_no_free_slots_title) .setMessage(getBaseContext().getString(R.string.world_clock_no_free_slots_description, String.format(Locale.getDefault(), "%d", deviceSlots))) .setIcon(R.drawable.ic_warning) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java index 39df505f1..cf9974b03 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java @@ -17,11 +17,11 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast; import android.Manifest; import android.annotation.TargetApi; -import android.app.AlertDialog; import android.app.Dialog; import android.app.NotificationManager; import android.content.ActivityNotFoundException; @@ -31,25 +31,27 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; +import android.util.TypedValue; import android.view.MenuItem; import android.view.View; import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.view.menu.MenuItemImpl; -import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; @@ -60,6 +62,9 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.appbar.MaterialToolbar; +import com.google.android.material.color.DynamicColors; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.navigation.NavigationView; @@ -77,10 +82,9 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; -import de.cketti.library.changelog.ChangeLog; +import nodomain.freeyourgadget.gadgetbridge.BuildConfig; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.BuildConfig; import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAdapterv2; import nodomain.freeyourgadget.gadgetbridge.database.DBAccess; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; @@ -91,12 +95,11 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.DailyTotals; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; +import nodomain.freeyourgadget.gadgetbridge.util.ChangeLog; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; - //TODO: extend AbstractGBActivity, but it requires actionbar that is not available public class ControlCenterv2 extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, GBActivity { @@ -119,6 +122,7 @@ public class ControlCenterv2 extends AppCompatActivity private RecyclerView deviceListView; private FloatingActionButton fab; private boolean isLanguageInvalid = false; + private boolean isThemeInvalid = false; List deviceList; private HashMap deviceActivityHashMap = new HashMap(); @@ -130,6 +134,9 @@ public class ControlCenterv2 extends AppCompatActivity case GBApplication.ACTION_LANGUAGE_CHANGE: setLanguage(GBApplication.getLanguage(), true); break; + case GBApplication.ACTION_THEME_CHANGE: + isThemeInvalid = true; + break; case GBApplication.ACTION_QUIT: finish(); break; @@ -179,9 +186,20 @@ public class ControlCenterv2 extends AppCompatActivity super.onCreate(savedInstanceState); setContentView(R.layout.activity_controlcenterv2); - Toolbar toolbar = findViewById(R.id.toolbar); + MaterialToolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); + if (GBApplication.areDynamicColorsEnabled()) { + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = getTheme(); + theme.resolveAttribute(R.attr.colorSurface, typedValue, true); + @ColorInt int toolbarBackground = typedValue.data; + toolbar.setBackgroundColor(toolbarBackground); + } else { + toolbar.setBackgroundColor(getResources().getColor(R.color.primarydark_light)); + toolbar.setTitleTextColor(getResources().getColor(android.R.color.white)); + } + DrawerLayout drawer = findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.controlcenter_navigation_drawer_open, R.string.controlcenter_navigation_drawer_close); @@ -264,6 +282,7 @@ public class ControlCenterv2 extends AppCompatActivity IntentFilter filterLocal = new IntentFilter(); filterLocal.addAction(GBApplication.ACTION_LANGUAGE_CHANGE); + filterLocal.addAction(GBApplication.ACTION_THEME_CHANGE); filterLocal.addAction(GBApplication.ACTION_QUIT); filterLocal.addAction(GBApplication.ACTION_NEW_DATA); filterLocal.addAction(DeviceManager.ACTION_DEVICES_CHANGED); @@ -346,7 +365,7 @@ public class ControlCenterv2 extends AppCompatActivity ChangeLog cl = createChangeLog(); if (cl.isFirstRun()) { try { - cl.getLogDialog().show(); + cl.getMaterialLogDialog().show(); } catch (Exception ignored) { GB.toast(getBaseContext(), "Error showing Changelog", Toast.LENGTH_LONG, GB.ERROR); } @@ -365,8 +384,9 @@ public class ControlCenterv2 extends AppCompatActivity protected void onResume() { super.onResume(); handleShortcut(getIntent()); - if (isLanguageInvalid) { + if (isLanguageInvalid || isThemeInvalid) { isLanguageInvalid = false; + isThemeInvalid = false; recreate(); } } @@ -434,7 +454,7 @@ public class ControlCenterv2 extends AppCompatActivity case R.id.external_changelog: ChangeLog cl = createChangeLog(); try { - cl.getLogDialog().show(); + cl.getMaterialLogDialog().show(); } catch (Exception ignored) { GB.toast(getBaseContext(), "Error showing Changelog", Toast.LENGTH_LONG, GB.ERROR); } @@ -658,7 +678,7 @@ public class ControlCenterv2 extends AppCompatActivity @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity()); final Context context = getContext(); builder.setMessage(context.getString(R.string.permission_notification_policy_access, getContext().getString(R.string.app_name), @@ -682,7 +702,7 @@ public class ControlCenterv2 extends AppCompatActivity @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity()); final Context context = getContext(); builder.setMessage(context.getString(R.string.permission_notification_listener, getContext().getString(R.string.app_name), @@ -705,7 +725,7 @@ public class ControlCenterv2 extends AppCompatActivity @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity()); Context context = getContext(); builder.setMessage(context.getString(R.string.permission_display_over_other_apps, getContext().getString(R.string.app_name), @@ -729,7 +749,7 @@ public class ControlCenterv2 extends AppCompatActivity @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity()); Context context = getContext(); builder.setMessage(context.getString(R.string.permission_location, getContext().getString(R.string.app_name), @@ -769,7 +789,7 @@ public class ControlCenterv2 extends AppCompatActivity @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity()); Context context = getContext(); builder.setMessage(context.getString(R.string.permission_request, getContext().getString(R.string.app_name), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java index 2b526cce8..f7d73600f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DataManagementActivity.java @@ -17,7 +17,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -35,8 +34,11 @@ import android.widget.Button; import android.widget.TextView; import android.widget.Toast; +import androidx.appcompat.app.AlertDialog; import androidx.core.app.NavUtils; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -98,7 +100,7 @@ public class DataManagementActivity extends AbstractGBActivity { e.printStackTrace(); } - AlertDialog.Builder builder = new AlertDialog.Builder(DataManagementActivity.this); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(DataManagementActivity.this); builder.setTitle("Export/Import directory content:"); ArrayAdapter directory_listing = new ArrayAdapter(DataManagementActivity.this, android.R.layout.simple_list_item_1, export_path.list()); @@ -328,7 +330,7 @@ public class DataManagementActivity extends AbstractGBActivity { } private void exportDB() { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setCancelable(true) .setIcon(R.drawable.ic_warning) .setTitle(R.string.dbmanagementactivity_export_data_title) @@ -356,7 +358,7 @@ public class DataManagementActivity extends AbstractGBActivity { } private void importDB() { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setCancelable(true) .setIcon(R.drawable.ic_warning) .setTitle(R.string.dbmanagementactivity_import_data_title) @@ -387,7 +389,7 @@ public class DataManagementActivity extends AbstractGBActivity { } private void deleteActivityDatabase() { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setCancelable(true) .setIcon(R.drawable.ic_warning) .setTitle(R.string.dbmanagementactivity_delete_activity_data_title) @@ -411,7 +413,7 @@ public class DataManagementActivity extends AbstractGBActivity { } private void deleteOldActivityDbFile() { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setCancelable(true) .setTitle(R.string.dbmanagementactivity_delete_old_activity_db) .setIcon(R.drawable.ic_warning) @@ -426,16 +428,16 @@ public class DataManagementActivity extends AbstractGBActivity { } } }); - new AlertDialog.Builder(this).setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() { + new MaterialAlertDialogBuilder(this).setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); - new AlertDialog.Builder(this).show(); + new MaterialAlertDialogBuilder(this).show(); } private void cleanExportDirectory() { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setCancelable(true) .setIcon(R.drawable.ic_warning) .setTitle(R.string.activity_DB_clean_export_directory_warning_title) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index d113f641b..e89d2300e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -22,7 +22,6 @@ import static android.content.Intent.EXTRA_SUBJECT; import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID; import android.app.Activity; -import android.app.AlertDialog; import android.app.DatePickerDialog; import android.app.PendingIntent; import android.appwidget.AppWidgetHost; @@ -63,6 +62,7 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; import androidx.core.app.NavUtils; import androidx.core.app.NotificationCompat; @@ -70,6 +70,8 @@ import androidx.core.app.RemoteInput; import androidx.core.content.FileProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -271,7 +273,7 @@ public class DebugActivity extends AbstractGBActivity { factoryResetButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - new AlertDialog.Builder(DebugActivity.this) + new MaterialAlertDialogBuilder(DebugActivity.this) .setCancelable(true) .setTitle(R.string.debugactivity_really_factoryreset_title) .setMessage(R.string.debugactivity_really_factoryreset) @@ -491,7 +493,7 @@ public class DebugActivity extends AbstractGBActivity { removeDevicePreferencesButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - new AlertDialog.Builder(DebugActivity.this) + new MaterialAlertDialogBuilder(DebugActivity.this) .setCancelable(true) .setTitle(R.string.debugactivity_confirm_remove_device_preferences_title) .setMessage(R.string.debugactivity_confirm_remove_device_preferences) @@ -569,7 +571,7 @@ public class DebugActivity extends AbstractGBActivity { linearLayout.addView(deviceListSpinner); linearLayout.addView(macLayout); - new AlertDialog.Builder(DebugActivity.this) + new MaterialAlertDialogBuilder(DebugActivity.this) .setCancelable(true) .setTitle(R.string.add_test_device) .setView(linearLayout) @@ -644,7 +646,7 @@ public class DebugActivity extends AbstractGBActivity { companionDevicesList += "\n\n" + StringUtils.join("\n", associations.toArray(new String[0])); } - new AlertDialog.Builder(DebugActivity.this) + new MaterialAlertDialogBuilder(DebugActivity.this) .setCancelable(false) .setTitle("Companion Devices") .setMessage(companionDevicesList) @@ -674,7 +676,7 @@ public class DebugActivity extends AbstractGBActivity { @Override public void onClick(View v) { - final AlertDialog.Builder fitnesStatusBuilder = new AlertDialog.Builder(DebugActivity.this); + final MaterialAlertDialogBuilder fitnesStatusBuilder = new MaterialAlertDialogBuilder(DebugActivity.this); fitnesStatusBuilder .setCancelable(false) .setTitle("openTracksObserver Status") @@ -857,7 +859,7 @@ public class DebugActivity extends AbstractGBActivity { } private void showLogSharingNotEnabledAlert() { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setTitle(R.string.note) .setPositiveButton(R.string.ok, null) .setMessage(R.string.share_log_not_enabled_message) @@ -865,7 +867,7 @@ public class DebugActivity extends AbstractGBActivity { } private void showLogSharingWarning() { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setCancelable(true) .setTitle(R.string.warning) .setMessage(R.string.share_log_warning) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index 5b0b3d872..6feb86b68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -18,9 +18,10 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; +import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast; + import android.Manifest; import android.app.Activity; -import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; @@ -64,6 +65,8 @@ import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -94,8 +97,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast; - public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, BondingInterface { private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class); @@ -834,7 +835,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView linearLayout.addView(deviceListSpinner); linearLayout.addView(macLayout); - new AlertDialog.Builder(DiscoveryActivity.this) + new MaterialAlertDialogBuilder(DiscoveryActivity.this) .setCancelable(true) .setTitle(R.string.add_test_device) .setView(linearLayout) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ReminderDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ReminderDetails.java index c0a675f97..9196445c8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ReminderDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ReminderDetails.java @@ -16,7 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.AlertDialog; import android.app.DatePickerDialog; import android.app.TimePickerDialog; import android.content.DialogInterface; @@ -34,6 +33,7 @@ import android.widget.TextView; import android.widget.TimePicker; import android.widget.Toast; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.slf4j.Logger; @@ -98,7 +98,7 @@ public class ReminderDetails extends AbstractGBActivity implements TimePickerDia cardRepeat.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - new AlertDialog.Builder(ReminderDetails.this).setAdapter(repeatAdapter, new DialogInterface.OnClickListener() { + new MaterialAlertDialogBuilder(ReminderDetails.this).setAdapter(repeatAdapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { reminder.setRepetition(i); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index 1b4c151c7..20597c9c1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -19,9 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.Manifest; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -44,10 +42,13 @@ import android.widget.Spinner; import android.widget.Toast; import androidx.core.app.ActivityCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -390,6 +391,16 @@ public class SettingsActivity extends AbstractSettingsActivityV2 { amoled_black.setEnabled(false); else amoled_black.setEnabled(true); + amoled_black.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + // Signal activities that the theme has changed + Intent intent = new Intent(); + intent.setAction(GBApplication.ACTION_THEME_CHANGE); + LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(intent); + return true; + } + }); } if (theme != null) { @@ -403,6 +414,10 @@ public class SettingsActivity extends AbstractSettingsActivityV2 { else amoled_black.setEnabled(true); } + // Signal activities that the theme has changed + Intent intent = new Intent(); + intent.setAction(GBApplication.ACTION_THEME_CHANGE); + LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(intent); return true; } }); @@ -448,7 +463,7 @@ public class SettingsActivity extends AbstractSettingsActivityV2 { outerLayout.addView(selectionListSpinner); outerLayout.addView(innerLayout); - new AlertDialog.Builder(requireContext()) + new MaterialAlertDialogBuilder(requireContext()) .setCancelable(true) .setTitle(R.string.pref_title_opentracks_packagename) .setView(outerLayout) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SleepAlarmWidgetConfigurationActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SleepAlarmWidgetConfigurationActivity.java index 0642a2c57..fead2a798 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SleepAlarmWidgetConfigurationActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SleepAlarmWidgetConfigurationActivity.java @@ -1,7 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.app.Activity; -import android.app.AlertDialog; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.DialogInterface; @@ -10,6 +9,10 @@ import android.os.Bundle; import android.util.Pair; import android.widget.ListView; +import androidx.appcompat.app.AlertDialog; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,7 +67,7 @@ public class SleepAlarmWidgetConfigurationActivity extends Activity { finish(); } - AlertDialog.Builder builder = new AlertDialog.Builder(SleepAlarmWidgetConfigurationActivity.this); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(SleepAlarmWidgetConfigurationActivity.this); builder.setTitle(R.string.widget_settings_select_device_title); allDevices = getAllDevices(getApplicationContext()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetConfigurationActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetConfigurationActivity.java index b89d83ce9..63aa883e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetConfigurationActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WidgetConfigurationActivity.java @@ -1,7 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.app.Activity; -import android.app.AlertDialog; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.DialogInterface; @@ -10,6 +9,10 @@ import android.os.Bundle; import android.util.Pair; import android.widget.ListView; +import androidx.appcompat.app.AlertDialog; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,7 +23,6 @@ import java.util.Map; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.Widget; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; @@ -59,7 +61,7 @@ public class WidgetConfigurationActivity extends Activity { finish(); } - AlertDialog.Builder builder = new AlertDialog.Builder(WidgetConfigurationActivity.this); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(WidgetConfigurationActivity.this); builder.setTitle(R.string.widget_settings_select_device_title); allDevices = getAllDevices(getApplicationContext()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WorldClockDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WorldClockDetails.java index f743935c0..47cfa2b68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WorldClockDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/WorldClockDetails.java @@ -16,7 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.text.Editable; @@ -30,6 +29,7 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.slf4j.Logger; @@ -89,7 +89,7 @@ public class WorldClockDetails extends AbstractGBActivity { cardTimezone.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - new AlertDialog.Builder(WorldClockDetails.this).setAdapter(timezoneAdapter, new DialogInterface.OnClickListener() { + new MaterialAlertDialogBuilder(WorldClockDetails.this).setAdapter(timezoneAdapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { worldClock.setTimeZoneId(timezoneIDs[i]); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index 5ebd47526..3784a3b6a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -20,7 +20,6 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.QHYBRID_ACTION_DOWNLOADED_FILE; import android.app.Activity; -import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.Context; @@ -39,6 +38,13 @@ import android.view.ViewGroup; import android.widget.PopupMenu; import android.widget.Toast; +import androidx.core.content.FileProvider; +import androidx.fragment.app.Fragment; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.apache.commons.lang3.StringUtils; @@ -55,21 +61,14 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import androidx.core.content.FileProvider; -import androidx.fragment.app.Fragment; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.RecyclerView; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.ExternalPebbleJSActivity; import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAppAdapter; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.FileManagementActivity; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.FossilFileReader; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.FossilHRInstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.QHybridConstants; -import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.QHybridCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; @@ -661,7 +660,7 @@ public abstract class AbstractAppManagerFragment extends Fragment { } private void deleteAppConfirm(final GBDeviceApp selectedApp, final boolean deleteFromCache) { - new AlertDialog.Builder(getContext()) + new MaterialAlertDialogBuilder(getContext()) .setTitle(R.string.Delete) .setMessage(requireContext().getString(R.string.contact_delete_confirm_description, selectedApp.getName())) .setIcon(R.drawable.ic_warning) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingDashboard.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingDashboard.java index cfd54bec4..74825c280 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingDashboard.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingDashboard.java @@ -13,7 +13,6 @@ import android.widget.SeekBar; import android.widget.TextView; import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentActivity; import org.slf4j.Logger; @@ -22,7 +21,6 @@ import org.slf4j.LoggerFactory; import java.util.Calendar; import java.util.Date; import java.util.List; -import java.util.concurrent.TimeUnit; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; @@ -36,15 +34,15 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; +import nodomain.freeyourgadget.gadgetbridge.util.dialogs.MaterialDialogFragment; -public class ActivityListingDashboard extends DialogFragment { +public class ActivityListingDashboard extends MaterialDialogFragment { protected static final Logger LOG = LoggerFactory.getLogger(ActivityListingDashboard.class); GBDevice gbDevice; ActivityListingAdapter stepListAdapter; ActivitySession stepSessionsSummary; private int timeFrom; private int timeTo; - private View fragmentView; public ActivityListingDashboard() { @@ -91,19 +89,18 @@ public class ActivityListingDashboard extends DialogFragment { throw new IllegalArgumentException("Must provide a device when invoking this activity"); } stepListAdapter = new ActivityListingAdapter(getContext()); - fragmentView = view; - final TextView battery_status_date_from_text = (TextView) view.findViewById(R.id.battery_status_date_from_text); - final TextView battery_status_date_to_text = (TextView) view.findViewById(R.id.battery_status_date_to_text); - LinearLayout battery_status_date_to_layout = (LinearLayout) view.findViewById(R.id.battery_status_date_to_layout); - final SeekBar battery_status_time_span_seekbar = (SeekBar) view.findViewById(R.id.battery_status_time_span_seekbar); + final TextView battery_status_date_from_text = (TextView) getView().findViewById(R.id.battery_status_date_from_text); + final TextView battery_status_date_to_text = (TextView) getView().findViewById(R.id.battery_status_date_to_text); + LinearLayout battery_status_date_to_layout = (LinearLayout) getView().findViewById(R.id.battery_status_date_to_layout); + final SeekBar battery_status_time_span_seekbar = (SeekBar) getView().findViewById(R.id.battery_status_time_span_seekbar); boolean activity_list_debug_extra_time_range_value = GBApplication.getPrefs().getPreferences().getBoolean("activity_list_debug_extra_time_range", false); if (!activity_list_debug_extra_time_range_value) { battery_status_time_span_seekbar.setMax(3); } - final TextView battery_status_time_span_text = (TextView) view.findViewById(R.id.battery_status_time_span_text); + final TextView battery_status_time_span_text = (TextView) getView().findViewById(R.id.battery_status_time_span_text); battery_status_time_span_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @@ -223,8 +220,8 @@ public class ActivityListingDashboard extends DialogFragment { } void indicate_progress(boolean inProgress) { - LinearLayout activity_list_dashboard_results_layout = fragmentView.findViewById(R.id.activity_list_dashboard_results_layout); - RelativeLayout activity_list_dashboard_loading_layout = fragmentView.findViewById(R.id.activity_list_dashboard_loading_layout); + LinearLayout activity_list_dashboard_results_layout = getView().findViewById(R.id.activity_list_dashboard_results_layout); + RelativeLayout activity_list_dashboard_loading_layout = getView().findViewById(R.id.activity_list_dashboard_loading_layout); if (inProgress) { activity_list_dashboard_results_layout.setVisibility(View.GONE); activity_list_dashboard_loading_layout.setVisibility(View.VISIBLE); @@ -235,16 +232,16 @@ public class ActivityListingDashboard extends DialogFragment { } void populateData(ActivitySession item) { - TextView stepLabel = fragmentView.findViewById(R.id.line_layout_step_label); - TextView stepTotalLabel = fragmentView.findViewById(R.id.line_layout_total_step_label); - TextView distanceLabel = fragmentView.findViewById(R.id.line_layout_distance_label); - TextView durationLabel = fragmentView.findViewById(R.id.line_layout_duration_label); - TextView sessionCountLabel = fragmentView.findViewById(R.id.line_layout_count_label); - LinearLayout durationLayout = fragmentView.findViewById(R.id.line_layout_duration); - LinearLayout countLayout = fragmentView.findViewById(R.id.line_layout_count); - LinearLayout stepsLayout = fragmentView.findViewById(R.id.line_layout_step); - LinearLayout stepsTotalLayout = fragmentView.findViewById(R.id.line_layout_total_step); - LinearLayout distanceLayout = fragmentView.findViewById(R.id.line_layout_distance); + TextView stepLabel = getView().findViewById(R.id.line_layout_step_label); + TextView stepTotalLabel = getView().findViewById(R.id.line_layout_total_step_label); + TextView distanceLabel = getView().findViewById(R.id.line_layout_distance_label); + TextView durationLabel = getView().findViewById(R.id.line_layout_duration_label); + TextView sessionCountLabel = getView().findViewById(R.id.line_layout_count_label); + LinearLayout durationLayout = getView().findViewById(R.id.line_layout_duration); + LinearLayout countLayout = getView().findViewById(R.id.line_layout_count); + LinearLayout stepsLayout = getView().findViewById(R.id.line_layout_step); + LinearLayout stepsTotalLayout = getView().findViewById(R.id.line_layout_total_step); + LinearLayout distanceLayout = getView().findViewById(R.id.line_layout_distance); stepLabel.setText(stepListAdapter.getStepLabel(item)); stepTotalLabel.setText(stepListAdapter.getStepTotalLabel(item)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/StepStreaksDashboard.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/StepStreaksDashboard.java index ac29d4e7a..e8396367e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/StepStreaksDashboard.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/StepStreaksDashboard.java @@ -23,7 +23,6 @@ import android.widget.Toast; import androidx.annotation.Nullable; import androidx.core.content.FileProvider; -import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentActivity; import org.slf4j.Logger; @@ -45,14 +44,14 @@ import nodomain.freeyourgadget.gadgetbridge.model.DailyTotals; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.dialogs.MaterialDialogFragment; -public class StepStreaksDashboard extends DialogFragment { +public class StepStreaksDashboard extends MaterialDialogFragment { protected static final Logger LOG = LoggerFactory.getLogger(StepStreaksDashboard.class); GBDevice gbDevice; int stepsGoal; boolean cancelTasks = false; boolean backgroundTaskFinished = false; - private View fragmentView; private StepsStreaks stepsStreaks = new StepsStreaks(); private static final String GOAL = "goal"; private static final String STREAKS = "streaks"; @@ -113,7 +112,6 @@ public class StepStreaksDashboard extends DialogFragment { stepsGoal = getArguments().getInt(GOAL, 0); gbDevice = getArguments().getParcelable(GBDevice.EXTRA_DEVICE); - fragmentView = view; if (gbDevice == null) { throw new IllegalArgumentException("Must provide a device when invoking this activity"); } @@ -133,7 +131,7 @@ public class StepStreaksDashboard extends DialogFragment { } void indicate_progress(boolean inProgress) { - ProgressBar step_streak_dashboard_loading_circle = fragmentView.findViewById(R.id.step_streak_dashboard_loading_circle); + ProgressBar step_streak_dashboard_loading_circle = getView().findViewById(R.id.step_streak_dashboard_loading_circle); if (inProgress) { step_streak_dashboard_loading_circle.setAlpha(0.4f); //make it a bit softer } else { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBContactListAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBContactListAdapter.java index 5084e4c83..4888d243b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBContactListAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBContactListAdapter.java @@ -17,7 +17,6 @@ package nodomain.freeyourgadget.gadgetbridge.adapter; -import android.app.AlertDialog; import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -28,6 +27,8 @@ import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import java.util.ArrayList; import java.util.List; @@ -69,7 +70,7 @@ public class GBContactListAdapter extends RecyclerView.Adapter ((ConfigureContacts) mContext).configureContact(contact)); holder.container.setOnLongClickListener(v -> { - new AlertDialog.Builder(v.getContext()) + new MaterialAlertDialogBuilder(v.getContext()) .setTitle(R.string.contact_delete_confirm_title) .setMessage(mContext.getString(R.string.contact_delete_confirm_description, contact.getName())) .setIcon(R.drawable.ic_warning) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java index 02e83384f..7509ca3e6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java @@ -17,8 +17,9 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.adapter; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; + import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -58,6 +59,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AlertDialog; import androidx.cardview.widget.CardView; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -71,6 +73,7 @@ import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.utils.MPPointF; import com.google.android.flexbox.FlexboxLayout; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import com.jaredrummler.android.colorpicker.ColorPickerDialog; import com.jaredrummler.android.colorpicker.ColorPickerDialogListener; @@ -120,8 +123,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.FormatUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; -import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; - /** * Adapter for displaying GBDevice instances. */ @@ -548,7 +549,7 @@ public class GBDeviceAdapterv2 extends ListAdapter FREQ_MAX) { - new AlertDialog.Builder(context) + new MaterialAlertDialogBuilder(context) .setTitle(R.string.pref_invalid_frequency_title) .setMessage(R.string.pref_invalid_frequency_message) .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -796,7 +797,7 @@ public class GBDeviceAdapterv2 extends ListAdapter. */ package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -30,6 +24,14 @@ import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; +import androidx.appcompat.app.AlertDialog; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.json.JSONArray; import org.json.JSONException; import org.slf4j.Logger; @@ -104,7 +106,7 @@ public class CommuteActionsActivity extends AbstractGBActivity implements Commut LinearLayout.LayoutParams.MATCH_PARENT); input.setLayoutParams(lp); - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setView(input) .setNegativeButton(R.string.fossil_hr_new_action_cancel, null) .setPositiveButton(R.string.ok, this) @@ -123,7 +125,7 @@ public class CommuteActionsActivity extends AbstractGBActivity implements Commut LinearLayout.LayoutParams.MATCH_PARENT); input.setLayoutParams(lp); - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setView(input) .setNegativeButton(R.string.fossil_hr_edit_action_delete, new DialogInterface.OnClickListener() { @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java index cb3ec20f7..638652823 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java @@ -17,7 +17,6 @@ package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; import android.app.Activity; -import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -56,8 +55,11 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.json.JSONArray; import org.json.JSONException; @@ -132,7 +134,7 @@ public class ConfigActivity extends AbstractGBActivity { layout2.setGravity(Gravity.CENTER); - new AlertDialog.Builder(ConfigActivity.this) + new MaterialAlertDialogBuilder(ConfigActivity.this) .setTitle(getString(R.string.qhybrid_offset_time_by)) .setView(layout2) .setPositiveButton("ok", new DialogInterface.OnClickListener() { @@ -177,7 +179,7 @@ public class ConfigActivity extends AbstractGBActivity { layout2.setGravity(Gravity.CENTER); - new AlertDialog.Builder(ConfigActivity.this) + new MaterialAlertDialogBuilder(ConfigActivity.this) .setTitle(getString(R.string.qhybrid_offset_timezone)) .setView(layout2) .setPositiveButton("ok", new DialogInterface.OnClickListener() { @@ -387,7 +389,7 @@ public class ConfigActivity extends AbstractGBActivity { @Override public void onCheckedChanged(CompoundButton buttonView, boolean checked) { if (!device.getDeviceInfo(QHybridSupport.ITEM_STEP_GOAL).getDetails().equals("1000000")) { - new AlertDialog.Builder(ConfigActivity.this) + new MaterialAlertDialogBuilder(ConfigActivity.this) .setMessage(getString(R.string.qhybrid_prompt_million_steps)) .setPositiveButton("ok", null) .show(); @@ -449,7 +451,7 @@ public class ConfigActivity extends AbstractGBActivity { buttonTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - AlertDialog dialog = new AlertDialog.Builder(ConfigActivity.this) + AlertDialog dialog = new MaterialAlertDialogBuilder(ConfigActivity.this) .setItems(names, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FileManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FileManagementActivity.java index c250c0264..0912089db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FileManagementActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FileManagementActivity.java @@ -16,7 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; -import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -32,6 +31,8 @@ import android.widget.Toast; import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import java.io.FileInputStream; import java.io.IOException; @@ -145,7 +146,7 @@ public class FileManagementActivity extends AbstractGBActivity implements View.O if (!handleFound) text = "File does not start with a known handle. Are you sure the header is already generated?"; text += " Repeat to continue anyway."; - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setTitle("warning") .setMessage(text) .setPositiveButton("ok", null) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HRConfigActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HRConfigActivity.java index eb26ff11b..d51e4481f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HRConfigActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HRConfigActivity.java @@ -16,7 +16,8 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; -import android.app.AlertDialog; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.QHYBRID_COMMAND_UPDATE_WIDGETS; + import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -35,6 +36,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -57,8 +60,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Version; -import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.QHYBRID_COMMAND_UPDATE_WIDGETS; - public class HRConfigActivity extends AbstractGBActivity { private SharedPreferences sharedPreferences; private WidgetListAdapter widgetListAdapter; @@ -110,7 +111,7 @@ public class HRConfigActivity extends AbstractGBActivity { findViewById(R.id.qhybrid_set_background).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - new AlertDialog.Builder(HRConfigActivity.this) + new MaterialAlertDialogBuilder(HRConfigActivity.this) .setTitle("whoop whoop") .setMessage("background has to be pushed every time a custom widget changes, causing traffic and battery drain. Consider that when using custom widgets.") .setPositiveButton("ok", new DialogInterface.OnClickListener() { @@ -153,7 +154,7 @@ public class HRConfigActivity extends AbstractGBActivity { } final String[] nameStrings = names.toArray(new String[0]); - new AlertDialog.Builder(HRConfigActivity.this) + new MaterialAlertDialogBuilder(HRConfigActivity.this) .setItems( nameStrings, new DialogInterface.OnClickListener() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java index fdbe0bab5..ef481e5b3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java @@ -17,7 +17,6 @@ package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; import android.app.Activity; -import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -31,7 +30,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; - import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -41,20 +39,17 @@ import android.view.DragEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; -import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -68,10 +63,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.TimeZone; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -115,7 +107,7 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem } } else { readyToCloseActivity = false; - new AlertDialog.Builder(HybridHRWatchfaceDesignerActivity.this) + new MaterialAlertDialogBuilder(HybridHRWatchfaceDesignerActivity.this) .setMessage(R.string.watchface_upload_failed) .setPositiveButton(R.string.ok, null) .show(); @@ -223,7 +215,7 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); input.setLayoutParams(lp); - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setView(input) .setNegativeButton(R.string.fossil_hr_new_action_cancel, null) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @@ -605,7 +597,7 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem File cacheDir = mCoordinator.getAppCacheDir(); File destFile = new File(cacheDir, app.getUUID().toString() + mCoordinator.getAppFileExtension()); if (destFile.exists()) { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setMessage(R.string.watchface_cache_confirm_overwrite) .setNegativeButton(R.string.no, null) .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/TimePicker.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/TimePicker.java index b5bdb1603..d85003a68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/TimePicker.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/TimePicker.java @@ -16,7 +16,6 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; -import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageInfo; @@ -37,9 +36,13 @@ import android.widget.RadioGroup; import android.widget.ScrollView; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.PlayNotificationRequest; -public class TimePicker extends AlertDialog.Builder { +public class TimePicker extends MaterialAlertDialogBuilder { ImageView pickerView; Canvas pickerCanvas; Bitmap pickerBitmap; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/WidgetSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/WidgetSettingsActivity.java index 05fb18ca7..647804451 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/WidgetSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/WidgetSettingsActivity.java @@ -23,8 +23,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ListView; import android.widget.RadioButton; @@ -36,6 +34,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; @@ -114,7 +114,7 @@ public class WidgetSettingsActivity extends AbstractGBActivity { } private void showElementDialog(@Nullable final CustomWidgetElement element){ - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(WidgetSettingsActivity.this) + MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(WidgetSettingsActivity.this) .setView(R.layout.qhybrid_element_popup_view); if(element == null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java index 7744b4a15..1003634bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java @@ -34,18 +34,17 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SURROUND_MODE; -import android.app.AlertDialog; +import android.app.ProgressDialog; import android.content.Context; +import android.content.DialogInterface; import android.os.Parcel; import androidx.preference.EditTextPreference; - -import android.app.ProgressDialog; -import android.content.DialogInterface; - import androidx.preference.ListPreference; import androidx.preference.Preference; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -154,7 +153,7 @@ public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC final Context context = preference.getContext(); - new AlertDialog.Builder(context) + new MaterialAlertDialogBuilder(context) .setTitle(R.string.sony_anc_optimize_confirmation_title) .setMessage(R.string.sony_anc_optimize_confirmation_description) .setIcon(R.drawable.ic_hearing) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java index aca747413..71079c585 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -23,6 +23,7 @@ import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.Color; import android.net.Uri; @@ -39,6 +40,8 @@ import androidx.annotation.Nullable; import androidx.core.content.FileProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.color.DynamicColors; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -113,7 +116,18 @@ public class AndroidUtils { */ public static String getTextColorHex(Context context) { int color; - if (GBApplication.isDarkThemeEnabled()) { + if (DynamicColors.isDynamicColorAvailable() && GBApplication.areDynamicColorsEnabled()) { + Context dynamicColorContext; + if (GBApplication.isDarkThemeEnabled()) { + dynamicColorContext = DynamicColors.wrapContextIfAvailable(context, R.style.GadgetbridgeThemeDynamicDark); + } else { + dynamicColorContext = DynamicColors.wrapContextIfAvailable(context, R.style.GadgetbridgeThemeDynamicLight); + } + int[] attrsToResolve = {R.attr.colorOnSurface}; + TypedArray ta = dynamicColorContext.obtainStyledAttributes(attrsToResolve); + color = ta.getColor(0, 0); + ta.recycle(); + } else if (GBApplication.isDarkThemeEnabled()) { color = context.getResources().getColor(R.color.primarytext_dark); } else { color = context.getResources().getColor(R.color.primarytext_light); @@ -127,7 +141,18 @@ public class AndroidUtils { */ public static String getBackgroundColorHex(Context context) { int color; - if (GBApplication.isDarkThemeEnabled()) { + if (DynamicColors.isDynamicColorAvailable() && GBApplication.areDynamicColorsEnabled()) { + Context dynamicColorContext; + if (GBApplication.isDarkThemeEnabled()) { + dynamicColorContext = DynamicColors.wrapContextIfAvailable(context, R.style.GadgetbridgeThemeDynamicDark); + } else { + dynamicColorContext = DynamicColors.wrapContextIfAvailable(context, R.style.GadgetbridgeThemeDynamicLight); + } + int[] attrsToResolve = {R.attr.colorSurface}; + TypedArray ta = dynamicColorContext.obtainStyledAttributes(attrsToResolve); + color = ta.getColor(0, 0); + ta.recycle(); + } else if (GBApplication.isDarkThemeEnabled()) { color = context.getResources().getColor(R.color.cardview_dark_background); } else { color = context.getResources().getColor(R.color.cardview_light_background); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/BondingUtil.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/BondingUtil.java index 5ed5b1f9d..8256544fa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/BondingUtil.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/BondingUtil.java @@ -20,7 +20,6 @@ import static androidx.core.app.ActivityCompat.startIntentSenderForResult; import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast; import android.app.Activity; -import android.app.AlertDialog; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.companion.AssociationRequest; @@ -38,6 +37,8 @@ import android.widget.Toast; import androidx.annotation.RequiresApi; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -195,7 +196,7 @@ public class BondingUtil { // Do nothing return; } else if (bondingStyle == DeviceCoordinator.BONDING_STYLE_ASK) { - new AlertDialog.Builder(bondingInterface.getContext()) + new MaterialAlertDialogBuilder(bondingInterface.getContext()) .setCancelable(true) .setTitle(bondingInterface.getContext().getString(R.string.discovery_pair_title, deviceCandidate.getName())) .setMessage(bondingInterface.getContext().getString(R.string.discovery_pair_question)) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ChangeLog.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ChangeLog.java new file mode 100644 index 000000000..cdc3904c4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ChangeLog.java @@ -0,0 +1,78 @@ +/* Copyright (C) 2023 Arjan Schrijver + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.util; + +import android.content.Context; +import android.content.DialogInterface; +import android.webkit.WebView; + +import androidx.appcompat.app.AlertDialog; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import nodomain.freeyourgadget.gadgetbridge.R; + +public class ChangeLog extends de.cketti.library.changelog.ChangeLog { + public ChangeLog(Context context, String css) { + super(context, css); + } + + public AlertDialog getMaterialLogDialog() { + return getMaterialDialog(isFirstRunEver()); + } + + public AlertDialog getMaterialFullLogDialog() { + return getMaterialDialog(true); + } + + protected AlertDialog getMaterialDialog(boolean full) { + WebView wv = new WebView(mContext); + //wv.setBackgroundColor(0); // transparent + wv.loadDataWithBaseURL(null, getLog(full), "text/html", "UTF-8", null); + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mContext); + builder.setTitle( + mContext.getResources().getString( + full ? R.string.changelog_full_title : R.string.changelog_title)) + .setView(wv) + .setCancelable(false) + // OK button + .setPositiveButton( + mContext.getResources().getString(R.string.changelog_ok_button), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // The user clicked "OK" so save the current version code as + // "last version code". + updateVersionInPreferences(); + } + }); + + if (!full) { + // Show "Moreā€¦" button if we're only displaying a partial change log. + builder.setNegativeButton(R.string.changelog_show_full, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + getMaterialFullLogDialog().show(); + } + }); + } + + return builder.create(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreferenceFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreferenceFragment.java index b0d481119..8762e830d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreferenceFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreferenceFragment.java @@ -23,9 +23,10 @@ import android.widget.TimePicker; import androidx.preference.DialogPreference; import androidx.preference.Preference; -import androidx.preference.PreferenceDialogFragmentCompat; -public class XTimePreferenceFragment extends PreferenceDialogFragmentCompat implements DialogPreference.TargetFragment { +import nodomain.freeyourgadget.gadgetbridge.util.dialogs.MaterialPreferenceDialogFragment; + +public class XTimePreferenceFragment extends MaterialPreferenceDialogFragment implements DialogPreference.TargetFragment { private TimePicker picker = null; @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialDialogFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialDialogFragment.java new file mode 100644 index 000000000..1b0cee5eb --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialDialogFragment.java @@ -0,0 +1,46 @@ +/* Copyright (C) 2023 Arjan Schrijver + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.util.dialogs; + +import android.app.Dialog; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +public class MaterialDialogFragment extends DialogFragment { + View theDialogView; + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity()); + theDialogView = onCreateView(LayoutInflater.from(requireContext()), null, savedInstanceState); + builder.setView(theDialogView); + + return builder.create(); + } + + @Override + public View getView() { + return theDialogView; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialEditTextPreferenceDialogFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialEditTextPreferenceDialogFragment.java new file mode 100644 index 000000000..fe50e27c0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialEditTextPreferenceDialogFragment.java @@ -0,0 +1,107 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of and modified for Gadgetbridge. + */ + +package nodomain.freeyourgadget.gadgetbridge.util.dialogs; + +import static androidx.annotation.RestrictTo.Scope.LIBRARY; + +import android.os.Bundle; +import android.view.View; +import android.widget.EditText; + +import androidx.annotation.NonNull; +import androidx.annotation.RestrictTo; +import androidx.preference.EditTextPreference; + +public class MaterialEditTextPreferenceDialogFragment extends MaterialPreferenceDialogFragment { + + private static final String SAVE_STATE_TEXT = "EditTextPreferenceDialogFragment.text"; + + private EditText mEditText; + + private CharSequence mText; + + public static MaterialEditTextPreferenceDialogFragment newInstance(String key) { + final MaterialEditTextPreferenceDialogFragment + fragment = new MaterialEditTextPreferenceDialogFragment(); + final Bundle b = new Bundle(1); + b.putString(ARG_KEY, key); + fragment.setArguments(b); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState == null) { + mText = getEditTextPreference().getText(); + } else { + mText = savedInstanceState.getCharSequence(SAVE_STATE_TEXT); + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putCharSequence(SAVE_STATE_TEXT, mText); + } + + @Override + protected void onBindDialogView(View view) { + super.onBindDialogView(view); + + mEditText = view.findViewById(android.R.id.edit); + + if (mEditText == null) { + throw new IllegalStateException("Dialog view must contain an EditText with id" + + " @android:id/edit"); + } + + mEditText.requestFocus(); + mEditText.setText(mText); + // Place cursor at the end + mEditText.setSelection(mEditText.getText().length()); +// if (getEditTextPreference().getOnBindEditTextListener() != null) { +// getEditTextPreference().getOnBindEditTextListener().onBindEditText(mEditText); +// } + } + + private EditTextPreference getEditTextPreference() { + return (EditTextPreference) getPreference(); + } + + /** @hide */ + @RestrictTo(LIBRARY) + @Override + protected boolean needInputMethod() { + // We want the input method to show, if possible, when dialog is displayed + return true; + } + + @Override + public void onDialogClosed(boolean positiveResult) { + if (positiveResult) { + String value = mEditText.getText().toString(); + final EditTextPreference preference = getEditTextPreference(); + if (preference.callChangeListener(value)) { + preference.setText(value); + } + } + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialListPreferenceDialogFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialListPreferenceDialogFragment.java new file mode 100644 index 000000000..04556b98f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialListPreferenceDialogFragment.java @@ -0,0 +1,117 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of and modified for Gadgetbridge. + */ + +package nodomain.freeyourgadget.gadgetbridge.util.dialogs; + +import android.content.DialogInterface; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.preference.ListPreference; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +public class MaterialListPreferenceDialogFragment extends MaterialPreferenceDialogFragment { + + private static final String SAVE_STATE_INDEX = "ListPreferenceDialogFragment.index"; + private static final String SAVE_STATE_ENTRIES = "ListPreferenceDialogFragment.entries"; + private static final String SAVE_STATE_ENTRY_VALUES = + "ListPreferenceDialogFragment.entryValues"; + + @SuppressWarnings("WeakerAccess") /* synthetic access */ + int mClickedDialogEntryIndex; + private CharSequence[] mEntries; + private CharSequence[] mEntryValues; + + public static MaterialListPreferenceDialogFragment newInstance(String key) { + final MaterialListPreferenceDialogFragment fragment = + new MaterialListPreferenceDialogFragment(); + final Bundle b = new Bundle(1); + b.putString(ARG_KEY, key); + fragment.setArguments(b); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState == null) { + final ListPreference preference = getListPreference(); + + if (preference.getEntries() == null || preference.getEntryValues() == null) { + throw new IllegalStateException( + "ListPreference requires an entries array and an entryValues array."); + } + + mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue()); + mEntries = preference.getEntries(); + mEntryValues = preference.getEntryValues(); + } else { + mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0); + mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES); + mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES); + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex); + outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries); + outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues); + } + + private ListPreference getListPreference() { + return (ListPreference) getPreference(); + } + + @Override + protected void onPrepareDialogBuilder(MaterialAlertDialogBuilder builder) { + super.onPrepareDialogBuilder(builder); + + builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mClickedDialogEntryIndex = which; + + // Clicking on an item simulates the positive button click, and dismisses + // the dialog. + MaterialListPreferenceDialogFragment.this.onClick(dialog, + DialogInterface.BUTTON_POSITIVE); + dialog.dismiss(); + } + }); + + // The typical interaction for list-based dialogs is to have click-on-an-item dismiss the + // dialog instead of the user having to press 'Ok'. + builder.setPositiveButton(null, null); + } + + @Override + public void onDialogClosed(boolean positiveResult) { + if (positiveResult && mClickedDialogEntryIndex >= 0) { + String value = mEntryValues[mClickedDialogEntryIndex].toString(); + final ListPreference preference = getListPreference(); + if (preference.callChangeListener(value)) { + preference.setValue(value); + } + } + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialMultiSelectListPreferenceDialogFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialMultiSelectListPreferenceDialogFragment.java new file mode 100644 index 000000000..4bc04fa1a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialMultiSelectListPreferenceDialogFragment.java @@ -0,0 +1,136 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of and modified for Gadgetbridge. + */ + +package nodomain.freeyourgadget.gadgetbridge.util.dialogs; + +import android.content.DialogInterface; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.preference.MultiSelectListPreference; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class MaterialMultiSelectListPreferenceDialogFragment extends MaterialPreferenceDialogFragment { + + private static final String SAVE_STATE_VALUES = + "MultiSelectListPreferenceDialogFragmentCompat.values"; + private static final String SAVE_STATE_CHANGED = + "MultiSelectListPreferenceDialogFragmentCompat.changed"; + private static final String SAVE_STATE_ENTRIES = + "MultiSelectListPreferenceDialogFragmentCompat.entries"; + private static final String SAVE_STATE_ENTRY_VALUES = + "MultiSelectListPreferenceDialogFragmentCompat.entryValues"; + + @SuppressWarnings("WeakerAccess") /* synthetic access */ + Set mNewValues = new HashSet<>(); + @SuppressWarnings("WeakerAccess") /* synthetic access */ + boolean mPreferenceChanged; + @SuppressWarnings("WeakerAccess") /* synthetic access */ + CharSequence[] mEntries; + @SuppressWarnings("WeakerAccess") /* synthetic access */ + CharSequence[] mEntryValues; + + public static MaterialMultiSelectListPreferenceDialogFragment newInstance(String key) { + final MaterialMultiSelectListPreferenceDialogFragment fragment = + new MaterialMultiSelectListPreferenceDialogFragment(); + final Bundle b = new Bundle(1); + b.putString(ARG_KEY, key); + fragment.setArguments(b); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState == null) { + final MultiSelectListPreference preference = getListPreference(); + + if (preference.getEntries() == null || preference.getEntryValues() == null) { + throw new IllegalStateException( + "MultiSelectListPreference requires an entries array and " + + "an entryValues array."); + } + + mNewValues.clear(); + mNewValues.addAll(preference.getValues()); + mPreferenceChanged = false; + mEntries = preference.getEntries(); + mEntryValues = preference.getEntryValues(); + } else { + mNewValues.clear(); + mNewValues.addAll(savedInstanceState.getStringArrayList(SAVE_STATE_VALUES)); + mPreferenceChanged = savedInstanceState.getBoolean(SAVE_STATE_CHANGED, false); + mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES); + mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES); + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putStringArrayList(SAVE_STATE_VALUES, new ArrayList<>(mNewValues)); + outState.putBoolean(SAVE_STATE_CHANGED, mPreferenceChanged); + outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries); + outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues); + } + + private MultiSelectListPreference getListPreference() { + return (MultiSelectListPreference) getPreference(); + } + + @Override + protected void onPrepareDialogBuilder(MaterialAlertDialogBuilder builder) { + super.onPrepareDialogBuilder(builder); + + final int entryCount = mEntryValues.length; + final boolean[] checkedItems = new boolean[entryCount]; + for (int i = 0; i < entryCount; i++) { + checkedItems[i] = mNewValues.contains(mEntryValues[i].toString()); + } + builder.setMultiChoiceItems(mEntries, checkedItems, + new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + if (isChecked) { + mPreferenceChanged |= mNewValues.add( + mEntryValues[which].toString()); + } else { + mPreferenceChanged |= mNewValues.remove( + mEntryValues[which].toString()); + } + } + }); + } + + @Override + public void onDialogClosed(boolean positiveResult) { + if (positiveResult && mPreferenceChanged) { + final MultiSelectListPreference preference = getListPreference(); + if (preference.callChangeListener(mNewValues)) { + preference.setValues(mNewValues); + } + } + mPreferenceChanged = false; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialPreferenceDialogFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialPreferenceDialogFragment.java new file mode 100644 index 000000000..9358d608f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/dialogs/MaterialPreferenceDialogFragment.java @@ -0,0 +1,269 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of and modified for Gadgetbridge. + */ + +package nodomain.freeyourgadget.gadgetbridge.util.dialogs; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.preference.DialogPreference; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +/** + * Abstract base class which presents a dialog associated with a {@link DialogPreference}. Since + * the preference object may not be available during fragment re-creation, the necessary + * information for displaying the dialog is read once during the initial call to + * {@link #onCreate(Bundle)} and saved/restored in the saved instance state. Custom subclasses + * should also follow this pattern. + */ +public abstract class MaterialPreferenceDialogFragment extends DialogFragment implements + DialogInterface.OnClickListener { + + protected static final String ARG_KEY = "key"; + + private static final String SAVE_STATE_TITLE = "PreferenceDialogFragment.title"; + private static final String SAVE_STATE_POSITIVE_TEXT = "PreferenceDialogFragment.positiveText"; + private static final String SAVE_STATE_NEGATIVE_TEXT = "PreferenceDialogFragment.negativeText"; + private static final String SAVE_STATE_MESSAGE = "PreferenceDialogFragment.message"; + private static final String SAVE_STATE_LAYOUT = "PreferenceDialogFragment.layout"; + private static final String SAVE_STATE_ICON = "PreferenceDialogFragment.icon"; + + private DialogPreference mPreference; + + private CharSequence mDialogTitle; + private CharSequence mPositiveButtonText; + private CharSequence mNegativeButtonText; + private CharSequence mDialogMessage; + private @LayoutRes int mDialogLayoutRes; + + private BitmapDrawable mDialogIcon; + + /** Which button was clicked. */ + private int mWhichButtonClicked; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Fragment rawFragment = getTargetFragment(); + if (!(rawFragment instanceof DialogPreference.TargetFragment)) { + throw new IllegalStateException("Target fragment must implement TargetFragment" + + " interface"); + } + + final DialogPreference.TargetFragment fragment = + (DialogPreference.TargetFragment) rawFragment; + + final String key = getArguments().getString(ARG_KEY); + if (savedInstanceState == null) { + mPreference = fragment.findPreference(key); + mDialogTitle = mPreference.getDialogTitle(); + mPositiveButtonText = mPreference.getPositiveButtonText(); + mNegativeButtonText = mPreference.getNegativeButtonText(); + mDialogMessage = mPreference.getDialogMessage(); + mDialogLayoutRes = mPreference.getDialogLayoutResource(); + + final Drawable icon = mPreference.getDialogIcon(); + if (icon == null || icon instanceof BitmapDrawable) { + mDialogIcon = (BitmapDrawable) icon; + } else { + final Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), + icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(bitmap); + icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + icon.draw(canvas); + mDialogIcon = new BitmapDrawable(getResources(), bitmap); + } + } else { + mDialogTitle = savedInstanceState.getCharSequence(SAVE_STATE_TITLE); + mPositiveButtonText = savedInstanceState.getCharSequence(SAVE_STATE_POSITIVE_TEXT); + mNegativeButtonText = savedInstanceState.getCharSequence(SAVE_STATE_NEGATIVE_TEXT); + mDialogMessage = savedInstanceState.getCharSequence(SAVE_STATE_MESSAGE); + mDialogLayoutRes = savedInstanceState.getInt(SAVE_STATE_LAYOUT, 0); + final Bitmap bitmap = savedInstanceState.getParcelable(SAVE_STATE_ICON); + if (bitmap != null) { + mDialogIcon = new BitmapDrawable(getResources(), bitmap); + } + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putCharSequence(SAVE_STATE_TITLE, mDialogTitle); + outState.putCharSequence(SAVE_STATE_POSITIVE_TEXT, mPositiveButtonText); + outState.putCharSequence(SAVE_STATE_NEGATIVE_TEXT, mNegativeButtonText); + outState.putCharSequence(SAVE_STATE_MESSAGE, mDialogMessage); + outState.putInt(SAVE_STATE_LAYOUT, mDialogLayoutRes); + if (mDialogIcon != null) { + outState.putParcelable(SAVE_STATE_ICON, mDialogIcon.getBitmap()); + } + } + + @Override + public @NonNull + Dialog onCreateDialog(Bundle savedInstanceState) { + final Context context = getActivity(); + mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE; + + final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context) + .setTitle(mDialogTitle) + .setIcon(mDialogIcon) + .setPositiveButton(mPositiveButtonText, this) + .setNegativeButton(mNegativeButtonText, this); + + View contentView = onCreateDialogView(context); + if (contentView != null) { + onBindDialogView(contentView); + builder.setView(contentView); + } else { + builder.setMessage(mDialogMessage); + } + + onPrepareDialogBuilder(builder); + + // Create the dialog + final Dialog dialog = builder.create(); + if (needInputMethod()) { + requestInputMethod(dialog); + } + + return dialog; + } + + /** + * Get the preference that requested this dialog. Available after {@link #onCreate(Bundle)} has + * been called on the {@link PreferenceFragmentCompat} which launched this dialog. + * + * @return The {@link DialogPreference} associated with this dialog + */ + public DialogPreference getPreference() { + if (mPreference == null) { + final String key = getArguments().getString(ARG_KEY); + final DialogPreference.TargetFragment fragment = + (DialogPreference.TargetFragment) getTargetFragment(); + mPreference = fragment.findPreference(key); + } + return mPreference; + } + + /** + * Prepares the dialog builder to be shown when the preference is clicked. + * Use this to set custom properties on the dialog. + * + *

Do not {@link MaterialAlertDialogBuilder#create()} or {@link MaterialAlertDialogBuilder#show()}. + */ + protected void onPrepareDialogBuilder(MaterialAlertDialogBuilder builder) {} + + /** + * Returns whether the preference needs to display a soft input method when the dialog is + * displayed. Default is false. Subclasses should override this method if they need the soft + * input method brought up automatically. + * + *

Note: If your application targets P or above, ensure your subclass manually requests + * focus (ideally in {@link #onBindDialogView(View)}) for the input field in order to + * correctly attach the input method to the field. + * + * @hide + */ + protected boolean needInputMethod() { + return false; + } + + /** + * Sets the required flags on the dialog window to enable input method window to show up. + */ + private void requestInputMethod(Dialog dialog) { + Window window = dialog.getWindow(); + window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + } + + /** + * Creates the content view for the dialog (if a custom content view is required). + * By default, it inflates the dialog layout resource if it is set. + * + * @return The content view for the dialog + * @see DialogPreference#setLayoutResource(int) + */ + protected View onCreateDialogView(Context context) { + final int resId = mDialogLayoutRes; + if (resId == 0) { + return null; + } + + return getLayoutInflater().inflate(resId, null); + } + + /** + * Binds views in the content view of the dialog to data. + * + *

Make sure to call through to the superclass implementation. + * + * @param view The content view of the dialog, if it is custom + */ + protected void onBindDialogView(View view) { + View dialogMessageView = view.findViewById(android.R.id.message); + + if (dialogMessageView != null) { + final CharSequence message = mDialogMessage; + int newVisibility = View.GONE; + + if (!TextUtils.isEmpty(message)) { + if (dialogMessageView instanceof TextView) { + ((TextView) dialogMessageView).setText(message); + } + + newVisibility = View.VISIBLE; + } + + if (dialogMessageView.getVisibility() != newVisibility) { + dialogMessageView.setVisibility(newVisibility); + } + } + } + + @Override + public void onClick(DialogInterface dialog, int which) { + mWhichButtonClicked = which; + } + + @Override + public void onDismiss(@NonNull DialogInterface dialog) { + super.onDismiss(dialog); + onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE); + } + + public abstract void onDialogClosed(boolean positiveResult); +} diff --git a/app/src/main/res/color/alarm_dow.xml b/app/src/main/res/color/alarm_dow.xml index 1bf49a8b9..3bb1f0c30 100644 --- a/app/src/main/res/color/alarm_dow.xml +++ b/app/src/main/res/color/alarm_dow.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_controlcenterv2_app_bar_main.xml b/app/src/main/res/layout/activity_controlcenterv2_app_bar_main.xml index 2f902eb20..211a1058e 100644 --- a/app/src/main/res/layout/activity_controlcenterv2_app_bar_main.xml +++ b/app/src/main/res/layout/activity_controlcenterv2_app_bar_main.xml @@ -1,5 +1,6 @@ - - diff --git a/app/src/main/res/layout/activity_controlcenterv2_content_main.xml b/app/src/main/res/layout/activity_controlcenterv2_content_main.xml index fea0748a4..6dcd9ba27 100644 --- a/app/src/main/res/layout/activity_controlcenterv2_content_main.xml +++ b/app/src/main/res/layout/activity_controlcenterv2_content_main.xml @@ -16,7 +16,6 @@ android:layout_height="wrap_content" android:layout_centerInParent="true" android:alpha="0.1" - android:tint="?attr/textColorPrimary" app:srcCompat="@drawable/gadgetbridge_img" />