Detect Mock Location is enabled or disabled in Flutter - flutter

My question is that I am using flutter platform to develop an app for my client and I want that my developed app should be able to be detect mock location status from android phone settings so I can check whether the location is coming from gps provider or mock location app. And if mock location is enabled then my app should throw an error msg

i had the same problem and i fixed it by coding in java and implement in flutter project.
here is what i did:
1) add this to your Main_Activity in flutter project.
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import java.util.List;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.io/location";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getLocation")) {
boolean b = getMockLocation();
result.success(b);
} else {
result.notImplemented();
}
}
});
}
public static boolean isMockSettingsON(Context context) {
// returns true if mock location enabled, false if not enabled.
if (VERSION.SDK_INT >= VERSION_CODES.CUPCAKE) {
if (Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
}
return false;
}
public static boolean areThereMockPermissionApps(Context context) {
int count = 0;
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> packages =
pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : packages) {
try {
PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName,
PackageManager.GET_PERMISSIONS);
// Get Permissions
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i]
.equals("android.permission.ACCESS_MOCK_LOCATION")
&& !applicationInfo.packageName.equals(context.getPackageName())) {
count++;
}
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.e("Got exception " , e.getMessage());
}
}
if (count > 0)
return true;
return false;
}
private boolean getMockLocation() {
boolean b ;
b= areThereMockPermissionApps(MainActivity.this);
return b;
}
}
2) use it in your flutter_dart Code like this:
static const platform = const MethodChannel('samples.flutter.io/location');
bool mocklocation = false;
Future<void> _getMockLocation() async {
bool b;
try {
final bool result = await platform.invokeMethod('getLocation');
b = result;
} on PlatformException catch (e) {
b = false;
}
mocklocation = b;
}
if (mocklocation == true) {
return showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: (){},
child: AlertDialog(
title: Text('Location'),
content: Text('Your Location is fake'),
),
);
});
}
3) for more information and example:
https://flutter.dev/docs/development/platform-integration/platform-channels

Barzan's answer is very good, there's also a Flutter package named trust_location, you can find it here.
You can use it as following to check mock location:
bool isMockLocation = await TrustLocation.isMockLocation;
So, I recommend to use it.

Related

Flutter/Dart unit tests with app localization

I am trying to write unit tests in Dart/Flutter for my TextField validations. However, I have a little problem here because the tests are working, but I want to return the value with localization now.
How exactly do I implement this into the tests now?
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class ValidationConstants {
static String? notEmpty(String? value, BuildContext context) {
if (value == null || value.isEmpty) {
return AppLocalizations.of(context)!.text_field_can_not_be_empty;
}
return null;
}
}
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('text field validations', () {
test('no empty text validation', () {
const emptyText = '';
const noEmptyText = 'Hello, World!';
// BuildContext is needed here
expect(ValidationConstants.notEmpty(emptyText, [...]).runtimeType, String);
expect(ValidationConstants.notEmpty(noEmptyText, [...]) == null, true);
});
});
}
You can use like below, use setUp() method provided by flutter_test
void main() {
S? mockAppLocal;
setUp(() async {
mockAppLocal = await S.delegate.load(const Locale('en'));
});
}

Why event channel disables when an asyncTask will execute? In flutter android

