diff --git a/.github/workflows/android.yml b/.github/workflows/ci.yml similarity index 55% rename from .github/workflows/android.yml rename to .github/workflows/ci.yml index 5a94984fa..a18332dbd 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Android CI +name: CI # We run the tests on master, development and for PR's. on: @@ -9,43 +9,46 @@ on: pull_request: jobs: - build-debug: - name: Test debug + test: + name: Test runs-on: ubuntu-latest + strategy: + matrix: + command: [testOpenDebug, testOpenRelease, testStoreDebug, testStoreRelease] steps: - uses: actions/checkout@v1 - - name: Set up JDK 11 + - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 13 - name: Cache Gradle files uses: actions/cache@v1 with: path: ~/.gradle/caches - key: debug-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + key: ${{ matrix.command }}-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} restore-keys: | - debug-${{ runner.os }}-gradle- + ${{ matrix.command }}-${{ runner.os }}-gradle- - name: Test with Gradle uses: eskatos/gradle-command-action@v1 with: - arguments: testDebug - build-release: - name: Test release + arguments: ${{ matrix.command }} + lint: + name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - name: Set up JDK 11 + - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 13 - name: Cache Gradle files uses: actions/cache@v1 with: path: ~/.gradle/caches - key: release-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + key: lint-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} restore-keys: | - release-${{ runner.os }}-gradle- - - name: Test with Gradle + lint-${{ runner.os }}-gradle- + - name: Lint with Gradle uses: eskatos/gradle-command-action@v1 with: - arguments: testRelease + arguments: lint diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7b7106b62..000000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: android -dist: trusty -jdk: openjdk11 - -# See https://stackoverflow.com/a/51644855/1831741 for the Java options -env: - - COMMAND=testDebug - - COMMAND=testRelease - -android: - components: - - tools - - build-tools-29.0.2 - - android-29 - licenses: - - 'android-sdk-preview-license-.+' - - 'android-sdk-license-.+' - - 'google-gdk-license-.+' - -script: - - ./gradlew $COMMAND - -# Caching according to the docs at https://docs.travis-ci.com/user/languages/java/#Projects-Using-Gradle -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - -# We only build on these branches. -branches: - only: - - master - - development diff --git a/README.md b/README.md index cb8efd882..8a387e62e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Hydra (Android) [![Build Status](https://travis-ci.org/ZeusWPI/hydra-android.svg?branch=development)](https://travis-ci.org/ZeusWPI/hydra-android) +# Hydra (Android) ![Android CI](https://github.com/ZeusWPI/hydra-android/workflows/Android%20CI/badge.svg?branch=development) Get it on Google Play -Android version of the Hydra app, available for Jelly Bean and up. Available on [Google Play](https://play.google.com/store/apps/details?id=be.ugent.zeus.hydra) or as a download [here](https://github.com/ZeusWPI/hydra-android/releases) (apk file). +The Android version of the Hydra app, available for Jelly Bean and up. Available on [Google Play](https://play.google.com/store/apps/details?id=be.ugent.zeus.hydra) or as a download [here](https://github.com/ZeusWPI/hydra-android/releases) (apk file). ## Contributing @@ -21,6 +21,11 @@ If you want to use the Google Maps integration, you will need the API keys. You After you've obtained the keys, you will need to copy the file `app/secrets.properties.example` to `app/secrets.properties` and insert the correct keys. +### Build variants + +Hydra comes in two build variants: `store` and `open`. +The `store` variant is the main variant, and used for the Play Store and is the recommended version for most people. The `open` variant only uses open-source software (e.g. OpenStreetMaps instead of Google Maps). Since the open variant contains no crash reporting functionality, crashes from that version not accompanied by a stack trace will not be considered. + ### Useful links - [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) - [Android Developer Guides](https://developer.android.com/guide/) diff --git a/app/build.gradle b/app/build.gradle index d14138ce0..b2ccb9477 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,14 @@ apply plugin: 'com.android.application' -apply plugin: 'com.google.android.gms.oss-licenses-plugin' -apply plugin: 'io.fabric' +apply plugin: 'be.ugent.zeus.hydra.licenses' +apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.google.firebase.crashlytics' // Read our properties, see bottom for details. def props = loadProperties() android { compileSdkVersion 29 - buildToolsVersion "29.0.2" + buildToolsVersion "29.0.3" // Use resources in robolectric testOptions.unitTests.includeAndroidResources = true @@ -17,14 +18,11 @@ android { applicationId "be.ugent.zeus.hydra" minSdkVersion 16 targetSdkVersion 29 - versionCode 21400 - versionName "2.14" + versionCode 21402 + versionName "2.14.2" vectorDrawables.useSupportLibrary = true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - manifestPlaceholders = [ - google_maps_key: props.getProperty('MAPS_API_KEY'), - ] // For a description of what these do, see the config.properties file. buildConfigField "boolean", "DEBUG_HOME_STREAM_PRIORITY", props.getProperty('hydra.debug.home.stream.priority') buildConfigField "boolean", "DEBUG_HOME_STREAM_STALL", props.getProperty('hydra.debug.home.stream.stall') @@ -42,6 +40,50 @@ android { } } + viewBinding { + enabled = true + } + + lintOptions { + warningsAsErrors true + // We don't support Right to Left layouts. + disable "RtlSymmetry", "RtlHardcoded", + // We don't care about long vector paths. + "VectorPath", + // Http is still allowed for now. + "InsecureBaseConfiguration", + // Allow overdraw, mainly used for selectable backgrounds + "Overdraw", + // Disabled until AS 4.0 is available + "UnusedResources", + // We use dependabot for dependencies + "GradleDependency", + // We will choose for ourselves when we update + "OldTargetApi" + } + + flavorDimensions "distribution" + + productFlavors { + + // Play Store and officially supported version + store { + isDefault = true + manifestPlaceholders = [ + google_maps_key: props.getProperty('MAPS_API_KEY'), + ] + } + + open { + ext.enableCrashlytics = false + versionNameSuffix "-open" + applicationIdSuffix ".open" + firebaseCrashlytics { + mappingFileUploadEnabled false + } + } + } + // used by Room, to test migrations sourceSets { test.resources.srcDirs += files("$projectDir/schemas".toString()) @@ -96,15 +138,15 @@ android { dependencies { implementation 'androidx.multidex:multidex:2.0.1' - implementation 'androidx.core:core:1.2.0' + implementation 'androidx.core:core:1.3.1' implementation 'androidx.media:media:1.1.0' - implementation 'androidx.fragment:fragment:1.2.3' - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.preference:preference:1.1.0' + implementation 'androidx.fragment:fragment:1.2.5' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' - implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0' - implementation 'com.google.android.material:material:1.1.0' + implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' + implementation 'com.google.android.material:material:1.2.0' implementation 'androidx.browser:browser:1.2.0' implementation 'androidx.slice:slice-builders:1.0.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' @@ -113,52 +155,61 @@ dependencies { annotationProcessor 'androidx.room:room-compiler:2.2.5' // Do not upgrade beyond 3.12.x until we require API 21+ //noinspection GradleDependency - implementation 'com.squareup.okhttp3:okhttp:3.12.10' - implementation 'com.squareup.moshi:moshi:1.9.2' - implementation 'com.jakewharton.threetenabp:threetenabp:1.2.2' - implementation 'net.sourceforge.streamsupport:android-retrostreams:1.7.1' - implementation 'com.squareup.picasso:picasso:2.71828' + implementation 'com.squareup.okhttp3:okhttp:3.12.12' + implementation 'com.squareup.moshi:moshi:1.10.0' + implementation 'com.jakewharton.threetenabp:threetenabp:1.2.4' + implementation 'net.sourceforge.streamsupport:android-retrostreams:1.7.2' + implementation 'com.squareup.picasso:picasso:2.8' implementation 'net.cachapa.expandablelayout:expandablelayout:2.9.2' - implementation 'com.heinrichreimersoftware:material-intro:2.0.0' + implementation 'com.github.niknetniko:material-intro:9903deb6c4' implementation 'com.jonathanfinerty.once:once:1.3.0' implementation 'blue.aodev:material-values:1.1.1' - implementation 'com.google.android.gms:play-services-oss-licenses:17.0.0' - implementation 'com.google.android.gms:play-services-maps:17.0.0' - implementation 'com.google.firebase:firebase-analytics:17.2.3' - implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' + + // Dependencies for the Play Store version. + storeImplementation 'com.google.android.gms:play-services-maps:17.0.0' + storeImplementation 'com.google.firebase:firebase-analytics:17.5.0' + storeImplementation 'com.google.firebase:firebase-crashlytics:17.2.1' + + // Dependencies for open version. + openImplementation 'org.osmdroid:osmdroid-android:6.1.8' if (props.getProperty("hydra.debug.leaks").toBoolean()) { logger.info("Leak tracking enabled...") - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4' } testImplementation 'junit:junit:4.13' - testImplementation 'org.mockito:mockito-core:3.2.4' - testImplementation 'org.robolectric:robolectric:4.3.1' - testImplementation 'androidx.test:core:1.2.0' - testImplementation 'androidx.test.ext:junit:1.1.1' - testImplementation 'androidx.test:rules:1.2.0' - testImplementation 'androidx.test.espresso:espresso-core:3.2.0' - testImplementation 'androidx.test.espresso:espresso-intents:3.2.0' - testImplementation 'androidx.test.espresso:espresso-contrib:3.2.0' + testImplementation 'org.mockito:mockito-core:3.5.7' + testImplementation 'org.robolectric:robolectric:4.4' + testImplementation 'androidx.test:core:1.3.0' + testImplementation 'androidx.test.ext:junit:1.1.2' + testImplementation 'androidx.test:rules:1.3.0' + testImplementation 'androidx.test.espresso:espresso-core:3.3.0' + testImplementation 'androidx.test.espresso:espresso-intents:3.3.0' + testImplementation 'androidx.test.espresso:espresso-contrib:3.3.0' testImplementation 'androidx.arch.core:core-testing:2.1.0' testImplementation 'androidx.room:room-testing:2.2.5' // Enable the normal library for unit tests. - testImplementation('org.threeten:threetenbp:1.4.1') { + testImplementation('org.threeten:threetenbp:1.4.4') { exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp' } //noinspection GradleDependency - testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.6' - testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.1.12' + testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.12' + testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.4.2' testImplementation 'com.github.niknetniko:GetterSetterVerifier:0.0.11' testImplementation 'com.shazam:shazamcrest:0.11' testImplementation 'org.skyscreamer:jsonassert:1.5.0' - testImplementation 'org.jeasy:easy-random-core:4.1.0' - testImplementation 'org.apache.commons:commons-lang3:3.9' + testImplementation 'org.jeasy:easy-random-core:4.2.0' + testImplementation 'org.apache.commons:commons-lang3:3.11' testImplementation 'commons-validator:commons-validator:1.6' } -apply plugin: 'com.google.gms.google-services' + +// Disable Google services for open variant. +android.applicationVariants.all { variant -> + def googleTask = tasks.findByName("process${variant.name.capitalize()}GoogleServices") + googleTask.enabled = "open" != variant.flavorName +} /** * Loads the default properties, and the user properties. This will also load the @@ -189,7 +240,7 @@ def loadProperties() { if (actualKeyFile.exists()) { actualKeys.load(actualKeyFile.newReader()) } else { - logger.warn('A secrets.properties file was not found. Maps will not work.') + logger.warn('A secrets.properties file was not found.') } return actualKeys diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1b0ecfc6f..3fc82b9c2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ - GoogleAppIndexingWarning: not currently used --> - - - - - - - - - - Android Design in Action: Navigation Anti-Patterns, pattern 6 */ -public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener { +public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener { public static final String ARG_TAB = "argTab"; @SuppressWarnings("WeakerAccess") @@ -164,7 +162,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig private static final String UFORA = "com.d2l.brightspace.student.android"; @VisibleForTesting - static final String ONCE_ONBOARDING = "once_onboarding_v1_debug"; + static final String ONCE_ONBOARDING = "once_onboarding_v1"; private static final int ONBOARDING_REQUEST = 5; private static final String STATE_IS_ONBOARDING_OPEN = "state_is_onboarding_open"; @@ -179,11 +177,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig private static final String SHORTCUT_EVENTS = "events"; private static final String SHORTCUT_LIBRARIES = "libraries"; - private DrawerLayout drawer; - private ProgressBar drawerLoader; private ActionBarDrawerToggle toggle; - private NavigationView navigationView; - private AppBarLayout appBarLayout; private boolean isOnboardingOpen; @@ -197,7 +191,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + setContentView(ActivityMainBinding::inflate); if (savedInstanceState != null) { isOnboardingOpen = savedInstanceState.getBoolean(STATE_IS_ONBOARDING_OPEN, false); @@ -220,15 +214,11 @@ protected void onSaveInstanceState(@NonNull Bundle outState) { } private void initialize(@Nullable Bundle savedInstanceState) { - drawer = findViewById(R.id.drawer_layout); - navigationView = findViewById(R.id.navigation_view); - appBarLayout = findViewById(R.id.app_bar_layout); - drawerLoader = findViewById(R.id.drawer_loading); // Register the listener for navigation events from the drawer. - navigationView.setNavigationItemSelectedListener(this); + binding.navigationView.setNavigationItemSelectedListener(this); - toggle = new ActionBarDrawerToggle(this, drawer, findViewById(R.id.toolbar), R.string.action_drawer_open, R.string.action_drawer_close) { + toggle = new ActionBarDrawerToggle(this, binding.drawerLayout, binding.toolbar, R.string.action_drawer_open, R.string.action_drawer_close) { @Override public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, 0); // this disables the animation @@ -240,8 +230,8 @@ public void onDrawerOpened(View drawerView) { Once.markDone(ONCE_DRAWER); } }; - drawer.addDrawerListener(toggle); - drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() { + binding.drawerLayout.addDrawerListener(toggle); + binding.drawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() { @Override public void onDrawerClosed(View drawerView) { // This is used to prevent lag during closing of the navigation drawer. @@ -257,23 +247,23 @@ public void onDrawerClosed(View drawerView) { // If we get a position, use that (for the shortcuts) if (getIntent().hasExtra(ARG_TAB_SHORTCUT)) { int position = getIntent().getIntExtra(ARG_TAB_SHORTCUT, 0); - MenuItem menuItem = navigationView.getMenu().getItem(position); + MenuItem menuItem = binding.navigationView.getMenu().getItem(position); selectDrawerItem(menuItem, NavigationSource.INITIALISATION); } else { // Get start position & select it int start = getIntent().getIntExtra(ARG_TAB, R.id.drawer_feed); - selectDrawerItem(navigationView.getMenu().findItem(start), NavigationSource.INITIALISATION); + selectDrawerItem(binding.navigationView.getMenu().findItem(start), NavigationSource.INITIALISATION); } } else { //Update title, since this is not saved apparently. //Current fragment FragmentManager manager = getSupportFragmentManager(); Fragment current = manager.findFragmentById(R.id.content); - setTitle(navigationView.getMenu().findItem(getFragmentMenuId(current)).getTitle()); + setTitle(binding.navigationView.getMenu().findItem(getFragmentMenuId(current)).getTitle()); } // If this is the first time, open the drawer. if (!Once.beenDone(ONCE_DRAWER)) { - drawer.openDrawer(GravityCompat.START); + binding.drawerLayout.openDrawer(GravityCompat.START); } } @@ -308,13 +298,13 @@ private void selectDrawerItem(MenuItem menuItem, @NavigationSource int navigatio // First check if it are settings, then we don't update anything. if (menuItem.getItemId() == R.id.drawer_pref) { - drawer.closeDrawer(GravityCompat.START); + binding.drawerLayout.closeDrawer(GravityCompat.START); PreferenceActivity.start(this, null); return; } if (menuItem.getItemId() == R.id.drawer_ufora) { - drawer.closeDrawer(GravityCompat.START); + binding.drawerLayout.closeDrawer(GravityCompat.START); Intent launchIntent = getPackageManager().getLaunchIntentForPackage(UFORA); if (launchIntent != null) { startActivity(launchIntent); @@ -379,13 +369,13 @@ private void selectDrawerItem(MenuItem menuItem, @NavigationSource int navigatio // Show the toolbar, in case the new fragment is not scrollable. We must do this before the fragment // begins animating, otherwise glitches can occur. - appBarLayout.setExpanded(true); + binding.appBarLayout.setExpanded(true); - if (drawer.isDrawerOpen(GravityCompat.START)) { + if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { // Hide the current fragment now, similar to how GMail handles things. if (current != null && current.getView() != null) { current.getView().setVisibility(View.GONE); - drawerLoader.setVisibility(View.VISIBLE); + binding.progressBar.progressBar.setVisibility(View.VISIBLE); } // Since there will be a delay, notify the fragment to prevent lingering snackbars or action modes. if (current instanceof ScheduledRemovalListener) { @@ -412,7 +402,7 @@ private void updateDrawer(Fragment fragment, MenuItem item) { Reporting.getTracker(this) .setCurrentScreen(this, item.getTitle().toString(), fragment.getClass().getSimpleName()); // Close the navigation drawer - drawer.closeDrawer(GravityCompat.START); + binding.drawerLayout.closeDrawer(GravityCompat.START); } /** @@ -447,7 +437,7 @@ private void setFragment(Fragment fragment, MenuItem menuItem, @NavigationSource transaction.commitAllowingStateLoss(); // Hide the loader. - drawerLoader.setVisibility(View.GONE); + binding.progressBar.progressBar.setVisibility(View.GONE); } /** @@ -457,8 +447,8 @@ private void setFragment(Fragment fragment, MenuItem menuItem, @NavigationSource @Override public void onBackPressed() { // If the drawer is open, close it. - if (drawer.isDrawerOpen(GravityCompat.START)) { - drawer.closeDrawer(GravityCompat.START); + if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { + binding.drawerLayout.closeDrawer(GravityCompat.START); return; } @@ -470,7 +460,7 @@ public void onBackPressed() { if (current == null) { return; } - MenuItem item = navigationView.getMenu().findItem(getFragmentMenuId(current)); + MenuItem item = binding.navigationView.getMenu().findItem(getFragmentMenuId(current)); updateDrawer(current, item); }; // We need to listen to the back stack to update the drawer. @@ -553,7 +543,7 @@ protected void onNewIntent(Intent intent) { if (intent.hasExtra(ARG_TAB)) { setIntent(intent); int start = intent.getIntExtra(ARG_TAB, R.id.drawer_feed); - selectDrawerItem(navigationView.getMenu().findItem(start), NavigationSource.INNER); + selectDrawerItem(binding.navigationView.getMenu().findItem(start), NavigationSource.INNER); } } 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 dcd48d207..9d59035c5 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 @@ -4,6 +4,8 @@ import android.os.Parcelable; import androidx.annotation.NonNull; +import java.util.Locale; + import be.ugent.zeus.hydra.common.network.Endpoints; import com.squareup.moshi.Json; import java9.util.Objects; @@ -63,7 +65,7 @@ public String getParentAssociation() { } public String getImageLink() { - return String.format(IMAGE_LINK, internalName.toLowerCase()); + return String.format(IMAGE_LINK, internalName.toLowerCase(Locale.ROOT)); } public static final Creator CREATOR = new Creator() { diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/event/DisabledEventRemover.java b/app/src/main/java/be/ugent/zeus/hydra/association/event/DisabledEventRemover.java index 14868163c..f316c0cbf 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/event/DisabledEventRemover.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/event/DisabledEventRemover.java @@ -8,6 +8,7 @@ import java9.util.stream.StreamSupport; import java.util.List; +import java.util.Locale; import java.util.Set; import static be.ugent.zeus.hydra.association.preference.AssociationSelectionPreferenceFragment.PREF_ASSOCIATIONS_SHOWING; @@ -31,7 +32,7 @@ public List apply(List events) { .map(String::toLowerCase) .collect(Collectors.toSet()); return StreamSupport.stream(events) - .filter(event -> !disabled.contains(event.getAssociation().getInternalName().toLowerCase())) + .filter(event -> !disabled.contains(event.getAssociation().getInternalName().toLowerCase(Locale.ROOT))) .collect(Collectors.toList()); } } 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 0e125d785..6e4ae830a 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 @@ -5,21 +5,21 @@ import android.net.Uri; import android.os.Bundle; import android.provider.CalendarContract; -import androidx.annotation.Nullable; -import androidx.core.text.util.LinkifyCompat; import android.text.util.Linkify; import android.view.Menu; import android.view.MenuItem; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.Nullable; +import androidx.core.text.util.LinkifyCompat; import be.ugent.zeus.hydra.R; -import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.reporting.BaseEvents; +import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.ui.BaseActivity; import be.ugent.zeus.hydra.common.utils.NetworkUtils; +import be.ugent.zeus.hydra.databinding.ActivityEventDetailBinding; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; import org.threeten.bp.format.DateTimeFormatter; @@ -29,7 +29,7 @@ * * @author Niko Strijbol */ -public class EventDetailsActivity extends BaseActivity { +public class EventDetailsActivity extends BaseActivity { public static final String PARCEL_EVENT = "eventParcelable"; private static final DateTimeFormatter format = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); @@ -46,59 +46,51 @@ public static Intent start(Context context, Event event) { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_event_detail); + setContentView(ActivityEventDetailBinding::inflate); //Get data from saved instance, or from intent event = getIntent().getParcelableExtra(PARCEL_EVENT); + assert event != null; - TextView title = findViewById(R.id.title); - TextView location = findViewById(R.id.location); - TextView description = findViewById(R.id.description); - final ImageView organisatorImage = findViewById(R.id.event_organisator_image); - TextView mainName = findViewById(R.id.event_organisator_main); - TextView smallName = findViewById(R.id.event_organisator_small); if (event.getTitle() != null) { - title.setText(event.getTitle()); + binding.title.setText(event.getTitle()); requireToolbar().setTitle(event.getTitle()); } if (event.getAssociation() != null) { - mainName.setText(event.getAssociation().getDisplayName()); - smallName.setText(event.getAssociation().getFullName()); + binding.eventOrganisatorMain.setText(event.getAssociation().getDisplayName()); + binding.eventOrganisatorSmall.setText(event.getAssociation().getFullName()); } if (event.getDescription() != null && !event.getDescription().trim().isEmpty()) { - description.setText(event.getDescription()); - LinkifyCompat.addLinks(description, Linkify.ALL); + binding.description.setText(event.getDescription()); + LinkifyCompat.addLinks(binding.description, Linkify.ALL); } if (event.hasPreciseLocation() || event.hasLocation()) { if (event.hasLocation()) { - location.setText(event.getLocation()); + binding.location.setText(event.getLocation()); } else { - location.setText(getString(R.string.event_detail_precise_location, event.getLatitude(), event.getLongitude())); + binding.location.setText(getString(R.string.event_detail_precise_location, event.getLatitude(), event.getLongitude())); } // Make location clickable - findViewById(R.id.location_row).setOnClickListener(view -> NetworkUtils.maybeLaunchIntent(this, getLocationIntent())); + binding.locationRow.setOnClickListener(view -> NetworkUtils.maybeLaunchIntent(this, getLocationIntent())); } else { - location.setText(R.string.event_detail_no_location); + binding.location.setText(R.string.event_detail_no_location); } - TextView startTime = findViewById(R.id.time_start); - TextView endTime = findViewById(R.id.time_end); - - startTime.setText(event.getLocalStart().format(format)); + binding.timeStart.setText(event.getLocalStart().format(format)); if (event.getLocalEnd() != null) { - endTime.setText(event.getLocalEnd().format(format)); + binding.timeEnd.setText(event.getLocalEnd().format(format)); } else { - endTime.setText(R.string.event_detail_date_unknown); + binding.timeEnd.setText(R.string.event_detail_date_unknown); } if (event.getAssociation() != null) { - Picasso.get().load(event.getAssociation().getImageLink()).into(organisatorImage, new EventCallback(organisatorImage)); + Picasso.get().load(event.getAssociation().getImageLink()).into(binding.eventOrganisatorImage, new EventCallback(binding.eventOrganisatorImage)); } else { - organisatorImage.setLayoutParams(new LinearLayout.LayoutParams(0, 0)); + binding.eventOrganisatorImage.setLayoutParams(new LinearLayout.LayoutParams(0, 0)); } Reporting.getTracker(this) diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/event/list/EventFragment.java b/app/src/main/java/be/ugent/zeus/hydra/association/event/list/EventFragment.java index 4c91398f5..58aef4b8e 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/event/list/EventFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/event/list/EventFragment.java @@ -9,7 +9,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -18,9 +18,9 @@ import be.ugent.zeus.hydra.common.arch.observers.PartialErrorObserver; import be.ugent.zeus.hydra.common.arch.observers.ProgressObserver; import be.ugent.zeus.hydra.common.ui.recyclerview.EmptyViewObserver; +import be.ugent.zeus.hydra.common.utils.ColourUtils; import be.ugent.zeus.hydra.preferences.PreferenceActivity; import be.ugent.zeus.hydra.preferences.PreferenceEntry; -import be.ugent.zeus.hydra.common.utils.ColourUtils; import com.google.android.material.snackbar.Snackbar; import static be.ugent.zeus.hydra.common.utils.FragmentUtils.requireBaseActivity; @@ -65,11 +65,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat SwipeRefreshLayout swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout); swipeRefreshLayout.setColorSchemeColors(secondaryColour); - viewModel = ViewModelProviders.of(this).get(EventViewModel.class); - viewModel.getData().observe(this, PartialErrorObserver.with(this::onError)); - viewModel.getData().observe(this, new ProgressObserver<>(view.findViewById(R.id.progress_bar))); - viewModel.getData().observe(this, new AdapterObserver<>(adapter)); - viewModel.getRefreshing().observe(this, swipeRefreshLayout::setRefreshing); + viewModel = new ViewModelProvider(this).get(EventViewModel.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 AdapterObserver<>(adapter)); + viewModel.getRefreshing().observe(getViewLifecycleOwner(), swipeRefreshLayout::setRefreshing); swipeRefreshLayout.setOnRefreshListener(viewModel); Button refresh = view.findViewById(R.id.events_no_data_button_refresh); diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/event/list/EventSearchPredicate.java b/app/src/main/java/be/ugent/zeus/hydra/association/event/list/EventSearchPredicate.java index 20bbe43c4..ed1c1010f 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/event/list/EventSearchPredicate.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/event/list/EventSearchPredicate.java @@ -1,5 +1,7 @@ package be.ugent.zeus.hydra.association.event.list; +import java.util.Locale; + import be.ugent.zeus.hydra.association.Association; import be.ugent.zeus.hydra.association.event.Event; import java9.util.function.BiPredicate; @@ -22,15 +24,15 @@ public boolean test(EventItem eventItem, String searchTerm) { return true; } Event event = eventItem.getItem(); - if (event.getTitle() != null && event.getTitle().toLowerCase().contains(searchTerm)) { + if (event.getTitle() != null && event.getTitle().toLowerCase(Locale.getDefault()).contains(searchTerm)) { return true; } if (event.getAssociation() != null) { Association association = event.getAssociation(); - if (association.getDisplayName() != null && association.getDisplayName().toLowerCase().contains(searchTerm)) { + if (association.getDisplayName() != null && association.getDisplayName().toLowerCase(Locale.ROOT).contains(searchTerm)) { return true; } - if (association.getFullName() != null && association.getFullName().toLowerCase().contains(searchTerm)) { + if (association.getFullName() != null && association.getFullName().toLowerCase(Locale.getDefault()).contains(searchTerm)) { return true; } if (association.getInternalName().contains(searchTerm)) { 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 05174dcfb..f5a15becc 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 @@ -5,13 +5,11 @@ import android.util.Log; import android.util.Pair; import android.view.*; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; -import androidx.lifecycle.ViewModelProviders; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; diff --git a/app/src/main/java/be/ugent/zeus/hydra/association/preference/SearchableAssociationsAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/association/preference/SearchableAssociationsAdapter.java index 170e9c762..e7b7b3bc7 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/association/preference/SearchableAssociationsAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/association/preference/SearchableAssociationsAdapter.java @@ -10,6 +10,7 @@ import be.ugent.zeus.hydra.common.ui.recyclerview.adapters.MultiSelectSearchableAdapter; import java.util.Collections; +import java.util.Locale; /** * @@ -18,9 +19,9 @@ class SearchableAssociationsAdapter extends MultiSelectSearchableAdapter { SearchableAssociationsAdapter() { - super(text -> a -> a.getDisplayName().toLowerCase().contains(text) || - (a.getFullName() != null && a.getFullName().toLowerCase().contains(text)) || - a.getInternalName().toLowerCase().contains(text)); + super(text -> a -> a.getDisplayName().toLowerCase(Locale.getDefault()).contains(text) || + (a.getFullName() != null && a.getFullName().toLowerCase(Locale.getDefault()).contains(text)) || + a.getInternalName().toLowerCase(Locale.ROOT).contains(text)); } @NonNull diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/ProgressObserver.java b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/ProgressObserver.java index 1d37a40a9..1034c1f71 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/ProgressObserver.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/arch/observers/ProgressObserver.java @@ -1,22 +1,30 @@ package be.ugent.zeus.hydra.common.arch.observers; +import androidx.annotation.NonNull; import androidx.lifecycle.Observer; import androidx.annotation.Nullable; import android.view.View; import android.widget.ProgressBar; import be.ugent.zeus.hydra.common.request.Result; +import be.ugent.zeus.hydra.databinding.XProgressBarBinding; /** + * Observes a progress bar and hides it once the data is loaded. + * * @author Niko Strijbol */ public class ProgressObserver implements Observer> { private final ProgressBar progressBar; - public ProgressObserver(ProgressBar progressBar) { + public ProgressObserver(@NonNull ProgressBar progressBar) { this.progressBar = progressBar; } + public ProgressObserver(@NonNull XProgressBarBinding binding) { + this.progressBar = binding.progressBar; + } + @Override public void onChanged(@Nullable Result result) { if (result == null || result.isDone()) { 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 205f26e4e..e3423072c 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 @@ -2,8 +2,6 @@ import android.content.Context; import android.os.Build; -import android.util.Log; - import androidx.annotation.VisibleForTesting; import java.io.File; @@ -11,11 +9,6 @@ import be.ugent.zeus.hydra.common.converter.BooleanJsonAdapter; import be.ugent.zeus.hydra.common.converter.DateThreeTenAdapter; import be.ugent.zeus.hydra.common.converter.DateTypeConverters; - -import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.GooglePlayServicesNotAvailableException; -import com.google.android.gms.common.GooglePlayServicesRepairableException; -import com.google.android.gms.security.ProviderInstaller; import com.squareup.moshi.Moshi; import okhttp3.Cache; import okhttp3.OkHttpClient; @@ -27,8 +20,6 @@ */ public final class InstanceProvider { - private static final String TAG = "InstanceProvider"; - private static OkHttpClient client; private static final long CACHE_SIZE = 20 * 1024 * 1024; // 20 MiB @@ -63,7 +54,7 @@ public static OkHttpClient.Builder getBuilder(File cacheDir) { public static synchronized OkHttpClient getClient(Context context) { if (Build.VERSION.SDK_INT <= 20) { - installGoogleProvider(context); + CertificateProvider.installProvider(context); } File cacheDir = new File(context.getCacheDir(), "http"); @@ -90,17 +81,4 @@ public static void reset() { client = null; moshi = null; } - - private static void installGoogleProvider(Context context) { - Log.i(TAG, "Installing Play Services to enable TLSv1.2"); - try { - ProviderInstaller.installIfNeeded(context); - } catch (GooglePlayServicesRepairableException e) { - Log.w(TAG, "Play Services are outdated", e); - // Prompt the user to install/update/enable Google Play services. - GoogleApiAvailability.getInstance().showErrorNotification(context, e.getConnectionStatusCode()); - } catch (GooglePlayServicesNotAvailableException e) { - Log.e(TAG, "Unable to install provider, SSL will not work", e); - } - } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/reporting/Reporting.java b/app/src/main/java/be/ugent/zeus/hydra/common/reporting/Manager.java similarity index 67% rename from app/src/main/java/be/ugent/zeus/hydra/common/reporting/Reporting.java rename to app/src/main/java/be/ugent/zeus/hydra/common/reporting/Manager.java index a245c86ea..2e6d8b5f5 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/reporting/Reporting.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/reporting/Manager.java @@ -3,44 +3,21 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Log; -import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceManager; + import be.ugent.zeus.hydra.BuildConfig; /** - * Produces the default tracker. + * Manage reporting permissions. * * @author Niko Strijbol */ -public final class Reporting { +public class Manager { private static final String TAG = "Reporting"; - private static final String PREF_ALLOW_ANALYTICS = "be.ugent.zeus.hydra.reporting.allow_analytics"; private static final String PREF_ALLOW_CRASH_REPORTING = "be.ugent.zeus.hydra.reporting.allow_crash_reporting"; - private static Tracker tracker; - private final static Object lock = new Object(); - - /** - * Get the default tracker. - * - * @param context The context. - * @return The tracker. - */ - public static Tracker getTracker(Context context) { - synchronized (lock) { - if (tracker == null) { - tracker = new FirebaseTracker(context); - } - } - return tracker; - } - - public static BaseEvents getEvents() { - return FirebaseEvents.getInstance(); - } - public static void saveAnalyticsPermission(Context context, boolean allowed) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); preferences.edit() @@ -65,7 +42,7 @@ public static void saveCrashReportingPermission(Context context, boolean allowed */ public static void syncPermissions(Context context) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - Tracker tracker = getTracker(context); + Tracker tracker = Reporting.getTracker(context); // Enable or disable analytics boolean areAnalyticsAllowed = preferences.getBoolean(PREF_ALLOW_ANALYTICS, false) && allowDebugReporting(); @@ -79,17 +56,6 @@ public static void syncPermissions(Context context) { } public static boolean allowDebugReporting() { - // If a DEBUG build, use the property, otherwise OK! - if (BuildConfig.DEBUG) { - Log.d(TAG, "allowDebugReporting: this is debug mode"); - return BuildConfig.DEBUG_ENABLE_REPORTING; - } else { - return true; - } - } - - @VisibleForTesting - public static void setTracker(Tracker tracker) { - Reporting.tracker = tracker; + return BuildConfig.DEBUG_ENABLE_REPORTING; } } 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 5190b1ab3..3bb30ee62 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 @@ -36,6 +36,7 @@ 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); /** @@ -53,13 +54,6 @@ public interface Tracker { */ void logError(Throwable throwable); - /** - * Log an error string to the crash service. - * - * @param message The message to log. - */ - void logErrorMessage(String message); - /** * Configure the underlying service to allow analytics or not. * 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 d4b740315..addf490e0 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 @@ -1,29 +1,44 @@ package be.ugent.zeus.hydra.common.ui; import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.Menu; import androidx.annotation.ColorInt; -import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; -import androidx.core.graphics.drawable.DrawableCompat; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; -import android.view.Menu; +import androidx.core.app.ActivityCompat; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.viewbinding.ViewBinding; + +import java9.util.function.Function; import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.utils.ColourUtils; /** * The base activity. Contains code related to common things for almost all activities. + * * Such features include: *
    *
  • Support for the toolbar
  • *
  • Better Google Reporting support
  • + *
  • View binding, see below.
  • *
