25-7-28-2
This commit is contained in:
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>
|
||||
Reference in New Issue
Block a user