flutter-geolocator icon indicating copy to clipboard operation
flutter-geolocator copied to clipboard

[Bug]: F-droid builds fail because of google service dependencies

Open Y0ngg4n opened this issue 1 year ago • 9 comments

Please check the following before submitting a new issue.

Please select affected platform(s)

  • [X] Android
  • [ ] iOS
  • [ ] Linux
  • [ ] macOS
  • [ ] Web
  • [ ] Windows

Steps to reproduce

I am trying to build this Fdroid app:

Categories:
  - Navigation
  - Internet
  - Wissenschaft / Bildung
License: GPL-3.0-only
AuthorName: Yonggan
AuthorEmail: [email protected]
AuthorWebSite: https://obco.pro
WebSite: https://kifferkarte.org
SourceCode: https://github.com/Y0ngg4n/kifferkarte
IssueTracker: https://github.com/Y0ngg4n/kifferkarte/issues
Changelog: https://github.com/Y0ngg4n/kifferkarte/commits/master
Donate: https://liberapay.com/Yonggan/
AutoName: Kifferkarte
RepoType: git
Repo: https://github.com/Y0ngg4n/kifferkarte
AutoUpdateMode: Version
UpdateCheckMode: Tags
Builds:
  - versionName: 1.0.3
    versionCode: 6
    commit: 1.0.3
    output: build/app/outputs/flutter-apk/app-release.apk
    srclibs:
      - [email protected]
    rm:
      - ios
      - linux
      - macos
      - test
      - web
      - windows
    build:
      - $$flutter$$/bin/flutter config --no-analytics
      - $$flutter$$/bin/flutter packages pub get
      - $$flutter$$/bin/flutter build apk
CurrentVersion: 1.0.3
CurrentVersionCode: 6

I have added

configurations.implementation {
   exclude group: 'com.google.android.gms'
}

https://github.com/Y0ngg4n/kifferkarte/blob/master/android/app/build.gradle and i am never calling any service methods in the code

Expected results

I would expect that all unfree classes are not contained in the apk

Actual results

2024-04-14 10:51:00,940 INFO: Successfully built version 1.0.3 of pro.obco.kifferkarte from 31a4372793877e9ef7e257a2133c104910944052
2024-04-14 10:51:00,979 DEBUG: AXML contains a RESOURCE MAP
2024-04-14 10:51:00,979 DEBUG: Start of Namespace mapping: prefix 27: 'android' --> uri 54: 'http://schemas.android.com/apk/res/android'
2024-04-14 10:51:00,979 DEBUG: Checking build/pro.obco.kifferkarte/build/app/outputs/flutter-apk/app-release.apk
2024-04-14 10:51:00,980 DEBUG: AXML contains a RESOURCE MAP
2024-04-14 10:51:00,980 DEBUG: Start of Namespace mapping: prefix 27: 'android' --> uri 54: 'http://schemas.android.com/apk/res/android'
2024-04-14 10:51:00,981 INFO: Scanning APK with dexdump for known non-free classes.
2024-04-14 10:51:01,004 DEBUG: > /opt/android-sdk/build-tools/31.0.0/dexdump /tmp/tmpzgfce7pv/classes.dex
2024-04-14 10:51:01,425 DEBUG: > /opt/android-sdk/build-tools/31.0.0/dexdump /tmp/tmpzgfce7pv/classes2.dex
2024-04-14 10:51:02,540 DEBUG: Problem: found class 'com/google/android/gms/location/LocationCallback'
2024-04-14 10:51:02,542 DEBUG: Problem: found class 'com/google/android/gms/tasks/OnSuccessListener'
2024-04-14 10:51:02,558 DEBUG: Problem: found class 'com/google/android/gms/tasks/OnFailureListener'
2024-04-14 10:51:02,561 DEBUG: Problem: found class 'com/google/android/gms/location/FusedLocationProviderClient'
2024-04-14 10:51:02,562 DEBUG: Problem: found class 'com/google/android/gms/location/LocationRequest'
2024-04-14 10:51:02,606 DEBUG: Problem: found class 'com/google/android/gms/location/LocationSettingsRequest'
2024-04-14 10:51:02,627 DEBUG: Problem: found class 'com/google/android/gms/tasks/OnCompleteListener'
2024-04-14 10:51:02,635 CRITICAL: Found 7 problems in build/pro.obco.kifferkarte/build/app/outputs/flutter-apk/app-release.apk
2024-04-14 10:51:02,635 ERROR: Could not build app pro.obco.kifferkarte: Found blocklisted packages in final apk!
2024-04-14 10:51:02,635 DEBUG: Error encountered, stopping by user request.