I have written a native code for flutter that will download file from given url. Everything works fine and file will download and store perfectly. The problem is that when i want to retrieve downloaded percentage(download progress). I used event channel. It will work until I tap download button. After I tap download button nothing will retrieve from event channel until the download is finish. The download is a class that implement asyncTask. Even the onProgressUpdate method in asyncTask is unavailable until the download is complete. looks like the only and only one place that I can see progress and that is inside the doInBackGround method.
I see the percentage by logs. I used event.success(percentage) inside while loop to retrieve percentage but got an error that said:
Methods marked with #UiThread must be executed on the main thread
Then used a Handler with mainloop but nothing happened.
and this is where i'm stuck and don't know what to do.
I appreciate any help.
Here is the codes:
Android Side: (MainActivity)
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "bot/native_services";
private static final String STREAM = "bot/native_services/stream";
private Download download = new Download();
static int percentage = 0;
static boolean dlStart = false;
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
EventChannel.StreamHandler handler = new EventChannel.StreamHandler() {
#Override
public void onListen(Object arguments, EventChannel.EventSink events) {
Log.i("MyTestApp","Registered");
Handler h = new Handler(Looper.getMainLooper());
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
h.post(new Runnable() {
#Override
public void run() {
events.success(percentage);
if (percentage == 100) {
events.endOfStream();
}
}
});
}
};
Timer timer = new Timer();
timer.schedule(timerTask,0,100);
if (percentage == 100) {
timer.cancel();
}
}
#Override
public void onCancel(Object arguments) {
}
};
new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),STREAM).setStreamHandler(handler);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),CHANNEL).setMethodCallHandler((call, result) -> {
if (call.method.equals("openUrl")) {
openUrl(call.argument("url"), result);
}else if(call.method.equals("checkNetwork")) {
checkNetwork(result);
}else if(call.method.equals("downloadFromUrl")) {
downloadFromUrl(call.argument("url"), result);
}
});
}
public void downloadFromUrl(String url,MethodChannel.Result result) {
download.execute(url);
try {
HashMap<String,String> res = download.get();
Log.i("MyTestApp",res.get("status"));
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Android side: (Download class)
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import javax.net.ssl.HttpsURLConnection;
import io.flutter.plugin.common.EventChannel;
class Download extends AsyncTask<String,Integer, HashMap<String,String>>{
String path = "";
#Override
protected HashMap<String,String> doInBackground(String... strings) {
int count = 0;
try {
URL url = new URL(strings[0]);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int length = connection.getContentLength();
Log.i("MyTestApp",String.valueOf(length));
File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/apk");
if (!folder.exists()) {
boolean res = folder.mkdirs();
}
String appName = "bot.apk";
File apk = new File(folder,appName);
path = apk.getPath();
FileOutputStream fos = new FileOutputStream(apk);
InputStream is = connection.getInputStream();
byte[] buffer = new byte[1024];
long totalReaded = 0l;
while ((count = is.read(buffer)) != -1) {
fos.write(buffer, 0, count);
totalReaded += count;
MainActivity.percentage = (int) (totalReaded * 100) / length;
Log.i("Value",String.valueOf( MainActivity.percentage));
}
fos.flush();
fos.close();
is.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
HashMap<String,String> result = new <String,String>HashMap();
result.put("status","OK");
result.put("path",path);
return result;
}
#Override
protected void onPostExecute(HashMap<String,String> s) {
super.onPostExecute(s);
}
}
Flutter side:
class CheckForUpdatesState extends State<CheckForUpdates> {
static const streamChannel =
EventChannel('bot/native_services/stream');
Stream<int> percentage = Stream.empty();
checkUpdates() async {
String url = 'App link';
Map data = await NativeService.downloadFromUrl(url);
}
Stream<int> getPercentage() {
percentage = streamChannel.receiveBroadcastStream().map<int>((event) {
print(event);
return event;
});
return percentage;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Checking for updates'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Looking for latest version'),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('Version 1.0.0')],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: checkUpdates,
child: Text(
'Check for updates',
style: TextStyle(color: Colors.green),
))
],
),
StreamBuilder(
initialData: percentage,
stream: getPercentage(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print('snap shot data is: ${snapshot.data}');
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('percentage:${snapshot.data}')],
);
} else {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('percentage:${snapshot.error}')],
);
}
},
)
],
),
);
}
}

Can a Flame game object such as PositionComponent be Collidable and Draggable

Successfully implemented a Draggable component. When adding the Hitbox and Collidable mixins to the class extended by PositionComponent the drag functionality stops working.
Is it possible to have a draggable component that is also collidable?
Flutter version: 2.2.3
Flame version: 1.0.0-releasecandidate.13
main.dart
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'DraggablesGame.dart';
void main() {
runApp(
GameWidget(
game: DraggablesGame(),
),
);
}
DraggablesGame.dart
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'DraggableSquare.dart';
class DraggablesGame extends BaseGame with HasDraggableComponents, HasCollidables {
#override
Future<void> onLoad() async {
add(DraggableSquare());
add(DraggableSquare()..y = 350);
}
}
DraggableSquare.dart
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/gestures.dart';
import 'package:flutter/material.dart' show Colors;
import 'DraggablesGame.dart';
class DraggableSquare extends PositionComponent
with Draggable, HasGameRef<DraggablesGame>, Hitbox, Collidable {
#override
bool debugMode = true;
DraggableSquare({Vector2? position})
: super(
position: position ?? Vector2.all(100),
size: Vector2.all(100),
);
Vector2? dragDeltaPosition;
bool get isDragging => dragDeltaPosition != null;
#override
void update(double dt) {
super.update(dt);
debugColor = isDragging ? Colors.greenAccent : Colors.purple;
}
#override
bool onDragStart(int pointerId, DragStartInfo info) {
dragDeltaPosition = info.eventPosition.game - position;
return false;
}
#override
bool onDragUpdate(int pointerId, DragUpdateInfo event) {
final dragDeltaPosition = this.dragDeltaPosition;
if (dragDeltaPosition == null) {
return false;
}
position.setFrom(event.eventPosition.game - dragDeltaPosition);
return false;
}
#override
bool onDragEnd(int pointerId, _) {
dragDeltaPosition = null;
return false;
}
#override
bool onDragCancel(int pointerId) {
dragDeltaPosition = null;
return false;
}
}
Update based on answer
Spydon's answer suggested using addHitbox(HitboxRectangle());. This resulted in the following error:
The method 'addHitbox' isn't defined for the type 'DraggableSquare'.
Instead this modified constructor allows for both the dragging and colliding.
Updated DraggableSquare Constructor
DraggableSquare({Vector2? position})
: super(
position: position,
size: Vector2.all(100),
) {
final hitBox = HitboxRectangle();
addShape(hitBox);
}
When you add the Hitbox mixin you also have to add some hitboxes, otherwise it will not be able to know what it should count as a "hit".
The simplest solution is to add an empty hitbox of either the HitboxRectangle or HitboxCircle type. These hitbox will will the full size of the component if you don't define anything more specific in them.
So to add a HitboxRectangle, you modify your constructor to this:
DraggableSquare({Vector2? position})
: super(
position: position ?? Vector2.all(100),
size: Vector2.all(100),
) {
addShape(HitboxRectangle());
}
If you set debugMode = true you will be able to visually see the hitboxes that you add.

