Skip to content

Commit a21112f

Browse files
committed
Add package for Android socket permission check
1 parent 5fca300 commit a21112f

35 files changed

Lines changed: 1726 additions & 0 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
## 0.1.1
2+
3+
* `AndroidLocalAreaSocket.connect` now automatically requests permission on first use.
4+
* Synchronized `AndroidLocalNetwork.requestPermission` to handle concurrent calls.
5+
6+
## 0.1.0
7+
8+
* Initial release.
9+
* Added `AndroidLocalNetwork` to check and request `ACCESS_LOCAL_NETWORK` permission.
10+
* Added `AndroidLocalAreaSocket` wrapper for `Socket.connect`.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# android_local_network
2+
3+
A Flutter package to handle the Android 17 Local Area Permission (`ACCESS_LOCAL_NETWORK`) for Dart sockets.
4+
5+
## Usage
6+
7+
Instead of using `Socket.connect` directly on Android 17+, use `AndroidLocalAreaSocket.connect`:
8+
9+
```dart
10+
import 'package:android_local_network/android_local_network.dart';
11+
12+
final socket = await AndroidLocalAreaSocket.connect('192.168.1.1', 8080);
13+
```
14+
15+
Or check and request the permission manually:
16+
17+
```dart
18+
import 'package:android_local_network/android_local_network.dart';
19+
20+
if (await AndroidLocalNetwork.requestPermission()) {
21+
// Permission granted
22+
}
23+
```
24+
25+
## Implementation Details
26+
27+
This package uses `jnigen` to interact with Android's permission system via FFI. This avoids the need for MethodChannels in the framework and provides a more direct way to handle permissions from Dart.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
group = "com.example.android_local_network"
2+
version = "1.0-SNAPSHOT"
3+
4+
plugins {
5+
id("com.android.library")
6+
}
7+
8+
android {
9+
namespace = "com.example.android_local_network"
10+
compileSdk = 35
11+
12+
defaultConfig {
13+
minSdk = 24
14+
}
15+
16+
compileOptions {
17+
sourceCompatibility = JavaVersion.VERSION_17
18+
targetCompatibility = JavaVersion.VERSION_17
19+
}
20+
}
21+
22+
dependencies {
23+
implementation("androidx.core:core:1.13.1")
24+
implementation("io.flutter:flutter_embedding_debug:1.0.0-cafac705f02f2a77cd72743c41b33a0fa97714e0")
25+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package com.example.android_local_network;
2+
3+
import android.app.Activity;
4+
import android.content.pm.PackageManager;
5+
import androidx.annotation.NonNull;
6+
import androidx.core.app.ActivityCompat;
7+
import androidx.core.content.ContextCompat;
8+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
9+
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
10+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
11+
import io.flutter.plugin.common.PluginRegistry;
12+
13+
import android.os.Build;
14+
import android.util.Log;
15+
16+
/**
17+
* AndroidLocalNetwork handles the ACCESS_LOCAL_NETWORK permission.
18+
* It can be used via jnigen to check and request permissions from Dart.
19+
*/
20+
public class AndroidLocalNetworkPlugin implements FlutterPlugin, ActivityAware, PluginRegistry.RequestPermissionsResultListener {
21+
private static final String TAG = "AndroidLocalNetwork";
22+
private static final String PERMISSION = "android.permission.ACCESS_LOCAL_NETWORK";
23+
private static final int REQUEST_CODE = 4853; // Arbitrary request code
24+
25+
private Activity activity;
26+
private PermissionCallback pendingCallback;
27+
28+
/**
29+
* Interface for permission result callbacks.
30+
*/
31+
public interface PermissionCallback {
32+
void onResult(boolean granted);
33+
}
34+
35+
private static AndroidLocalNetworkPlugin instance;
36+
37+
public static AndroidLocalNetworkPlugin getInstance() {
38+
Log.d(TAG, "getInstance called, instance is " + (instance == null ? "null" : "not null"));
39+
return instance;
40+
}
41+
42+
@Override
43+
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
44+
Log.d(TAG, "onAttachedToEngine");
45+
instance = this;
46+
}
47+
48+
@Override
49+
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
50+
Log.d(TAG, "onDetachedFromEngine");
51+
if (instance == this) {
52+
instance = null;
53+
}
54+
}
55+
56+
@Override
57+
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
58+
Log.d(TAG, "onAttachedToActivity");
59+
this.activity = binding.getActivity();
60+
binding.addRequestPermissionsResultListener(this);
61+
}
62+
63+
@Override
64+
public void onDetachedFromActivityForConfigChanges() {
65+
Log.d(TAG, "onDetachedFromActivityForConfigChanges");
66+
this.activity = null;
67+
}
68+
69+
@Override
70+
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
71+
Log.d(TAG, "onReattachedToActivityForConfigChanges");
72+
this.activity = binding.getActivity();
73+
binding.addRequestPermissionsResultListener(this);
74+
}
75+
76+
@Override
77+
public void onDetachedFromActivity() {
78+
Log.d(TAG, "onDetachedFromActivity");
79+
this.activity = null;
80+
}
81+
82+
/**
83+
* Checks if the local area permission is granted.
84+
*/
85+
public boolean checkPermission() {
86+
if (activity == null) {
87+
Log.w(TAG, "checkPermission: activity is null");
88+
return false;
89+
}
90+
try {
91+
boolean granted = ContextCompat.checkSelfPermission(activity, PERMISSION) == PackageManager.PERMISSION_GRANTED;
92+
Log.d(TAG, "checkPermission for " + PERMISSION + ": " + granted);
93+
return granted;
94+
} catch (Exception e) {
95+
Log.e(TAG, "checkPermission error", e);
96+
return false;
97+
}
98+
}
99+
100+
/**
101+
* Requests the local area permission.
102+
*/
103+
public void requestPermission(PermissionCallback callback) {
104+
Log.d(TAG, "requestPermission called. Current API: " + Build.VERSION.SDK_INT);
105+
106+
if (activity == null) {
107+
Log.w(TAG, "requestPermission: activity is null");
108+
callback.onResult(false);
109+
return;
110+
}
111+
112+
if (checkPermission()) {
113+
callback.onResult(true);
114+
return;
115+
}
116+
117+
pendingCallback = callback;
118+
Log.d(TAG, "requestPermission: requesting from OS: " + PERMISSION);
119+
ActivityCompat.requestPermissions(activity, new String[]{PERMISSION}, REQUEST_CODE);
120+
}
121+
122+
@Override
123+
public boolean onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
124+
Log.d(TAG, "onRequestPermissionsResult: requestCode=" + requestCode);
125+
if (requestCode == REQUEST_CODE) {
126+
boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
127+
Log.d(TAG, "onRequestPermissionsResult: granted=" + granted);
128+
if (pendingCallback != null) {
129+
pendingCallback.onResult(granted);
130+
pendingCallback = null;
131+
}
132+
return true;
133+
}
134+
return false;
135+
}
136+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.build/
9+
.buildlog/
10+
.history
11+
.svn/
12+
.swiftpm/
13+
migrate_working_dir/
14+
15+
# IntelliJ related
16+
*.iml
17+
*.ipr
18+
*.iws
19+
.idea/
20+
21+
# The .vscode folder contains launch configuration and tasks you configure in
22+
# VS Code which you may wish to be included in version control, so this line
23+
# is commented out by default.
24+
#.vscode/
25+
26+
# Flutter/Dart/Pub related
27+
**/doc/api/
28+
**/ios/Flutter/.last_build_id
29+
.dart_tool/
30+
.flutter-plugins-dependencies
31+
.pub-cache/
32+
.pub/
33+
/build/
34+
/coverage/
35+
36+
# Symbolication related
37+
app.*.symbols
38+
39+
# Obfuscation related
40+
app.*.map.json
41+
42+
# Android Studio will place build artifacts here
43+
/android/app/debug
44+
/android/app/profile
45+
/android/app/release
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: "aa55a185405913779b327aa3d6c6639d2487642f"
8+
channel: "master"
9+
10+
project_type: app
11+
12+
# Tracks metadata for the flutter migrate command
13+
migration:
14+
platforms:
15+
- platform: root
16+
create_revision: aa55a185405913779b327aa3d6c6639d2487642f
17+
base_revision: aa55a185405913779b327aa3d6c6639d2487642f
18+
- platform: android
19+
create_revision: aa55a185405913779b327aa3d6c6639d2487642f
20+
base_revision: aa55a185405913779b327aa3d6c6639d2487642f
21+
22+
# User provided section
23+
24+
# List of Local paths (relative to this file) that should be
25+
# ignored by the migrate tool.
26+
#
27+
# Files that are not part of the templates will be ignored by default.
28+
unmanaged_files:
29+
- 'lib/main.dart'
30+
- 'ios/Runner.xcodeproj/project.pbxproj'
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# example
2+
3+
A new Flutter project.
4+
5+
## Getting Started
6+
7+
This project is a starting point for a Flutter application.
8+
9+
A few resources to get you started if this is your first Flutter project:
10+
11+
- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
12+
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
13+
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
14+
15+
For help getting started with Flutter development, view the
16+
[online documentation](https://docs.flutter.dev/), which offers tutorials,
17+
samples, guidance on mobile development, and a full API reference.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This file configures the analyzer, which statically analyzes Dart code to
2+
# check for errors, warnings, and lints.
3+
#
4+
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5+
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6+
# invoked from the command line by running `flutter analyze`.
7+
8+
# The following line activates a set of recommended lints for Flutter apps,
9+
# packages, and plugins designed to encourage good coding practices.
10+
include: package:flutter_lints/flutter.yaml
11+
12+
linter:
13+
# The lint rules applied to this project can be customized in the
14+
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
15+
# included above or to enable additional rules. A list of all available lints
16+
# and their documentation is published at https://dart.dev/lints.
17+
#
18+
# Instead of disabling a lint rule for the entire project in the
19+
# section below, it can also be suppressed for a single line of code
20+
# or a specific dart file by using the `// ignore: name_of_lint` and
21+
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
22+
# producing the lint.
23+
rules:
24+
# avoid_print: false # Uncomment to disable the `avoid_print` rule
25+
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
26+
27+
# Additional information about this file can be found at
28+
# https://dart.dev/guides/language/analysis-options
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
gradle-wrapper.jar
2+
/.gradle
3+
/captures/
4+
/gradlew
5+
/gradlew.bat
6+
/local.properties
7+
GeneratedPluginRegistrant.java
8+
.cxx/
9+
10+
# Remember to never publicly share your keystore.
11+
# See https://flutter.dev/to/reference-keystore
12+
key.properties
13+
**/*.keystore
14+
**/*.jks
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
plugins {
2+
id("com.android.application")
3+
id("kotlin-android")
4+
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5+
id("dev.flutter.flutter-gradle-plugin")
6+
}
7+
8+
android {
9+
namespace = "com.example.example"
10+
compileSdk = 36
11+
ndkVersion = flutter.ndkVersion
12+
13+
compileOptions {
14+
sourceCompatibility = JavaVersion.VERSION_17
15+
targetCompatibility = JavaVersion.VERSION_17
16+
}
17+
18+
kotlinOptions {
19+
jvmTarget = JavaVersion.VERSION_17.toString()
20+
}
21+
22+
defaultConfig {
23+
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
24+
applicationId = "com.example.example"
25+
// You can update the following values to match your application needs.
26+
// For more information, see: https://flutter.dev/to/review-gradle-config.
27+
minSdk = 24
28+
targetSdk = 36
29+
versionCode = flutter.versionCode
30+
versionName = flutter.versionName
31+
}
32+
33+
buildTypes {
34+
release {
35+
// TODO: Add your own signing config for the release build.
36+
// Signing with the debug keys for now, so `flutter run --release` works.
37+
signingConfig = signingConfigs.getByName("debug")
38+
}
39+
}
40+
}
41+
42+
flutter {
43+
source = "../.."
44+
}

0 commit comments

Comments
 (0)