7/31/1
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 30
|
||||
|
||||
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
|
||||
5
bubble-master/android/gradle/wrapper/gradle-wrapper.properties
vendored
Executable file
5
bubble-master/android/gradle/wrapper/gradle-wrapper.properties
vendored
Executable file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||
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>
|
||||
39
bubble-master/lib/bubble.dart
Executable file
39
bubble-master/lib/bubble.dart
Executable file
@@ -0,0 +1,39 @@
|
||||
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/logo1.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: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
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: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.4"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
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: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
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: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.1"
|
||||
sdks:
|
||||
dart: ">=3.3.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