I get an IllegalStateException sometimes in flutter app after adding native ads to a listview

this is the error I get:
setState() called after dispose(): _PlatformViewLinkState#bc2a5(lifecycle state: defunct, not mounted)
This error happens if you...
E/flutter ( 4758): [ERROR:flutter/shell/platform/android/platform_view_android_jni_impl.cc(49)] java.lang.IllegalStateException: PlatformView#getView() returned null, but an Android view reference was expected.
I'm using the google_mobile_ads package and not the firebase_ads one since it's deprecated.
I'm trying to put native ads in a list of cards, after a fixed number of elements in the listview.
The way I did this is by using a provider in the main.dart file:
...
void main() {
WidgetsFlutterBinding.ensureInitialized();
final initFuture = MobileAds.instance.initialize();
final adState = AdState(initFuture);
runApp(Provider.value(
value: adState,
builder: (context, child) => MyApp(),
));
}
...
the screen containing the listview is called chapters_screen.dart, when going to this screen the app sometimes crashes. In this file there is a list of items List<Object> itemList = []; as a state variable. In init state the list is populated with offline data as so: itemList = List.from(chaptersData);,
this is how I add the ads in the list:
#override
void didChangeDependencies() {
super.didChangeDependencies();
final adState = Provider.of<AdState>(context);
if(this.mounted){
adState.initialization.then((status) {
if(this.mounted){
setState(() {
for (int i = itemList.length - 2; i >= 1; i -= 2) {
itemList.insert(
i,
// BannerAd(
// adUnitId: adState.bannerAdUnitId,
// size: AdSize.banner,
// request: AdRequest(),
// listener: adState.adListener,
// )..load(),
NativeAd(
adUnitId: adState.nativeAdUnitId,
factoryId: 'listTile',
request: AdRequest(),
listener: NativeAdListener(
onAdLoaded: (_) {
print('ad is loaded succesfully!');
},
onAdFailedToLoad: (ad, error) {
// Releases an ad resource when it fails to load
ad.dispose();
print('Ad load failed (code=${error.code} message=${error.message})'); },
),
)..load(),
);
}
});
}
});
}
}
I tried it before with a banner ad and there is no problem. I followed this tutorial: https://www.youtube.com/watch?v=m0d_pbgeeG8 but I had to change the google_mobile_ads package version to a newer one to be able to use the same NativeAdFactory as in the flutter documentation. The problem is in the newer version the AdListener object no longer exists so instead of passing: listener: adState.adListener like in the video. I put the listener as the code above in the 'NativeAd()' widget.
this is how I'm checking for the type of the listItem to build it in the listView:
Widget _buildListView(BuildContext context) {
List<Widget> widgetList = [];
for (int i = 0; i < itemList.length; i++) {
if (itemList[i] is NativeAd) {
widgetList.add(Container(
height: 200,
child: AdWidget(ad: itemList[i] as NativeAd),
color: Colors.black));
} else {
widgetList.add(ChapterCard(chapter: Chapter.fromJson(itemList[i])));
}
}
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/blured_background.jpg"),
fit: BoxFit.cover,
),
),
child: ListView(children: widgetList.toList()),
);
}
I think I'm doing something wrong in the provider or in the factory class but I'm not sure.
the factory class for android (in java):
import com.google.android.gms.ads.nativead.NativeAd;
import com.google.android.gms.ads.nativead.NativeAdView;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.Map;
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin;
class ListTileNativeAdFactory implements GoogleMobileAdsPlugin.NativeAdFactory {
private final Context context;
ListTileNativeAdFactory(Context context) {
this.context = context;
}
#Override
public NativeAdView createNativeAd(
NativeAd nativeAd, Map<String, Object> customOptions) {
NativeAdView nativeAdView = (NativeAdView) LayoutInflater.from(context)
.inflate(R.layout.list_tile_native_ad, null);
TextView attributionViewSmall = nativeAdView
.findViewById(R.id.tv_list_tile_native_ad_attribution_small);
TextView attributionViewLarge = nativeAdView
.findViewById(R.id.tv_list_tile_native_ad_attribution_large);
ImageView iconView = nativeAdView.findViewById(R.id.iv_list_tile_native_ad_icon);
NativeAd.Image icon = nativeAd.getIcon();
if (icon != null) {
attributionViewSmall.setVisibility(View.VISIBLE);
attributionViewLarge.setVisibility(View.INVISIBLE);
iconView.setImageDrawable(icon.getDrawable());
} else {
attributionViewSmall.setVisibility(View.INVISIBLE);
attributionViewLarge.setVisibility(View.VISIBLE);
}
nativeAdView.setIconView(iconView);
TextView headlineView = nativeAdView.findViewById(R.id.tv_list_tile_native_ad_headline);
headlineView.setText(nativeAd.getHeadline());
nativeAdView.setHeadlineView(headlineView);
TextView bodyView = nativeAdView.findViewById(R.id.tv_list_tile_native_ad_body);
bodyView.setText(nativeAd.getBody());
bodyView.setVisibility(nativeAd.getBody() != null ? View.VISIBLE : View.INVISIBLE);
nativeAdView.setBodyView(bodyView);
nativeAdView.setNativeAd(nativeAd);
return nativeAdView;
}
}
The MainActivity class:
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin;
public class MainActivity extends FlutterActivity {
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
// TODO: Register the ListTileNativeAdFactory
GoogleMobileAdsPlugin.registerNativeAdFactory(flutterEngine, "listTile",
new ListTileNativeAdFactory(getContext()));
}
#Override
public void cleanUpFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.cleanUpFlutterEngine(flutterEngine);
// TODO: Unregister the ListTileNativeAdFactory
GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "listTile");
}
}