https://gitlab.com/yonggan/fdroiddata/-/jobs/6620611451

Code sample

Code sample
import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:kifferkarte/map.dart';
import 'package:kifferkarte/overpass.dart';
import 'package:kifferkarte/provider_manager.dart';
import 'package:latlong2/latlong.dart';
import 'package:vibration/vibration.dart';
import 'package:point_in_polygon/point_in_polygon.dart' as pip;

class LocationManager {
  final GeolocatorPlatform _geolocatorPlatform = GeolocatorPlatform.instance;
  StreamSubscription<Position>? _positionStreamSubscription;
  StreamSubscription<Position>? _updatePositionStreamSubscription;
  bool listeningToPosition = false;
  Position? lastPosition;
  bool serviceEnabled = true;
  LocationPermission permission = LocationPermission.always;

  Future<Position?> determinePosition() async {
    await checkPermissions();
    // When we reach here, permissions are granted and we can
    // continue accessing the position of the device.
    try {
      Position currentPosition = await Geolocator.getCurrentPosition(
        forceAndroidLocationManager: true,
        timeLimit: Duration(seconds: 5),
      );
      lastPosition = currentPosition;
      return lastPosition;
    } catch (Exception) {
      return lastPosition;
    }
  }

  Future<bool> checkPermissions() async {
    // // Test if location services are enabled.
    // serviceEnabled = await Geolocator.isLocationServiceEnabled();
    // if (!serviceEnabled) {
    //   // Location services are not enabled don't continue
    //   // accessing the position and request users of the
    //   // App to enable the location services.
    //   print('Location services are disabled.');
    //   Geolocator.openLocationSettings();
    // }

    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        // Permissions are denied, next time you could try
        // requesting permissions again (this is also where
        // Android's shouldShowRequestPermissionRationale
        // returned true. According to Android guidelines
        // your App should show an explanatory UI now
        print('Location permissions are denied');
        permission = await Geolocator.requestPermission();
        if (permission == LocationPermission.denied) {
          print('Location permissions are denied');
          permission = await Geolocator.requestPermission();
          return false;
        }
      }
    }

    if (permission == LocationPermission.deniedForever) {
      // Permissions are denied forever, handle appropriately.
      print(
          'Location permissions are permanently denied, we cannot request permissions.');
      return false;
    }
    return true;
  }

  Future<bool> startPositionCheck(WidgetRef ref, Function callUpdate) async {
    bool wasNull = _positionStreamSubscription == null;
    _positionStreamSubscription?.cancel();
    _updatePositionStreamSubscription?.cancel();
    if (!(await checkPermissions())) {
      print("Check permission faileds");
      return false;
    }
    LocationSettings locationSettings = LocationSettings(distanceFilter: 3);
    LocationSettings updateLocationSettings =
        LocationSettings(distanceFilter: 10);
    if (Platform.isAndroid) {
      print("Its android");
      locationSettings =
          AndroidSettings(forceLocationManager: true, distanceFilter: 10);
      updateLocationSettings =
          AndroidSettings(forceLocationManager: true, distanceFilter: 20);
    }
    var stream = _geolocatorPlatform.getPositionStream(
        locationSettings: locationSettings);
    _positionStreamSubscription = stream.listen((event) {
      print("position via stream");
      checkPositionInCircle(ref, event);
      lastPosition = event;
      ref.read(lastPositionProvider.notifier).set(event);
    });

    var updateStream = _geolocatorPlatform.getPositionStream(
        locationSettings: updateLocationSettings);

    _updatePositionStreamSubscription = updateStream.listen((event) {
      callUpdate();
    });
    listeningToPosition = true;
    if (wasNull) callUpdate();
    return true;
  }

  void stopPositionCheck(WidgetRef ref) async {
    _positionStreamSubscription?.cancel();
    listeningToPosition = false;
  }

  Future<void> checkPositionInCircle(WidgetRef ref, Position? position) async {
    if (position == null) return;
    List<Poi> pois = ref.watch(poiProvider);
    List<Way> ways = ref.watch(wayProvider);
    Distance distance = const Distance();
    bool inCircle = false;
    bool inWay = false;
    for (Poi poi in pois) {
      if (poi.poiElement.lat != null &&
          poi.poiElement.lon != null &&
          distance.as(
                  LengthUnit.Meter,
                  LatLng(position.latitude, position.longitude),
                  LatLng(poi.poiElement.lat!, poi.poiElement.lon!)) <
              radius) {
        inCircle = true;
      }
    }
    DateTime now = DateTime.now();
    if (now.hour >= 7 && now.hour < 20) {
      for (Way way in ways) {
        List<pip.Point> bounds = way.boundaries
            .map((e) => pip.Point(x: e.latitude, y: e.longitude))
            .toList();
        if (pip.Poly.isPointInPolygon(
            pip.Point(x: position.latitude, y: position.longitude), bounds)) {
          inWay = true;
        }
      }
    }
    bool currentInCircleState = ref.read(inCircleProvider);
    bool currentInWayState = ref.read(inWayProvider);
    print("currentInCirclestate $currentInCircleState");
    print("inCircle $inCircle");
    if (currentInCircleState != inCircle) {
      if (inCircle) {
        vibrate(ref);
        await Future.delayed(const Duration(seconds: 1));
        vibrate(ref);
      } else {
        vibrate(ref);
      }
      ref.read(inCircleProvider.notifier).set(inCircle);
    }
    if (currentInWayState != inWay) {
      if (inWay) {
        vibrate(ref);
        await Future.delayed(const Duration(milliseconds: 500));
        vibrate(ref);
        await Future.delayed(const Duration(milliseconds: 500));
        vibrate(ref);
      } else {
        vibrate(ref);
      }
      ref.read(inWayProvider.notifier).set(inWay);
    } else {
      print("Chek position in circle");
    }
  }

  Future<void> vibrate(WidgetRef ref) async {
    if (!ref.watch(vibrateProvider)) return;
    bool? hasVibrator = await Vibration.hasVibrator();
    if (hasVibrator != null && hasVibrator) {
      Vibration.vibrate();
    }
  }
}

