Skip to content

Commit 714510a

Browse files
committed
feat: get/start overlay from specific position
1 parent c4f3e23 commit 714510a

File tree

10 files changed

+220
-21
lines changed

10 files changed

+220
-21
lines changed

‎README.md‎

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
Flutter plugin for displaying your Flutter app over other apps on the screen
2525
</p>
2626

27+
---
28+
2729
## Preview
2830

29-
|TrueCaller overlay example|click-through overlay example| Messanger chat-head example |
30-
| :-:| :-: | :-: |
31+
| TrueCaller overlay example | click-through overlay example | Messanger chat-head example |
32+
| :------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------: |
3133
| <img src='https://user-images.githubusercontent.com/22800380/165636217-8957396b-dc54-4e6d-aa50-e8bfdb9383cf.gif' height='600' width='300' /> | <img src='https://user-images.githubusercontent.com/22800380/165636120-dcd9ee13-5fca-4f8a-a562-b2f53c0b5e24.gif' height='600' width='300'/> | <img src='https://user-images.githubusercontent.com/22800380/178730917-40f267bb-63a2-4ad3-ba69-f7c1285a1882.gif' height='600' width='300'/> |
3234

3335
## Installation
@@ -79,17 +81,29 @@ void overlayMain() {
7981
/// it will open the overlay settings page and return `true` once the permission granted.
8082
final bool status = await FlutterOverlayWindow.requestPermission();
8183

82-
/// Open overLay content
83-
///
84-
/// - Optional arguments:
85-
/// `height` the overlay height and default is [overlaySizeFill]
86-
/// `width` the overlay width and default is [overlaySizeFill]
87-
/// `OverlayAlignment` the alignment postion on screen and default is [OverlayAlignment.center]
88-
/// `OverlayFlag` the overlay flag and default is [OverlayFlag.defaultFlag]
89-
/// `overlayTitle` the notification message and default is "overlay activated"
90-
/// `overlayContent` the notification message
91-
/// `enableDrag` to enable/disable dragging the overlay over the screen and default is "false"
92-
/// `positionGravity` the overlay postion after drag and default is [PositionGravity.none]
84+
/// Open overLay content
85+
///
86+
/// - Optional arguments:
87+
///
88+
/// `height` the overlay height and default is [WindowSize.fullCover]
89+
///
90+
/// `width` the overlay width and default is [WindowSize.matchParent]
91+
///
92+
/// `alignment` the alignment postion on screen and default is [OverlayAlignment.center]
93+
///
94+
/// `visibilitySecret` the detail displayed in notifications on the lock screen and default is [NotificationVisibility.visibilitySecret]
95+
///
96+
/// `OverlayFlag` the overlay flag and default is [OverlayFlag.defaultFlag]
97+
///
98+
/// `overlayTitle` the notification message and default is "overlay activated"
99+
///
100+
/// `overlayContent` the notification message
101+
///
102+
/// `enableDrag` to enable/disable dragging the overlay over the screen and default is "false"
103+
///
104+
/// `positionGravity` the overlay postion after drag and default is [PositionGravity.none]
105+
///
106+
/// `startPosition` the overlay start position and default is null
93107
await FlutterOverlayWindow.showOverlay();
94108

95109
/// closes overlay if open
@@ -113,6 +127,18 @@ void overlayMain() {
113127
/// Update the overlay size in the screen
114128
await FlutterOverlayWindow.resizeOverlay(80, 120);
115129

130+
/// Update the overlay position in the screen
131+
///
132+
/// `position` the new position of the overlay
133+
///
134+
/// `return` true if the position updated successfully
135+
await FlutterOverlayWindow.moveOverlay(OverlayPosition(0, 156))
136+
137+
/// Get the current overlay position
138+
///
139+
/// `return` the current overlay position
140+
await FlutterOverlayWindow.getOverlayPosition()
141+
116142
```
117143

118144
```dart

‎android/src/main/java/flutter/overlay/window/flutter_overlay_window/FlutterOverlayWindowPlugin.java‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@
99
import android.provider.Settings;
1010
import android.service.notification.StatusBarNotification;
1111
import android.util.Log;
12+
import android.view.WindowManager;
1213

1314
import androidx.annotation.NonNull;
1415
import androidx.annotation.Nullable;
16+
import androidx.annotation.RequiresApi;
1517
import androidx.core.app.NotificationManagerCompat;
1618

19+
import java.util.Map;
20+
1721
import io.flutter.FlutterInjector;
1822
import io.flutter.embedding.engine.FlutterEngine;
1923
import io.flutter.embedding.engine.FlutterEngineCache;
@@ -55,6 +59,7 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin
5559
WindowSetup.messenger.setMessageHandler(this);
5660
}
5761

62+
@RequiresApi(api = Build.VERSION_CODES.N)
5863
@Override
5964
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
6065
pendingResult = result;
@@ -82,6 +87,10 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
8287
String notificationVisibility = call.argument("notificationVisibility");
8388
boolean enableDrag = call.argument("enableDrag");
8489
String positionGravity = call.argument("positionGravity");
90+
Map<String, Integer> startPosition = call.argument("startPosition");
91+
int startX = startPosition != null ? startPosition.getOrDefault("x", OverlayConstants.DEFAULT_XY) : OverlayConstants.DEFAULT_XY;
92+
int startY = startPosition != null ? startPosition.getOrDefault("y", OverlayConstants.DEFAULT_XY) : OverlayConstants.DEFAULT_XY;
93+
8594

8695
WindowSetup.width = width != null ? width : -1;
8796
WindowSetup.height = height != null ? height : -1;
@@ -96,11 +105,22 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
96105
final Intent intent = new Intent(context, OverlayService.class);
97106
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
98107
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
108+
intent.putExtra("startX", startX);
109+
intent.putExtra("startY", startY);
99110
context.startService(intent);
100111
result.success(null);
101112
} else if (call.method.equals("isOverlayActive")) {
102113
result.success(OverlayService.isRunning);
103114
return;
115+
} else if (call.method.equals("isOverlayActive")) {
116+
result.success(OverlayService.isRunning);
117+
return;
118+
} else if (call.method.equals("moveOverlay")) {
119+
int x = call.argument("x");
120+
int y = call.argument("y");
121+
result.success(OverlayService.moveOverlay(x, y));
122+
} else if (call.method.equals("getOverlayPosition")) {
123+
result.success(OverlayService.getCurrentPosition());
104124
} else if (call.method.equals("closeOverlay")) {
105125
if (OverlayService.isRunning) {
106126
final Intent i = new Intent(context, OverlayService.class);

‎android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayConstants.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ final public class OverlayConstants {
88
static final String MESSENGER_TAG = "x-slayer/overlay_messenger";
99
static final String CHANNEL_ID = "Overlay Channel";
1010
static final int NOTIFICATION_ID = 4579;
11+
static final int DEFAULT_XY = -6;
1112
}

‎android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayService.java‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
import com.example.flutter_overlay_window.R;
3232

33+
import java.util.HashMap;
34+
import java.util.Map;
3335
import java.util.Timer;
3436
import java.util.TimerTask;
3537

@@ -50,6 +52,8 @@ public class OverlayService extends Service implements View.OnTouchListener {
5052
private Resources mResources;
5153

5254
public static final String INTENT_EXTRA_IS_CLOSE_WINDOW = "IsCloseWindow";
55+
56+
private static OverlayService instance;
5357
public static boolean isRunning = false;
5458
private WindowManager windowManager = null;
5559
private FlutterView flutterView;
@@ -86,12 +90,15 @@ public void onDestroy() {
8690
isRunning = false;
8791
NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
8892
notificationManager.cancel(OverlayConstants.NOTIFICATION_ID);
93+
instance = null;
8994
}
9095

9196
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
9297
@Override
9398
public int onStartCommand(Intent intent, int flags, int startId) {
9499
mResources = getApplicationContext().getResources();
100+
int startX = intent.getIntExtra("startX", OverlayConstants.DEFAULT_XY);
101+
int startY = intent.getIntExtra("startY", OverlayConstants.DEFAULT_XY);
95102
boolean isCloseWindow = intent.getBooleanExtra(INTENT_EXTRA_IS_CLOSE_WINDOW, false);
96103
if (isCloseWindow) {
97104
if (windowManager != null) {
@@ -123,6 +130,10 @@ public int onStartCommand(Intent intent, int flags, int startId) {
123130
if (call.method.equals("updateFlag")) {
124131
String flag = call.argument("flag").toString();
125132
updateOverlayFlag(result, flag);
133+
} else if (call.method.equals("updateOverlayPosition")) {
134+
int x = call.<Integer>argument("x");
135+
int y = call.<Integer>argument("y");
136+
moveOverlay(x, y, result);
126137
} else if (call.method.equals("resizeOverlay")) {
127138
int width = call.argument("width");
128139
int height = call.argument("height");
@@ -144,6 +155,8 @@ public int onStartCommand(Intent intent, int flags, int startId) {
144155
int h = displaymetrics.heightPixels;
145156
szWindow.set(w, h);
146157
}
158+
int dx = startX == OverlayConstants.DEFAULT_XY ? 0 : startX;
159+
int dy = startY == OverlayConstants.DEFAULT_XY ? -statusBarHeightPx() : startY;
147160
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
148161
WindowSetup.width == -1999 ? -1 : WindowSetup.width,
149162
WindowSetup.height != -1999 ? WindowSetup.height : screenHeight(),
@@ -162,6 +175,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
162175
params.gravity = WindowSetup.gravity;
163176
flutterView.setOnTouchListener(this);
164177
windowManager.addView(flutterView, params);
178+
moveOverlay(dx, dy, null);
165179
return START_STICKY;
166180
}
167181

@@ -238,6 +252,48 @@ private void resizeOverlay(int width, int height, boolean enableDrag, MethodChan
238252
}
239253
}
240254

255+
private void moveOverlay(int x, int y, MethodChannel.Result result) {
256+
if (windowManager != null) {
257+
WindowManager.LayoutParams params = (WindowManager.LayoutParams) flutterView.getLayoutParams();
258+
params.x = (x == -1999 || x == -1) ? -1 : dpToPx(x);
259+
params.y = dpToPx(y);
260+
windowManager.updateViewLayout(flutterView, params);
261+
if (result != null)
262+
result.success(true);
263+
} else {
264+
if (result != null)
265+
result.success(false);
266+
}
267+
}
268+
269+
270+
public static Map<String, Double> getCurrentPosition() {
271+
if (instance != null && instance.flutterView != null) {
272+
WindowManager.LayoutParams params = (WindowManager.LayoutParams) instance.flutterView.getLayoutParams();
273+
Map<String, Double> position = new HashMap<>();
274+
position.put("x", instance.pxToDp(params.x));
275+
position.put("y", instance.pxToDp(params.y));
276+
return position;
277+
}
278+
return null;
279+
}
280+
281+
public static boolean moveOverlay(int x, int y) {
282+
if (instance != null && instance.flutterView != null) {
283+
if (instance.windowManager != null) {
284+
WindowManager.LayoutParams params = (WindowManager.LayoutParams) instance.flutterView.getLayoutParams();
285+
params.x = (x == -1999 || x == -1) ? -1 : instance.dpToPx(x);
286+
params.y = instance.dpToPx(y);
287+
instance.windowManager.updateViewLayout(instance.flutterView, params);
288+
return true;
289+
} else {
290+
return false;
291+
}
292+
} else {
293+
return false;
294+
}
295+
}
296+
241297

242298
@Override
243299
public void onCreate() {
@@ -260,6 +316,7 @@ public void onCreate() {
260316
.setVisibility(WindowSetup.notificationVisibility)
261317
.build();
262318
startForeground(OverlayConstants.NOTIFICATION_ID, notification);
319+
instance = this;
263320
}
264321

265322
private void createNotificationChannel() {
@@ -284,6 +341,10 @@ private int dpToPx(int dp) {
284341
Float.parseFloat(dp + ""), mResources.getDisplayMetrics());
285342
}
286343

344+
private double pxToDp(int px) {
345+
return (double) px / mResources.getDisplayMetrics().density;
346+
}
347+
287348
private boolean inPortrait() {
288349
return mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
289350
}

‎android/src/main/java/flutter/overlay/window/flutter_overlay_window/WindowSetup.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ static void setFlag(String name) {
4747
}
4848
}
4949

50+
static void showWhenLocked(String name) {
51+
if (name.equalsIgnoreCase("flagNotFocusable") || name.equalsIgnoreCase("defaultFlag")) {
52+
flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
53+
}
54+
if (name.equalsIgnoreCase("flagNotTouchable") || name.equalsIgnoreCase("clickThrough")) {
55+
flag = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
56+
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
57+
}
58+
if (name.equalsIgnoreCase("flagNotTouchModal") || name.equalsIgnoreCase("focusPointer")) {
59+
flag = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
60+
}
61+
}
62+
5063
static void setGravityFromAlignment(String alignment) {
5164
if (alignment.equalsIgnoreCase("topLeft")) {
5265
gravity = Gravity.TOP | Gravity.LEFT;

‎example/lib/home_page.dart‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class _HomePageState extends State<HomePage> {
7474
positionGravity: PositionGravity.auto,
7575
height: (MediaQuery.of(context).size.height * 0.6).toInt(),
7676
width: WindowSize.matchParent,
77+
startPosition: const OverlayPosition(0, -259),
7778
);
7879
},
7980
child: const Text("Show Overlay"),
@@ -115,6 +116,27 @@ class _HomePageState extends State<HomePage> {
115116
},
116117
child: const Text("Send message to overlay"),
117118
),
119+
const SizedBox(height: 20.0),
120+
TextButton(
121+
onPressed: () {
122+
FlutterOverlayWindow.getOverlayPosition().then((value) {
123+
log('Overlay Position: $value');
124+
setState(() {
125+
latestMessageFromOverlay = 'Overlay Position: $value';
126+
});
127+
});
128+
},
129+
child: const Text("Get overlay position"),
130+
),
131+
const SizedBox(height: 20.0),
132+
TextButton(
133+
onPressed: () {
134+
FlutterOverlayWindow.moveOverlay(
135+
const OverlayPosition(0, 0),
136+
);
137+
},
138+
child: const Text("Move overlay position to (0, 0)"),
139+
),
118140
const SizedBox(height: 20),
119141
Text(latestMessageFromOverlay ?? ''),
120142
],

‎example/lib/overlays/true_caller_overlay.dart‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ class _TrueCallerOverlayState extends State<TrueCallerOverlay> {
2929
@override
3030
void initState() {
3131
super.initState();
32-
FlutterOverlayWindow.overlayListener.listen((event) {
33-
log("$event");
34-
setState(() {
35-
isGold = !isGold;
36-
});
37-
});
3832
}
3933

4034
@override
@@ -55,8 +49,12 @@ class _TrueCallerOverlayState extends State<TrueCallerOverlay> {
5549
),
5650
child: GestureDetector(
5751
onTap: () {
58-
FlutterOverlayWindow.shareData(
59-
"Heyy this is a data from the overlay");
52+
setState(() {
53+
isGold = !isGold;
54+
});
55+
FlutterOverlayWindow.getOverlayPosition().then((value) {
56+
log("Overlay Position: $value");
57+
});
6058
},
6159
child: Stack(
6260
children: [

‎lib/flutter_overlay_window.dart‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ library flutter_overlay_window;
22

33
export 'package:flutter_overlay_window/src/overlay_window.dart';
44
export 'package:flutter_overlay_window/src/overlay_config.dart';
5+
export 'package:flutter_overlay_window/src/models/overlay_position.dart';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'package:flutter/foundation.dart';
2+
3+
@immutable
4+
class OverlayPosition {
5+
final double x;
6+
final double y;
7+
8+
const OverlayPosition(this.x, this.y);
9+
10+
factory OverlayPosition.fromMap(Map<Object?, Object?>? map) =>
11+
OverlayPosition(map?['x'] as double? ?? 0, map?['y'] as double? ?? 0);
12+
13+
Map<String, dynamic> toMap() =>
14+
<String, dynamic>{'x': x.toInt(), 'y': y.toInt()};
15+
16+
@override
17+
String toString() {
18+
return 'OverlayPosition{x=$x, y=$y}';
19+
}
20+
}

0 commit comments

Comments
 (0)