Send data from native to flutter

Im extending the native implementation of FirebaseMessagingService to wait for push notifications in native android.
I need to start my flutter app when the user clicks the push notification, so.. How can I send data to my flutter app?
In Flutter
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget{
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ScreenPage(),
);
}
}
class ScreenPage extends StatefulWidget {
#override
_ScreenPageState createState() => _ScreenPageState();
}
class _ScreenPageState extends State<ScreenPage> {
static const platform = const MethodChannel("myChannel");
#override
void initState() {
platform.setMethodCallHandler(nativeMethodCallHandler);
super.initState();
}
Future<dynamic> nativeMethodCallHandler(MethodCall methodCall) async {
print('Native call!');
switch (methodCall.method) {
case "methodNameItz" :
return "This data from flutter.....";
break;
default:
return "Nothing";
break;
}
}
#override
Widget build(BuildContext context) {
//return ();
}
}
In Java
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodChannel;
//import io.flutter.view.FlutterNativeView;
public class MyJavaFile extends FlutterActivity {
Button clickMeButton;
MethodChannel channel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
channel = new MethodChannel(getFlutterView(), "myChannel");
setContentView(R.layout.home_activity);
clickMeButton = findViewById(R.id.clickMeButton);
clickMeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
channel.invokeMethod("methodNameItz", null, new MethodChannel.Result() {
#Override
public void success(Object o) {
Log.d("Results", o.toString());
}
#Override
public void error(String s, String s1, Object o) {
}
#Override
public void notImplemented() {
}
});
}
});
}
}
You can still basically look at the firebase messaging example (or the same for android)
Create a Platform Channel in your AppDelegate (or MainActivity on android) and the register for the same channel on the dart/flutter side (maybe main.dart and register a method like onPushClicked)
Listen for your parse messages in your native code
Send them to your dart code using the platform channel (as seen in the above linked example) channel.invokeMethod('onPushClicked', myMessageArguments)
flutter
Map<String,dynamic> data = <String,dynamic>{};
final res = await methodChannel.invokeMethod('getDistance');
data = Map<String,dynamic>.from(res);
final dis = data['distance'] as String;
final tim = data['time'] as String;
ios native
switch call.method {
case "getDistance":
let data =
["distance":routeFormattedDistance,"time":routeFormattedTravelTime]
result(data)
default :
result("")
}