* + * + *

View binding

+ * + * This activity requires the use of view binding. To set up the view on the + * activity, call {@link #setContentView(Function)}, to which you must pass + * the view binding constructor. + * * @author Niko Strijbol */ -public abstract class BaseActivity extends AppCompatActivity { +public abstract class BaseActivity extends AppCompatActivity { + + protected B binding; /** * Returns the action bar of this activity. If the ActionBar is not present or the method is called at the wrong @@ -42,15 +57,11 @@ public ActionBar requireToolbar() { } } - private Toolbar findToolbar() { - return findViewById(R.id.toolbar); - } - /** * Set the toolbar as action bar, and set it up to have an up button. */ private void setUpActionBar() { - Toolbar toolbar = findToolbar(); + Toolbar toolbar = ActivityCompat.requireViewById(this, R.id.toolbar); setSupportActionBar(toolbar); @@ -60,9 +71,9 @@ private void setUpActionBar() { } } - @Override - public void setContentView(@LayoutRes int layoutResID) { - super.setContentView(layoutResID); + public void setContentView(Function binder) { + this.binding = binder.apply(getLayoutInflater()); + setContentView(this.binding.getRoot()); setUpActionBar(); } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/WebViewActivity.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/WebViewActivity.java index 32972a7b6..f45531317 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/WebViewActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/WebViewActivity.java @@ -8,15 +8,15 @@ import android.webkit.WebView; import android.widget.ProgressBar; -import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.network.InterceptingWebViewClient; +import be.ugent.zeus.hydra.databinding.ActivityWebviewBinding; /** * Displays a web view. * * @author Niko Strijbol */ -public class WebViewActivity extends BaseActivity { +public class WebViewActivity extends BaseActivity { public static final String URL = "be.ugent.zeus.hydra.url"; public static final String TITLE = "be.ugent.zeus.hydra.title"; @@ -25,13 +25,10 @@ public class WebViewActivity extends BaseActivity { @SuppressLint("SetJavaScriptEnabled") protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_webview); + setContentView(ActivityWebviewBinding::inflate); - WebView webView = findViewById(R.id.web_view); - ProgressBar progressBar = findViewById(R.id.progress_bar); - - webView.getSettings().setJavaScriptEnabled(true); - webView.setWebViewClient(new ProgressClient(progressBar, this)); + binding.webView.getSettings().setJavaScriptEnabled(true); + binding.webView.setWebViewClient(new ProgressClient(binding.progressBar.progressBar, this)); Intent intent = getIntent(); String url = intent.getStringExtra(URL); @@ -41,7 +38,7 @@ protected void onCreate(Bundle savedInstanceState) { getSupportActionBar().setTitle(title); } - webView.loadUrl(url); + binding.webView.loadUrl(url); } private static class ProgressClient extends InterceptingWebViewClient { diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/customtabs/ActivityHelper.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/customtabs/ActivityHelper.java index cde27e245..445fb9adc 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/customtabs/ActivityHelper.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/customtabs/ActivityHelper.java @@ -3,8 +3,6 @@ import android.app.Activity; import android.net.Uri; import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.browser.customtabs.CustomTabsCallback; import java.util.List; 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 f98d4f964..7fdaaa738 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 @@ -90,7 +90,7 @@ public boolean onQueryTextChange(String newText) { } this.isSearching = true; List filtered = StreamSupport.stream(allData) - .filter(s -> searchPredicate.test(s, newText.toLowerCase())) + .filter(s -> searchPredicate.test(s, newText.toLowerCase(Locale.getDefault()))) .collect(Collectors.toList()); filtered = filter.apply(filtered); super.submitData(filtered); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/viewholders/DateHeaderViewHolder.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/viewholders/DateHeaderViewHolder.java index f4eb6c157..185a08a7b 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/viewholders/DateHeaderViewHolder.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/recyclerview/viewholders/DateHeaderViewHolder.java @@ -1,11 +1,15 @@ package be.ugent.zeus.hydra.common.ui.recyclerview.viewholders; import android.content.Context; +import android.text.TextUtils; import android.view.View; import android.widget.TextView; +import java.util.Locale; + import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.utils.DateUtils; +import be.ugent.zeus.hydra.common.utils.StringUtils; import org.threeten.bp.LocalDate; import org.threeten.bp.OffsetDateTime; import org.threeten.bp.format.FormatStyle; @@ -31,7 +35,7 @@ public void populate(OffsetDateTime date) { public static String format(Context context, LocalDate localDate) { String date = DateUtils.getFriendlyDate(context, localDate, FormatStyle.LONG); - date = date.substring(0, 1).toUpperCase() + date.substring(1); + date = StringUtils.capitaliseFirst(date); return date; } } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/ButtonBarLayout.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/ButtonBarLayout.java index db81208f3..96c8886f5 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/ButtonBarLayout.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/ButtonBarLayout.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2020 Niko Strijbol * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +16,6 @@ */ package be.ugent.zeus.hydra.common.ui.widgets; -import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; @@ -133,7 +133,6 @@ public int getMinimumHeight() { return Math.max(mMinimumHeight, super.getMinimumHeight()); } - @SuppressLint("RtlHardcoded") private void setStacked(boolean stacked) { setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL); setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM); diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/DisplayableMenu.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/DisplayableMenu.java index d5bc6bc31..6422a0079 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/DisplayableMenu.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/DisplayableMenu.java @@ -1,15 +1,14 @@ package be.ugent.zeus.hydra.common.ui.widgets; import android.content.Context; -import androidx.annotation.DrawableRes; -import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.content.res.TypedArrayUtils; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TableRow; import android.widget.TextView; +import androidx.annotation.DrawableRes; +import androidx.appcompat.content.res.AppCompatResources; import java.util.List; @@ -40,7 +39,7 @@ public class DisplayableMenu { DisplayableMenu(Context context, RestoMenu menu, boolean selectable) { this.menu = menu; this.selectable = selectable; - normalStyle = TypedArrayUtils.getAttr(context, R.attr.textAppearanceBody2, 0); + normalStyle = ViewUtils.getAttr(context, R.attr.textAppearanceBody2); } /** diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/MenuTable.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/MenuTable.java index af81a7bde..921aad16a 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/MenuTable.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/MenuTable.java @@ -7,17 +7,16 @@ import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; - import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import androidx.core.content.res.TypedArrayUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.ui.html.Utils; +import be.ugent.zeus.hydra.common.utils.ViewUtils; import be.ugent.zeus.hydra.resto.RestoMenu; import com.google.android.material.textview.MaterialTextView; @@ -78,7 +77,7 @@ private void init(Context context, @Nullable AttributeSet attrs) { selectable = a.getBoolean(R.styleable.MenuTable_selectable, false); showTitles = a.getBoolean(R.styleable.MenuTable_showTitles, false); messagePaddingTop = a.getBoolean(R.styleable.MenuTable_messagePaddingTop, false); - normalStyle = TypedArrayUtils.getAttr(context, R.attr.textAppearanceBody2, 0); + normalStyle = ViewUtils.getAttr(context, R.attr.textAppearanceBody2); } finally { a.recycle(); } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/TimePreference.java b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/TimePreference.java index e26210e2e..99459533a 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/TimePreference.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/ui/widgets/TimePreference.java @@ -5,13 +5,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.content.res.TypedArrayUtils; import androidx.preference.DialogPreference; import be.ugent.zeus.hydra.R; +import be.ugent.zeus.hydra.common.utils.ViewUtils; import org.threeten.bp.LocalTime; /** @@ -35,7 +34,7 @@ public TimePreference(Context context, AttributeSet attrs, int defStyleAttr, int TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TimePreference, defStyleAttr, defStyleRes); - if (TypedArrayUtils.getBoolean(a, R.styleable.TimePreference_useDefaultSummary, + if (ViewUtils.getBoolean(a, R.styleable.TimePreference_useDefaultSummary, R.styleable.TimePreference_useDefaultSummary, false)) { setSummaryProvider(new TimeSummaryProvider()); } @@ -48,7 +47,7 @@ public TimePreference(Context context, AttributeSet attrs, int defStyleAttr) { } public TimePreference(Context context, AttributeSet attrs) { - this(context, attrs, TypedArrayUtils.getAttr(context, + this(context, attrs, ViewUtils.getAttr(context, androidx.preference.R.attr.dialogPreferenceStyle, android.R.attr.dialogPreferenceStyle)); } 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 62e66cade..8c068b56f 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 @@ -1,9 +1,7 @@ package be.ugent.zeus.hydra.common.utils; import android.content.Context; -import android.content.res.Resources; import android.content.res.TypedArray; -import android.util.TypedValue; import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; import androidx.core.graphics.ColorUtils; diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/utils/FragmentUtils.java b/app/src/main/java/be/ugent/zeus/hydra/common/utils/FragmentUtils.java index c468458e4..1d16ca40a 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/utils/FragmentUtils.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/utils/FragmentUtils.java @@ -13,10 +13,10 @@ public class FragmentUtils { @NonNull - public static BaseActivity requireBaseActivity(Fragment fragment) { + public static BaseActivity requireBaseActivity(Fragment fragment) { Activity activity = fragment.requireActivity(); if (activity instanceof BaseActivity) { - return (BaseActivity) activity; + return (BaseActivity) activity; } else { throw new IllegalStateException("This method can only be used if the Fragment is attached to a BaseActivity."); } 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 210821c4e..8c372f239 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 @@ -2,6 +2,8 @@ import androidx.annotation.NonNull; +import java.util.Locale; + /** * @author Niko Strijbol */ @@ -15,6 +17,6 @@ public class StringUtils { * @return Capitalised string. */ public static String capitaliseFirst(@NonNull String s) { - return s.substring(0, 1).toUpperCase() + s.substring(1); + return s.substring(0, 1).toUpperCase(Locale.getDefault()) + s.substring(1); } } diff --git a/app/src/main/java/be/ugent/zeus/hydra/common/utils/ViewUtils.java b/app/src/main/java/be/ugent/zeus/hydra/common/utils/ViewUtils.java index 6aa53af92..8ee134aba 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/common/utils/ViewUtils.java +++ b/app/src/main/java/be/ugent/zeus/hydra/common/utils/ViewUtils.java @@ -2,8 +2,10 @@ import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,8 +13,6 @@ import androidx.appcompat.content.res.AppCompatResources; import androidx.core.graphics.drawable.DrawableCompat; -import be.ugent.zeus.hydra.common.utils.ColourUtils; - /** * @author Niko Strijbol */ @@ -91,4 +91,30 @@ public static View inflate(ViewGroup parent, @LayoutRes int resource) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); return inflater.inflate(resource, parent, false); } + + /** + * @return The resource ID value in the {@code context} specified by {@code attr}. + */ + public static int getAttr(@NonNull Context context, int attr) { + return getAttr(context, attr, 0); + } + + public static int getAttr(@NonNull Context context, int attr, int defaultAttr) { + TypedValue value = new TypedValue(); + context.getTheme().resolveAttribute(attr, value, true); + if (value.resourceId != 0) { + return value.resourceId; + } + return defaultAttr; + } + + /** + * @return a boolean value of {@code index}. If it does not exist, a boolean value of + * {@code fallbackIndex}. If it still does not exist, {@code defaultValue}. + */ + public static boolean getBoolean(@NonNull TypedArray a, @StyleableRes int index, + @StyleableRes int fallbackIndex, boolean defaultValue) { + boolean val = a.getBoolean(fallbackIndex, defaultValue); + return a.getBoolean(index, val); + } } 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 40b594d13..326360f5e 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 @@ -1,6 +1,7 @@ package be.ugent.zeus.hydra.feed; import android.app.Application; +import android.os.AsyncTask; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; @@ -13,7 +14,6 @@ import be.ugent.zeus.hydra.feed.cards.Card; import be.ugent.zeus.hydra.feed.commands.CommandResult; import be.ugent.zeus.hydra.feed.commands.FeedCommand; -import io.fabric.sdk.android.services.concurrency.AsyncTask; /** * @author Niko Strijbol 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 0bd525875..71baf81cb 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 @@ -7,13 +7,13 @@ import android.widget.LinearLayout; import android.widget.TableRow; import android.widget.TextView; -import androidx.core.content.res.TypedArrayUtils; import java9.util.Optional; import be.ugent.zeus.hydra.MainActivity; import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.request.Result; +import be.ugent.zeus.hydra.common.utils.ViewUtils; import be.ugent.zeus.hydra.feed.HomeFeedAdapter; import be.ugent.zeus.hydra.feed.cards.Card; import be.ugent.zeus.hydra.feed.cards.CardViewHolder; @@ -38,8 +38,8 @@ public LibraryViewHolder(View itemView, HomeFeedAdapter adapter) { list = itemView.findViewById(R.id.library_list); Context c = itemView.getContext(); - styleName = TypedArrayUtils.getAttr(c, R.attr.textAppearanceBody2, 0); - styleHours = TypedArrayUtils.getAttr(c, R.attr.textAppearanceCaption, 0); + styleName = ViewUtils.getAttr(c, R.attr.textAppearanceBody2); + styleHours = ViewUtils.getAttr(c, R.attr.textAppearanceCaption); rowPadding = c.getResources().getDimensionPixelSize(R.dimen.material_baseline_grid_1x); itemView.setOnClickListener(v -> { diff --git a/app/src/main/java/be/ugent/zeus/hydra/feed/preferences/HomeFeedPrefActivity.java b/app/src/main/java/be/ugent/zeus/hydra/feed/preferences/HomeFeedPrefActivity.java index cfe1969c0..ab450780a 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/feed/preferences/HomeFeedPrefActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/feed/preferences/HomeFeedPrefActivity.java @@ -5,8 +5,8 @@ import android.os.Parcelable; import androidx.annotation.Nullable; -import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.ui.BaseActivity; +import be.ugent.zeus.hydra.databinding.ActivityPreferencesHomefeedBinding; import be.ugent.zeus.hydra.preferences.PreferenceActivity; import be.ugent.zeus.hydra.preferences.PreferenceEntry; @@ -15,12 +15,12 @@ * * @author Niko Strijbol */ -public class HomeFeedPrefActivity extends BaseActivity { +public class HomeFeedPrefActivity extends BaseActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_preferences_homefeed); + setContentView(ActivityPreferencesHomefeedBinding::inflate); } @Override 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 7e5b5b71c..e5065e4a6 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 @@ -3,9 +3,14 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.StringDef; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.preference.PreferenceManager; import java.lang.annotation.Retention; @@ -32,8 +37,14 @@ public class HomeFragment extends PreferenceFragment { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setPreferencesFromResource(R.xml.pref_home_feed, rootKey); - viewModel = ViewModelProviders.of(this).get(DeleteViewModel.class); - viewModel.getLiveData().observe(this, new EventObserver() { + + } + + @Override + 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() { @Override protected void onUnhandled(Context data) { Toast.makeText(data, R.string.feed_pref_hidden_cleared, Toast.LENGTH_SHORT).show(); @@ -44,6 +55,7 @@ protected void onUnhandled(Context data) { viewModel.deleteAll(); return true; }); + return v; } private static final String PREF_RESTO_KINDS = "pref_feed_resto_kinds"; 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 9476783a3..86dedb2e6 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 @@ -5,10 +5,11 @@ import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.ui.BaseActivity; +import be.ugent.zeus.hydra.databinding.ActivityInfoSubItemBinding; import java.util.ArrayList; -public class InfoSubItemActivity extends BaseActivity { +public class InfoSubItemActivity extends BaseActivity { public static final String INFO_TITLE = "be.ugent.zeus.hydra.infoTitle"; public static final String INFO_ITEMS = "be.ugent.zeus.hydra.infoItems"; @@ -16,7 +17,7 @@ public class InfoSubItemActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_info_sub_item); + setContentView(ActivityInfoSubItemBinding::inflate); // Display the fragment as the main content. InfoFragment fragment = new InfoFragment(); 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 cf5b8303d..2731d4ed1 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 @@ -12,7 +12,9 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.*; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; @@ -34,14 +36,14 @@ import be.ugent.zeus.hydra.common.reporting.Event; import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.ui.BaseActivity; -import be.ugent.zeus.hydra.common.utils.ViewUtils; 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.databinding.ActivityLibraryDetailsBinding; import be.ugent.zeus.hydra.library.Library; import be.ugent.zeus.hydra.library.favourites.FavouritesRepository; import be.ugent.zeus.hydra.library.favourites.LibraryFavourite; -import be.ugent.zeus.hydra.common.utils.DateUtils; -import be.ugent.zeus.hydra.common.utils.NetworkUtils; -import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.snackbar.Snackbar; import com.squareup.picasso.Picasso; import net.cachapa.expandablelayout.ExpandableLayout; @@ -51,16 +53,13 @@ * * @author Niko Strijbol */ -public class LibraryDetailActivity extends BaseActivity { +public class LibraryDetailActivity extends BaseActivity { public static final String ARG_LIBRARY = "argLibrary"; private static final String TAG = "LibraryDetailActivity"; private Library library; - private Button button; - private Button expandButton; - private FrameLayout layout; public static void launchActivity(Context context, Library library) { Intent intent = new Intent(context, LibraryDetailActivity.class); @@ -71,90 +70,79 @@ public static void launchActivity(Context context, Library library) { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_library_details); + setContentView(ActivityLibraryDetailsBinding::inflate); library = getIntent().getParcelableExtra(ARG_LIBRARY); - layout = findViewById(R.id.frame_layout); - ImageView header = findViewById(R.id.header_image); - Picasso.get().load(library.getHeaderImage(this)).into(header); + Picasso.get().load(library.getHeaderImage(this)).into(binding.headerImage); - CollapsingToolbarLayout collapsingToolbarLayout = findViewById(R.id.collapsing_toolbar); - collapsingToolbarLayout.setTitle(library.getName()); + binding.collapsingToolbar.setTitle(library.getName()); // TODO: why is this necessary? int white = ContextCompat.getColor(this, R.color.white); - collapsingToolbarLayout.setExpandedTitleColor(white); - collapsingToolbarLayout.setCollapsedTitleTextColor(white); + binding.collapsingToolbar.setExpandedTitleColor(white); + binding.collapsingToolbar.setCollapsedTitleTextColor(white); String address = makeFullAddressText(); if (TextUtils.isEmpty(address)) { - findViewById(R.id.library_address_card).setVisibility(View.GONE); + binding.libraryAddressCard.setVisibility(View.GONE); } else { - TextView textView = findViewById(R.id.library_address); - textView.setText(makeFullAddressText()); - textView.setOnClickListener(v -> NetworkUtils.maybeLaunchIntent(LibraryDetailActivity.this, mapsIntent())); + binding.libraryAddress.setText(makeFullAddressText()); + binding.libraryAddress.setOnClickListener(v -> NetworkUtils.maybeLaunchIntent(LibraryDetailActivity.this, mapsIntent())); } final ViewModelProvider provider = new ViewModelProvider(this); - button = findViewById(R.id.library_favourite); FavouriteViewModel viewModel = provider.get(FavouriteViewModel.class); viewModel.setLibrary(library); viewModel.getData().observe(this, isFavourite -> { Drawable drawable; Context c = LibraryDetailActivity.this; if (isFavourite) { - button.setSelected(true); + binding.libraryFavourite.setSelected(true); drawable = ViewUtils.getTintedVectorDrawableAttr(c, R.drawable.ic_star, R.attr.colorSecondary); } else { drawable = ViewUtils.getTintedVectorDrawableAttr(c, R.drawable.ic_star, R.attr.colorPrimary); - button.setSelected(false); + binding.libraryFavourite.setSelected(false); } - button.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); + binding.libraryFavourite.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); }); - button.setOnClickListener(v -> updateStatus(library, button.isSelected())); + binding.libraryFavourite.setOnClickListener(v -> updateStatus(library, binding.libraryFavourite.isSelected())); - ExpandableLayout layout = findViewById(R.id.expandable_layout); - expandButton = findViewById(R.id.expand_button); - expandButton.setOnClickListener(v -> layout.toggle()); + binding.expandButton.setOnClickListener(v -> binding.expandableLayout.toggle()); - layout.setOnExpansionUpdateListener((value, state) -> { + binding.expandableLayout.setOnExpansionUpdateListener((value, state) -> { if (state == ExpandableLayout.State.COLLAPSED) { - expandButton.setText(R.string.library_more); + binding.expandButton.setText(R.string.library_more); } else if (state == ExpandableLayout.State.EXPANDED) { - expandButton.setText(R.string.library_less); + binding.expandButton.setText(R.string.library_less); } }); - TextView remarks = findViewById(R.id.library_remarks); String comments = library.getCommentsAsString(); if (TextUtils.isEmpty(comments)) { - remarks.setVisibility(View.GONE); - findViewById(R.id.library_remarks_divider).setVisibility(View.GONE); - findViewById(R.id.library_remarks_title).setVisibility(View.GONE); + binding.libraryRemarks.setVisibility(View.GONE); + binding.libraryRemarksDivider.setVisibility(View.GONE); + binding.libraryRemarksTitle.setVisibility(View.GONE); } else { - remarks.setText(Utils.fromHtml(comments)); - layout.setExpanded(true, false); + binding.libraryRemarks.setText(Utils.fromHtml(comments)); + binding.expandableLayout.setExpanded(true, false); } - TextView email = findViewById(R.id.library_mail_row_text); - email.setText(library.getEmail()); - LinkifyCompat.addLinks(email, Linkify.EMAIL_ADDRESSES); - TextView phone = findViewById(R.id.library_phone_row_text); + binding.libraryMailRowText.setText(library.getEmail()); + LinkifyCompat.addLinks(binding.libraryMailRowText, Linkify.EMAIL_ADDRESSES); String phoneString = library.getPhones(); if (TextUtils.isEmpty(phoneString)) { - phone.setText(R.string.library_detail_no_phone); + binding.libraryPhoneRowText.setText(R.string.library_detail_no_phone); } else { - phone.setText(phoneString); - LinkifyCompat.addLinks(phone, Linkify.PHONE_NUMBERS); + binding.libraryPhoneRowText.setText(phoneString); + LinkifyCompat.addLinks(binding.libraryPhoneRowText, Linkify.PHONE_NUMBERS); } - TextView contact = findViewById(R.id.library_contact_row_text); - contact.setText(library.getContact()); + binding.libraryContactRowText.setText(library.getContact()); HoursViewModel model = provider.get(HoursViewModel.class); model.setLibrary(library); model.getData().observe(this, PartialErrorObserver.with(this::onError)); - model.getData().observe(this, new ProgressObserver<>(findViewById(R.id.progress_bar))); + model.getData().observe(this, new ProgressObserver<>(binding.progressBar)); model.getData().observe(this, SuccessObserver.with(this::receiveData)); Reporting.getTracker(this).log(new LibraryViewEvent(library)); @@ -175,7 +163,7 @@ private void updateStatus(Library library, boolean isSelected) { protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); // Save the toggle button state. - outState.putString("button", String.valueOf(expandButton.getText())); + outState.putString("button", String.valueOf(binding.expandButton.getText())); } @Override @@ -183,7 +171,7 @@ protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); // Restore the toggle button state. if (savedInstanceState.get("button") != null) { - expandButton.setText(savedInstanceState.getString("button")); + binding.expandButton.setText(savedInstanceState.getString("button")); } } @@ -221,7 +209,7 @@ private void receiveData(List list) { tableLayout.addView(tableRow); } - layout.addView(tableLayout); + binding.frameLayout.addView(tableLayout); } @Override diff --git a/app/src/main/java/be/ugent/zeus/hydra/library/details/OpeningHoursRequest.java b/app/src/main/java/be/ugent/zeus/hydra/library/details/OpeningHoursRequest.java index 83c87523f..ccf0b9530 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/library/details/OpeningHoursRequest.java +++ b/app/src/main/java/be/ugent/zeus/hydra/library/details/OpeningHoursRequest.java @@ -3,10 +3,7 @@ import android.content.Context; import androidx.annotation.NonNull; -import java.util.List; - import java9.util.Optional; -import java9.util.function.Function; import java9.util.stream.StreamSupport; import be.ugent.zeus.hydra.common.network.Endpoints; diff --git a/app/src/main/java/be/ugent/zeus/hydra/library/favourites/FavouritesRepository.java b/app/src/main/java/be/ugent/zeus/hydra/library/favourites/FavouritesRepository.java index 3d3d9bfbd..10fa9696a 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/library/favourites/FavouritesRepository.java +++ b/app/src/main/java/be/ugent/zeus/hydra/library/favourites/FavouritesRepository.java @@ -1,14 +1,11 @@ package be.ugent.zeus.hydra.library.favourites; -import androidx.arch.core.util.Function; import androidx.lifecycle.LiveData; import androidx.lifecycle.Transformations; import androidx.room.*; import java.util.List; -import java.util.Set; -import be.ugent.zeus.hydra.common.arch.data.BaseLiveData; import be.ugent.zeus.hydra.library.Library; /** 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 1d3b99697..51e8282e3 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 @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java9.util.Optional; import java9.util.stream.StreamSupport; @@ -34,10 +35,10 @@ class LibraryListAdapter extends SearchableAdapter, Libra super((pair, s) -> { Library library = pair.first; boolean contained = false; - if (library.getName() != null && library.getName().toLowerCase().contains(s)) { + if (library.getName() != null && library.getName().toLowerCase(Locale.getDefault()).contains(s)) { contained = true; } - if (library.getCampus() != null && library.getCampus().toLowerCase().contains(s)) { + if (library.getCampus() != null && library.getCampus().toLowerCase(Locale.getDefault()).contains(s)) { contained = true; } return contained; diff --git a/app/src/main/java/be/ugent/zeus/hydra/onboarding/OnboardingActivity.java b/app/src/main/java/be/ugent/zeus/hydra/onboarding/OnboardingActivity.java index b9dce3365..4af744ef5 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/onboarding/OnboardingActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/onboarding/OnboardingActivity.java @@ -35,13 +35,15 @@ protected void onCreate(Bundle savedInstanceState) { .backgroundDark(R.color.hydra_color_primary) .build()); - // Check for permission for data collection - addSlide(new FragmentSlide.Builder() - .background(R.color.hydra_color_primary) - .backgroundDark(R.color.hydra_color_primary) - .fragment(new ReportingFragment()) - .build() - ); + if (Reporting.hasReportingOptions()) { + // Check for permission for data collection + addSlide(new FragmentSlide.Builder() + .background(R.color.hydra_color_primary) + .backgroundDark(R.color.hydra_color_primary) + .fragment(new ReportingFragment()) + .build() + ); + } // Home feed selector addSlide(new FragmentSlide.Builder() diff --git a/app/src/main/java/be/ugent/zeus/hydra/onboarding/ReportingFragment.java b/app/src/main/java/be/ugent/zeus/hydra/onboarding/ReportingFragment.java index 51f6b0833..c573d724a 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/onboarding/ReportingFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/onboarding/ReportingFragment.java @@ -12,7 +12,7 @@ import be.ugent.zeus.hydra.BuildConfig; import be.ugent.zeus.hydra.R; -import be.ugent.zeus.hydra.common.reporting.Reporting; +import be.ugent.zeus.hydra.common.reporting.Manager; import be.ugent.zeus.hydra.common.ui.customtabs.CustomTabsHelper; import com.google.android.material.radiobutton.MaterialRadioButton; import com.google.android.material.switchmaterial.SwitchMaterial; @@ -67,9 +67,9 @@ public void onPause() { super.onPause(); // Save settings if present. if (canGoForward()) { - Reporting.saveAnalyticsPermission(requireContext(), allowsAnalytics()); - Reporting.saveCrashReportingPermission(requireContext(), allowsCrashReporting()); - Reporting.syncPermissions(requireContext()); + Manager.saveAnalyticsPermission(requireContext(), allowsAnalytics()); + Manager.saveCrashReportingPermission(requireContext(), allowsCrashReporting()); + Manager.syncPermissions(requireContext()); } } diff --git a/app/src/main/java/be/ugent/zeus/hydra/preferences/AboutFragment.java b/app/src/main/java/be/ugent/zeus/hydra/preferences/AboutFragment.java index 2eb7b7d3e..6830574f2 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/preferences/AboutFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/preferences/AboutFragment.java @@ -3,7 +3,6 @@ import android.content.Intent; import android.os.Build; import android.os.Bundle; - import androidx.appcompat.content.res.AppCompatResources; import androidx.preference.Preference; @@ -11,7 +10,7 @@ import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.ui.PreferenceFragment; -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity; +import be.ugent.zeus.hydra.common.ui.WebViewActivity; /** * @author Niko Strijbol @@ -32,8 +31,10 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { )); requirePreference("pref_about_licenses").setOnPreferenceClickListener(preference -> { - OssLicensesMenuActivity.setActivityTitle(getString(R.string.pref_licenses_title)); - startActivity(new Intent(getActivity(), OssLicensesMenuActivity.class)); + Intent intent = new Intent(requireActivity(), WebViewActivity.class); + intent.putExtra(WebViewActivity.TITLE, getString(R.string.pref_licenses_title)); + intent.putExtra(WebViewActivity.URL, "file:///android_res/raw/third_party_licenses.html"); + startActivity(intent); return false; }); diff --git a/app/src/main/java/be/ugent/zeus/hydra/preferences/OverviewFragment.java b/app/src/main/java/be/ugent/zeus/hydra/preferences/OverviewFragment.java index 717636627..0a700fa9b 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/preferences/OverviewFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/preferences/OverviewFragment.java @@ -8,6 +8,7 @@ import androidx.preference.PreferenceScreen; import be.ugent.zeus.hydra.R; +import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.utils.ViewUtils; import be.ugent.zeus.hydra.common.utils.ColourUtils; @@ -24,6 +25,12 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(context); for (PreferenceEntry entry : PreferenceEntry.values()) { + + // Skip data reporting if there is no data reporting. + if (entry == PreferenceEntry.DATA && !Reporting.hasReportingOptions()) { + continue; + } + Preference preference = new Preference(context); preference.setTitle(entry.getName()); preference.setSummary(entry.getDescription()); @@ -38,6 +45,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { return true; }); screen.addPreference(preference); + } setPreferenceScreen(screen); } 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 f2b36fd9e..c009a5c30 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 @@ -11,13 +11,14 @@ import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.ui.BaseActivity; +import be.ugent.zeus.hydra.databinding.ActivityPreferencesBinding; /** * Activity that will show a fragment. * * @author Niko Strijbol */ -public class PreferenceActivity extends BaseActivity { +public class PreferenceActivity extends BaseActivity { /** * Argument for the activity, indicating which fragment should be shown. @@ -40,7 +41,7 @@ public static void start(@NonNull Context context, @Nullable PreferenceEntry ent @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_preferences); + setContentView(ActivityPreferencesBinding::inflate); if (savedInstanceState != null) { // If a specific screen is requested, use that one. diff --git a/app/src/main/java/be/ugent/zeus/hydra/preferences/ReportingFragment.java b/app/src/main/java/be/ugent/zeus/hydra/preferences/ReportingFragment.java index 89659e9f6..091257e62 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/preferences/ReportingFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/preferences/ReportingFragment.java @@ -5,6 +5,7 @@ import androidx.preference.PreferenceFragmentCompat; import be.ugent.zeus.hydra.R; +import be.ugent.zeus.hydra.common.reporting.Manager; import be.ugent.zeus.hydra.common.reporting.Reporting; /** @@ -29,6 +30,6 @@ public void onResume() { @Override public void onPause() { super.onPause(); - Reporting.syncPermissions(getActivity()); + Manager.syncPermissions(getActivity()); } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/ExtraFoodActivity.java b/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/ExtraFoodActivity.java index 63d660396..7b3a4aa1f 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/ExtraFoodActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/ExtraFoodActivity.java @@ -10,28 +10,24 @@ import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.ui.BaseActivity; -import com.google.android.material.tabs.TabLayout; +import be.ugent.zeus.hydra.databinding.ActivityExtraFoodBinding; -public class ExtraFoodActivity extends BaseActivity { +public class ExtraFoodActivity extends BaseActivity { private ExtraFoodViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_extra_food); - - TabLayout tabLayout = findViewById(R.id.tab_layout); - ViewPager viewPager = findViewById(R.id.pager); + setContentView(ActivityExtraFoodBinding::inflate); ExtraFoodPagerAdapter adapter = new ExtraFoodPagerAdapter(getSupportFragmentManager(), this); - viewPager.setAdapter(adapter); - tabLayout.setupWithViewPager(viewPager); - viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + binding.pager.setAdapter(adapter); + binding.tabLayout.setupWithViewPager(binding.pager); + binding.pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { - //noinspection ConstantConditions - Reporting.getTracker(getApplicationContext()) + Reporting.getTracker(ExtraFoodActivity.this) .setCurrentScreen( ExtraFoodActivity.this, adapter.getPageTitle(position).toString(), diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/ExtraFoodPagerAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/ExtraFoodPagerAdapter.java index acfaa8b87..08d945a11 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/ExtraFoodPagerAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/extrafood/ExtraFoodPagerAdapter.java @@ -30,6 +30,7 @@ public Fragment getItem(int position) { return FoodFragment.newInstance(position); } + @NonNull @Override public CharSequence getPageTitle(int position) { @StringRes int string; 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 ae1a7b3de..d1caca73e 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 @@ -4,9 +4,10 @@ import android.os.Bundle; import android.util.Log; import android.view.View; -import android.widget.*; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.DatePicker; import androidx.annotation.NonNull; -import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.ViewModelProvider; @@ -20,17 +21,20 @@ import be.ugent.zeus.hydra.common.network.IOFailureException; import be.ugent.zeus.hydra.common.ui.BaseActivity; import be.ugent.zeus.hydra.common.ui.NoPaddingArrayAdapter; +import be.ugent.zeus.hydra.common.utils.DateUtils; +import be.ugent.zeus.hydra.databinding.ActivityRestoHistoryBinding; import be.ugent.zeus.hydra.resto.RestoChoice; import be.ugent.zeus.hydra.resto.RestoMenu; import be.ugent.zeus.hydra.resto.SingleDayFragment; import be.ugent.zeus.hydra.resto.meta.selectable.SelectableMetaViewModel; import be.ugent.zeus.hydra.resto.meta.selectable.SelectedResto; -import be.ugent.zeus.hydra.common.utils.DateUtils; import org.threeten.bp.LocalDate; import org.threeten.bp.Month; import org.threeten.bp.ZoneId; -public class HistoryActivity extends BaseActivity implements DatePickerDialog.OnDateSetListener, AdapterView.OnItemSelectedListener { +public class HistoryActivity + extends BaseActivity + implements DatePickerDialog.OnDateSetListener, AdapterView.OnItemSelectedListener { private static final String ARG_DATE = "arg_date"; @@ -39,15 +43,12 @@ public class HistoryActivity extends BaseActivity implements DatePickerDialog.On private LocalDate localDate; private RestoChoice restoChoice; private SingleDayViewModel viewModel; - private Spinner restoSpinner; - private ProgressBar restoProgressBar; private ArrayAdapter restoAdapter; - private TextView errorView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_resto_history); + setContentView(ActivityRestoHistoryBinding::inflate); if (savedInstanceState != null && savedInstanceState.containsKey(ARG_DATE)) { localDate = (LocalDate) savedInstanceState.getSerializable(ARG_DATE); @@ -57,32 +58,27 @@ protected void onCreate(Bundle savedInstanceState) { localDate = LocalDate.now(); } - Toolbar bottomToolbar = findViewById(R.id.bottom_toolbar); - - restoSpinner = findViewById(R.id.resto_spinner); - restoSpinner.setEnabled(false); - restoProgressBar = findViewById(R.id.resto_progress_bar); - restoAdapter = new NoPaddingArrayAdapter<>(bottomToolbar.getContext(), R.layout.x_spinner_title_resto); + binding.restoSpinner.setEnabled(false); + restoAdapter = new NoPaddingArrayAdapter<>(binding.bottomToolbar.getContext(), R.layout.x_spinner_title_resto); restoAdapter.add(new SelectedResto.Wrapper(getString(R.string.resto_spinner_loading))); restoAdapter.setDropDownViewResource(R.layout.x_simple_spinner_dropdown_item); - restoSpinner.setAdapter(restoAdapter); + binding.restoSpinner.setAdapter(restoAdapter); final ViewModelProvider provider = new ViewModelProvider(this); - errorView = findViewById(R.id.error_view); viewModel = provider.get(SingleDayViewModel.class); viewModel.changeDate(localDate); // Set the initial date viewModel.getData().observe(this, new SuccessObserver() { @Override protected void onSuccess(@NonNull RestoMenu data) { // Add the fragment - errorView.setVisibility(View.GONE); + binding.errorView.setVisibility(View.GONE); setTitle(getString(R.string.resto_history_title, DateUtils.getFriendlyDate(HistoryActivity.this, data.getDate()))); showFragment(data); } }); viewModel.getData().observe(this, PartialErrorObserver.with(this::onError)); - viewModel.getData().observe(this, new ProgressObserver<>(findViewById(R.id.progress_bar))); + viewModel.getData().observe(this, new ProgressObserver<>(binding.progressBar)); SelectableMetaViewModel metaViewModel = provider.get(SelectableMetaViewModel.class); metaViewModel.getData().observe(this, SuccessObserver.with(this::onReceiveRestos)); @@ -112,11 +108,11 @@ private void onError(Throwable throwable) { Log.w(TAG, "Error", throwable); hideFragment(); setTitle(R.string.resto_history_error); - errorView.setVisibility(View.VISIBLE); + binding.errorView.setVisibility(View.VISIBLE); if (throwable instanceof IOFailureException) { - errorView.setText(R.string.error_network); + binding.errorView.setText(R.string.error_network); } else { - errorView.setText(getString(R.string.resto_history_not_found, DateUtils.getFriendlyDate(this, localDate))); + binding.errorView.setText(getString(R.string.resto_history_not_found, DateUtils.getFriendlyDate(this, localDate))); } } @@ -129,10 +125,10 @@ private void onReceiveRestos(List choices) { List wrappers = selectedResto.getAsWrappers(); restoAdapter.clear(); restoAdapter.addAll(wrappers); - restoSpinner.setSelection(selectedResto.getSelectedIndex(), false); - restoSpinner.setEnabled(true); - restoProgressBar.setVisibility(View.GONE); - restoSpinner.setOnItemSelectedListener(this); + binding.restoSpinner.setSelection(selectedResto.getSelectedIndex(), false); + binding.restoSpinner.setEnabled(true); + binding.restoProgressBar.setVisibility(View.GONE); + binding.restoSpinner.setOnItemSelectedListener(this); } private DatePickerDialog createAndSetupDialog() { diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/menu/RestoFragment.java b/app/src/main/java/be/ugent/zeus/hydra/resto/menu/RestoFragment.java index bd49f472d..3d1e2b53e 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/menu/RestoFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/menu/RestoFragment.java @@ -21,8 +21,8 @@ import be.ugent.zeus.hydra.common.arch.observers.ErrorObserver; import be.ugent.zeus.hydra.common.arch.observers.ProgressObserver; import be.ugent.zeus.hydra.common.arch.observers.SuccessObserver; -import be.ugent.zeus.hydra.common.ui.BaseActivity; import be.ugent.zeus.hydra.common.ui.NoPaddingArrayAdapter; +import be.ugent.zeus.hydra.common.utils.NetworkUtils; import be.ugent.zeus.hydra.resto.RestoChoice; import be.ugent.zeus.hydra.resto.RestoMenu; import be.ugent.zeus.hydra.resto.RestoPreferenceFragment; @@ -32,7 +32,6 @@ import be.ugent.zeus.hydra.resto.meta.selectable.SelectableMetaViewModel; import be.ugent.zeus.hydra.resto.meta.selectable.SelectedResto; import be.ugent.zeus.hydra.resto.sandwich.SandwichActivity; -import be.ugent.zeus.hydra.common.utils.NetworkUtils; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.snackbar.Snackbar; @@ -113,7 +112,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat super.onViewCreated(view, savedInstanceState); Log.d(TAG, "receiveResto: on view created"); - getBaseActivity().requireToolbar().setDisplayShowTitleEnabled(false); + requireBaseActivity(this).requireToolbar().setDisplayShowTitleEnabled(false); // Create the adapter that will return a fragment for each of the three // primary sections of the activity. @@ -147,7 +146,7 @@ public void onPageSelected(int position) { spinner = requireActivity().findViewById(R.id.spinner); spinner.setEnabled(false); spinner.setVisibility(View.VISIBLE); - restoAdapter = new NoPaddingArrayAdapter<>(getBaseActivity().requireToolbar().getThemedContext(), R.layout.x_spinner_title_main); + restoAdapter = new NoPaddingArrayAdapter<>(requireBaseActivity(this).requireToolbar().getThemedContext(), R.layout.x_spinner_title_main); restoAdapter.add(new SelectedResto.Wrapper(getString(R.string.resto_spinner_loading))); restoAdapter.setDropDownViewResource(R.layout.x_simple_spinner_dropdown_item); spinner.setAdapter(restoAdapter); @@ -327,7 +326,7 @@ public void onDestroyView() { tabLayout.setVisibility(View.GONE); spinner.setVisibility(View.GONE); spinnerProgress.setVisibility(View.GONE); - getBaseActivity().requireToolbar().setDisplayShowTitleEnabled(true); + requireBaseActivity(this).requireToolbar().setDisplayShowTitleEnabled(true); spinner.setOnItemSelectedListener(null); } @@ -335,8 +334,4 @@ private void hideExternalViews() { bottomNavigation.setVisibility(View.GONE); bottomNavigation.setOnNavigationItemSelectedListener(null); } - - private BaseActivity getBaseActivity() { - return (BaseActivity) getActivity(); - } } diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/SandwichActivity.java b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/SandwichActivity.java index 43f489a1f..565d8637f 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/SandwichActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/SandwichActivity.java @@ -8,32 +8,28 @@ import be.ugent.zeus.hydra.R; import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.ui.BaseActivity; -import be.ugent.zeus.hydra.resto.extrafood.FoodFragment; import be.ugent.zeus.hydra.common.utils.NetworkUtils; -import com.google.android.material.tabs.TabLayout; +import be.ugent.zeus.hydra.databinding.ActivityExtraFoodBinding; +import be.ugent.zeus.hydra.resto.extrafood.FoodFragment; /** * Activity for the sandwiches. */ -public class SandwichActivity extends BaseActivity { +public class SandwichActivity extends BaseActivity{ public static final String URL = "https://www.ugent.be/student/nl/meer-dan-studeren/resto/broodjes"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_extra_food); - - TabLayout tabLayout = findViewById(R.id.tab_layout); - ViewPager viewPager = findViewById(R.id.pager); + setContentView(ActivityExtraFoodBinding::inflate); SandwichPagerAdapter adapter = new SandwichPagerAdapter(getSupportFragmentManager(), this); - viewPager.setAdapter(adapter); - tabLayout.setupWithViewPager(viewPager); - viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + binding.pager.setAdapter(adapter); + binding.tabLayout.setupWithViewPager(binding.pager); + binding.pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { - //noinspection ConstantConditions Reporting.getTracker(getApplicationContext()) .setCurrentScreen( SandwichActivity.this, diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/SandwichPagerAdapter.java b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/SandwichPagerAdapter.java index e48976aa7..7dd7e338e 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/SandwichPagerAdapter.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/SandwichPagerAdapter.java @@ -40,6 +40,7 @@ public Fragment getItem(int position) { } @Override + @NonNull public CharSequence getPageTitle(int position) { @StringRes int string; switch (position) { diff --git a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/ecological/EcologicalFragment.java b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/ecological/EcologicalFragment.java index de228577f..72d662ab9 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/ecological/EcologicalFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/resto/sandwich/ecological/EcologicalFragment.java @@ -3,11 +3,10 @@ import android.os.Bundle; import android.util.Log; import android.view.*; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -72,11 +71,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat SwipeRefreshLayout swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout); swipeRefreshLayout.setColorSchemeColors(ColourUtils.resolveColour(requireContext(), R.attr.colorSecondary)); - viewModel = ViewModelProviders.of(this).get(EcologicalViewModel.class); - viewModel.getData().observe(this, PartialErrorObserver.with(this::onError)); - viewModel.getData().observe(this, new ProgressObserver<>(view.findViewById(R.id.progress_bar))); - viewModel.getData().observe(this, new AdapterObserver<>(adapter)); - viewModel.getRefreshing().observe(this, swipeRefreshLayout::setRefreshing); + viewModel = new ViewModelProvider(this).get(EcologicalViewModel.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 AdapterObserver<>(adapter)); + viewModel.getRefreshing().observe(getViewLifecycleOwner(), swipeRefreshLayout::setRefreshing); swipeRefreshLayout.setOnRefreshListener(viewModel); } diff --git a/app/src/main/java/be/ugent/zeus/hydra/schamper/SchamperFragment.java b/app/src/main/java/be/ugent/zeus/hydra/schamper/SchamperFragment.java index 45751bd93..7fb82d48d 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/schamper/SchamperFragment.java +++ b/app/src/main/java/be/ugent/zeus/hydra/schamper/SchamperFragment.java @@ -74,7 +74,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_refresh, menu); - BaseActivity activity = requireBaseActivity(this); + BaseActivity activity = requireBaseActivity(this); activity.tintToolbarIcons(menu, R.id.action_refresh); } diff --git a/app/src/main/java/be/ugent/zeus/hydra/sko/ArtistDetailsActivity.java b/app/src/main/java/be/ugent/zeus/hydra/sko/ArtistDetailsActivity.java index 10db75ed4..8e077f749 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/sko/ArtistDetailsActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/sko/ArtistDetailsActivity.java @@ -8,9 +8,7 @@ import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; -import android.widget.ImageView; -import android.widget.TextView; - +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import be.ugent.zeus.hydra.R; @@ -19,6 +17,7 @@ import be.ugent.zeus.hydra.common.reporting.Reporting; import be.ugent.zeus.hydra.common.ui.BaseActivity; import be.ugent.zeus.hydra.common.utils.NetworkUtils; +import be.ugent.zeus.hydra.databinding.ActivitySkoArtistBinding; import com.squareup.picasso.Picasso; /** @@ -26,13 +25,14 @@ * * @author Niko Strijbol */ -public class ArtistDetailsActivity extends BaseActivity { +public class ArtistDetailsActivity extends BaseActivity { private static final String PARCEL_ARTIST = "artist"; private Artist artist; - public static Intent start(Context context, Artist artist) { + @NonNull + public static Intent start(@NonNull Context context, @NonNull Artist artist) { Intent intent = new Intent(context, ArtistDetailsActivity.class); intent.putExtra(PARCEL_ARTIST, artist); return intent; @@ -41,38 +41,34 @@ public static Intent start(Context context, Artist artist) { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_sko_artist); + setContentView(ActivitySkoArtistBinding::inflate); Intent intent = getIntent(); artist = intent.getParcelableExtra(PARCEL_ARTIST); + assert artist != null; - TextView title = findViewById(R.id.title); - TextView date = findViewById(R.id.date); - TextView content = findViewById(R.id.content); - ImageView headerImage = findViewById(R.id.header_image); - - title.setText(artist.getName()); + binding.title.setText(artist.getName()); setTitle(artist.getName()); if (artist.getImage() != null) { - Picasso.get().load(artist.getImage()).fit().centerInside().into(headerImage); + Picasso.get().load(artist.getImage()).fit().centerInside().into(binding.headerImage); } - date.setText(artist.getDisplayDate(this)); + binding.date.setText(artist.getDisplayDate(this)); if (!TextUtils.isEmpty(artist.getDescription())) { - content.setText(artist.getDescription()); + binding.content.setText(artist.getDescription()); } else { - content.setText(R.string.sko_artist_no_content); + binding.content.setText(R.string.sko_artist_no_content); } - findViewById(R.id.sko_artist_search_web).setOnClickListener(view -> { + binding.skoArtistSearchWeb.setOnClickListener(view -> { Intent searchIntent = new Intent(Intent.ACTION_WEB_SEARCH); searchIntent.putExtra(SearchManager.QUERY, artist.getName()); NetworkUtils.maybeLaunchIntent(ArtistDetailsActivity.this, searchIntent); }); - findViewById(R.id.sko_artist_search_music).setOnClickListener(view -> { + binding.skoArtistSearchMusic.setOnClickListener(view -> { Intent musicIntent = new Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH); musicIntent.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE); musicIntent.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist.getName()); diff --git a/app/src/main/java/be/ugent/zeus/hydra/sko/OverviewActivity.java b/app/src/main/java/be/ugent/zeus/hydra/sko/OverviewActivity.java index 381048b02..7ff8bb7c7 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/sko/OverviewActivity.java +++ b/app/src/main/java/be/ugent/zeus/hydra/sko/OverviewActivity.java @@ -12,13 +12,14 @@ import be.ugent.zeus.hydra.common.network.Endpoints; import be.ugent.zeus.hydra.common.ui.BaseActivity; import be.ugent.zeus.hydra.common.utils.NetworkUtils; +import be.ugent.zeus.hydra.databinding.ActivitySkoMainBinding; /** * SKO overview activity. Only displays the line-up. * * @author Niko Strijbol */ -public class OverviewActivity extends BaseActivity { +public class OverviewActivity extends BaseActivity { private static final String SKO_WEBSITE = Endpoints.SKO; @@ -36,7 +37,7 @@ public static Intent start(Context context) { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_sko_main); + setContentView(ActivitySkoMainBinding::inflate); } @Override diff --git a/app/src/main/java/be/ugent/zeus/hydra/specialevent/SpecialEvent.java b/app/src/main/java/be/ugent/zeus/hydra/specialevent/SpecialEvent.java index 831406e63..cec9cfeda 100644 --- a/app/src/main/java/be/ugent/zeus/hydra/specialevent/SpecialEvent.java +++ b/app/src/main/java/be/ugent/zeus/hydra/specialevent/SpecialEvent.java @@ -92,6 +92,7 @@ public void setPriority(int priority) { this.priority = priority; } + @NonNull public OffsetDateTime getStart() { return start; } diff --git a/app/src/main/res/drawable/ic_settings_24dp.xml b/app/src/main/res/drawable/ic_settings_24dp.xml index ace746c40..7c0394bbe 100644 --- a/app/src/main/res/drawable/ic_settings_24dp.xml +++ b/app/src/main/res/drawable/ic_settings_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z" /> diff --git a/app/src/main/res/layout-sw600dp-land/activity_event_detail.xml b/app/src/main/res/layout-sw600dp-land/activity_event_detail.xml index 3beab1048..170c07f08 100644 --- a/app/src/main/res/layout-sw600dp-land/activity_event_detail.xml +++ b/app/src/main/res/layout-sw600dp-land/activity_event_detail.xml @@ -37,11 +37,11 @@ android:layout_marginRight="32dp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:tint="@color/white" app:srcCompat="@drawable/ic_event_note" tools:src="@drawable/ic_event_note" - tools:ignore="MissingPrefix,RtlHardcoded" - android:contentDescription="@string/content_desc_calendar_icon"/> + tools:ignore="MissingPrefix" + android:contentDescription="@string/content_desc_calendar_icon" + app:tint="@color/white" /> + android:layout_marginTop="@dimen/card_spacing"> + android:contentDescription="@string/content_desc_location" + app:tint="?colorPrimaryVariant" /> + android:contentDescription="@string/content_desc_time" + app:tint="?colorPrimaryVariant" /> + style="?textAppearanceBody2" /> + android:text="@string/event_detail_to" /> - + android:contentDescription="@string/content_desc_library" /> + android:contentDescription="@string/content_desc_library" /> - - - + style="?textAppearanceBody2" + android:paddingBottom="@dimen/card_text_padding_bottom" + android:focusable="true" + tools:ignore="UnusedAttribute" /> - + -