25-7-28-2
This commit is contained in:
7
bubble-master/.gitignore
vendored
Executable file
7
bubble-master/.gitignore
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
.DS_Store
|
||||
.dart_tool/
|
||||
|
||||
.packages
|
||||
.pub/
|
||||
|
||||
build/
|
||||
10
bubble-master/.metadata
Executable file
10
bubble-master/.metadata
Executable file
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: f4abaa0735eba4dfd8f33f73363911d63931fe03
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
14
bubble-master/CHANGELOG.md
Executable file
14
bubble-master/CHANGELOG.md
Executable file
@@ -0,0 +1,14 @@
|
||||
## 0.0.1
|
||||
|
||||
* Created and implemented startBubbleHead and closeBubbleHead usecases for bubble head package
|
||||
|
||||
|
||||
## 0.0.2
|
||||
* Fix read-me documentation
|
||||
|
||||
## 0.0.3
|
||||
* Fix read-me documentation (added `Buy me a coffee link`)
|
||||
|
||||
## 0.0.4
|
||||
* Added optional parameter to enable or disable send-app-to-background
|
||||
* Updated documentation
|
||||
21
bubble-master/LICENSE
Executable file
21
bubble-master/LICENSE
Executable file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 chrisoftech
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
128
bubble-master/README.md
Executable file
128
bubble-master/README.md
Executable file
@@ -0,0 +1,128 @@
|
||||
# bubble_head
|
||||
|
||||
|
||||
A flutter plugin to enable you launch a bubble while putting your application to background and upon clicking the bubble brings your application back to foreground
|
||||
|
||||
## Getting Started
|
||||
### Add dependency
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
bubble_head: ^0.0.4
|
||||
```
|
||||
|
||||
|
||||
### Add in android-manifest file (**../main/AndroidManifest.xml**)
|
||||
|
||||
If you are unsure on where to do this, you can reference the example project AndroidManifest.xml file [here](example/android/app/src/main/AndroidManifest.xml)
|
||||
|
||||
|
||||
Add `SYSTEM_ALERT_WINDOW` permission in manifest
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
```
|
||||
|
||||
NOTE: For best UX practices, you should request for `SYSTEM_ALERT_WINDOW` permission on your application launch (if permission `status` is not granted)
|
||||
To request for permission, we advise the use of this [package](https://pub.dev/packages/permission_handler)
|
||||
|
||||
|
||||
Add `intent-filter` in activity tag
|
||||
|
||||
```xml
|
||||
<intent-filter>
|
||||
<action android:name="intent.bring.app.to.foreground" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
```
|
||||
|
||||
Add `service` in application tag
|
||||
```xml
|
||||
<service
|
||||
android:name="com.dsaved.bubblehead.bubble.BubbleHeadService"
|
||||
android:enabled="true"
|
||||
android:exported="false"/>
|
||||
```
|
||||
|
||||
### Note: To set bubble icon, create `assets/images` folder path and add your png icon with name `icon.png` to the directory (ensure to import assets in your `pubspec.yaml` file)
|
||||
|
||||
**GIF illustration**
|
||||
|
||||
[](example/assets/images/bubble_head_example.gif "Bubble-head example")
|
||||
|
||||
### Examples
|
||||
|
||||
**To start bubble**
|
||||
[This puts your app in background and can be re-launched (brought to foreground) on tap of the bubble]
|
||||
|
||||
```dart
|
||||
Bubble _bubble = new Bubble();
|
||||
|
||||
Future<void> startBubbleHead() async {
|
||||
|
||||
try {
|
||||
await _bubble.startBubbleHead();
|
||||
} on PlatformException {
|
||||
print('Failed to call startBubbleHead');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To stop/close bubble**
|
||||
|
||||
```dart
|
||||
Bubble _bubble = new Bubble();
|
||||
|
||||
Future<void> stopBubbleHead() async {
|
||||
|
||||
try {
|
||||
await _bubble.stopBubbleHead();
|
||||
} on PlatformException {
|
||||
print('Failed to call stopBubbleHead');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can prevent the default action of putting your application in background when starting `bubble_head` by setting `sendAppToBackground` parameter when starting bubble head (if you choose to use another means of sending your application to background)
|
||||
|
||||
```dart
|
||||
Bubble _bubble = new Bubble();
|
||||
|
||||
Future<void> startBubbleHead() async {
|
||||
|
||||
try {
|
||||
// this will only display the bubble-head without sending the application to background
|
||||
await _bubble.startBubbleHead(sendAppToBackground: false);
|
||||
} on PlatformException {
|
||||
print('Failed to call startBubbleHead');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**Other parameters**
|
||||
(You can choose to tweak **optional** parameters when initializing bubble)
|
||||
|
||||
|
||||
```dart
|
||||
Bubble({
|
||||
this.shouldBounce = true,
|
||||
this.allowDragToClose = true,
|
||||
this.showCloseButton = false,
|
||||
});
|
||||
```
|
||||
```dart
|
||||
Bubble().startBubbleHead(sendAppToBackground: true);
|
||||
```
|
||||
|
||||
**Parameter Definition**
|
||||
- shouldBounce - Defaults to `True`
|
||||
(Adds animation to bubble-head)
|
||||
- allowDragToClose - Defaults to `True`
|
||||
(Enables dragging bubble to bottom screen to exit)
|
||||
- showCloseButton - Defaults to `False`
|
||||
(Adds a close button icon to the bubble-head)
|
||||
- sendAppToBackground - Defaults to `True`
|
||||
(Sends application to background)
|
||||
|
||||
## [Buy me a Coffee](https://www.buymeacoffee.com/dsaved)
|
||||
|
||||
8
bubble-master/android/.gitignore
vendored
Executable file
8
bubble-master/android/.gitignore
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
35
bubble-master/android/build.gradle
Executable file
35
bubble-master/android/build.gradle
Executable file
@@ -0,0 +1,35 @@
|
||||
group 'com.dsaved.bubblehead.bubble'
|
||||
version '1.0'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
namespace 'com.dsaved.bubblehead.bubble'
|
||||
compileSdkVersion 33
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
}
|
||||
3
bubble-master/android/gradle.properties
Executable file
3
bubble-master/android/gradle.properties
Executable file
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
6
bubble-master/android/gradle/wrapper/gradle-wrapper.properties
vendored
Executable file
6
bubble-master/android/gradle/wrapper/gradle-wrapper.properties
vendored
Executable file
@@ -0,0 +1,6 @@
|
||||
#Thu Jul 10 12:15:00 EET 2025
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
1
bubble-master/android/settings.gradle
Executable file
1
bubble-master/android/settings.gradle
Executable file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'bubble'
|
||||
4
bubble-master/android/src/main/AndroidManifest.xml
Executable file
4
bubble-master/android/src/main/AndroidManifest.xml
Executable file
@@ -0,0 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.dsaved.bubblehead.bubble">
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,456 @@
|
||||
package com.dsaved.bubblehead.bubble;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.os.Build;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.util.Base64;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
|
||||
public class BubbleHeadService extends Service implements View.OnClickListener {
|
||||
private WindowManager mWindowManager;
|
||||
private View mFloatingWidgetView;
|
||||
private ImageView remove_image_view;
|
||||
private final Point szWindow = new Point();
|
||||
private View removeFloatingWidgetView;
|
||||
private static boolean showCloseButton = false, _bounce = true, _dragToClose = true, _sendAppToBackground = true;
|
||||
private boolean _continueToSnap = false;
|
||||
|
||||
private int x_init_cord, y_init_cord, x_init_margin, y_init_margin;
|
||||
static Bitmap _image;
|
||||
|
||||
// Set the value for showing close button to true or false
|
||||
public static void shouldShowCloseButton(Boolean show) {
|
||||
showCloseButton = show;
|
||||
}
|
||||
|
||||
// set to true to enable bouncing
|
||||
public static void bounce(Boolean bounce) {
|
||||
_bounce = bounce;
|
||||
}
|
||||
|
||||
// Set the value for drag to close to enable dragging bubble to close
|
||||
public static void dragToClose(Boolean dragToClose) {
|
||||
_dragToClose = dragToClose;
|
||||
}
|
||||
|
||||
public static void sendAppToBackground(Boolean sendAppToBackground) {
|
||||
_sendAppToBackground = sendAppToBackground;
|
||||
}
|
||||
|
||||
public static void startService(Context activity, String image) {
|
||||
byte[] decodedBytes = Base64.decode(image, Base64.DEFAULT);
|
||||
_image = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
|
||||
|
||||
// send application to background if this is true
|
||||
if (_sendAppToBackground) {
|
||||
Intent i = new Intent();
|
||||
i.setAction(Intent.ACTION_MAIN);
|
||||
i.addCategory(Intent.CATEGORY_HOME);
|
||||
activity.startActivity(i);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(activity, BubbleHeadService.class);
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
public static void stopService(Context activity) {
|
||||
Intent intent = new Intent(activity, BubbleHeadService.class);
|
||||
activity.stopService(intent);
|
||||
}
|
||||
|
||||
// create an empty constructor
|
||||
public BubbleHeadService() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressLint({"ClickableViewAccessibility", "InflateParams"})
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// init WindowManager
|
||||
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
getWindowManagerDefaultDisplay();
|
||||
|
||||
// Init LayoutInflater
|
||||
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
// Inflate the removing view layout we created
|
||||
removeFloatingWidgetView = inflater.inflate(R.layout.bubble_head_remove_widget, null);
|
||||
|
||||
// Add the view to the window.
|
||||
WindowManager.LayoutParams paramRemove;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
paramRemove = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_PHONE,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
} else {
|
||||
paramRemove = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
}
|
||||
|
||||
// Specify the view position
|
||||
paramRemove.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
|
||||
// Initially the Removing widget view is not visible, so set visibility to GONE
|
||||
removeFloatingWidgetView.setVisibility(View.GONE);
|
||||
remove_image_view = (ImageView) removeFloatingWidgetView.findViewById(R.id.remove_img);
|
||||
|
||||
// Add the view to the window
|
||||
mWindowManager.addView(removeFloatingWidgetView, paramRemove);
|
||||
|
||||
// Inflate the floating view layout we created
|
||||
mFloatingWidgetView = inflater.inflate(R.layout.layout_bubble_head, null);
|
||||
|
||||
// Add the view to the window.
|
||||
WindowManager.LayoutParams params;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_PHONE,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
} else {
|
||||
params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
}
|
||||
|
||||
// Specify the view position
|
||||
params.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
|
||||
// Initially view will be added to top-left corner, you change x-y coordinates according to your need
|
||||
params.x = 0;
|
||||
params.y = 100;
|
||||
|
||||
// Add the view to the window
|
||||
mWindowManager.addView(mFloatingWidgetView, params);
|
||||
|
||||
//set image to chatHead
|
||||
ImageView chatHeadImage = mFloatingWidgetView.findViewById(R.id.chat_head_profile_iv);
|
||||
chatHeadImage.setImageBitmap(_image);
|
||||
|
||||
// find id of close image button
|
||||
ImageView closeBubbleHead = mFloatingWidgetView.findViewById(R.id.close_bubble_head);
|
||||
closeBubbleHead.setOnClickListener(this);
|
||||
if (!showCloseButton) {
|
||||
closeBubbleHead.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
implementTouchListenerToFloatingWidgetView();
|
||||
}
|
||||
|
||||
private void getWindowManagerDefaultDisplay() {
|
||||
mWindowManager.getDefaultDisplay().getSize(szWindow);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void implementTouchListenerToFloatingWidgetView() {
|
||||
_continueToSnap = true;
|
||||
// Drag and move chat head using user's touch action.
|
||||
mFloatingWidgetView.findViewById(R.id.root_container);
|
||||
mFloatingWidgetView.setOnTouchListener(new View.OnTouchListener() {
|
||||
long time_start = 0, time_end = 0;
|
||||
|
||||
boolean isLongClick = false;
|
||||
boolean inBounded = false;
|
||||
int remove_img_width = 0, remove_img_height = 0;
|
||||
|
||||
final Handler handler_longClick = new Handler();
|
||||
final Runnable runnable_longClick = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
isLongClick = true;
|
||||
removeFloatingWidgetView.setVisibility(View.VISIBLE);
|
||||
onFloatingWidgetLongClick();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
|
||||
|
||||
int x_cord = (int) event.getRawX();
|
||||
int y_cord = (int) event.getRawY();
|
||||
|
||||
int x_cord_Destination, y_cord_Destination;
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
time_start = System.currentTimeMillis();
|
||||
|
||||
if (_dragToClose) {
|
||||
handler_longClick.postDelayed(runnable_longClick, 100);
|
||||
}
|
||||
remove_img_width = remove_image_view.getLayoutParams().width;
|
||||
remove_img_height = remove_image_view.getLayoutParams().height;
|
||||
|
||||
x_init_cord = x_cord;
|
||||
y_init_cord = y_cord;
|
||||
|
||||
// remember the initial position.
|
||||
x_init_margin = layoutParams.x;
|
||||
y_init_margin = layoutParams.y;
|
||||
return true;
|
||||
case MotionEvent.ACTION_UP:
|
||||
isLongClick = false;
|
||||
removeFloatingWidgetView.setVisibility(View.GONE);
|
||||
remove_image_view.getLayoutParams().height = remove_img_height;
|
||||
remove_image_view.getLayoutParams().width = remove_img_width;
|
||||
|
||||
if (_dragToClose) {
|
||||
handler_longClick.removeCallbacks(runnable_longClick);
|
||||
}
|
||||
|
||||
// If user drag and drop the floating widget view
|
||||
// into remove view then stop the service
|
||||
if (inBounded) {
|
||||
stopSelf();
|
||||
inBounded = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Difference between initial coordinate and current coordinate
|
||||
int x_diff = x_cord - x_init_cord;
|
||||
int y_diff = y_cord - y_init_cord;
|
||||
|
||||
// check if action move is little as move happen on view with just a tap
|
||||
if (Math.abs(x_diff) < 5 && Math.abs(y_diff) < 5) {
|
||||
time_end = System.currentTimeMillis();
|
||||
// only perform click if time is less than 200ms
|
||||
if ((time_end - time_start) < 200) {
|
||||
onFloatingWidgetClick();
|
||||
}
|
||||
}
|
||||
|
||||
y_cord_Destination = y_init_margin + y_diff;
|
||||
|
||||
int barHeight = getStatusBarHeight();
|
||||
if (y_cord_Destination < 0) {
|
||||
y_cord_Destination = 0;
|
||||
} else if (y_cord_Destination + (mFloatingWidgetView.getHeight() + barHeight) > szWindow.y) {
|
||||
y_cord_Destination = szWindow.y - (mFloatingWidgetView.getHeight() + barHeight);
|
||||
}
|
||||
|
||||
layoutParams.y = y_cord_Destination;
|
||||
|
||||
inBounded = false;
|
||||
|
||||
// reset position
|
||||
resetPosition(x_cord);
|
||||
return true;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int x_diff_move = x_cord - x_init_cord;
|
||||
int y_diff_move = y_cord - y_init_cord;
|
||||
|
||||
x_cord_Destination = x_init_margin + x_diff_move;
|
||||
y_cord_Destination = y_init_margin + y_diff_move;
|
||||
|
||||
// If user long click the floating view, update remove view
|
||||
if (isLongClick) {
|
||||
int x_bound_left = szWindow.x / 2 - (int) (remove_img_width * 1.5);
|
||||
int x_bound_right = szWindow.x / 2 + (int) (remove_img_width * 1.5);
|
||||
int y_bound_top = szWindow.y - (int) (remove_img_height * 1.5);
|
||||
|
||||
// If Floating view comes under Remove View update Window Manager
|
||||
if ((x_cord >= x_bound_left && x_cord <= x_bound_right) && y_cord >= y_bound_top) {
|
||||
inBounded = true;
|
||||
|
||||
int x_cord_remove = (int) ((szWindow.x - (remove_img_height * 1.5)) / 2);
|
||||
int y_cord_remove = (int) (szWindow.y - ((remove_img_width * 1.5) + getStatusBarHeight()));
|
||||
|
||||
if (remove_image_view.getLayoutParams().height == remove_img_height) {
|
||||
WindowManager.LayoutParams param_remove = (WindowManager.LayoutParams) removeFloatingWidgetView.getLayoutParams();
|
||||
param_remove.x = x_cord_remove;
|
||||
param_remove.y = y_cord_remove;
|
||||
|
||||
mWindowManager.updateViewLayout(removeFloatingWidgetView, param_remove);
|
||||
}
|
||||
|
||||
layoutParams.x = x_cord_remove + (Math.abs(removeFloatingWidgetView.getWidth() - mFloatingWidgetView.getWidth())) / 2;
|
||||
layoutParams.y = y_cord_remove + (Math.abs(removeFloatingWidgetView.getHeight() - mFloatingWidgetView.getHeight())) / 2;
|
||||
|
||||
// Update the layout with new X & Y coordinate
|
||||
mWindowManager.updateViewLayout(mFloatingWidgetView, layoutParams);
|
||||
break;
|
||||
} else {
|
||||
// If Floating window gets out of the Remove view update Remove view again
|
||||
inBounded = false;
|
||||
remove_image_view.getLayoutParams().height = remove_img_height;
|
||||
remove_image_view.getLayoutParams().width = remove_img_width;
|
||||
// onFloatingWidgetClick();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
layoutParams.x = x_cord_Destination;
|
||||
layoutParams.y = y_cord_Destination;
|
||||
|
||||
// Update the layout with new X & Y coordinate
|
||||
mWindowManager.updateViewLayout(mFloatingWidgetView, layoutParams);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.close_bubble_head) {
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
private void onFloatingWidgetLongClick() {
|
||||
// Get remove Floating view params
|
||||
WindowManager.LayoutParams removeParams = (WindowManager.LayoutParams) removeFloatingWidgetView.getLayoutParams();
|
||||
|
||||
// get x and y coordinates of remove view
|
||||
int x_cord = (szWindow.x - removeFloatingWidgetView.getWidth()) / 2;
|
||||
int y_cord = szWindow.y - (removeFloatingWidgetView.getHeight() + getStatusBarHeight());
|
||||
|
||||
removeParams.x = x_cord;
|
||||
removeParams.y = y_cord;
|
||||
|
||||
// Update Remove view params
|
||||
mWindowManager.updateViewLayout(removeFloatingWidgetView, removeParams);
|
||||
}
|
||||
|
||||
// Reset position of Floating Widget view on dragging
|
||||
private void resetPosition(int x_cord_now) {
|
||||
if (_continueToSnap) {
|
||||
if (x_cord_now <= szWindow.x / 2) {
|
||||
snapToLeft(x_cord_now);
|
||||
} else {
|
||||
snapToRight(x_cord_now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void snapToLeft(final int current_x_cord) {
|
||||
final int x = szWindow.x - current_x_cord;
|
||||
new CountDownTimer(500, 5) {
|
||||
final WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
|
||||
|
||||
public void onTick(long t) {
|
||||
long step = (500 - t) / 5;
|
||||
mParams.x = -(int) (current_x_cord * current_x_cord * step);
|
||||
if (_bounce) {
|
||||
mParams.x = -(int) (double) bounceValue(step, x);
|
||||
}
|
||||
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
|
||||
}
|
||||
|
||||
public void onFinish() {
|
||||
mParams.x = 0;
|
||||
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private void snapToRight(final int current_x_cord) {
|
||||
new CountDownTimer(500, 5) {
|
||||
final WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
|
||||
|
||||
public void onTick(long t) {
|
||||
long step = (500 - t) / 5;
|
||||
mParams.x = (int) (szWindow.x + (current_x_cord * current_x_cord * step) - mFloatingWidgetView.getWidth());
|
||||
if (_bounce) {
|
||||
mParams.x = szWindow.x + (int) (double) bounceValue(step, current_x_cord) - mFloatingWidgetView.getWidth();
|
||||
}
|
||||
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
|
||||
}
|
||||
|
||||
public void onFinish() {
|
||||
mParams.x = szWindow.x - mFloatingWidgetView.getWidth();
|
||||
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private double bounceValue(long step, long scale) {
|
||||
return scale * Math.exp(-0.15 * step) * Math.cos(0.08 * step);
|
||||
}
|
||||
|
||||
private int getStatusBarHeight() {
|
||||
return (int) Math.ceil(25 * getApplicationContext().getResources().getDisplayMetrics().density);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
getWindowManagerDefaultDisplay();
|
||||
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
|
||||
|
||||
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
if (layoutParams.y + (mFloatingWidgetView.getHeight() + getStatusBarHeight()) > szWindow.y) {
|
||||
layoutParams.y = szWindow.y - (mFloatingWidgetView.getHeight() + getStatusBarHeight());
|
||||
mWindowManager.updateViewLayout(mFloatingWidgetView, layoutParams);
|
||||
}
|
||||
|
||||
if (layoutParams.x != 0 && layoutParams.x < szWindow.x) {
|
||||
resetPosition(szWindow.x);
|
||||
}
|
||||
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
if (layoutParams.x > szWindow.x) {
|
||||
resetPosition(szWindow.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onFloatingWidgetClick() {
|
||||
_continueToSnap = false;
|
||||
// bring the application to front
|
||||
Intent it = new Intent("intent.bring.app.to.foreground");
|
||||
it.setComponent(new ComponentName(getPackageName(), getApplicationContext().getPackageName() + ".MainActivity"));
|
||||
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
getApplicationContext().startActivity(it);
|
||||
|
||||
// stop the service
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mFloatingWidgetView != null) {
|
||||
mWindowManager.removeView(mFloatingWidgetView);
|
||||
}
|
||||
if (removeFloatingWidgetView != null) {
|
||||
mWindowManager.removeView(removeFloatingWidgetView);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.dsaved.bubblehead.bubble;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||
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;
|
||||
|
||||
/**
|
||||
* BubblePlugin
|
||||
*/
|
||||
public class BubblePlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
private MethodChannel channel;
|
||||
private Context activity;
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "com.dsaved.bubble.head");
|
||||
channel.setMethodCallHandler(this);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||
if (call.method.equals("startBubbleHead")) {
|
||||
startBubbleHead(result, call);
|
||||
} else if (call.method.equals("stopBubbleHead")) {
|
||||
BubbleHeadService.stopService(activity);
|
||||
} else {
|
||||
result.notImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public void startBubbleHead(@NonNull Result result, MethodCall call) {
|
||||
if (Settings.canDrawOverlays(activity)) {
|
||||
boolean bounce = call.argument("bounce");
|
||||
BubbleHeadService.bounce(bounce);
|
||||
|
||||
boolean showClose = call.argument("showClose");
|
||||
BubbleHeadService.shouldShowCloseButton(showClose);
|
||||
|
||||
boolean dragToClose = call.argument("dragToClose");
|
||||
BubbleHeadService.dragToClose(dragToClose);
|
||||
|
||||
boolean sendAppToBackground = call.argument("sendAppToBackground");
|
||||
BubbleHeadService.sendAppToBackground(sendAppToBackground);
|
||||
|
||||
String imageByte = call.argument("image");
|
||||
BubbleHeadService.startService(activity, imageByte);
|
||||
} else {
|
||||
//Permission is not available
|
||||
result.error("EPERMNOTGRANTED", "permission not available", "Please request permission for: android.permission.SYSTEM_ALERT_WINDOW. with out this permission you cannot launch the bubble head.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||
channel.setMethodCallHandler(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
|
||||
this.activity = binding.getActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromActivityForConfigChanges() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromActivity() {
|
||||
|
||||
}
|
||||
}
|
||||
6
bubble-master/android/src/main/res/drawable/circle_shape.xml
Executable file
6
bubble-master/android/src/main/res/drawable/circle_shape.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@android:color/black" />
|
||||
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3L10,7L5,7v5h2L7,9zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.99h18v14.02z"/>
|
||||
</vector>
|
||||
9
bubble-master/android/src/main/res/drawable/ic_close_black_24dp.xml
Executable file
9
bubble-master/android/src/main/res/drawable/ic_close_black_24dp.xml
Executable file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
||||
9
bubble-master/android/src/main/res/drawable/ic_close_white_24dp.xml
Executable file
9
bubble-master/android/src/main/res/drawable/ic_close_white_24dp.xml
Executable file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
|
||||
</vector>
|
||||
BIN
bubble-master/android/src/main/res/drawable/ic_launcher.png
Executable file
BIN
bubble-master/android/src/main/res/drawable/ic_launcher.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
9
bubble-master/android/src/main/res/drawable/white_circle_shape.xml
Executable file
9
bubble-master/android/src/main/res/drawable/white_circle_shape.xml
Executable file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@android:color/white" />
|
||||
|
||||
|
||||
</shape>
|
||||
16
bubble-master/android/src/main/res/layout/bubble_head_remove_widget.xml
Executable file
16
bubble-master/android/src/main/res/layout/bubble_head_remove_widget.xml
Executable file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/remove_relativelayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/remove_img"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:background="@drawable/white_circle_shape"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_close_white_24dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
45
bubble-master/android/src/main/res/layout/layout_bubble_head.xml
Executable file
45
bubble-master/android/src/main/res/layout/layout_bubble_head.xml
Executable file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- Root container of Floating Widget View -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/root_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- View while view is collapsed -->
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/collapse_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<!-- ImageView of floating widget -->
|
||||
<ImageView
|
||||
android:id="@+id/chat_head_profile_iv"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:src="@drawable/ic_launcher"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<!-- Close button to close Floating Widget View -->
|
||||
<ImageView
|
||||
android:id="@+id/close_bubble_head"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="50dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:padding="1dp"
|
||||
android:background="@drawable/circle_shape"
|
||||
android:src="@drawable/ic_close_white_24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
16
bubble-master/android/src/main/res/values/styles.xml
Executable file
16
bubble-master/android/src/main/res/values/styles.xml
Executable file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="ShapeAppearance.Image.PILL" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">50%</item>
|
||||
</style>
|
||||
|
||||
<style name="ShapeAppearance.Image.Top.PILL" parent="">
|
||||
<item name="cornerSizeTopLeft">6dp</item>
|
||||
<item name="cornerFamilyTopLeft">rounded</item>
|
||||
<item name="cornerSizeTopRight">6dp</item>
|
||||
<item name="cornerFamilyTopRight">rounded</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
41
bubble-master/lib/bubble.dart
Executable file
41
bubble-master/lib/bubble.dart
Executable file
@@ -0,0 +1,41 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class Bubble {
|
||||
static const MethodChannel _channel =
|
||||
const MethodChannel('com.dsaved.bubble.head');
|
||||
|
||||
bool shouldBounce;
|
||||
bool showCloseButton;
|
||||
bool allowDragToClose;
|
||||
|
||||
Bubble({
|
||||
this.shouldBounce = true,
|
||||
this.allowDragToClose = true,
|
||||
this.showCloseButton = false,
|
||||
});
|
||||
|
||||
/// puts app in background and shows floaty-bubble head
|
||||
Future<void> startBubbleHead({bool sendAppToBackground = true}) async {
|
||||
ByteData bytes = await rootBundle.load(
|
||||
'assets/images/s.png',
|
||||
);
|
||||
var buffer = bytes.buffer;
|
||||
var encodedImage = base64.encode(Uint8List.view(buffer));
|
||||
await _channel.invokeMethod('startBubbleHead', {
|
||||
"image": encodedImage,
|
||||
"bounce": shouldBounce,
|
||||
"showClose": showCloseButton,
|
||||
"dragToClose": allowDragToClose,
|
||||
"sendAppToBackground": sendAppToBackground,
|
||||
});
|
||||
}
|
||||
|
||||
/// closes floaty-bubble head
|
||||
Future<void> stopBubbleHead() async {
|
||||
await _channel.invokeMethod('stopBubbleHead');
|
||||
}
|
||||
}
|
||||
189
bubble-master/pubspec.lock
Executable file
189
bubble-master/pubspec.lock
Executable file
@@ -0,0 +1,189 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.0"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
63
bubble-master/pubspec.yaml
Executable file
63
bubble-master/pubspec.yaml
Executable file
@@ -0,0 +1,63 @@
|
||||
name: bubble_head
|
||||
description: A flutter plugin to enable you launch a bubble while putting your application to background and upon clicking the bubble brings your application back to foreground
|
||||
version: 0.0.4
|
||||
homepage: https://github.com/chrisoftech/bubble
|
||||
# publish_to:
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
flutter: ">=1.20.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
# This section identifies this Flutter project as a plugin project.
|
||||
# The 'pluginClass' and Android 'package' identifiers should not ordinarily
|
||||
# be modified. They are used by the tooling to maintain consistency when
|
||||
# adding or updating assets for this project.
|
||||
plugin:
|
||||
platforms:
|
||||
android:
|
||||
package: com.dsaved.bubblehead.bubble
|
||||
pluginClass: BubblePlugin
|
||||
|
||||
# To add assets to your plugin package, add an assets section, like this:
|
||||
# assets:
|
||||
# - assets/images/
|
||||
# - images/a_dot_ham.jpeg
|
||||
#
|
||||
# For details regarding assets in packages, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
#
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
# To add custom fonts to your plugin package, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts in packages, see
|
||||
# https://flutter.dev/custom-fonts/#from-packages
|
||||
Reference in New Issue
Block a user