Screenshots or video

Screenshots or video demonstration

[Upload media here]

Version

11.0.0

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.19.0, on NixOS 23.11 (Tapir) 6.1.79, locale de_DE.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[✓] Linux toolchain - develop for Linux desktop
[!] Android Studio (not installed)
[✓] Connected device (1 available)
[✓] Network resources

! Doctor found issues in 2 categories.

Y0ngg4n avatar Apr 14 '24 16:04 Y0ngg4n

Dear @Y0ngg4n,

Thanks for filing this issue. According to our best knowledge we don't "officially" support F-droid. Furthermore, I don't fully understand your issue. To use the geolocator the google play services should be installed on the device. Could this be an indication of the error?

Kind regards,

TimHoogstrate avatar Apr 23 '24 12:04 TimHoogstrate

@TimHoogstrate it should work without Google Play Services on android, because i use forceAndroidLocationManager always with true. And there was already a different issue that was closed because it was fixed: https://github.com/Baseflow/flutter-geolocator/issues/841 But it seems like it does not work anymore

Y0ngg4n avatar Apr 23 '24 14:04 Y0ngg4n

Dear @Y0ngg4n,

Then, can you elaborate a bit more on the subject? Your issue is a bit unclear to me.

Kind regards,

TimHoogstrate avatar Apr 24 '24 08:04 TimHoogstrate

@TimHoogstrate ok so for building apps for f-droid you have to remove all proprietary dependencies from the app. I tried that by adding the gradle exclude like described in the other issue. but it seems like there are still proprietary dependencies even if i dont use them and also exclude them in gradle like described in #841 So there should be an option to exclude all proprietary dependencies for fdroid builds. as described in #841 it should work with the gradle exclude but as you can see in the gitlab build there are still google service dependencies in the build even when i exclude them

Y0ngg4n avatar Apr 29 '24 21:04 Y0ngg4n

@TimHoogstrate is that helpfull?

Y0ngg4n avatar May 08 '24 08:05 Y0ngg4n

According to our best knowledge we don't "officially" support F-droid.

https://pub.dev/documentation/geolocator/latest/geolocator/AndroidSettings/forceLocationManager.html

To exclude Google mobile services from your app (for example because you want to publish your app to the F-Droid app store) you can add the following code to your android/app/build.gradle file:

configurations.implementation {
  exclude group: 'com.google.android.gms'
}

rusty-snake avatar Jul 12 '24 16:07 rusty-snake

@rusty-snake thats exactly what i did but fdroid builds are still failing because the google library is still there

Y0ngg4n avatar Jul 14 '24 01:07 Y0ngg4n