diff --git a/README.md b/README.md index 6835b13c6..9681737b2 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Alternatively, you can [checkout](https://www.jetbrains.com/help/idea/set-up-a-g People who use Nix(OS) can use the `flake.nix` file in the repo for a dev shell. Note that we don't build the app using flakes; it's only used for a dev shell. +Additionally, the flake is very flaky, since Android development in Nixpkgs currently isn't the best. ### Keys If you want to use the Google Maps integration, you will need the API keys. You can contact us for more information and to obtain the keys. _This is not required to compile and build the app._ diff --git a/android-versions.toml b/android-versions.toml new file mode 100644 index 000000000..04afda118 --- /dev/null +++ b/android-versions.toml @@ -0,0 +1,9 @@ +# Common file with version numbers, used by the different subprojects +# Also used by the flake file to get the correct versions if you need it. +# IMPORTANT: this file is read as a "Java Properties file" by Gradle +# AND as a "TOML file" by Nix. So it needs to be valid in both. +# Since properties strings are not quoted, we unquote the values in Gradle. +cmdLineToolsVersion = "12.0-rc15" +platformToolsVersion = "34.0.4" +buildToolsVersions = "34.0.0" +platformVersions = "34" diff --git a/app/build.gradle b/app/build.gradle index a2d94545b..8dc646a02 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,16 +27,19 @@ apply plugin: 'com.google.firebase.crashlytics' // Read our properties, see bottom for details. def props = loadProperties() +def versions = loadAndroidVersions() android { - buildToolsVersion "33.0.0" + // We need this for Nix flakes + //noinspection GrDeprecatedAPIUsage + buildToolsVersion versions.buildToolsVersions defaultConfig { - compileSdk 33 + compileSdk versions.platformVersions as Integer applicationId "be.ugent.zeus.hydra" minSdk 21 - targetSdk 33 + targetSdk 34 versionCode 36000 versionName "3.6.0" vectorDrawables.useSupportLibrary = true @@ -101,8 +104,8 @@ android { compileOptions { coreLibraryDesugaringEnabled true - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } buildTypes { @@ -158,7 +161,7 @@ android { namespace 'be.ugent.zeus.hydra' lint { - disable 'RtlSymmetry', 'VectorPath', 'InsecureBaseConfiguration', 'Overdraw', 'GradleDependency', 'OldTargetApi', 'ObsoleteLintCustomCheck', 'NotificationPermission' + disable 'RtlSymmetry', 'VectorPath', 'Overdraw', 'GradleDependency', 'NotificationPermission' showAll true warningsAsErrors true } @@ -167,34 +170,24 @@ android { dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' - // Needed until all dependencies will be fixed - constraints { - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10") { - because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib") - } - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10") { - because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib") - } - } - - implementation 'androidx.core:core:1.10.1' + implementation 'androidx.core:core:1.12.0' implementation 'androidx.media:media:1.6.0' implementation 'androidx.fragment:fragment:1.6.1' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.preference:preference:1.2.1' implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'com.google.android.material:material:1.9.0' - implementation 'androidx.browser:browser:1.5.0' + implementation 'com.google.android.material:material:1.10.0' + implementation 'androidx.browser:browser:1.6.0' implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.2' implementation 'androidx.lifecycle:lifecycle-livedata:2.6.2' implementation 'androidx.lifecycle:lifecycle-livedata:2.6.2' implementation 'androidx.lifecycle:lifecycle-common-java8:2.6.2' implementation 'androidx.viewpager2:viewpager2:1.0.0' - implementation 'androidx.room:room-runtime:2.5.2' - annotationProcessor 'androidx.room:room-compiler:2.5.2' + implementation 'androidx.room:room-runtime:2.6.0' + annotationProcessor 'androidx.room:room-compiler:2.6.0' implementation 'com.artemzin.rxjava:proguard-rules:1.3.3.0' implementation 'com.squareup.okhttp3:okhttp:4.11.0' implementation 'com.squareup.moshi:moshi:1.15.0' @@ -207,8 +200,8 @@ dependencies { implementation 'com.github.niqdev:ipcam-view:2.4.0' // Dependencies for the Play Store version. - storeImplementation 'com.google.android.gms:play-services-maps:18.1.0' - storeImplementation 'com.google.firebase:firebase-analytics:21.3.0' + storeImplementation 'com.google.android.gms:play-services-maps:18.2.0' + storeImplementation 'com.google.firebase:firebase-analytics:21.4.0' storeImplementation 'com.google.firebase:firebase-crashlytics:18.5.0' storeImplementation 'com.google.android.gms:play-services-code-scanner:16.1.0' @@ -224,7 +217,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' // Once final classes can be mocked, go back to mockito-core. testImplementation 'org.mockito:mockito-inline:5.2.0' - testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'org.robolectric:robolectric:4.11-beta-2' testImplementation 'androidx.test:core:1.5.0' testImplementation 'androidx.test.ext:junit:1.1.5' testImplementation 'androidx.test:rules:1.5.0' @@ -232,7 +225,7 @@ dependencies { testImplementation 'androidx.test.espresso:espresso-intents:3.5.1' testImplementation 'androidx.test.espresso:espresso-contrib:3.5.1' testImplementation 'androidx.arch.core:core-testing:2.2.0' - testImplementation 'androidx.room:room-testing:2.5.2' + testImplementation 'androidx.room:room-testing:2.6.0' testImplementation 'com.squareup.okhttp3:mockwebserver:4.11.0' testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.15.2' testImplementation 'com.shazam:shazamcrest:0.11' @@ -244,9 +237,10 @@ dependencies { // Disable Google services for open variant. -android.applicationVariants.all { variant -> - def googleTask = tasks.findByName("process${variant.name.capitalize()}GoogleServices") - googleTask.enabled = "open" != variant.flavorName +android.applicationVariants.configureEach { variant -> + tasks.named("process${variant.name.capitalize()}GoogleServices").configure { + it.enabled = "open" != variant.flavorName + } } /** @@ -282,3 +276,19 @@ def loadProperties() { return actualKeys } + +/** + * Loads the default properties, and the user properties. This will also load the + * secret keys. + */ +def loadAndroidVersions() { + def defaultProps = new Properties() + defaultProps.load(file("../android-versions.toml").newReader()) + + def strippedProps = new Properties() + for (e in defaultProps) { + strippedProps.setProperty(e.key, e.value.replaceAll('"', '')) + } + + return strippedProps +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 60311757d..ddbb5717f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,6 +31,7 @@ + @@ -55,6 +56,7 @@ android:supportsRtl="false" android:theme="@style/Hydra.Material" android:localeConfig="@xml/locales_config" + android:networkSecurityConfig="@xml/network_security_config" tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute" tools:replace="android:supportsRtl"> diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/Association.java b/app/src/main/java/be/ugent/zeus/hydra/association/Association.java index 9ff5920c4..4e6d5dbf2 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/Association.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/Association.java @@ -52,6 +52,7 @@ public Association() { // Moshi uses this! } + /** @noinspection ProtectedMemberInFinalClass*/ protected Association(Parcel in) { abbreviation = in.readString(); name = in.readString(); @@ -86,7 +87,7 @@ public int describeContents() { return 0; } - public static final Creator CREATOR = new Creator() { + public static final Creator CREATOR = new Creator<>() { @Override public Association createFromParcel(Parcel in) { return new Association(in); diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/common/AssociationRequestBuilder.java b/app/src/main/java/be/ugent/zeus/hydra/association/common/AssociationRequestBuilder.java index 6622e59fc..387b84959 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/common/AssociationRequestBuilder.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/common/AssociationRequestBuilder.java @@ -119,11 +119,6 @@ public Duration getCacheDuration() { } } - @NonNull - public static Request createRawAssociationRequest(@NonNull Context context) { - return new RawRequest(context); - } - @NonNull public static Request> createListRequest(@NonNull Context context) { return new RawRequest(context) @@ -131,7 +126,7 @@ public static Request> createListRequest(@NonNull Context cont } public static Request>> createItemFilteredEventRequest(@NonNull Context context, EventFilter filter) { - return createRawAssociationRequest(context) + return new RawRequest(context) .andThen((Function, Set>>>) data -> { EventRequest.Filter newFilter = filter.toRequestFilter(context, data.getAssociations()); Set requestedAssociations = newFilter.getRequestedAssociations(); @@ -144,7 +139,7 @@ public static Request>> createItemFilteredE } public static Request>> createFilteredEventRequest(@NonNull Context context) { - return createRawAssociationRequest(context) + return new RawRequest(context) .andThen((Function, Set>>>) data -> { EventFilter eventFilter = new EventFilter(); EventRequest.Filter newFilter = eventFilter.toRequestFilter(context, data.getAssociations()); diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/common/EventItem.java b/app/src/main/java/be/ugent/zeus/hydra/association/common/EventItem.java index cdd1a9370..d77fec757 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/common/EventItem.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/common/EventItem.java @@ -99,10 +99,6 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(event, header, isLastOfSection); } - - void markAsLastOfSection() { - this.isLastOfSection = true; - } public OffsetDateTime getDate() { if (isItem()) { diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/common/EventRequest.java b/app/src/main/java/be/ugent/zeus/hydra/association/common/EventRequest.java index 922a28b8a..3ecc7fb09 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/common/EventRequest.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/common/EventRequest.java @@ -42,9 +42,9 @@ /** * Get the events for all associations. - * - * The general event flow it like this: - * + *

+ * The general event flow is like this: + *

* 1. We get the list of associations from the server. * 2. Use the filter to update the blacklist. * 3. Use the blacklist to get a whitelist. diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/event/EventDetailsActivity.java b/app/src/main/java/be/ugent/zeus/hydra/association/event/EventDetailsActivity.java index 8d8628185..8e967b68b 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/event/EventDetailsActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/event/EventDetailsActivity.java @@ -36,6 +36,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.IntentCompat; import androidx.core.text.util.LinkifyCompat; import androidx.core.view.WindowCompat; @@ -80,8 +81,8 @@ protected void onCreate(Bundle savedInstanceState) { boolean hasDescription = true; // Get data from saved instance, or from intent. - event = getIntent().getParcelableExtra(PARCEL_EVENT); - Association association = getIntent().getParcelableExtra(PARCEL_ASSOCIATION); + event = IntentCompat.getParcelableExtra(getIntent(), PARCEL_EVENT, Event.class); + Association association = IntentCompat.getParcelableExtra(getIntent(), PARCEL_ASSOCIATION, Association.class); assert event != null; assert association != null; @@ -95,7 +96,7 @@ protected void onCreate(Bundle savedInstanceState) { if (event.getDescription() != null && !event.getDescription().trim().isEmpty()) { binding.description.setText(event.getDescription()); - LinkifyCompat.addLinks(binding.description, Linkify.ALL); + LinkifyCompat.addLinks(binding.description, Linkify.EMAIL_ADDRESSES | Linkify.WEB_URLS); } else { hasDescription = false; binding.eventDescriptionBlock.setVisibility(View.GONE); diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/event/EventList.java b/app/src/main/java/be/ugent/zeus/hydra/association/event/EventList.java index 6a7612dc1..ef853a823 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/event/EventList.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/event/EventList.java @@ -22,7 +22,6 @@ package be.ugent.zeus.hydra.association.event; -import java.util.Map; import java.util.Objects; /** @@ -30,6 +29,7 @@ */ public final class EventList { + /** @noinspection unused*/ private EventPage page; public EventList() { diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/event/EventPage.java b/app/src/main/java/be/ugent/zeus/hydra/association/event/EventPage.java index 87d249cc4..5d5619a4a 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/event/EventPage.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/event/EventPage.java @@ -30,6 +30,7 @@ */ public final class EventPage { + /** @noinspection unused*/ private List entries; public List getEntries() { diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/list/EventFragment.java b/app/src/main/java/be/ugent/zeus/hydra/association/list/EventFragment.java index f0fd577a0..590beeb0f 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/list/EventFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/list/EventFragment.java @@ -32,8 +32,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; +import androidx.core.view.MenuProvider; import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; +import androidx.lifecycle.Lifecycle; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -59,6 +61,7 @@ import com.google.android.material.datepicker.MaterialDatePicker; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.textfield.TextInputLayout; +import org.jetbrains.annotations.NotNull; import static be.ugent.zeus.hydra.common.utils.FragmentUtils.requireBaseActivity; @@ -83,12 +86,6 @@ public class EventFragment extends Fragment implements MainActivity.ScheduledRem private TextInputLayout startTime; private TextInputLayout endTime; - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -99,6 +96,27 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + requireActivity().addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull @NotNull Menu menu, @NonNull @NotNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.menu_main_events, menu); + requireBaseActivity(EventFragment.this).tintToolbarIcons(menu, R.id.action_refresh, R.id.action_search); + } + + @Override + public boolean onMenuItemSelected(@NonNull @NotNull MenuItem menuItem) { + int itemId = menuItem.getItemId(); + if (itemId == R.id.action_refresh) { + viewModel.onRefresh(); + return true; + } else if (itemId == R.id.action_search) { + showSheet(); + return true; + } + return false; + } + }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); + bottomSheet = requireActivity().findViewById(R.id.bottom_sheet); // Load the options. getLayoutInflater().inflate(R.layout.item_search_filter, bottomSheet, true); @@ -221,26 +239,6 @@ private void onError(Throwable throwable) { .show(); } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_main_events, menu); - requireBaseActivity(this).tintToolbarIcons(menu, R.id.action_refresh, R.id.action_search); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - int itemId = item.getItemId(); - if (itemId == R.id.action_refresh) { - viewModel.onRefresh(); - return true; - } else if (itemId == R.id.action_search) { - showSheet(); - return true; - } - return super.onOptionsItemSelected(item); - } - @Override public void onRemovalScheduled() { hideExternalViews(); diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/preference/AssociationSelectionPreferenceFragment.java b/app/src/main/java/be/ugent/zeus/hydra/association/preference/AssociationSelectionPreferenceFragment.java index 8532e823d..aa18434a7 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/preference/AssociationSelectionPreferenceFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/preference/AssociationSelectionPreferenceFragment.java @@ -28,6 +28,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.SearchView; +import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; @@ -43,6 +44,7 @@ import be.ugent.zeus.hydra.common.arch.observers.ProgressObserver; import be.ugent.zeus.hydra.common.arch.observers.SuccessObserver; import com.google.android.material.snackbar.Snackbar; +import org.jetbrains.annotations.NotNull; import static be.ugent.zeus.hydra.common.utils.FragmentUtils.requireBaseActivity; @@ -57,12 +59,6 @@ public class AssociationSelectionPreferenceFragment extends Fragment { private final SearchableAssociationsAdapter adapter = new SearchableAssociationsAdapter(); - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -73,6 +69,27 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + requireActivity().addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull @NotNull Menu menu, @NonNull @NotNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.menu_pref_selectors, menu); + requireBaseActivity(AssociationSelectionPreferenceFragment.this).tintToolbarIcons(menu, R.id.action_select_all, R.id.action_select_none); + } + + @Override + public boolean onMenuItemSelected(@NonNull @NotNull MenuItem menuItem) { + int itemId = menuItem.getItemId(); + if (itemId == R.id.action_select_all) { + adapter.setAllChecked(true); + return true; + } else if (itemId == R.id.action_select_none) { + adapter.setAllChecked(false); + return true; + } + return false; + } + }, getViewLifecycleOwner()); + RecyclerView recyclerView = view.findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); SearchView searchView = view.findViewById(R.id.search_view); @@ -89,26 +106,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat model.getData().observe(getViewLifecycleOwner(), new ProgressObserver<>(view.findViewById(R.id.progress_bar))); } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - inflater.inflate(R.menu.menu_pref_selectors, menu); - requireBaseActivity(this).tintToolbarIcons(menu, R.id.action_select_all, R.id.action_select_none); - super.onCreateOptionsMenu(menu, inflater); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - int itemId = item.getItemId(); - if (itemId == R.id.action_select_all) { - adapter.setAllChecked(true); - return true; - } else if (itemId == R.id.action_select_none) { - adapter.setAllChecked(false); - return true; - } - return super.onOptionsItemSelected(item); - } - @Override public void onPause() { super.onPause(); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ExtendedSparseArray.java b/app/src/main/java/be/ugent/zeus/hydra/common/ExtendedSparseArray.java index b54f1d5a4..61b4dd0f3 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ExtendedSparseArray.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ExtendedSparseArray.java @@ -47,7 +47,7 @@ public class ExtendedSparseArray extends SparseArray implements Iterable iterator() { - return new Iterator() { + return new Iterator<>() { private int current; @Override diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/arch/data/BaseLiveData.java b/app/src/main/java/be/ugent/zeus/hydra/common/arch/data/BaseLiveData.java index c92e94ace..78b2b4e6f 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/arch/data/BaseLiveData.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/arch/data/BaseLiveData.java @@ -91,13 +91,4 @@ protected void onActive() { protected void loadData() { this.loadData(Bundle.EMPTY); } - - @FunctionalInterface - public interface OnRefreshStartListener { - - /** - * Starts when the refresh begins. - */ - void onRefreshStart(); - } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/ErrorObserver.java b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/ErrorObserver.java index 772b9e203..15fd4f5f9 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/ErrorObserver.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/ErrorObserver.java @@ -41,7 +41,7 @@ public abstract class ErrorObserver implements Observer> { public static ErrorObserver with(Consumer consumer) { - return new ErrorObserver() { + return new ErrorObserver<>() { @Override protected void onError(RequestException throwable) { consumer.accept(throwable); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/EventObserver.java b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/EventObserver.java index 3a22906ae..da377acac 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/EventObserver.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/EventObserver.java @@ -36,7 +36,7 @@ public abstract class EventObserver implements Observer> { public static EventObserver with(Consumer consumer) { - return new EventObserver() { + return new EventObserver<>() { @Override protected void onUnhandled(D data) { consumer.accept(data); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/PartialErrorObserver.java b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/PartialErrorObserver.java index 00eaa774d..4b0886cdb 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/PartialErrorObserver.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/PartialErrorObserver.java @@ -38,7 +38,7 @@ public abstract class PartialErrorObserver implements Observer> { public static PartialErrorObserver with(Consumer consumer) { - return new PartialErrorObserver() { + return new PartialErrorObserver<>() { @Override protected void onPartialError(RequestException throwable) { consumer.accept(throwable); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/SuccessObserver.java b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/SuccessObserver.java index dbf1d66da..9bd1906bb 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/SuccessObserver.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/SuccessObserver.java @@ -38,7 +38,7 @@ public abstract class SuccessObserver implements Observer> { public static SuccessObserver with(Consumer onSuccess) { - return new SuccessObserver() { + return new SuccessObserver<>() { @Override protected void onSuccess(@NonNull D data) { onSuccess.accept(data); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/converter/PairJsonAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/common/converter/PairJsonAdapter.java index c44b0c1fb..aa1aa5370 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/converter/PairJsonAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/converter/PairJsonAdapter.java @@ -23,20 +23,17 @@ package be.ugent.zeus.hydra.common.converter; import android.util.Pair; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.squareup.moshi.*; - import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; import java.util.Set; +import com.squareup.moshi.*; + /** * @author Niko Strijbol */ diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/database/migrations/Migration_10_11.java b/app/src/main/java/be/ugent/zeus/hydra/common/database/migrations/Migration_10_11.java index 5e244994f..00b891753 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/database/migrations/Migration_10_11.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/database/migrations/Migration_10_11.java @@ -23,7 +23,6 @@ package be.ugent.zeus.hydra.common.database.migrations; import android.content.ContentValues; -import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.util.Log; @@ -110,32 +109,28 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { "CREATE TABLE `new_minerva_announcements` (`title` TEXT, `content` TEXT, `email_sent` INTEGER NOT NULL, `_id` INTEGER NOT NULL, `last_edit_user` TEXT, `date` TEXT, `read_at` TEXT, `course` TEXT, PRIMARY KEY(`_id`), FOREIGN KEY(`course`) REFERENCES `minerva_courses`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE)" ); // We cannot just copy the data, since we need to modify the format of the data. - Cursor cursor = database.query("SELECT * FROM minerva_announcements"); - if (cursor != null) { - while (cursor.moveToNext()) { - ContentValues contentValues = new ContentValues(); - DatabaseUtils.cursorRowToContentValues(cursor, contentValues); - // We need to adjust the 'date' and 'read_at' field. - ZonedDateTime originalDate = legacyUnserialize(contentValues.getAsLong("date")); - if (originalDate != null) { - OffsetDateTime newDate = originalDate.toOffsetDateTime(); - contentValues.put("date", fromOffsetDateTime(newDate)); - } else { - contentValues.put("date", (String) null); - } - ZonedDateTime originalReadDate = legacyUnserialize(contentValues.getAsLong("read_at")); - if (originalReadDate != null) { - Instant newReadDate = originalReadDate.toInstant(); - contentValues.put("read_at", fromInstant(newReadDate)); - } else { - contentValues.put("read_at", (String) null); - } - - // Insert the row into the new table. - database.insert("new_minerva_announcements", SQLiteDatabase.CONFLICT_NONE, contentValues); + var cursor = database.query("SELECT * FROM minerva_announcements"); + while (cursor.moveToNext()) { + ContentValues contentValues = new ContentValues(); + DatabaseUtils.cursorRowToContentValues(cursor, contentValues); + // We need to adjust the 'date' and 'read_at' field. + ZonedDateTime originalDate = legacyUnserialize(contentValues.getAsLong("date")); + if (originalDate != null) { + OffsetDateTime newDate = originalDate.toOffsetDateTime(); + contentValues.put("date", fromOffsetDateTime(newDate)); + } else { + contentValues.put("date", (String) null); } - } else { - Log.w(TAG, "Cursor for announcements is null, skipping data conversion."); + ZonedDateTime originalReadDate = legacyUnserialize(contentValues.getAsLong("read_at")); + if (originalReadDate != null) { + Instant newReadDate = originalReadDate.toInstant(); + contentValues.put("read_at", fromInstant(newReadDate)); + } else { + contentValues.put("read_at", (String) null); + } + + // Insert the row into the new table. + database.insert("new_minerva_announcements", SQLiteDatabase.CONFLICT_NONE, contentValues); } // Drop the old table. database.execSQL("DROP TABLE minerva_announcements"); @@ -150,39 +145,35 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { "CREATE TABLE `new_minerva_calendar` (`_id` INTEGER NOT NULL, `title` TEXT, `content` TEXT, `start_date` TEXT, `end_date` TEXT, `location` TEXT, `type` TEXT, `last_edit_user` TEXT, `last_edit` TEXT, `last_edit_type` TEXT, `course` TEXT, `calendar_id` INTEGER NOT NULL, `is_merged` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`_id`), FOREIGN KEY(`course`) REFERENCES `minerva_courses`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE)" ); // We cannot just copy the data, since we need to modify the format of the data. - Cursor calendarCursor = database.query("SELECT * FROM minerva_calendar"); - if (calendarCursor != null) { - while (calendarCursor.moveToNext()) { - ContentValues contentValues = new ContentValues(); - DatabaseUtils.cursorRowToContentValues(calendarCursor, contentValues); - // We must convert the start_date and end_date. - ZonedDateTime originalStartDate = legacyUnserialize(contentValues.getAsLong("start_date")); - if (originalStartDate != null) { - OffsetDateTime newStartDate = originalStartDate.toOffsetDateTime(); - contentValues.put("start_date", fromOffsetDateTime(newStartDate)); - } else { - contentValues.put("start_date", (String) null); - } - ZonedDateTime originalEndDate = legacyUnserialize(contentValues.getAsLong("end_date")); - if (originalEndDate != null) { - OffsetDateTime newEndDate = originalEndDate.toOffsetDateTime(); - contentValues.put("end_date", fromOffsetDateTime(newEndDate)); - } else { - contentValues.put("end_date", (String) null); - } - ZonedDateTime originalEditDate = legacyUnserialize(contentValues.getAsLong("last_edit")); - if (originalEditDate != null) { - OffsetDateTime newEditDate = originalEditDate.toOffsetDateTime(); - contentValues.put("last_edit", fromOffsetDateTime(newEditDate)); - } else { - contentValues.put("last_edit", (String) null); - } - - // Insert the row into the new table. - database.insert("new_minerva_calendar", SQLiteDatabase.CONFLICT_NONE, contentValues); + var calendarCursor = database.query("SELECT * FROM minerva_calendar"); + while (calendarCursor.moveToNext()) { + ContentValues contentValues = new ContentValues(); + DatabaseUtils.cursorRowToContentValues(calendarCursor, contentValues); + // We must convert the start_date and end_date. + ZonedDateTime originalStartDate = legacyUnserialize(contentValues.getAsLong("start_date")); + if (originalStartDate != null) { + OffsetDateTime newStartDate = originalStartDate.toOffsetDateTime(); + contentValues.put("start_date", fromOffsetDateTime(newStartDate)); + } else { + contentValues.put("start_date", (String) null); } - } else { - Log.w(TAG, "Cursor for calendar is null, skipping data conversion."); + ZonedDateTime originalEndDate = legacyUnserialize(contentValues.getAsLong("end_date")); + if (originalEndDate != null) { + OffsetDateTime newEndDate = originalEndDate.toOffsetDateTime(); + contentValues.put("end_date", fromOffsetDateTime(newEndDate)); + } else { + contentValues.put("end_date", (String) null); + } + ZonedDateTime originalEditDate = legacyUnserialize(contentValues.getAsLong("last_edit")); + if (originalEditDate != null) { + OffsetDateTime newEditDate = originalEditDate.toOffsetDateTime(); + contentValues.put("last_edit", fromOffsetDateTime(newEditDate)); + } else { + contentValues.put("last_edit", (String) null); + } + + // Insert the row into the new table. + database.insert("new_minerva_calendar", SQLiteDatabase.CONFLICT_NONE, contentValues); } // Drop the old table. database.execSQL("DROP TABLE minerva_calendar"); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/network/InstanceProvider.java b/app/src/main/java/be/ugent/zeus/hydra/common/network/InstanceProvider.java index 718a0fe44..a17c0f600 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/network/InstanceProvider.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/network/InstanceProvider.java @@ -60,6 +60,7 @@ public static void setClient(OkHttpClient client) { @VisibleForTesting public static OkHttpClient.Builder getBuilder(File cacheDir) { + //noinspection KotlinInternalInJava return new OkHttpClient.Builder().cache(new Cache(cacheDir, CACHE_SIZE)); } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/network/JsonOkHttpRequest.java b/app/src/main/java/be/ugent/zeus/hydra/common/network/JsonOkHttpRequest.java index 2def0be21..31ef87489 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/network/JsonOkHttpRequest.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/network/JsonOkHttpRequest.java @@ -46,7 +46,7 @@ /** * Common implementation base for requests that are network requests. This request provides built-in caching on the * networking level. - * + *

*

Caching

* The caching implementation uses OkHttp's cache implementation. How long a response is cached is determined by * {@link #getCacheDuration()}. By default, requests are not cached. @@ -54,13 +54,12 @@ * Additionally, stale data is always used by default if needed. *

* To disable the cache, pass {@link BaseLiveData#REFRESH_COLD} as an argument to the request. - * + *

*

Decode

* The request uses Moshi to decode the json response into Java objects. * * @author Niko Strijbol */ -@SuppressWarnings("WeakerAccess") public abstract class JsonOkHttpRequest extends OkHttpRequest { private static final String TAG = "JsonOkHttpRequest"; diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/network/OkHttpRequest.java b/app/src/main/java/be/ugent/zeus/hydra/common/network/OkHttpRequest.java index 668403a09..836e62026 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/network/OkHttpRequest.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/network/OkHttpRequest.java @@ -23,42 +23,21 @@ package be.ugent.zeus.hydra.common.network; import android.content.Context; -import android.os.Bundle; -import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.annotation.WorkerThread; -import com.squareup.moshi.JsonAdapter; -import com.squareup.moshi.JsonDataException; -import com.squareup.moshi.Moshi; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.net.UnknownServiceException; -import java.time.Duration; -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -import be.ugent.zeus.hydra.common.arch.data.BaseLiveData; import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.reporting.Tracker; import be.ugent.zeus.hydra.common.request.Request; -import be.ugent.zeus.hydra.common.request.Result; -import okhttp3.CacheControl; +import com.squareup.moshi.Moshi; import okhttp3.OkHttpClient; -import okhttp3.Response; /** * Common implementation for requests using OkHttp. * * @author Niko Strijbol */ -@SuppressWarnings("WeakerAccess") public abstract class OkHttpRequest implements Request { - private static final String TAG = "OkHttpRequest"; - protected final Moshi moshi; protected final OkHttpClient client; protected final Tracker tracker; diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/reporting/BaseEvents.java b/app/src/main/java/be/ugent/zeus/hydra/common/reporting/BaseEvents.java index 2fe04b506..b5c531feb 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/reporting/BaseEvents.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/reporting/BaseEvents.java @@ -44,16 +44,6 @@ public interface BaseEvents { */ String login(); - /** - * Search event. Apps that support search features can use this event to contextualize search operations by - * supplying the appropriate, corresponding parameters. This event can help you identify the most popular content in - * your app. Params: - *
    - *
  • {@link Params#searchTerm()}
  • - *
- */ - String search(); - /** * Select Content event. This general purpose event signifies that a user has selected some content of a certain * type in an app. The content can be any object in your app. This event can help you identify popular content and @@ -121,11 +111,6 @@ interface Params { */ String method(); - /** - * The search string/keywords used (String). - */ - String searchTerm(); - /** * Type of content selected (String). */ diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/reporting/Tracker.java b/app/src/main/java/be/ugent/zeus/hydra/common/reporting/Tracker.java index b0507763b..0f8e3d899 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/reporting/Tracker.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/reporting/Tracker.java @@ -57,7 +57,6 @@ public interface Tracker { * @param classOverride The name of the screen class. By default, this is the current activity. */ @MainThread - @SuppressWarnings("EmptyMethod") void setCurrentScreen(@NonNull Activity activity, String screenName, String classOverride); /** diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/request/Result.java b/app/src/main/java/be/ugent/zeus/hydra/common/request/Result.java index cccd743eb..967aa7b14 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/request/Result.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/request/Result.java @@ -32,7 +32,7 @@ /** * The result of a {@link Request}. - * + *

*

Either-or-both

* This class represents an "inclusive or" type, sometimes referred to as an "outer join" type. * This means the class holds either a value, an error or both. To prevent wrong usage, a builder is provided, while the @@ -42,7 +42,7 @@ * exception. Note that implementing monad laws is not the goal of this class. *

* The class supports various methods for working with this, similar to {@link java.util.Optional}. - * + *

*

Status

* In addition to data, this class has support for indicating the status of the request result. The * status is either done or continuing. @@ -243,9 +243,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Result result = (Result) o; - return done == result.done && - Objects.equals(data, result.data) && - Objects.equals(throwable, result.throwable); + return done == result.done && Objects.equals(data, result.data) && Objects.equals(throwable, result.throwable); } @Override diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/scanner/BarcodeScanner.java b/app/src/main/java/be/ugent/zeus/hydra/common/scanner/BarcodeScanner.java index eb276f373..7f76c1db5 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/scanner/BarcodeScanner.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/scanner/BarcodeScanner.java @@ -31,7 +31,7 @@ /** * Ask some service to scan for barcodes. - * + *

* TODO: this is an ugly interface. * * @author Niko Strijbol @@ -66,7 +66,7 @@ public interface BarcodeScanner { /** * Get a barcode without activity. - * + *

* Implementations should optimize, if possible, for scanning product barcodes. * This includes EAN/UPC codes. */ diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/BaseActivity.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/BaseActivity.java index 51a2b2a3a..19cc125bc 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/BaseActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/BaseActivity.java @@ -164,6 +164,7 @@ protected boolean hasParent() { * will return null. In that case, you shouldn't show the a new snackbar. * Otherwise, you will get a snackbar for the message and length. This is * a new snackbar, which you can do with want you want. + * @noinspection SameParameterValue */ @Nullable protected Snackbar createSnackbar(String text, @BaseTransientBottomBar.Duration int duration) { diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/RefreshViewModel.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/RefreshViewModel.java index 8ab87eda5..9a3e30657 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/RefreshViewModel.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/RefreshViewModel.java @@ -113,7 +113,7 @@ private LiveData buildRefreshLiveData() { } else { busyData.add(data.hashCode()); } - refreshing.setValue(busyData.size() != 0); + refreshing.setValue(!busyData.isEmpty()); }); } return refreshing; diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/customtabs/HasTabActivityHelper.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/customtabs/HasTabActivityHelper.java index a90ce0fbc..f64c3525b 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/customtabs/HasTabActivityHelper.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/customtabs/HasTabActivityHelper.java @@ -45,7 +45,7 @@ /** * Helper for activities that use custom tabs. *

- * Taken from https://github.com/hitherejoe/Tabby + * Taken from Tabby * * @author Niko Strijbol */ diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/html/HtmlTagHandler.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/html/HtmlTagHandler.java index 5fd15469c..0b202b5c5 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/html/HtmlTagHandler.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/html/HtmlTagHandler.java @@ -43,7 +43,7 @@ * More elements can be added. However, some elements, such as center, are no longer part of the HTML standard. Thus, * this is 'pseudo-HTML'. *

- * This class is based on work in https://github.com/skimarxall/RealTextView. + * This class is based on work in RealTextView. * * @author Niko Strijbol * @author Dominik Schürmann diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/html/PicassoImageGetter.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/html/PicassoImageGetter.java index caa21fa16..713951e20 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/html/PicassoImageGetter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/html/PicassoImageGetter.java @@ -22,17 +22,15 @@ package be.ugent.zeus.hydra.common.ui.html; -import android.annotation.SuppressLint; import android.content.res.Resources; -import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.text.Html; import android.util.Log; import android.widget.TextView; +import be.ugent.zeus.hydra.common.utils.ThreadingUtils; import com.squareup.picasso.Picasso; /** @@ -55,36 +53,25 @@ public PicassoImageGetter(TextView textView, Resources resources) { @Override public Drawable getDrawable(final String source) { final DrawableWrapper result = new DrawableWrapper(new ColorDrawable()); - - // TODO: move out of async task? - @SuppressLint("StaticFieldLeak") // There is no leak - AsyncTask t = new AsyncTask() { - @Override - protected Bitmap doInBackground(final Void... meh) { - try { - return Picasso.get().load(source).get(); - } catch (Exception e) { - return null; - } + ThreadingUtils.executeWithResult(() -> { + try { + return Picasso.get().load(source).get(); + } catch (Exception e) { + return null; } + }, bitmap -> { + try { + final BitmapDrawable drawable = new BitmapDrawable(resources, bitmap); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + result.setWrappedDrawable(drawable); + result.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + result.invalidateSelf(); - @Override - protected void onPostExecute(final Bitmap bitmap) { - try { - final BitmapDrawable drawable = new BitmapDrawable(resources, bitmap); - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - result.setWrappedDrawable(drawable); - result.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - result.invalidateSelf(); - - view.setText(view.getText()); - } catch (Exception e) { - Log.w(TAG, "Error while setting image", e); - } + view.setText(view.getText()); + } catch (Exception e) { + Log.w(TAG, "Error while setting image", e); } - - }; - t.execute(); + }); return result; } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/ResultStarter.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/ResultStarter.java index 9d818a4fb..b5f81f8d2 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/ResultStarter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/ResultStarter.java @@ -32,8 +32,8 @@ * {@link androidx.fragment.app.Fragment#startActivity(Intent, Bundle)}. * * @author Niko Strijbol + * @noinspection unused */ -@SuppressWarnings("unused") public interface ResultStarter { Context getContext(); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/SpanItemSpacingDecoration.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/SpanItemSpacingDecoration.java index d948ac925..a1c60d912 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/SpanItemSpacingDecoration.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/SpanItemSpacingDecoration.java @@ -43,7 +43,6 @@ public class SpanItemSpacingDecoration extends RecyclerView.ItemDecoration { private final int spacing; - @SuppressWarnings("WeakerAccess") public SpanItemSpacingDecoration(int spacing) { this.spacing = spacing; } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/DataContainer.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/DataContainer.java index f2e5dc099..5fe1cf441 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/DataContainer.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/DataContainer.java @@ -84,8 +84,7 @@ private static Executor getDefaultBackgroundExecutor() { * @param update The update to apply. */ @MainThread - public void submitUpdate(AdapterUpdate update) { - + void submitUpdate(AdapterUpdate update) { int generation = ++maxScheduledGeneration; // Construct a unit of work for the update. @@ -102,7 +101,7 @@ public void submitUpdate(AdapterUpdate update) { // Schedule the update. backgroundExecutor.execute(work); } else { - // Just run execute the work if threading is not allowed. + // Just execute the work if threading is not allowed. work.run(); } } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/DiffAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/DiffAdapter.java index 5256a5669..fc8218822 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/DiffAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/DiffAdapter.java @@ -30,11 +30,11 @@ /** * Generic adapter with support for calculating diffs on a background thread for data updates. - * + *

*

View types

* By default, the adapter only supports one view type. Additional types must be implemented by the * subclasses. - * + *

*

Data updates

* When calling {@link #submitData(List)}, a diff with the current data will be calculated on a background thread, * after which the changes will be dispatched to this adapter. This uses {@link androidx.recyclerview.widget.DiffUtil} diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/MultiSelectAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/MultiSelectAdapter.java index 5925b8532..541d92921 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/MultiSelectAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/MultiSelectAdapter.java @@ -29,7 +29,6 @@ import androidx.annotation.NonNull; import java.util.*; -import java.util.stream.Stream; import be.ugent.zeus.hydra.common.ui.recyclerview.viewholders.DataViewHolder; @@ -37,8 +36,8 @@ * Adapter with items that are checkable. An item is considered checked if the state is {@code true}. * * @author Niko Strijbol + * @noinspection unused */ -@SuppressWarnings("WeakerAccess") public abstract class MultiSelectAdapter extends DiffAdapter> { private static final String TAG = "MultiSelectAdapter"; diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/MultiSelectSearchableAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/MultiSelectSearchableAdapter.java index d357a8e34..b1ce3a8d0 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/MultiSelectSearchableAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/MultiSelectSearchableAdapter.java @@ -80,7 +80,7 @@ public void clear() { * @param position The position. */ public void setChecked(int position) { - dataContainer.submitUpdate(new AdapterUpdate>() { + dataContainer.submitUpdate(new AdapterUpdate<>() { @Override public List> getNewData(List> existingData) { @@ -115,7 +115,7 @@ public boolean shouldUseMultiThreading() { * @param checked The value. */ public void setAllChecked(boolean checked) { - dataContainer.submitUpdate(new AdapterUpdate>() { + dataContainer.submitUpdate(new AdapterUpdate<>() { @Override @Nullable public List> getNewData(@Nullable List> existingData) { diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/SearchableAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/SearchableAdapter.java index 051dd56bc..18f4dc390 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/SearchableAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/adapters/SearchableAdapter.java @@ -95,7 +95,7 @@ public boolean onQueryTextSubmit(String query) { @Override public void submitData(List data) { - this.allData = Collections.unmodifiableList(new ArrayList<>(data)); + this.allData = List.copyOf(data); super.submitData(data); } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/headers/HeaderAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/headers/HeaderAdapter.java index 1da31489c..03035433d 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/headers/HeaderAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/headers/HeaderAdapter.java @@ -55,15 +55,6 @@ public class HeaderAdapter extends RecyclerView.Adapter @@ -46,38 +44,30 @@ * * @author Rien Maertens * @author Niko Strijbol + * @noinspection unused * @see Based on this library * @see LocalTime#toString() The exact documentation on how the value is saved. */ -@SuppressWarnings({"WeakerAccess"}) public class TimePreference extends DialogPreference { public TimePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TimePreference, defStyleAttr, defStyleRes); - - if (ViewUtils.getBoolean(a, R.styleable.TimePreference_useDefaultSummary, - R.styleable.TimePreference_useDefaultSummary, false)) { - setSummaryProvider(new TimeSummaryProvider()); - } - - a.recycle(); + setSummaryProvider(new TimeSummaryProvider()); } public TimePreference(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); + super(context, attrs, defStyleAttr); + setSummaryProvider(new TimeSummaryProvider()); } public TimePreference(Context context, AttributeSet attrs) { - this(context, attrs, ViewUtils.getAttr(context, - androidx.preference.R.attr.dialogPreferenceStyle, - android.R.attr.dialogPreferenceStyle)); + super(context, attrs); + setSummaryProvider(new TimeSummaryProvider()); } - @SuppressWarnings({"unused", "RedundantSuppression"}) public TimePreference(Context context) { - this(context, null); + super(context); + setSummaryProvider(new TimeSummaryProvider()); } @Nullable @@ -159,7 +149,7 @@ private static class SavedState extends BaseSavedState { public SavedState(Parcel source) { super(source); - time = (LocalTime) source.readSerializable(); + time = (LocalTime) ParcelCompat.readSerializable(source, LocalTime.class.getClassLoader(), LocalTime.class); } public SavedState(Parcelable superState) { @@ -173,7 +163,7 @@ public void writeToParcel(Parcel dest, int flags) { } public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { + new Parcelable.Creator<>() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/TimePreferenceDialogFragmentCompat.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/TimePreferenceDialogFragmentCompat.java index beb0ef05b..7ae1bbced 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/TimePreferenceDialogFragmentCompat.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/TimePreferenceDialogFragmentCompat.java @@ -76,7 +76,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { } @Override - public void onClick(DialogInterface dialog, int which) { + public void onClick(@NonNull DialogInterface dialog, int which) { super.onClick(dialog, which); if (which == DialogInterface.BUTTON_POSITIVE) { @@ -97,6 +97,6 @@ public void onDialogClosed(boolean positiveResult) { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { this.time = LocalTime.of(hourOfDay, minute); - super.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE); + super.onClick(requireDialog(), DialogInterface.BUTTON_POSITIVE); } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/utils/ColourUtils.java b/app/src/main/java/be/ugent/zeus/hydra/common/utils/ColourUtils.java index ec36edd0f..c1c8917a5 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/utils/ColourUtils.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/utils/ColourUtils.java @@ -47,6 +47,7 @@ public static boolean isDark(@ColorInt int colorInt) { @ColorInt public static int resolveColour(Context context, @AttrRes int attribute) { int[] attrs = {attribute}; + //noinspection resource @SuppressLint("ResourceType") // False positive I think. TypedArray ta = context.obtainStyledAttributes(attrs); try { diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/utils/NetworkUtils.java b/app/src/main/java/be/ugent/zeus/hydra/common/utils/NetworkUtils.java index 039623616..c956dcecc 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/utils/NetworkUtils.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/utils/NetworkUtils.java @@ -55,11 +55,13 @@ public static boolean isConnected(Context context) { } if (Build.VERSION.SDK_INT < 29) { - NetworkInfo networkInfo = manager.getActiveNetworkInfo(); + //noinspection deprecation + var networkInfo = manager.getActiveNetworkInfo(); + //noinspection deprecation return (networkInfo != null && networkInfo.isConnected()); } else { - Network network = manager.getActiveNetwork(); - NetworkCapabilities capabilities = manager.getNetworkCapabilities(network); + var network = manager.getActiveNetwork(); + var capabilities = manager.getNetworkCapabilities(network); if (capabilities != null) { return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); } else { diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/utils/StringUtils.java b/app/src/main/java/be/ugent/zeus/hydra/common/utils/StringUtils.java index 56c248414..b7c801515 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/utils/StringUtils.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/utils/StringUtils.java @@ -26,7 +26,6 @@ import androidx.annotation.NonNull; import java.util.Collection; -import java.util.List; import java.util.Locale; /** @@ -46,7 +45,7 @@ public static String capitaliseFirst(@NonNull String s) { /** * Format a set of elements into a textual list (e.g. "one, two and three"). - * + *

* On supported Android versions, this will use the ICU library to get a * nicely formatted list. On older versions, the items will be joined using * a comma. diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/FeedException.java b/app/src/main/java/be/ugent/zeus/hydra/feed/FeedException.java index 09585f49d..2be6a9041 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/FeedException.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/FeedException.java @@ -39,6 +39,7 @@ public class FeedException extends RequestException { this.failedTypes = failedTypes; } + /** @noinspection unused*/ public Set getFailedTypes() { return failedTypes; } diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/FeedLiveData.java b/app/src/main/java/be/ugent/zeus/hydra/feed/FeedLiveData.java index 144024586..8f4f73e2b 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/FeedLiveData.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/FeedLiveData.java @@ -32,7 +32,6 @@ import androidx.preference.PreferenceManager; import java.util.*; -import java.util.function.Consumer; import java.util.function.IntPredicate; import java.util.stream.Collectors; @@ -84,13 +83,7 @@ public class FeedLiveData extends BaseLiveData>> { private static final String TAG = "HomeFeedLoader"; // For which settings the loader must refresh. - private static final String[] watchedPreferences = { - HomeFeedFragment.PREF_DISABLED_CARD_TYPES, - AssociationVisibilityStorage.PREF_BLACKLIST, - RestoPreferenceFragment.PREF_RESTO_KEY, - RestoPreferenceFragment.PREF_RESTO_NAME, - HomeFeedFragment.PREF_DISABLED_CARD_HACK - }; + private static final String[] watchedPreferences = {HomeFeedFragment.PREF_DISABLED_CARD_TYPES, AssociationVisibilityStorage.PREF_BLACKLIST, RestoPreferenceFragment.PREF_RESTO_KEY, RestoPreferenceFragment.PREF_RESTO_NAME, HomeFeedFragment.PREF_DISABLED_CARD_HACK}; private final SharedPreferences.OnSharedPreferenceChangeListener restoListener = new RestoListener(); private final Context applicationContext; private final Map oldPreferences = new HashMap<>(); @@ -100,10 +93,7 @@ public class FeedLiveData extends BaseLiveData>> { loadData(); } - private static List executeOperation(@Nullable Bundle args, - FeedOperation operation, - Collection errors, - List results) { + private static List executeOperation(@Nullable Bundle args, FeedOperation operation, Collection errors, List results) { Result> result = operation.transform(args, results); @@ -176,48 +166,44 @@ protected void onInactive() { @Override @SuppressLint("StaticFieldLeak") protected void loadData(@NonNull Bundle bundle) { - ThreadingUtils.executeWithProgress(new Consumer>>>() { - @Override - public void accept(Consumer>> publishProgress) { - // Get the operations. - Log.d(TAG, "doInBackground: received load request with " + bundle); - Iterable operations = findOperations(scheduleOperations(), bundle); - - // Get existing value if needed. - Result> loaderResult = getValue(); - //We initialize with a copy of the existing data; we do reset the errors. - List results; - - if (loaderResult == null) { - results = Collections.emptyList(); - } else { - results = loaderResult.orElse(new ArrayList<>()); - } + ThreadingUtils.executeWithProgress(publishProgress -> { + // Get the operations. + Log.d(TAG, "doInBackground: received load request with " + bundle); + Iterable operations = findOperations(scheduleOperations(), bundle); + + // Get existing value if needed. + Result> loaderResult = getValue(); + //We initialize with a copy of the existing data; we do reset the errors. + List results; + + if (loaderResult == null) { + results = Collections.emptyList(); + } else { + results = loaderResult.orElse(new ArrayList<>()); + } - Set errors = new HashSet<>(); - Result> result = null; + Set errors = new HashSet<>(); + Result> result = null; - for (FeedOperation operation : operations) { - results = executeOperation(bundle, operation, errors, results); + for (FeedOperation operation : operations) { + results = executeOperation(bundle, operation, errors, results); - List finalResults = new ArrayList<>(results); - // Deliver intermediary results. - Log.d(TAG, "loadInBackground: Operation " + operation + " completed."); - Result.Builder> builder = new Result.Builder>() - .withData(finalResults); + List finalResults = new ArrayList<>(results); + // Deliver intermediary results. + Log.d(TAG, "loadInBackground: Operation " + operation + " completed."); + Result.Builder> builder = new Result.Builder>().withData(finalResults); - if (!errors.isEmpty()) { - builder.withError(new FeedException(errors)); - } + if (!errors.isEmpty()) { + builder.withError(new FeedException(errors)); + } - result = builder.buildPartial(); + result = builder.buildPartial(); - publishProgress.accept(result); - } + publishProgress.accept(result); + } - if (result != null) { - publishProgress.accept(result.asCompleted()); - } + if (result != null) { + publishProgress.accept(result.asCompleted()); } }, this::setValue); } @@ -232,11 +218,7 @@ private ExtendedSparseArray scheduleOperations() { FeedCollection operations = new FeedCollection(); Context c = applicationContext; - Set disabled = PreferenceManager.getDefaultSharedPreferences(c) - .getStringSet(HomeFeedFragment.PREF_DISABLED_CARD_TYPES, Collections.emptySet()) - .stream() - .map(Integer::parseInt) - .collect(Collectors.toSet()); + Set disabled = PreferenceManager.getDefaultSharedPreferences(c).getStringSet(HomeFeedFragment.PREF_DISABLED_CARD_TYPES, Collections.emptySet()).stream().map(Integer::parseInt).collect(Collectors.toSet()); // Don't do Urgent.fm if there is no network. if (!NetworkUtils.isConnected(c)) { diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/FeedViewModel.java b/app/src/main/java/be/ugent/zeus/hydra/feed/FeedViewModel.java index b3c593db0..9a12b3e37 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/FeedViewModel.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/FeedViewModel.java @@ -23,7 +23,6 @@ package be.ugent.zeus.hydra.feed; import android.app.Application; -import android.os.AsyncTask; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; @@ -33,6 +32,7 @@ import be.ugent.zeus.hydra.common.arch.data.Event; import be.ugent.zeus.hydra.common.request.Result; import be.ugent.zeus.hydra.common.ui.SingleRefreshViewModel; +import be.ugent.zeus.hydra.common.utils.ThreadingUtils; import be.ugent.zeus.hydra.feed.cards.Card; import be.ugent.zeus.hydra.feed.commands.CommandResult; import be.ugent.zeus.hydra.feed.commands.FeedCommand; @@ -61,14 +61,14 @@ LiveData> getCommandLiveData() { } void execute(FeedCommand command) { - AsyncTask.execute(() -> { + ThreadingUtils.execute(() -> { int result = command.execute(getApplication()); commandLiveData.postValue(new Event<>(CommandResult.forExecute(command, result))); }); } void undo(FeedCommand command) { - AsyncTask.execute(() -> { + ThreadingUtils.execute(() -> { int result = command.undo(getApplication()); commandLiveData.postValue(new Event<>(CommandResult.forUndo(result))); }); diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/HomeFeedFragment.java b/app/src/main/java/be/ugent/zeus/hydra/feed/HomeFeedFragment.java index 6cd7a31cf..ef5c933bb 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/HomeFeedFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/HomeFeedFragment.java @@ -27,6 +27,7 @@ import android.view.*; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.ItemTouchHelper; @@ -44,6 +45,7 @@ import be.ugent.zeus.hydra.feed.commands.CommandResult; import be.ugent.zeus.hydra.feed.commands.FeedCommand; import com.google.android.material.snackbar.Snackbar; +import org.jetbrains.annotations.NotNull; import static be.ugent.zeus.hydra.common.utils.FragmentUtils.requireBaseActivity; import static be.ugent.zeus.hydra.feed.FeedLiveData.REFRESH_HOMECARD_TYPE; @@ -80,7 +82,6 @@ public class HomeFeedFragment extends Fragment implements SwipeRefreshLayout.OnR @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); helper = CustomTabsHelper.initHelper(getActivity(), null); helper.setShareMenu(); } @@ -99,6 +100,23 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + requireActivity().addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull @NotNull Menu menu, @NonNull @NotNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.menu_refresh, menu); + requireBaseActivity(HomeFeedFragment.this).tintToolbarIcons(menu, R.id.action_refresh); + } + + @Override + public boolean onMenuItemSelected(@NonNull @NotNull MenuItem menuItem) { + if (menuItem.getItemId() == R.id.action_refresh) { + onRefresh(); + return true; + } + return false; + } + }, getViewLifecycleOwner()); + RecyclerView recyclerView = view.findViewById(R.id.home_cards_view); recyclerView.setHasFixedSize(true); SwipeRefreshLayout swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout); @@ -162,23 +180,6 @@ public void onRemovalScheduled() { } } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - inflater.inflate(R.menu.menu_refresh, menu); - requireBaseActivity(this).tintToolbarIcons(menu, R.id.action_refresh); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - if (item.getItemId() == R.id.action_refresh) { - onRefresh(); - return true; - } - - return super.onOptionsItemSelected(item); - } - @Override public void onRefresh() { model.requestRefresh(); diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/cards/Card.java b/app/src/main/java/be/ugent/zeus/hydra/feed/cards/Card.java index e58bcafed..25499693e 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/cards/Card.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/cards/Card.java @@ -32,7 +32,7 @@ /** * Every subclass should have a {@link Card.Type} associated with it. This is to facilitate working with adapters. - * + *

*

Priority

* Every card must give itself a priority in [0,1000]. This defines the natural ordening of the cards; 0 is the * card with the highest priority, 1000 has the lowest priority. Cards should generally strive to produce unique @@ -45,7 +45,7 @@ * special occasions, such as giving the resto card a temporarily higher score because it is eating time. *

* The negative values ]-Inf,0[ are reserved for use with special cards. - * + *

*

Identifier

* Each card instance should have an unique identifier. The identifier must be unique within the card type. *

diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/cards/library/LibraryViewHolder.java b/app/src/main/java/be/ugent/zeus/hydra/feed/cards/library/LibraryViewHolder.java index 4c68a8e9d..3580c1481 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/cards/library/LibraryViewHolder.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/cards/library/LibraryViewHolder.java @@ -109,7 +109,7 @@ private void addOpeningHour(Context context, Result> resu } v.setLayoutParams(textParam); - if (!result.hasData() || !result.getData().isPresent()) { + if (!result.hasData() || result.getData().isEmpty()) { v.setText(R.string.library_list_no_opening_hours); } else { v.setText(context.getString(R.string.library_list_opening_hours_today, result.getData().get().getHours())); diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/operations/RemoveOperation.java b/app/src/main/java/be/ugent/zeus/hydra/feed/operations/RemoveOperation.java index 5ab0370dd..a91ee3a70 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/operations/RemoveOperation.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/operations/RemoveOperation.java @@ -63,6 +63,7 @@ public int getCardType() { return cardType; } + @NonNull @Override public String toString() { return "REMOVE -> Card Type " + cardType; diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/operations/RequestOperation.java b/app/src/main/java/be/ugent/zeus/hydra/feed/operations/RequestOperation.java index b4ca5984b..f34559038 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/operations/RequestOperation.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/operations/RequestOperation.java @@ -73,6 +73,7 @@ public int getCardType() { return request.getCardType(); } + @NonNull @Override public String toString() { return "REQUEST -> Card Type " + request.getCardType(); diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/preferences/HomeFragment.java b/app/src/main/java/be/ugent/zeus/hydra/feed/preferences/HomeFragment.java index 303fce2a6..c5c9257ad 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/preferences/HomeFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/preferences/HomeFragment.java @@ -86,7 +86,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View v = super.onCreateView(inflater, container, savedInstanceState); viewModel = new ViewModelProvider(this).get(DeleteViewModel.class); - viewModel.getLiveData().observe(getViewLifecycleOwner(), new EventObserver() { + viewModel.getLiveData().observe(getViewLifecycleOwner(), new EventObserver<>() { @Override protected void onUnhandled(Context data) { Toast.makeText(data, R.string.feed_pref_hidden_cleared, Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/be/ugent/zeus/hydra/info/InfoFragment.java b/app/src/main/java/be/ugent/zeus/hydra/info/InfoFragment.java index d10d862d0..1233d77da 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/info/InfoFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/info/InfoFragment.java @@ -28,6 +28,8 @@ import android.widget.ProgressBar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.os.BundleCompat; +import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; @@ -40,6 +42,7 @@ import be.ugent.zeus.hydra.common.ui.customtabs.ActivityHelper; import be.ugent.zeus.hydra.common.ui.customtabs.CustomTabsHelper; import com.google.android.material.snackbar.Snackbar; +import org.jetbrains.annotations.NotNull; import static be.ugent.zeus.hydra.common.utils.FragmentUtils.requireBaseActivity; @@ -53,8 +56,6 @@ public class InfoFragment extends Fragment { private static final String TAG = "InfoFragment"; private ActivityHelper helper; - @Nullable - private InfoViewModel model; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -66,7 +67,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); helper = CustomTabsHelper.initHelper(getActivity(), null); helper.setShareMenu(); - setHasOptionsMenu(true); } @Override @@ -84,7 +84,7 @@ public void onStop() { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - + InfoListAdapter adapter = new InfoListAdapter(helper); RecyclerView recyclerView = view.findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); @@ -93,39 +93,43 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat ProgressBar progressBar = view.findViewById(R.id.progress_bar); + InfoViewModel model; Bundle bundle = getArguments(); // If we receive a list as argument, just show that list. No need to load anything. - if (bundle != null && bundle.getParcelableArrayList(InfoSubItemActivity.INFO_ITEMS) != null) { - adapter.submitData(bundle.getParcelableArrayList(InfoSubItemActivity.INFO_ITEMS)); + if (bundle != null && BundleCompat.getParcelableArrayList(bundle, InfoSubItemActivity.INFO_ITEMS, InfoItem.class) != null) { + adapter.submitData(BundleCompat.getParcelableArrayList(bundle, InfoSubItemActivity.INFO_ITEMS, InfoItem.class)); progressBar.setVisibility(View.GONE); + model = null; } else { model = new ViewModelProvider(this).get(InfoViewModel.class); model.getData().observe(getViewLifecycleOwner(), PartialErrorObserver.with(this::onError)); model.getData().observe(getViewLifecycleOwner(), new ProgressObserver<>(progressBar)); model.getData().observe(getViewLifecycleOwner(), new AdapterObserver<>(adapter)); } + + requireActivity().addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull @NotNull Menu menu, @NonNull @NotNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.menu_refresh, menu); + requireBaseActivity(InfoFragment.this).tintToolbarIcons(menu, R.id.action_refresh); + if (model == null) { + menu.findItem(R.id.action_refresh).setVisible(false); + } + } + + @Override + public boolean onMenuItemSelected(@NonNull @NotNull MenuItem menuItem) { + if (menuItem.getItemId() == R.id.action_refresh && model != null) { + model.requestRefresh(); + return true; + } + return false; + } + }, getViewLifecycleOwner()); } private void onError(Throwable throwable) { Log.e(TAG, "Error while getting data.", throwable); Snackbar.make(requireView(), getString(R.string.error_network), Snackbar.LENGTH_LONG).show(); } - - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - inflater.inflate(R.menu.menu_refresh, menu); - requireBaseActivity(this).tintToolbarIcons(menu, R.id.action_refresh); - if (model == null) { - menu.findItem(R.id.action_refresh).setVisible(false); - } - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.action_refresh && model != null) { - model.requestRefresh(); - return true; - } - return super.onOptionsItemSelected(item); - } } diff --git a/app/src/main/java/be/ugent/zeus/hydra/info/InfoItem.java b/app/src/main/java/be/ugent/zeus/hydra/info/InfoItem.java index 70df4d009..cf0f0e9ea 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/info/InfoItem.java +++ b/app/src/main/java/be/ugent/zeus/hydra/info/InfoItem.java @@ -35,8 +35,8 @@ * * @author Juta * @author Niko Strijbol + * @noinspection unused */ -@SuppressWarnings("unused") public final class InfoItem implements Parcelable { private String title; @@ -52,6 +52,7 @@ public InfoItem() { // Used by Moshi. } + /** @noinspection ProtectedMemberInFinalClass*/ protected InfoItem(Parcel in) { title = in.readString(); image = in.readString(); @@ -61,7 +62,7 @@ protected InfoItem(Parcel in) { subContent = in.createTypedArrayList(CREATOR); } - public static final Creator CREATOR = new Creator() { + public static final Creator CREATOR = new Creator<>() { @Override public InfoItem createFromParcel(Parcel in) { return new InfoItem(in); diff --git a/app/src/main/java/be/ugent/zeus/hydra/info/InfoSubItemActivity.java b/app/src/main/java/be/ugent/zeus/hydra/info/InfoSubItemActivity.java index 768a01f16..9e4a9d6f5 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/info/InfoSubItemActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/info/InfoSubItemActivity.java @@ -27,6 +27,8 @@ import java.util.ArrayList; +import androidx.core.content.IntentCompat; + import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.ui.BaseActivity; import be.ugent.zeus.hydra.databinding.ActivityInfoSubItemBinding; @@ -50,7 +52,7 @@ protected void onCreate(Bundle savedInstanceState) { requireToolbar().setTitle(title); // Create bundle for fragment - ArrayList infoList = intent.getParcelableArrayListExtra(INFO_ITEMS); + ArrayList infoList = IntentCompat.getParcelableArrayListExtra(intent, INFO_ITEMS, InfoItem.class); Bundle bundle = new Bundle(); bundle.putParcelableArrayList(INFO_ITEMS, infoList); fragment.setArguments(bundle); diff --git a/app/src/main/java/be/ugent/zeus/hydra/library/details/LibraryDetailActivity.java b/app/src/main/java/be/ugent/zeus/hydra/library/details/LibraryDetailActivity.java index c86530e3f..4510304dc 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/library/details/LibraryDetailActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/library/details/LibraryDetailActivity.java @@ -26,7 +26,6 @@ import android.content.Intent; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.text.TextUtils; import android.text.util.Linkify; @@ -39,7 +38,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; +import androidx.core.content.IntentCompat; import androidx.core.text.util.LinkifyCompat; import androidx.core.view.WindowCompat; import androidx.lifecycle.ViewModelProvider; @@ -58,9 +57,7 @@ import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.ui.BaseActivity; import be.ugent.zeus.hydra.common.ui.html.Utils; -import be.ugent.zeus.hydra.common.utils.DateUtils; -import be.ugent.zeus.hydra.common.utils.NetworkUtils; -import be.ugent.zeus.hydra.common.utils.ViewUtils; +import be.ugent.zeus.hydra.common.utils.*; import be.ugent.zeus.hydra.databinding.ActivityLibraryDetailsBinding; import be.ugent.zeus.hydra.library.Library; import be.ugent.zeus.hydra.library.favourites.FavouritesRepository; @@ -94,7 +91,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { setContentView(ActivityLibraryDetailsBinding::inflate); WindowCompat.setDecorFitsSystemWindows(getWindow(), false); - library = getIntent().getParcelableExtra(ARG_LIBRARY); + library = IntentCompat.getParcelableExtra(getIntent(), ARG_LIBRARY, Library.class); Picasso.get().load(library.getHeaderImage(this)).into(binding.headerImage); @@ -167,7 +164,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { private void updateStatus(Library library, boolean isSelected) { FavouritesRepository repository = Database.get(this).getFavouritesRepository(); - AsyncTask.execute(() -> { + ThreadingUtils.execute(() -> { if (isSelected) { repository.delete(LibraryFavourite.from(library)); } else { @@ -187,7 +184,7 @@ protected void onSaveInstanceState(@NonNull Bundle outState) { protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); // Restore the toggle button state. - if (savedInstanceState.get("button") != null) { + if (savedInstanceState.getString("button") != null) { binding.expandButton.setText(savedInstanceState.getString("button")); } } diff --git a/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryList.java b/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryList.java index 3ff0a297e..eac0d8fe4 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryList.java +++ b/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryList.java @@ -38,7 +38,7 @@ */ public final class LibraryList implements Parcelable { - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<>() { @Override public LibraryList createFromParcel(Parcel source) { return new LibraryList(source); diff --git a/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryListAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryListAdapter.java index 7b2fd6934..951df6e9e 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryListAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryListAdapter.java @@ -51,10 +51,7 @@ class LibraryListAdapter extends SearchableAdapter, Libra LibraryListAdapter(LibraryViewModel viewModel) { super((pair, s) -> { Library library = pair.first; - boolean contained = false; - if (library.getName() != null && library.getName().toLowerCase(Locale.getDefault()).contains(s)) { - contained = true; - } + boolean contained = library.getName() != null && library.getName().toLowerCase(Locale.getDefault()).contains(s); if (library.getCampus() != null && library.getCampus().toLowerCase(Locale.getDefault()).contains(s)) { contained = true; } diff --git a/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryListFragment.java b/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryListFragment.java index 4baf404ca..734aa38af 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryListFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryListFragment.java @@ -28,6 +28,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.SearchView; +import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; @@ -41,6 +42,7 @@ import be.ugent.zeus.hydra.common.utils.ColourUtils; import be.ugent.zeus.hydra.common.utils.NetworkUtils; import com.google.android.material.snackbar.Snackbar; +import org.jetbrains.annotations.NotNull; import static be.ugent.zeus.hydra.common.utils.FragmentUtils.requireBaseActivity; @@ -55,12 +57,6 @@ public class LibraryListFragment extends Fragment { private LibraryListAdapter adapter; private LibraryViewModel viewModel; - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -70,6 +66,32 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + + requireActivity().addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull @NotNull Menu menu, @NonNull @NotNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.menu_library_list, menu); + requireBaseActivity(LibraryListFragment.this).tintToolbarIcons(menu, R.id.library_visit_catalogue, R.id.action_refresh); + SearchView view = (SearchView) menu.findItem(R.id.action_search).getActionView(); + view.setOnQueryTextListener(adapter); + view.setOnCloseListener(adapter); + view.setOnSearchClickListener(v -> adapter.onOpen()); + } + + @Override + public boolean onMenuItemSelected(@NonNull @NotNull MenuItem menuItem) { + int itemId = menuItem.getItemId(); + if (itemId == R.id.library_visit_catalogue) { + NetworkUtils.maybeLaunchBrowser(getContext(), LIB_URL); + return true; + } else if (itemId == R.id.action_refresh) { + viewModel.onRefresh(); + return true; + } + return false; + } + }, getViewLifecycleOwner()); + RecyclerView recyclerView = view.findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)); @@ -88,30 +110,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat swipeRefreshLayout.setOnRefreshListener(viewModel); } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - inflater.inflate(R.menu.menu_library_list, menu); - requireBaseActivity(this).tintToolbarIcons(menu, R.id.library_visit_catalogue, R.id.action_refresh); - SearchView view = (SearchView) menu.findItem(R.id.action_search).getActionView(); - view.setOnQueryTextListener(adapter); - view.setOnCloseListener(adapter); - view.setOnSearchClickListener(v -> adapter.onOpen()); - super.onCreateOptionsMenu(menu, inflater); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - int itemId = item.getItemId(); - if (itemId == R.id.library_visit_catalogue) { - NetworkUtils.maybeLaunchBrowser(getContext(), LIB_URL); - return true; - } else if (itemId == R.id.action_refresh) { - viewModel.onRefresh(); - return true; - } - return super.onOptionsItemSelected(item); - } - private void onError(Throwable throwable) { Log.e(TAG, "Error while getting data.", throwable); Snackbar.make(requireView(), getString(R.string.error_network), Snackbar.LENGTH_LONG) diff --git a/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryViewHolder.java b/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryViewHolder.java index 2e8a1022a..80f5ef88c 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryViewHolder.java +++ b/app/src/main/java/be/ugent/zeus/hydra/library/list/LibraryViewHolder.java @@ -82,7 +82,7 @@ public void onViewRecycled() { @Override public void onChanged(@Nullable Result> result) { - if (result == null || !result.hasData() || !result.getData().isPresent()) { + if (result == null || !result.hasData() || result.getData().isEmpty()) { openingHours.setText(R.string.library_list_no_opening_hours); } else { OpeningHours hours = result.getData().get(); diff --git a/app/src/main/java/be/ugent/zeus/hydra/news/NewsFragment.java b/app/src/main/java/be/ugent/zeus/hydra/news/NewsFragment.java index c3a78eaf5..98f2b1758 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/news/NewsFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/news/NewsFragment.java @@ -27,6 +27,7 @@ import android.view.*; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; @@ -41,6 +42,7 @@ import be.ugent.zeus.hydra.common.ui.recyclerview.SpanItemSpacingDecoration; import be.ugent.zeus.hydra.common.utils.ColourUtils; import com.google.android.material.snackbar.Snackbar; +import org.jetbrains.annotations.NotNull; import static be.ugent.zeus.hydra.common.utils.FragmentUtils.requireBaseActivity; @@ -61,7 +63,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); helper = CustomTabsHelper.initHelper(getActivity(), null); helper.setShareMenu(); - setHasOptionsMenu(true); } @Override @@ -73,6 +74,23 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + requireActivity().addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull @NotNull Menu menu, @NonNull @NotNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.menu_refresh, menu); + requireBaseActivity(NewsFragment.this).tintToolbarIcons(menu, R.id.action_refresh); + } + + @Override + public boolean onMenuItemSelected(@NonNull @NotNull MenuItem menuItem) { + if (menuItem.getItemId() == R.id.action_refresh) { + viewModel.onRefresh(); + return true; + } + return false; + } + }, getViewLifecycleOwner()); + RecyclerView recyclerView = view.findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new SpanItemSpacingDecoration(requireContext())); @@ -90,24 +108,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat swipeRefreshLayout.setOnRefreshListener(viewModel); } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_refresh, menu); - requireBaseActivity(this).tintToolbarIcons(menu, R.id.action_refresh); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - if (item.getItemId() == R.id.action_refresh) { - viewModel.onRefresh(); - return true; - } - - return super.onOptionsItemSelected(item); - } - private void onError(Throwable throwable) { Log.e(TAG, "Error while getting data.", throwable); Snackbar.make(requireView(), getString(R.string.error_network), Snackbar.LENGTH_LONG) diff --git a/app/src/main/java/be/ugent/zeus/hydra/news/NewsStream.java b/app/src/main/java/be/ugent/zeus/hydra/news/NewsStream.java index 9aef3ec9a..a694000d9 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/news/NewsStream.java +++ b/app/src/main/java/be/ugent/zeus/hydra/news/NewsStream.java @@ -28,6 +28,7 @@ /** * @author Niko Strijbol + * @noinspection unused */ public final class NewsStream { diff --git a/app/src/main/java/be/ugent/zeus/hydra/preferences/PreferenceActivity.java b/app/src/main/java/be/ugent/zeus/hydra/preferences/PreferenceActivity.java index bbe36a673..53eebf214 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/preferences/PreferenceActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/preferences/PreferenceActivity.java @@ -28,6 +28,8 @@ import android.os.Parcelable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.IntentCompat; +import androidx.core.os.BundleCompat; import androidx.fragment.app.Fragment; import be.ugent.zeus.hydra.R; @@ -71,13 +73,13 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState != null) { // If a specific screen is requested, use that one. - entry = savedInstanceState.getParcelable(ARG_FRAGMENT); + entry = BundleCompat.getParcelable(savedInstanceState, ARG_FRAGMENT, PreferenceEntry.class); } else if (Intent.ACTION_MANAGE_NETWORK_USAGE.equals(getIntent().getAction())) { // We come from the device data usage settings screen, show network options. entry = PreferenceEntry.HOME; } else { // Nothing was requested, show the overview. - entry = getIntent().getParcelableExtra(ARG_FRAGMENT); + entry = IntentCompat.getParcelableExtra(getIntent(), ARG_FRAGMENT, PreferenceEntry.class); } setFragment(); @@ -86,7 +88,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - entry = intent.getParcelableExtra(ARG_FRAGMENT); + entry = IntentCompat.getParcelableExtra(intent, ARG_FRAGMENT, PreferenceEntry.class); setFragment(); } diff --git a/app/src/main/java/be/ugent/zeus/hydra/preferences/PreferenceEntry.java b/app/src/main/java/be/ugent/zeus/hydra/preferences/PreferenceEntry.java index b69b9eb7b..3c139d888 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/preferences/PreferenceEntry.java +++ b/app/src/main/java/be/ugent/zeus/hydra/preferences/PreferenceEntry.java @@ -116,7 +116,7 @@ public int describeContents() { return 0; } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<>() { @Override public PreferenceEntry createFromParcel(Parcel in) { return PreferenceEntry.values()[in.readInt()]; diff --git a/app/src/main/java/be/ugent/zeus/hydra/preferences/ThemeFragment.java b/app/src/main/java/be/ugent/zeus/hydra/preferences/ThemeFragment.java index 9d79be11e..ea94e8b4e 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/preferences/ThemeFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/preferences/ThemeFragment.java @@ -37,6 +37,8 @@ import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.ui.PreferenceFragment; +import java.util.Objects; + /** * Show preferences related to the theme of the app. * @@ -78,7 +80,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setPreferencesFromResource(R.xml.pref_theme, rootKey); SharedPreferences preferences = getPreferenceManager().getSharedPreferences(); - existing = preferences.getString(PREF_THEME_NIGHT_MODE, defaultValue()); + existing = Objects.requireNonNull(preferences).getString(PREF_THEME_NIGHT_MODE, defaultValue()); ListPreference listPreference = requirePreference(PREF_THEME_NIGHT_MODE); if (Build.VERSION.SDK_INT < 29) { diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/FoodAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/FoodAdapter.java index 804ccf54f..ef5cf988f 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/FoodAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/FoodAdapter.java @@ -34,7 +34,7 @@ * * @author Niko Strijbol */ -public class FoodAdapter extends DiffAdapter { +class FoodAdapter extends DiffAdapter { @NonNull @Override diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/FoodFragment.java b/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/FoodFragment.java index 929bdcce6..c444b23e7 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/FoodFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/FoodFragment.java @@ -79,7 +79,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat viewModel = new ViewModelProvider(this).get(ExtraFoodViewModel.class); viewModel.getData().observe(getViewLifecycleOwner(), PartialErrorObserver.with(this::onError)); viewModel.getData().observe(getViewLifecycleOwner(), new ProgressObserver<>(view.findViewById(R.id.progress_bar))); - viewModel.getData().observe(getViewLifecycleOwner(), new SuccessObserver() { + viewModel.getData().observe(getViewLifecycleOwner(), new SuccessObserver<>() { @Override protected void onSuccess(@NonNull ExtraFood data) { adapter.submitData(ExtraFoodViewModel.getFor(position, data)); diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/history/HistoryActivity.java b/app/src/main/java/be/ugent/zeus/hydra/resto/history/HistoryActivity.java index 1d504c361..95f07dccf 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/history/HistoryActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/history/HistoryActivity.java @@ -85,7 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { viewModel = provider.get(SingleDayViewModel.class); viewModel.changeDate(localDate); // Set the initial date - viewModel.getData().observe(this, new SuccessObserver() { + viewModel.getData().observe(this, new SuccessObserver<>() { @Override protected void onSuccess(@NonNull RestoMenu data) { // Add the fragment diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/ecological/EcologicalViewHolder.java b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/ecological/EcologicalViewHolder.java index 10fb52816..15c0835df 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/ecological/EcologicalViewHolder.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/ecological/EcologicalViewHolder.java @@ -24,7 +24,6 @@ import android.content.Context; import android.graphics.drawable.Drawable; -import android.text.TextUtils; import android.view.View; import android.widget.TextView; import androidx.appcompat.content.res.AppCompatResources; @@ -33,6 +32,7 @@ import be.ugent.zeus.hydra.common.ui.recyclerview.adapters.MultiSelectAdapter; import be.ugent.zeus.hydra.common.ui.recyclerview.viewholders.DataViewHolder; import be.ugent.zeus.hydra.common.utils.DateUtils; +import be.ugent.zeus.hydra.common.utils.StringUtils; import net.cachapa.expandablelayout.ExpandableLayout; /** @@ -69,7 +69,7 @@ public void populate(EcologicalSandwich sandwich) { name.setCompoundDrawables(null, null, null, null); } dates.setText(String.format(c.getString(R.string.date_between), start, end)); - String ingredientsString = TextUtils.join(", ", sandwich.getIngredients()); + String ingredientsString = StringUtils.formatList(sandwich.getIngredients()); ingredients.setText(String.format(c.getString(R.string.resto_sandwich_ingredients), ingredientsString)); expandableLayout.setExpanded(adapter.isChecked(getBindingAdapterPosition()), false); itemView.setOnClickListener(v -> { diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/regular/RegularHolder.java b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/regular/RegularHolder.java index b8cbeaad1..7815ffa4d 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/regular/RegularHolder.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/regular/RegularHolder.java @@ -23,13 +23,13 @@ package be.ugent.zeus.hydra.resto.sandwich.regular; import android.content.Context; -import android.text.TextUtils; import android.view.View; import android.widget.TextView; import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.ui.recyclerview.adapters.MultiSelectAdapter; import be.ugent.zeus.hydra.common.ui.recyclerview.viewholders.DataViewHolder; +import be.ugent.zeus.hydra.common.utils.StringUtils; import net.cachapa.expandablelayout.ExpandableLayout; /** @@ -38,7 +38,6 @@ class RegularHolder extends DataViewHolder { private final TextView name; - private final TextView smallPrice; private final TextView mediumPrice; private final ExpandableLayout expandableLayout; private final TextView ingredients; @@ -49,7 +48,6 @@ class RegularHolder extends DataViewHolder { name = itemView.findViewById(R.id.sandwich_name); mediumPrice = itemView.findViewById(R.id.sandwich_price_medium); - smallPrice = itemView.findViewById(R.id.sandwich_price_small); expandableLayout = itemView.findViewById(R.id.expandable_layout); ingredients = itemView.findViewById(R.id.sandwich_ingredients); this.adapter = adapter; @@ -60,9 +58,9 @@ public void populate(RegularSandwich sandwich) { Context c = itemView.getContext(); name.setText(sandwich.getName()); mediumPrice.setText(String.format(c.getString(R.string.resto_sandwich_price_medium), sandwich.getPriceMedium())); - smallPrice.setText(String.format(c.getString(R.string.resto_sandwich_price_small), sandwich.getPriceSmall())); - String ingredientsString = TextUtils.join(", ", sandwich.getIngredients()); - ingredients.setText(String.format(c.getString(R.string.resto_sandwich_ingredients), ingredientsString)); + String ingredientsString = StringUtils.formatList(sandwich.getIngredients()); + String ingredientSentence = StringUtils.capitaliseFirst(ingredientsString) + "."; + ingredients.setText(ingredientSentence); expandableLayout.setExpanded(adapter.isChecked(getBindingAdapterPosition()), false); itemView.setOnClickListener(v -> { adapter.setChecked(getBindingAdapterPosition()); diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/regular/RegularSandwich.java b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/regular/RegularSandwich.java index aa03d40ab..0a1ac4cce 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/regular/RegularSandwich.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/regular/RegularSandwich.java @@ -28,6 +28,8 @@ import java.util.List; import java.util.Objects; +import androidx.annotation.NonNull; + import com.squareup.moshi.Json; /** @@ -35,11 +37,9 @@ */ @SuppressWarnings("WeakerAccess") public final class RegularSandwich implements Parcelable { - + private String name; private List ingredients; - @Json(name = "price_small") - private String priceSmall; @Json(name = "price_medium") private String priceMedium; @@ -51,7 +51,6 @@ public RegularSandwich() { public RegularSandwich(Parcel in) { this.name = in.readString(); this.ingredients = in.createStringArrayList(); - this.priceSmall = in.readString(); this.priceMedium = in.readString(); } @@ -63,10 +62,6 @@ public List getIngredients() { return ingredients; } - public String getPriceSmall() { - return priceSmall; - } - public String getPriceMedium() { return priceMedium; } @@ -80,7 +75,6 @@ public int describeContents() { public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.name); dest.writeStringList(this.ingredients); - dest.writeString(this.priceSmall); dest.writeString(this.priceMedium); } @@ -91,16 +85,16 @@ public boolean equals(Object o) { RegularSandwich sandwich = (RegularSandwich) o; return Objects.equals(name, sandwich.name) && Objects.equals(ingredients, sandwich.ingredients) && - Objects.equals(priceSmall, sandwich.priceSmall) && Objects.equals(priceMedium, sandwich.priceMedium); } @Override public int hashCode() { - return Objects.hash(name, ingredients, priceSmall, priceMedium); + return Objects.hash(name, ingredients, priceMedium); } @Override + @NonNull public String toString() { return name; } diff --git a/app/src/main/java/be/ugent/zeus/hydra/urgent/MusicService.java b/app/src/main/java/be/ugent/zeus/hydra/urgent/MusicService.java index c160cdd72..59e5982af 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/urgent/MusicService.java +++ b/app/src/main/java/be/ugent/zeus/hydra/urgent/MusicService.java @@ -27,6 +27,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.pm.ServiceInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.os.Bundle; @@ -36,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationManagerCompat; +import androidx.core.app.ServiceCompat; import androidx.core.content.ContextCompat; import androidx.media.MediaBrowserServiceCompat; import androidx.media.session.MediaButtonReceiver; @@ -241,7 +243,14 @@ public void onPlay() { ContextCompat.startForegroundService(getApplicationContext(), new Intent(getApplicationContext(), MusicService.class)); mediaSession.setActive(true); Log.d(TAG, "onPlay: starting foreground service"); - startForeground(MUSIC_SERVICE_ID, notification); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + // only for gingerbread and newer versions + ServiceCompat.startForeground(this, MUSIC_SERVICE_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK); + } else { + // Last param is not used here. + ServiceCompat.startForeground(this, MUSIC_SERVICE_ID, notification, 0); + } + Reporting.getTracker(this).log(new MusicStartEvent()); foreground = true; } diff --git a/app/src/main/java/be/ugent/zeus/hydra/wpi/tab/create/TabRequestException.java b/app/src/main/java/be/ugent/zeus/hydra/wpi/tab/create/TabRequestException.java index e504b8a48..1c862d3d3 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/wpi/tab/create/TabRequestException.java +++ b/app/src/main/java/be/ugent/zeus/hydra/wpi/tab/create/TabRequestException.java @@ -22,22 +22,21 @@ package be.ugent.zeus.hydra.wpi.tab.create; -import android.text.TextUtils; - import java.util.List; import be.ugent.zeus.hydra.common.request.RequestException; +import be.ugent.zeus.hydra.common.utils.StringUtils; /** * Error when posting a transaction results in 422. - * + * * @author Niko Strijbol */ public class TabRequestException extends RequestException { private final List messages; public TabRequestException(List messages) { - super(TextUtils.join(", ", messages)); + super(StringUtils.formatList(messages)); this.messages = messages; } diff --git a/app/src/main/res/layout/item_sandwich.xml b/app/src/main/res/layout/item_sandwich.xml index 12d9579b3..cf956dcc2 100644 --- a/app/src/main/res/layout/item_sandwich.xml +++ b/app/src/main/res/layout/item_sandwich.xml @@ -47,29 +47,14 @@ android:layout_weight="1" tools:text="Naam van het broodje" /> - - - + android:textIsSelectable="true" + tools:text="Prijs groot" /> - - Menu Sandwiches - Large: € %s - Small: € %s + € %s Ingredients: %s. Salad bowls diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 4d3476d5d..1b5cb5c4f 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -43,10 +43,6 @@ - - - - + + + + + radiostudio.be + + \ No newline at end of file diff --git a/app/src/main/res/xml/pref_resto.xml b/app/src/main/res/xml/pref_resto.xml index a81c40b87..60b7b5af6 100644 --- a/app/src/main/res/xml/pref_resto.xml +++ b/app/src/main/res/xml/pref_resto.xml @@ -35,9 +35,7 @@ app:allowDividerBelow="true" app:defaultValue="21:00" app:key="pref_resto_closing_hour" - app:summary="@string/resto_pref_closing_description" - app:title="@string/resto_pref_closing_title" - app:useDefaultSummary="true" /> + app:title="@string/resto_pref_closing_title" /> fields = this.fields.getFields(); - - try { - for (Field field : fields) { - + var checkedField = false; + for (Field field : fields) { + try { Object expected = FieldUtils.readField(field, expectedBean, true); Object actual = FieldUtils.readField(field, item, true); @@ -116,11 +115,16 @@ private boolean hasMatchingValues(T item, Description mismatchDescription) { return false; } } + checkedField = true; + } catch (Exception e) { + // Do the easy way out, and ignore this field. + // This error happens on modern JDK's. } - + } + + if (checkedField) { return true; - } catch (IllegalAccessException e) { - e.printStackTrace(); + } else { mismatchDescription.appendText(" error occurred while accessing field."); return false; } diff --git a/app/src/test/resources/resto/sandwiches.json b/app/src/test/resources/resto/sandwiches.json index 631d4dc4b..c639701ad 100644 --- a/app/src/test/resources/resto/sandwiches.json +++ b/app/src/test/resources/resto/sandwiches.json @@ -9,7 +9,6 @@ "ei", "mayonaise" ], - "price_small": "1.40", "price_medium": "2.00" }, { @@ -21,7 +20,6 @@ "komkommer", "ei" ], - "price_small": "1.30", "price_medium": "2.00" }, { @@ -33,7 +31,6 @@ "komkommer", "ei" ], - "price_small": "1.40", "price_medium": "2.10" }, { @@ -45,7 +42,6 @@ "ei", "ananas" ], - "price_small": "1.40", "price_medium": "2.10" }, { @@ -57,7 +53,6 @@ "komkommer", "ei" ], - "price_small": "1.40", "price_medium": "2.10" }, { @@ -69,7 +64,6 @@ "mosterd", "tabasco" ], - "price_small": "1.40", "price_medium": "2.10" }, { @@ -81,7 +75,6 @@ "komkommer", "ei" ], - "price_small": "1.40", "price_medium": "2.10" }, { @@ -94,7 +87,6 @@ "ei", "mayonaise" ], - "price_small": "1.50", "price_medium": "2.20" }, { @@ -105,7 +97,6 @@ "honing", "pijnboompitten" ], - "price_small": "1.50", "price_medium": "2.30" }, { @@ -120,7 +111,6 @@ "mayonaise", "cocktailsaus" ], - "price_small": "1.50", "price_medium": "2.30" }, { @@ -134,7 +124,6 @@ "ananas", "cocktailsaus" ], - "price_small": "1.50", "price_medium": "2.30" }, { @@ -144,7 +133,6 @@ "kruidenkaas", "ui" ], - "price_small": "1.50", "price_medium": "2.40" }, { @@ -155,7 +143,6 @@ "appeltjes", "sla" ], - "price_small": "1.60", "price_medium": "2.40" }, { @@ -166,7 +153,6 @@ "sla", "groene pesto met basilicum" ], - "price_small": "1.60", "price_medium": "2.40" }, { @@ -175,7 +161,6 @@ "wisselend broodbeleg", "sla" ], - "price_small": "1.50", "price_medium": "2.50" }, { @@ -186,7 +171,6 @@ "sla", "mozarella" ], - "price_small": "1.50", "price_medium": "2.50" } ] diff --git a/build.gradle b/build.gradle index 2802eb14f..5e5af8e4c 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.1.2' classpath 'com.google.gms:google-services:4.4.0' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9' } @@ -44,8 +44,8 @@ allprojects { } } -task clean(type: Delete) { - delete rootProject.buildDir +tasks.register('clean', Delete) { + delete rootProject.layout.buildDirectory } // We want sources to improve IDE features inside our Gradle files. diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index e2c14dccc..e75da7f9c 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -13,7 +13,7 @@ repositories { dependencies { implementation gradleApi() implementation localGroovy() - implementation 'com.android.tools.build:gradle:7.4.2' + implementation 'com.android.tools.build:gradle:8.1.2' implementation 'org.jetbrains:annotations:24.0.1' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.5.0' diff --git a/flake.lock b/flake.lock index dd26952be..38c5dee1d 100644 --- a/flake.lock +++ b/flake.lock @@ -1,70 +1,18 @@ { "nodes": { - "android-nixpkgs": { - "inputs": { - "devshell": "devshell", - "flake-utils": [ - "flake-utils" - ], - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1681590045, - "narHash": "sha256-gT3SeY4RM+sKmRsmt6hTwNA5mzj7njF+BioExI2vR08=", - "owner": "tadfisher", - "repo": "android-nixpkgs", - "rev": "1559090d18aa12d2e0b684da4cf497d8e2038612", - "type": "github" - }, - "original": { - "owner": "tadfisher", - "ref": "beta", - "repo": "android-nixpkgs", - "type": "github" - } - }, "devshell": { "inputs": { - "flake-utils": [ - "android-nixpkgs", - "nixpkgs" - ], "nixpkgs": [ - "android-nixpkgs", "nixpkgs" - ] - }, - "locked": { - "lastModified": 1678957337, - "narHash": "sha256-Gw4nVbuKRdTwPngeOZQOzH/IFowmz4LryMPDiJN/ah4=", - "owner": "numtide", - "repo": "devshell", - "rev": "3e0e60ab37cd0bf7ab59888f5c32499d851edb47", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "devshell", - "type": "github" - } - }, - "devshell_2": { - "inputs": { - "flake-utils": [ - "flake-utils" ], - "nixpkgs": [ - "nixpkgs" - ] + "systems": "systems" }, "locked": { - "lastModified": 1678957337, - "narHash": "sha256-Gw4nVbuKRdTwPngeOZQOzH/IFowmz4LryMPDiJN/ah4=", + "lastModified": 1695973661, + "narHash": "sha256-BP2H4c42GThPIhERtTpV1yCtwQHYHEKdRu7pjrmQAwo=", "owner": "numtide", "repo": "devshell", - "rev": "3e0e60ab37cd0bf7ab59888f5c32499d851edb47", + "rev": "cd4e2fda3150dd2f689caeac07b7f47df5197c31", "type": "github" }, "original": { @@ -75,14 +23,14 @@ }, "flake-utils": { "inputs": { - "systems": "systems" + "systems": "systems_2" }, "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "owner": "numtide", "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { @@ -93,11 +41,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1681465517, - "narHash": "sha256-EasJh15/jcJNAHtq2SGbiADRXteURAnQbj1NqBoKkzU=", + "lastModified": 1698134075, + "narHash": "sha256-foCD+nuKzfh49bIoiCBur4+Fx1nozo+4C/6k8BYk4sg=", "owner": "nixos", "repo": "nixpkgs", - "rev": "abe7316dd51a313ce528972b104f4f04f56eefc4", + "rev": "8efd5d1e283604f75a808a20e6cde0ef313d07d4", "type": "github" }, "original": { @@ -109,8 +57,7 @@ }, "root": { "inputs": { - "android-nixpkgs": "android-nixpkgs", - "devshell": "devshell_2", + "devshell": "devshell", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } @@ -129,6 +76,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index a1fd62862..8c90800c1 100644 --- a/flake.nix +++ b/flake.nix @@ -4,52 +4,40 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; - android-nixpkgs = { - # url = "github:tadfisher/android-nixpkgs"; - - # The main branch follows the "canary" channel of the Android SDK - # repository. Use another android-nixpkgs branch to explicitly - # track an SDK release channel. - # - # url = "github:tadfisher/android-nixpkgs/stable"; - url = "github:tadfisher/android-nixpkgs/beta"; - # url = "github:tadfisher/android-nixpkgs/preview"; - # url = "github:tadfisher/android-nixpkgs/canary"; - - # If you have nixpkgs as an input, this will replace the "nixpkgs" input - # for the "android" flake. - # - inputs.nixpkgs.follows = "nixpkgs"; - inputs.flake-utils.follows = "flake-utils"; - }; devshell = { url = "github:numtide/devshell"; - inputs = { - flake-utils.follows = "flake-utils"; - nixpkgs.follows = "nixpkgs"; - }; + inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { self, nixpkgs, devshell, flake-utils, android-nixpkgs }: + outputs = { self, nixpkgs, devshell, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; overlays = [ devshell.overlays.default ]; config.allowUnfree = true; + config.android_sdk.accept_license = true; + }; + androidVersions = builtins.fromTOML (builtins.readFile ./android-versions.toml); + androidComposition = pkgs.androidenv.composeAndroidPackages { + cmdLineToolsVersion = androidVersions.cmdLineToolsVersion; + toolsVersion = null; + platformToolsVersion = androidVersions.platformToolsVersion; + buildToolsVersions = [ androidVersions.buildToolsVersions ]; + includeEmulator = false; + platformVersions = [ androidVersions.platformVersions ]; + # Enable once nixpkgs is fixed... + includeSources = false; + includeNDK = false; + useGoogleAPIs = true; + includeExtras = [ + "extras;google;gcm" + ]; + extraLicenses = [ + "android-sdk-license" + ]; }; - android-sdk = android-nixpkgs.sdk.${system} (sdkPkgs: with sdkPkgs; [ - cmdline-tools-latest - build-tools-33-0-0 - platform-tools - platforms-android-33 - sources-android-33 - emulator - system-images-android-21-google-apis-x86 - system-images-android-21-default-x86 - system-images-android-33-google-apis-x86-64 - ]); in { devShells = rec { @@ -57,24 +45,32 @@ hydra-android = pkgs.devshell.mkShell { name = "hydra-android"; packages = [ - android-sdk pkgs.jdk11 pkgs.git pkgs.androidStudioPackages.beta + androidComposition.androidsdk pkgs.jdk11 pkgs.git pkgs.androidStudioPackages.beta ]; env = [ { name = "JAVA_HOME"; - eval = pkgs.jdk11.home; + eval = pkgs.jdk17.home; } { name = "ANDROID_SDK_ROOT"; - eval = "${android-sdk}/share/android-sdk"; + eval = "${androidComposition.androidsdk}/libexec/android-sdk"; } { name = "GRADLE_OPTS"; - eval = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${android-sdk}/share/android-sdk/build-tools/33.0.0/aapt2"; + eval = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${androidComposition.androidsdk}/libexec/android-sdk/build-tools/${androidVersions.buildToolsVersions}/aapt2"; + } + { + name = "GRADLE_HOME"; + eval = "$PRJ_DATA_DIR/.gradle"; } ]; - commands = [ - ]; + devshell.startup.link.text = '' + mkdir -p "$PRJ_DATA_DIR/.gradle" + cat < "$PRJ_DATA_DIR/.gradle/gradle.properties" + org.gradle.jvmargs="$GRADLE_OPTS" + EOF + ''; }; }; } diff --git a/gradle.properties b/gradle.properties index 99e08001f..cfb250417 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,5 +20,8 @@ # SOFTWARE. # +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false +android.nonTransitiveRClass=false android.useAndroidX=true -org.gradle.jvmargs=-Xmx4608m \ No newline at end of file +org.gradle.jvmargs="-Xmx4608m" \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4..ccebba771 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3796d3cd3..309b4e18d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c78733..79a61d421 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -205,6 +209,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f93..6689b85be 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/material-intro/build.gradle b/material-intro/build.gradle index 0ecd4feb2..63918113a 100644 --- a/material-intro/build.gradle +++ b/material-intro/build.gradle @@ -1,12 +1,16 @@ apply plugin: 'com.android.library' +def versions = loadAndroidVersions() + android { - buildToolsVersion "33.0.0" + // We need this for Nix flakes + //noinspection GrDeprecatedAPIUsage + buildToolsVersion versions.buildToolsVersions defaultConfig { - compileSdk 33 + compileSdk versions.platformVersions as Integer minSdk 21 - targetSdk 33 + targetSdk 34 } buildTypes { @@ -19,7 +23,7 @@ android { namespace 'com.heinrichreimersoftware.materialintro' lint { - disable 'Overdraw', 'OldTargetApi', 'GradleDependency', 'ObsoleteLintCustomCheck' + disable 'Overdraw', 'OldTargetApi', 'GradleDependency' showAll true warningsAsErrors true } @@ -28,5 +32,22 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.10.0' } + +// TODO: extract this duplicate code... +/** + * Loads the default properties, and the user properties. This will also load the + * secret keys. + */ +def loadAndroidVersions() { + def defaultProps = new Properties() + defaultProps.load(file("../android-versions.toml").newReader()) + + def strippedProps = new Properties() + for (e in defaultProps) { + strippedProps.setProperty(e.key, e.value.replaceAll('"', '')) + } + + return strippedProps +} \ No newline at end of file