├── .gitignore
├── README.md
├── build.gradle
├── floating-bubble
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── gen
│ └── com
│ │ └── bsk
│ │ └── floatingbubblelib
│ │ ├── BuildConfig.java
│ │ ├── Manifest.java
│ │ └── R.java
│ ├── java
│ └── com
│ │ └── bsk
│ │ └── floatingbubblelib
│ │ ├── DefaultFloatingBubbleTouchListener.java
│ │ ├── FloatingBubbleAnimator.java
│ │ ├── FloatingBubbleConfig.java
│ │ ├── FloatingBubbleLogger.java
│ │ ├── FloatingBubblePermissions.java
│ │ ├── FloatingBubblePhysics.java
│ │ ├── FloatingBubbleService.java
│ │ ├── FloatingBubbleTouch.java
│ │ └── FloatingBubbleTouchListener.java
│ └── res
│ ├── drawable
│ ├── bubble_default_icon.png
│ ├── close_default_icon.png
│ └── triangle_icon.png
│ ├── layout
│ ├── floating_bubble_view.xml
│ ├── floating_expandable_view.xml
│ └── floating_remove_bubble_view.xml
│ └── values
│ └── strings.xml
├── sampleapp
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── gen
│ └── com
│ │ └── bsk
│ │ └── sampleapp
│ │ ├── BuildConfig.java
│ │ ├── Manifest.java
│ │ └── R.java
│ ├── java
│ └── com
│ │ └── bsk
│ │ └── sampleapp
│ │ ├── MainActivity.java
│ │ └── SimpleService.java
│ └── res
│ ├── drawable
│ ├── close_default_icon.png
│ ├── cover.jpg
│ └── web_icon.png
│ ├── layout
│ ├── activity_main.xml
│ └── sample_view_1.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── screenshots
├── demo.gif
├── screenshot_1.png
└── screenshot_2.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .externalNativeBuild
2 | .gradle
3 | local.properties
4 | .idea/
5 | .DS_Store
6 | build/
7 | captures/
8 | *.iml
9 | gradle.properties
10 | gradle/
11 | gradlew
12 | gradlew.bat
13 | app/build/
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Floating Bubble Library
2 | Simple library for adding a floating bubble in your application!
3 |
4 | ## Installation
5 |
6 | ```java
7 | compile 'com.github.bijoysingh.floating-bubble:3.0.0'
8 |
9 | // compile 'com.github.bijoysingh.floating-bubble:2.0.0' for Android Support v27
10 | // compile 'com.github.bijoysingh.floating-bubble:1.2.0' for Android Support v26
11 | ```
12 |
13 | ## Demos
14 | 
15 |
16 | ## Usage
17 |
18 | ### Setup
19 | Let's start with a simple setup for the Service
20 | ```java
21 | public class FloatingService extends FloatingBubbleService {
22 | ...
23 | }
24 | ```
25 |
26 | Adding your library in the manifest
27 | ```java
28 |
29 | ```
30 |
31 | Start the service
32 | ```java
33 | startService(new Intent(context, FloatingService.class));
34 | ```
35 |
36 | ### Customising the Service
37 | ```java
38 | public class FloatingService extends FloatingBubbleService {
39 |
40 | @Override
41 | protected FloatingBubbleConfig getConfig() {
42 | return new FloatingBubbleConfig.Builder()
43 | // Set the drawable for the bubble
44 | .bubbleIcon(bubbleDrawable)
45 |
46 | // Set the drawable for the remove bubble
47 | .removeBubbleIcon(removeIconDrawable)
48 |
49 | // Set the size of the bubble in dp
50 | .bubbleIconDp(64)
51 |
52 | // Set the size of the remove bubble in dp
53 | .removeBubbleIconDp(64)
54 |
55 | // Set the padding of the view from the boundary
56 | .paddingDp(4)
57 |
58 | // Set the radius of the border of the expandable view
59 | .borderRadiusDp(4)
60 |
61 | // Does the bubble attract towards the walls
62 | .physicsEnabled(true)
63 |
64 | // The color of background of the layout
65 | .expandableColor(Color.WHITE)
66 |
67 | // The color of the triangular layout
68 | .triangleColor(Color.WHITE)
69 |
70 | // Horizontal gravity of the bubble when expanded
71 | .gravity(Gravity.END)
72 |
73 | // The view which is visible in the expanded view
74 | .expandableView(yourViewAfterClick)
75 |
76 | // Set the alpha value for the remove bubble icon
77 | .removeBubbleAlpha(0.75f)
78 |
79 | // Building
80 | .build();
81 | }
82 | }
83 | ```
84 |
85 | Override the onGetIntent function. It will return true if the intent is valid, else false
86 | ```java
87 | @Override
88 | protected boolean onGetIntent(@NonNull Intent intent) {
89 | // your logic to get information from the intent
90 | return true;
91 | }
92 | ```
93 |
94 | You can change the state of the expanded view at runtime by
95 | ```java
96 | // To expand
97 | setState(true);
98 |
99 | // To compress
100 | setState(false);
101 | ```
102 |
103 | ## License
104 | ```java
105 | Copyright 2016 Bijoy Singh Kochar
106 |
107 | Licensed under the Apache License, Version 2.0 (the "License");
108 | you may not use this file except in compliance with the License.
109 | You may obtain a copy of the License at
110 |
111 | http://www.apache.org/licenses/LICENSE-2.0
112 |
113 | Unless required by applicable law or agreed to in writing, software
114 | distributed under the License is distributed on an "AS IS" BASIS,
115 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
116 | See the License for the specific language governing permissions and
117 | limitations under the License.
118 | ```
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.3.1'
10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
11 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | jcenter()
21 | google()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/floating-bubble/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/floating-bubble/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 |
4 | android {
5 | compileSdkVersion 28
6 | buildToolsVersion "28.0.3"
7 |
8 | defaultConfig {
9 | minSdkVersion 17
10 | targetSdkVersion 28
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | implementation 'com.android.support:support-core-utils:28.0.0'
25 | implementation 'com.android.support:support-v4:28.0.0'
26 | implementation 'com.android.support:cardview-v7:28.0.0'
27 | }
28 |
29 | ext {
30 | bintrayRepo = 'maven'
31 | bintrayName = 'floating-bubble'
32 |
33 | publishedGroupId = 'com.github.bijoysingh'
34 | libraryName = 'floating-bubble'
35 | artifact = 'floating-bubble'
36 |
37 | libraryDescription = 'Simple library to add Floating Bubble into your app!'
38 |
39 | siteUrl = 'https://github.com/BijoySingh/FloatingBubbleLib'
40 | gitUrl = 'https://github.com/BijoySingh/FloatingBubbleLib.git'
41 |
42 | libraryVersion = '3.0.0'
43 |
44 | developerId = 'bijoysingh693'
45 | developerName = 'Bijoy Singh Kochar'
46 | developerEmail = 'bijoysingh693@gmail.com'
47 |
48 | licenseName = 'The Apache Software License, Version 2.0'
49 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
50 | allLicenses = ["Apache-2.0"]
51 | }
52 |
53 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
54 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle'
55 |
--------------------------------------------------------------------------------
/floating-bubble/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/bijoy/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/gen/com/bsk/floatingbubblelib/BuildConfig.java:
--------------------------------------------------------------------------------
1 | /*___Generated_by_IDEA___*/
2 |
3 | package com.bsk.floatingbubblelib;
4 |
5 | /* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
6 | public final class BuildConfig {
7 | public final static boolean DEBUG = Boolean.parseBoolean(null);
8 | }
--------------------------------------------------------------------------------
/floating-bubble/src/main/gen/com/bsk/floatingbubblelib/Manifest.java:
--------------------------------------------------------------------------------
1 | /*___Generated_by_IDEA___*/
2 |
3 | package com.bsk.floatingbubblelib;
4 |
5 | /* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
6 | public final class Manifest {
7 | }
--------------------------------------------------------------------------------
/floating-bubble/src/main/gen/com/bsk/floatingbubblelib/R.java:
--------------------------------------------------------------------------------
1 | /*___Generated_by_IDEA___*/
2 |
3 | package com.bsk.floatingbubblelib;
4 |
5 | /* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
6 | public final class R {
7 | }
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/DefaultFloatingBubbleTouchListener.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | /**
4 | * Created by bijoysingh on 2/19/17.
5 | */
6 |
7 | public class DefaultFloatingBubbleTouchListener implements FloatingBubbleTouchListener {
8 | @Override
9 | public void onDown(float x, float y) {
10 |
11 | }
12 |
13 | @Override
14 | public void onTap(boolean expanded) {
15 |
16 | }
17 |
18 | @Override
19 | public void onRemove() {
20 |
21 | }
22 |
23 | @Override
24 | public void onMove(float x, float y) {
25 |
26 | }
27 |
28 | @Override
29 | public void onUp(float x, float y) {
30 |
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/FloatingBubbleAnimator.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.util.Log;
5 | import android.view.View;
6 | import android.view.WindowManager;
7 |
8 | /**
9 | * Animator
10 | * Created by bijoysingh on 2/19/17.
11 | */
12 |
13 | public class FloatingBubbleAnimator {
14 |
15 | private static final int ANIMATION_TIME = 100;
16 | private static final int ANIMATION_STEPS = 5;
17 |
18 | private View bubbleView;
19 | private WindowManager.LayoutParams bubbleParams;
20 | private WindowManager windowManager;
21 | private int sizeX;
22 | private int sizeY;
23 |
24 | private FloatingBubbleAnimator(Builder builder) {
25 | bubbleView = builder.bubbleView;
26 | bubbleParams = builder.bubbleParams;
27 | windowManager = builder.windowManager;
28 | sizeX = builder.sizeX;
29 | sizeY = builder.sizeY;
30 | }
31 |
32 | public void animate(
33 | final float x,
34 | final float y) {
35 | final float startX = bubbleParams.x;
36 | final float startY = bubbleParams.y;
37 | ValueAnimator animator = ValueAnimator.ofInt(0, 5)
38 | .setDuration(ANIMATION_TIME);
39 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
40 | @Override
41 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
42 | try {
43 | float currentX = startX + ((x - startX) *
44 | (Integer) valueAnimator.getAnimatedValue() / ANIMATION_STEPS);
45 | float currentY = startY + ((y - startY) *
46 | (Integer) valueAnimator.getAnimatedValue() / ANIMATION_STEPS);
47 | bubbleParams.x = (int) currentX;
48 | bubbleParams.x = bubbleParams.x < 0 ? 0 : bubbleParams.x;
49 | bubbleParams.x = bubbleParams.x > sizeX - bubbleView.getWidth() ? sizeX - bubbleView.getWidth() : bubbleParams.x;
50 |
51 | bubbleParams.y = (int) currentY;
52 | bubbleParams.y = bubbleParams.y < 0 ? 0 : bubbleParams.y;
53 | bubbleParams.y = bubbleParams.y > sizeY - bubbleView.getWidth() ? sizeY - bubbleView.getWidth() : bubbleParams.y;
54 |
55 | windowManager.updateViewLayout(bubbleView, bubbleParams);
56 | } catch (Exception exception) {
57 | Log.e(FloatingBubbleAnimator.class.getSimpleName(), exception.getMessage());
58 | }
59 | }
60 | });
61 | animator.start();
62 | }
63 |
64 | public static final class Builder {
65 | private View bubbleView;
66 | private WindowManager.LayoutParams bubbleParams;
67 | private WindowManager windowManager;
68 | private int sizeX;
69 | private int sizeY;
70 |
71 | public Builder() {
72 | }
73 |
74 | public Builder bubbleView(View val) {
75 | bubbleView = val;
76 | return this;
77 | }
78 |
79 | public Builder bubbleParams(WindowManager.LayoutParams val) {
80 | bubbleParams = val;
81 | return this;
82 | }
83 |
84 | public Builder windowManager(WindowManager val) {
85 | windowManager = val;
86 | return this;
87 | }
88 |
89 | public Builder sizeX(int val) {
90 | sizeX = val;
91 | return this;
92 | }
93 |
94 | public Builder sizeY(int val) {
95 | sizeY = val;
96 | return this;
97 | }
98 |
99 | public FloatingBubbleAnimator build() {
100 | return new FloatingBubbleAnimator(this);
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/FloatingBubbleConfig.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.graphics.drawable.Drawable;
6 | import android.view.Gravity;
7 | import android.view.View;
8 | import android.support.v4.content.ContextCompat;
9 | import android.widget.TextView;
10 |
11 | /**
12 | * Floating configurations
13 | * Created by bijoysingh on 2/19/17.
14 | */
15 |
16 | public class FloatingBubbleConfig {
17 | private Drawable bubbleIcon;
18 | private Drawable removeBubbleIcon;
19 | private View expandableView;
20 | private int bubbleIconDp;
21 | private int removeBubbleIconDp;
22 | private float removeBubbleAlpha;
23 | private int expandableColor;
24 | private int triangleColor;
25 | private int gravity;
26 | private int paddingDp;
27 | private int borderRadiusDp;
28 | private boolean physicsEnabled;
29 |
30 | private FloatingBubbleConfig(Builder builder) {
31 | bubbleIcon = builder.bubbleIcon;
32 | removeBubbleIcon = builder.removeBubbleIcon;
33 | expandableView = builder.expandableView;
34 | bubbleIconDp = builder.bubbleIconDp;
35 | removeBubbleIconDp = builder.removeBubbleIconDp;
36 | expandableColor = builder.expandableColor;
37 | triangleColor = builder.triangleColor;
38 | gravity = builder.gravity;
39 | paddingDp = builder.paddingDp;
40 | borderRadiusDp = builder.borderRadiusDp;
41 | physicsEnabled = builder.physicsEnabled;
42 | removeBubbleAlpha = builder.removeBubbleAlpha;
43 | }
44 |
45 | public static Builder getDefaultBuilder(Context context) {
46 | return new Builder()
47 | .bubbleIcon(ContextCompat.getDrawable(context, R.drawable.bubble_default_icon))
48 | .removeBubbleIcon(ContextCompat.getDrawable(context, R.drawable.close_default_icon))
49 | .bubbleIconDp(64)
50 | .removeBubbleIconDp(64)
51 | .paddingDp(4)
52 | .removeBubbleAlpha(1.0f)
53 | .physicsEnabled(true)
54 | .expandableColor(Color.WHITE)
55 | .triangleColor(Color.WHITE)
56 | .gravity(Gravity.END);
57 | }
58 |
59 | public static FloatingBubbleConfig getDefault(Context context) {
60 | return getDefaultBuilder(context).build();
61 | }
62 |
63 | public Drawable getBubbleIcon() {
64 | return bubbleIcon;
65 | }
66 |
67 | public Drawable getRemoveBubbleIcon() {
68 | return removeBubbleIcon;
69 | }
70 |
71 | public View getExpandableView() {
72 | return expandableView;
73 | }
74 |
75 | public int getBubbleIconDp() {
76 | return bubbleIconDp;
77 | }
78 |
79 | public int getRemoveBubbleIconDp() {
80 | return removeBubbleIconDp;
81 | }
82 |
83 | public int getExpandableColor() {
84 | return expandableColor;
85 | }
86 |
87 | public int getTriangleColor() {
88 | return triangleColor;
89 | }
90 |
91 | public int getGravity() {
92 | return gravity;
93 | }
94 |
95 | public int getPaddingDp() {
96 | return paddingDp;
97 | }
98 |
99 | public boolean isPhysicsEnabled() {
100 | return physicsEnabled;
101 | }
102 |
103 | public int getBorderRadiusDp() {
104 | return borderRadiusDp;
105 | }
106 |
107 | public float getRemoveBubbleAlpha() {
108 | return removeBubbleAlpha;
109 | }
110 |
111 | public static final class Builder {
112 | private Drawable bubbleIcon;
113 | private Drawable removeBubbleIcon;
114 | private View expandableView;
115 | private int bubbleIconDp = 64;
116 | private int removeBubbleIconDp = 64;
117 | private int expandableColor = Color.WHITE;
118 | private int triangleColor = Color.WHITE;
119 | private int gravity = Gravity.END;
120 | private int paddingDp = 4;
121 | private int borderRadiusDp = 4;
122 | private float removeBubbleAlpha = 1.0f;
123 | private boolean physicsEnabled = true;
124 |
125 | public Builder() {
126 | }
127 |
128 | public Builder bubbleIcon(Drawable val) {
129 | bubbleIcon = val;
130 | return this;
131 | }
132 |
133 | public Builder removeBubbleIcon(Drawable val) {
134 | removeBubbleIcon = val;
135 | return this;
136 | }
137 |
138 | public Builder expandableView(View val) {
139 | expandableView = val;
140 | return this;
141 | }
142 |
143 | public Builder bubbleIconDp(int val) {
144 | bubbleIconDp = val;
145 | return this;
146 | }
147 |
148 | public Builder removeBubbleIconDp(int val) {
149 | removeBubbleIconDp = val;
150 | return this;
151 | }
152 |
153 | public Builder triangleColor(int val) {
154 | triangleColor = val;
155 | return this;
156 | }
157 |
158 | public Builder expandableColor(int val) {
159 | expandableColor = val;
160 | return this;
161 | }
162 |
163 | public FloatingBubbleConfig build() {
164 | return new FloatingBubbleConfig(this);
165 | }
166 |
167 | public Builder gravity(int val) {
168 | gravity = val;
169 | if (gravity == Gravity.CENTER ||
170 | gravity == Gravity.CENTER_VERTICAL ||
171 | gravity == Gravity.CENTER_HORIZONTAL) {
172 | gravity = Gravity.CENTER_HORIZONTAL;
173 | } else if (gravity == Gravity.TOP ||
174 | gravity == Gravity.BOTTOM) {
175 | gravity = Gravity.END;
176 | }
177 | return this;
178 | }
179 |
180 | public Builder paddingDp(int val) {
181 | paddingDp = val;
182 | return this;
183 | }
184 |
185 | public Builder borderRadiusDp(int val) {
186 | borderRadiusDp = val;
187 | return this;
188 | }
189 |
190 | public Builder physicsEnabled(boolean val) {
191 | physicsEnabled = val;
192 | return this;
193 | }
194 |
195 | public Builder removeBubbleAlpha(float val) {
196 | removeBubbleAlpha = val;
197 | return this;
198 | }
199 | }
200 |
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/FloatingBubbleLogger.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Floating Bubble Logger
7 | * Created by bijoy on 1/6/17.
8 | */
9 |
10 | public class FloatingBubbleLogger {
11 | private boolean isDebugEnabled;
12 | private String tag;
13 |
14 | public FloatingBubbleLogger() {
15 | isDebugEnabled = false;
16 | tag = FloatingBubbleLogger.class.getSimpleName();
17 | }
18 |
19 | public FloatingBubbleLogger setTag(String tag) {
20 | this.tag = tag;
21 | return this;
22 | }
23 |
24 | public FloatingBubbleLogger setDebugEnabled(boolean enabled) {
25 | this.isDebugEnabled = enabled;
26 | return this;
27 | }
28 |
29 | public void log(String message) {
30 | if (isDebugEnabled) {
31 | Log.d(tag, message);
32 | }
33 | }
34 |
35 | public void log(String message, Throwable throwable) {
36 | if (isDebugEnabled) {
37 | Log.e(tag, message, throwable);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/FloatingBubblePermissions.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.os.Build;
8 | import android.provider.Settings;
9 |
10 | /**
11 | * Utilities for handling permissions
12 | * Created by bijoy on 1/6/17.
13 | */
14 |
15 | public class FloatingBubblePermissions {
16 |
17 | public static final Integer REQUEST_CODE_ASK_PERMISSIONS = 1201;
18 |
19 | /**
20 | * Checks if the permissions is required
21 | *
22 | * @param context the application context
23 | * @return is the permission request needed
24 | */
25 | public static boolean requiresPermission(Context context) {
26 | return Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(context);
27 | }
28 |
29 | /**
30 | * Start the permission request
31 | *
32 | * @param activity the activity
33 | */
34 | public static void startPermissionRequest(Activity activity) {
35 | if (Build.VERSION.SDK_INT >= 23 && requiresPermission(activity)) {
36 | Intent intent = new Intent(
37 | Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
38 | Uri.parse("package:" + activity.getPackageName()));
39 | activity.startActivityForResult(
40 | intent,
41 | REQUEST_CODE_ASK_PERMISSIONS);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/FloatingBubblePhysics.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | import android.graphics.Point;
4 | import android.util.Log;
5 | import android.view.View;
6 | import android.view.WindowManager;
7 |
8 | /**
9 | * FLoating Bubble Physics
10 | * Created by bijoysingh on 2/19/17.
11 | */
12 |
13 | public class FloatingBubblePhysics extends DefaultFloatingBubbleTouchListener {
14 |
15 | private int sizeX;
16 | private int sizeY;
17 | private View bubbleView;
18 | private WindowManager windowManager;
19 | private FloatingBubbleConfig config;
20 |
21 | private WindowManager.LayoutParams bubbleParams;
22 | private FloatingBubbleAnimator animator;
23 | private Point[] previous = {null, null};
24 |
25 | private FloatingBubblePhysics(Builder builder) {
26 | sizeX = builder.sizeX;
27 | sizeY = builder.sizeY;
28 | bubbleView = builder.bubbleView;
29 | windowManager = builder.windowManager;
30 | config = builder.config;
31 |
32 | bubbleParams = (WindowManager.LayoutParams) bubbleView.getLayoutParams();
33 | animator = new FloatingBubbleAnimator.Builder()
34 | .bubbleParams(bubbleParams)
35 | .bubbleView(bubbleView)
36 | .sizeX(sizeX)
37 | .sizeY(sizeY)
38 | .windowManager(windowManager)
39 | .build();
40 | }
41 |
42 | @Override
43 | public void onDown(float x, float y) {
44 | super.onDown(x, y);
45 | previous[0] = null;
46 | previous[1] = new Point((int) x, (int) y);
47 | }
48 |
49 | @Override
50 | public void onMove(float x, float y) {
51 | super.onMove(x, y);
52 | addSelectively(x, y);
53 | }
54 |
55 | @Override
56 | public void onUp(float x, float y) {
57 | addSelectively(x, y);
58 | Log.d(FloatingBubblePhysics.class.getSimpleName(), previous.toString());
59 |
60 | if (previous[0] == null) {
61 | moveToCorner();
62 | } else {
63 | moveLinearlyToCorner();
64 | }
65 | }
66 |
67 | private void moveLinearlyToCorner() {
68 | int x1 = previous[0].x;
69 | int y1 = previous[0].y;
70 | int x2 = previous[1].x;
71 | int y2 = previous[1].y;
72 |
73 | if (x2 == x1) {
74 | moveToCorner();
75 | return;
76 | }
77 |
78 | int xf = x1 < x2 ? sizeX - bubbleView.getWidth() : 0;
79 | int yf = y1 + (y2 - y1) * (xf - x1) / (x2 - x1);
80 | animator.animate(xf, yf);
81 | }
82 |
83 | private void moveToCorner() {
84 | if (previous[1].x < sizeX / 2) {
85 | animator.animate(0, previous[1].y);
86 | } else {
87 | animator.animate(sizeX - bubbleView.getWidth(), previous[1].y);
88 | }
89 | }
90 |
91 | private void addSelectively(float x, float y) {
92 | if (previous[1] != null && previous[1].x == (int) x && previous[1].y == (int) y) {
93 | return;
94 | }
95 |
96 | previous[0] = previous[1];
97 | previous[1] = new Point((int) x, (int) y);
98 | }
99 |
100 | public static final class Builder {
101 | private int sizeX;
102 | private int sizeY;
103 | private View bubbleView;
104 | private WindowManager windowManager;
105 | private FloatingBubbleConfig config;
106 |
107 | public Builder() {
108 | }
109 |
110 | public Builder sizeX(int val) {
111 | sizeX = val;
112 | return this;
113 | }
114 |
115 | public Builder sizeY(int val) {
116 | sizeY = val;
117 | return this;
118 | }
119 |
120 | public Builder bubbleView(View val) {
121 | bubbleView = val;
122 | return this;
123 | }
124 |
125 | public Builder windowManager(WindowManager val) {
126 | windowManager = val;
127 | return this;
128 | }
129 |
130 | public Builder config(FloatingBubbleConfig val) {
131 | config = val;
132 | return this;
133 | }
134 |
135 | public FloatingBubblePhysics build() {
136 | return new FloatingBubblePhysics(this);
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/FloatingBubbleService.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | import android.app.Service;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.res.Configuration;
7 | import android.content.res.Resources;
8 | import android.graphics.PixelFormat;
9 | import android.graphics.Point;
10 | import android.os.Build;
11 | import android.os.IBinder;
12 | import android.support.annotation.NonNull;
13 | import android.support.v7.widget.CardView;
14 | import android.util.DisplayMetrics;
15 | import android.view.Gravity;
16 | import android.view.LayoutInflater;
17 | import android.view.View;
18 | import android.view.ViewGroup;
19 | import android.view.WindowManager;
20 | import android.widget.ImageView;
21 | import android.widget.LinearLayout;
22 |
23 | /**
24 | * Floating Bubble Service. This file is the actual bubble view.
25 | * Created by bijoy on 1/6/17.
26 | */
27 |
28 | public class FloatingBubbleService extends Service {
29 |
30 | protected static final String TAG = FloatingBubbleService.class.getSimpleName();
31 |
32 | // Constructor Variable
33 | protected FloatingBubbleLogger logger;
34 |
35 | // The Window Manager View
36 | protected WindowManager windowManager;
37 |
38 | // The layout inflater
39 | protected LayoutInflater inflater;
40 |
41 | // Window Dimensions
42 | protected Point windowSize = new Point();
43 |
44 | // The Views
45 | protected View bubbleView;
46 | protected View removeBubbleView;
47 | protected View expandableView;
48 |
49 | protected WindowManager.LayoutParams bubbleParams;
50 | protected WindowManager.LayoutParams removeBubbleParams;
51 | protected WindowManager.LayoutParams expandableParams;
52 |
53 | private FloatingBubbleConfig config;
54 | private FloatingBubblePhysics physics;
55 | private FloatingBubbleTouch touch;
56 |
57 | @Override
58 | public void onCreate() {
59 | super.onCreate();
60 | logger = new FloatingBubbleLogger().setDebugEnabled(true).setTag(TAG);
61 | }
62 |
63 | @Override
64 | public IBinder onBind(Intent intent) {
65 | return null;
66 | }
67 |
68 | @Override
69 | public int onStartCommand(Intent intent, int flags, int startId) {
70 | if (intent == null || !onGetIntent(intent)) {
71 | return Service.START_NOT_STICKY;
72 | }
73 |
74 | logger.log("Start with START_STICKY");
75 |
76 | // Remove existing views
77 | removeAllViews();
78 |
79 | // Load the Window Managers
80 | setupWindowManager();
81 | setupViews();
82 | setTouchListener();
83 | return super.onStartCommand(intent, flags, Service.START_STICKY);
84 |
85 | }
86 |
87 |
88 | @Override
89 | public void onDestroy() {
90 | super.onDestroy();
91 | logger.log("onDestroy");
92 | removeAllViews();
93 | }
94 |
95 | private void removeAllViews() {
96 | if (windowManager == null) {
97 | return;
98 | }
99 |
100 | if (bubbleView != null) {
101 | windowManager.removeView(bubbleView);
102 | bubbleView = null;
103 | }
104 |
105 | if (removeBubbleView != null) {
106 | windowManager.removeView(removeBubbleView);
107 | removeBubbleView = null;
108 | }
109 |
110 | if (expandableView != null) {
111 | windowManager.removeView(expandableView);
112 | expandableView = null;
113 | }
114 | }
115 |
116 | @Override
117 | public void onConfigurationChanged(Configuration newConfig) {
118 | super.onConfigurationChanged(newConfig);
119 |
120 | }
121 |
122 | private void setupWindowManager() {
123 | windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
124 | setLayoutInflater();
125 | windowManager.getDefaultDisplay().getSize(windowSize);
126 | }
127 |
128 | protected LayoutInflater setLayoutInflater() {
129 | inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
130 | return inflater;
131 | }
132 |
133 | /**
134 | * Creates the views
135 | */
136 | protected void setupViews() {
137 | config = getConfig();
138 | int padding = dpToPixels(config.getPaddingDp());
139 | int iconSize = dpToPixels(config.getBubbleIconDp());
140 | int bottomMargin = getExpandableViewBottomMargin();
141 |
142 | // Setting up view
143 | bubbleView = inflater.inflate(R.layout.floating_bubble_view, null);
144 | removeBubbleView = inflater.inflate(R.layout.floating_remove_bubble_view, null);
145 | expandableView = inflater.inflate(R.layout.floating_expandable_view, null);
146 |
147 | // Setting up the Remove Bubble View setup
148 | removeBubbleParams = getDefaultWindowParams();
149 | removeBubbleParams.gravity = Gravity.TOP | Gravity.START;
150 | removeBubbleParams.width = dpToPixels(config.getRemoveBubbleIconDp());
151 | removeBubbleParams.height = dpToPixels(config.getRemoveBubbleIconDp());
152 | removeBubbleParams.x = (windowSize.x - removeBubbleParams.width) / 2;
153 | removeBubbleParams.y = windowSize.y - removeBubbleParams.height - bottomMargin;
154 | removeBubbleView.setVisibility(View.GONE);
155 | removeBubbleView.setAlpha(config.getRemoveBubbleAlpha());
156 | windowManager.addView(removeBubbleView, removeBubbleParams);
157 |
158 | // Setting up the Expandable View setup
159 | expandableParams = getDefaultWindowParams(
160 | WindowManager.LayoutParams.MATCH_PARENT,
161 | WindowManager.LayoutParams.MATCH_PARENT);
162 | expandableParams.height = windowSize.y - iconSize - bottomMargin;
163 | expandableParams.gravity = Gravity.TOP | Gravity.START;
164 | expandableView.setVisibility(View.GONE);
165 | ((LinearLayout) expandableView).setGravity(config.getGravity());
166 | expandableView.setPadding(padding, padding, padding, padding);
167 | windowManager.addView(expandableView, expandableParams);
168 |
169 | // Setting up the Floating Bubble View
170 | bubbleParams = getDefaultWindowParams();
171 | bubbleParams.gravity = Gravity.TOP | Gravity.START;
172 | bubbleParams.width = iconSize;
173 | bubbleParams.height = iconSize;
174 | windowManager.addView(bubbleView, bubbleParams);
175 |
176 | // Setting the configuration
177 | if (config.getRemoveBubbleIcon() != null) {
178 | ((ImageView) removeBubbleView).setImageDrawable(config.getRemoveBubbleIcon());
179 | }
180 | if (config.getBubbleIcon() != null) {
181 | ((ImageView) bubbleView).setImageDrawable(config.getBubbleIcon());
182 | }
183 |
184 | CardView card = (CardView) expandableView.findViewById(R.id.expandableViewCard);
185 | card.setRadius(dpToPixels(config.getBorderRadiusDp()));
186 |
187 | ImageView triangle = (ImageView) expandableView.findViewById(R.id.expandableViewTriangle);
188 | LinearLayout container =
189 | (LinearLayout) expandableView.findViewById(R.id.expandableViewContainer);
190 | if (config.getExpandableView() != null) {
191 | triangle.setColorFilter(config.getTriangleColor());
192 | ViewGroup.MarginLayoutParams params =
193 | (ViewGroup.MarginLayoutParams) triangle.getLayoutParams();
194 | params.leftMargin = dpToPixels((config.getBubbleIconDp() - 16) / 2);
195 | params.rightMargin = dpToPixels((config.getBubbleIconDp() - 16) / 2);
196 |
197 | triangle.setVisibility(View.VISIBLE);
198 | container.setVisibility(View.VISIBLE);
199 | card.setVisibility(View.VISIBLE);
200 |
201 | container.setBackgroundColor(config.getExpandableColor());
202 | container.removeAllViews();
203 | container.addView(config.getExpandableView());
204 | } else {
205 | triangle.setVisibility(View.GONE);
206 | container.setVisibility(View.GONE);
207 | card.setVisibility(View.GONE);
208 | }
209 | }
210 |
211 | /**
212 | * Get the Bubble config
213 | *
214 | * @return the config
215 | */
216 | protected FloatingBubbleConfig getConfig() {
217 | return FloatingBubbleConfig.getDefault(getContext());
218 | }
219 |
220 | /**
221 | * Sets the touch listener
222 | */
223 | protected void setTouchListener() {
224 | physics = new FloatingBubblePhysics.Builder()
225 | .sizeX(windowSize.x)
226 | .sizeY(windowSize.y)
227 | .bubbleView(bubbleView)
228 | .config(config)
229 | .windowManager(windowManager)
230 | .build();
231 |
232 | touch = new FloatingBubbleTouch.Builder()
233 | .sizeX(windowSize.x)
234 | .sizeY(windowSize.y)
235 | .listener(getTouchListener())
236 | .physics(physics)
237 | .bubbleView(bubbleView)
238 | .removeBubbleSize(dpToPixels(config.getRemoveBubbleIconDp()))
239 | .windowManager(windowManager)
240 | .expandableView(expandableView)
241 | .removeBubbleView(removeBubbleView)
242 | .config(config)
243 | .marginBottom(getExpandableViewBottomMargin())
244 | .padding(dpToPixels(config.getPaddingDp()))
245 | .build();
246 |
247 | bubbleView.setOnTouchListener(touch);
248 | }
249 |
250 | /**
251 | * Gets the touch listener for the bubble
252 | *
253 | * @return the touch listener
254 | */
255 | public FloatingBubbleTouchListener getTouchListener() {
256 | return new DefaultFloatingBubbleTouchListener() {
257 | @Override
258 | public void onRemove() {
259 | stopSelf();
260 | }
261 | };
262 | }
263 |
264 | /**
265 | * Get the default window layout params
266 | *
267 | * @return the layout param
268 | */
269 | protected WindowManager.LayoutParams getDefaultWindowParams() {
270 | return getDefaultWindowParams(
271 | WindowManager.LayoutParams.WRAP_CONTENT,
272 | WindowManager.LayoutParams.WRAP_CONTENT);
273 | }
274 |
275 | /**
276 | * Get the default window layout params
277 | *
278 | * @return the layout param
279 | */
280 | protected WindowManager.LayoutParams getDefaultWindowParams(int width, int height) {
281 | return new WindowManager.LayoutParams(
282 | width,
283 | height,
284 | Build.VERSION.SDK_INT >= 26
285 | ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
286 | : WindowManager.LayoutParams.TYPE_PHONE,
287 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
288 | | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
289 | | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
290 | PixelFormat.TRANSLUCENT);
291 | }
292 |
293 | /**
294 | * Handles the intent for the service (only if it is not null)
295 | *
296 | * @param intent the intent
297 | */
298 | protected boolean onGetIntent(@NonNull Intent intent) {
299 | return true;
300 | }
301 |
302 | /**
303 | * Get the layout inflater for view inflation
304 | *
305 | * @return the layout inflater
306 | */
307 | protected LayoutInflater getInflater() {
308 | return inflater == null ? setLayoutInflater() : inflater;
309 | }
310 |
311 | /**
312 | * Get the context for the service
313 | *
314 | * @return the context
315 | */
316 | protected Context getContext() {
317 | return getApplicationContext();
318 | }
319 |
320 | /**
321 | * Sets the state of the expanded view
322 | * @param expanded the expanded view state
323 | */
324 | protected void setState(boolean expanded) {
325 | touch.setState(expanded);
326 | }
327 |
328 | /**
329 | * Get the expandable view's bottom margin
330 | *
331 | * @return margin
332 | */
333 | private int getExpandableViewBottomMargin() {
334 | Resources resources = getContext().getResources();
335 | int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
336 | int navBarHeight = 0;
337 | if (resourceId > 0) {
338 | navBarHeight = resources.getDimensionPixelSize(resourceId);
339 | }
340 |
341 | return navBarHeight;
342 | }
343 |
344 | /**
345 | * Converts DPs to Pixel values
346 | *
347 | * @return the pixel value
348 | */
349 | private int dpToPixels(int dpSize) {
350 | DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
351 | return Math.round(dpSize * (displayMetrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
352 | }
353 | }
354 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/FloatingBubbleTouch.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | import android.view.Gravity;
4 | import android.view.MotionEvent;
5 | import android.view.View;
6 | import android.view.WindowManager;
7 |
8 | /**
9 | * Touch event for the Floating Bubble Service
10 | * Created by bijoysingh on 2/19/17.
11 | */
12 |
13 | public class FloatingBubbleTouch implements View.OnTouchListener {
14 |
15 | private static final int TOUCH_CLICK_TIME = 250;
16 | private static final float EXPANSION_FACTOR = 1.25f;
17 |
18 | private int sizeX;
19 | private int sizeY;
20 |
21 | private View bubbleView;
22 | private View removeBubbleView;
23 | private View expandableView;
24 | private WindowManager windowManager;
25 | private FloatingBubbleTouchListener listener;
26 | private FloatingBubbleTouchListener physics;
27 | private int removeBubbleSize;
28 | private FloatingBubbleConfig config;
29 | private int padding;
30 | private int marginBottom;
31 |
32 | private WindowManager.LayoutParams bubbleParams;
33 | private WindowManager.LayoutParams removeBubbleParams;
34 | private WindowManager.LayoutParams expandableParams;
35 | private int removeBubbleStartSize;
36 | private int removeBubbleExpandedSize;
37 | private FloatingBubbleAnimator animator;
38 |
39 | private long touchStartTime = 0;
40 | private long lastTouchTime = 0;
41 | private boolean expanded = false;
42 |
43 | private FloatingBubbleTouch(Builder builder) {
44 | padding = builder.padding;
45 | config = builder.config;
46 | removeBubbleSize = builder.removeBubbleSize;
47 | physics = builder.physics;
48 | listener = builder.listener;
49 | windowManager = builder.windowManager;
50 | expandableView = builder.expandableView;
51 | removeBubbleView = builder.removeBubbleView;
52 | bubbleView = builder.bubbleView;
53 | sizeY = builder.sizeY;
54 | sizeX = builder.sizeX;
55 | marginBottom = builder.marginBottom;
56 |
57 | bubbleParams = (WindowManager.LayoutParams) bubbleView.getLayoutParams();
58 | removeBubbleParams = (WindowManager.LayoutParams) removeBubbleView.getLayoutParams();
59 | expandableParams = (WindowManager.LayoutParams) expandableView.getLayoutParams();
60 | removeBubbleStartSize = removeBubbleSize;
61 | removeBubbleExpandedSize = (int) (EXPANSION_FACTOR * removeBubbleSize);
62 | animator = new FloatingBubbleAnimator.Builder()
63 | .sizeX(sizeX)
64 | .sizeY(sizeY)
65 | .windowManager(windowManager)
66 | .bubbleView(bubbleView)
67 | .bubbleParams(bubbleParams)
68 | .build();
69 | }
70 |
71 | @Override
72 | public boolean onTouch(View view, MotionEvent motionEvent) {
73 | switch (motionEvent.getActionMasked()) {
74 | case MotionEvent.ACTION_DOWN:
75 | touchStartTime = System.currentTimeMillis();
76 | if (listener != null) {
77 | listener.onDown(motionEvent.getRawX(), motionEvent.getRawY());
78 | }
79 | if (sendEventToPhysics()) {
80 | physics.onDown(motionEvent.getRawX(), motionEvent.getRawY());
81 | }
82 | break;
83 |
84 | case MotionEvent.ACTION_MOVE:
85 | lastTouchTime = System.currentTimeMillis();
86 | moveBubbleView(motionEvent);
87 | if (lastTouchTime - touchStartTime > TOUCH_CLICK_TIME) {
88 | compressView();
89 | showRemoveBubble(View.VISIBLE);
90 | }
91 |
92 | if (listener != null) {
93 | listener.onMove(motionEvent.getRawX(), motionEvent.getRawY());
94 | }
95 | if (sendEventToPhysics()) {
96 | physics.onMove(motionEvent.getRawX(), motionEvent.getRawY());
97 | }
98 | break;
99 |
100 | case MotionEvent.ACTION_UP:
101 | case MotionEvent.ACTION_CANCEL:
102 | showRemoveBubble(View.GONE);
103 | lastTouchTime = System.currentTimeMillis();
104 | if (lastTouchTime - touchStartTime < TOUCH_CLICK_TIME) {
105 | toggleView();
106 | if (listener != null) {
107 | listener.onTap(expanded);
108 | }
109 | if (sendEventToPhysics()) {
110 | physics.onTap(expanded);
111 | }
112 | } else {
113 | boolean isRemoved = checkRemoveBubble();
114 | if (listener != null) {
115 | listener.onUp(motionEvent.getRawX(), motionEvent.getRawY());
116 | }
117 | if (!isRemoved && sendEventToPhysics()) {
118 | physics.onUp(motionEvent.getRawX(), motionEvent.getRawY());
119 | }
120 | }
121 | }
122 | return true;
123 | }
124 |
125 | private void moveBubbleView(MotionEvent motionEvent) {
126 | float halfClipSize = bubbleView.getWidth() / 2;
127 | float clipSize = bubbleView.getWidth();
128 |
129 | float leftX = motionEvent.getRawX() - halfClipSize;
130 | leftX = (leftX > sizeX - clipSize) ? (sizeX - clipSize) : leftX;
131 | leftX = leftX < 0 ? 0 : leftX;
132 |
133 | float topY = motionEvent.getRawY() - halfClipSize;
134 | topY = (topY > sizeY - clipSize) ? (sizeY - clipSize) : topY;
135 | topY = topY < 0 ? 0 : topY;
136 |
137 | bubbleParams.x = (int) leftX;
138 | bubbleParams.y = (int) topY;
139 |
140 | handleRemove();
141 | windowManager.updateViewLayout(bubbleView, bubbleParams);
142 | windowManager.updateViewLayout(removeBubbleView, removeBubbleParams);
143 | }
144 |
145 | private void handleRemove() {
146 | if (isInsideRemoveBubble()) {
147 | removeBubbleParams.height = removeBubbleExpandedSize;
148 | removeBubbleParams.width = removeBubbleExpandedSize;
149 | removeBubbleParams.x = (sizeX - removeBubbleParams.width) / 2;
150 | removeBubbleParams.y = sizeY - removeBubbleParams.height - marginBottom;
151 | bubbleParams.x = removeBubbleParams.x + (removeBubbleExpandedSize - bubbleView.getWidth()) / 2;
152 | bubbleParams.y = removeBubbleParams.y + (removeBubbleExpandedSize - bubbleView.getWidth()) / 2;
153 | } else {
154 | removeBubbleParams.height = removeBubbleStartSize;
155 | removeBubbleParams.width = removeBubbleStartSize;
156 | removeBubbleParams.x = (sizeX - removeBubbleParams.width) / 2;
157 | removeBubbleParams.y = sizeY - removeBubbleParams.height - marginBottom;
158 | }
159 | }
160 |
161 | private boolean isInsideRemoveBubble() {
162 | int bubbleSize = removeBubbleView.getWidth() == 0
163 | ? removeBubbleStartSize
164 | : removeBubbleView.getWidth();
165 | int top = removeBubbleParams.y;
166 | int right = removeBubbleParams.x + bubbleSize;
167 | int bottom = removeBubbleParams.y + bubbleSize;
168 | int left = removeBubbleParams.x;
169 |
170 | int centerX = bubbleParams.x + bubbleView.getWidth() / 2;
171 | int centerY = bubbleParams.y + bubbleView.getWidth() / 2;
172 |
173 | return centerX > left && centerX < right && centerY > top && centerY < bottom;
174 | }
175 |
176 | private boolean checkRemoveBubble() {
177 | if (isInsideRemoveBubble()) {
178 | if (listener != null) {
179 | listener.onRemove();
180 | }
181 | if (sendEventToPhysics()) {
182 | physics.onRemove();
183 | }
184 | return true;
185 | }
186 | return false;
187 | }
188 |
189 | private boolean sendEventToPhysics() {
190 | return config.isPhysicsEnabled() && physics != null;
191 | }
192 |
193 | private void showRemoveBubble(int visibility) {
194 | removeBubbleView.setVisibility(visibility);
195 | }
196 |
197 | public void setState(boolean state) {
198 | expanded = state;
199 | if (expanded) {
200 | expandView();
201 | } else {
202 | compressView();
203 | }
204 | }
205 |
206 | private void toggleView() {
207 | expanded = !expanded;
208 | setState(expanded);
209 | }
210 |
211 | private void compressView() {
212 | expandableView.setVisibility(View.GONE);
213 | }
214 |
215 | private void expandView() {
216 | int x = 0;
217 | int y = padding;
218 | switch (config.getGravity()) {
219 | case Gravity.CENTER:
220 | case Gravity.CENTER_HORIZONTAL:
221 | case Gravity.CENTER_VERTICAL:
222 | x = (sizeX - bubbleView.getWidth()) / 2;
223 | break;
224 | case Gravity.LEFT:
225 | case Gravity.START:
226 | x = padding;
227 | break;
228 | case Gravity.RIGHT:
229 | case Gravity.END:
230 | x = sizeX - bubbleView.getWidth() - padding;
231 | break;
232 | }
233 |
234 | animator.animate(x, y);
235 | expandableView.setVisibility(View.VISIBLE);
236 | expandableParams.y = y + bubbleView.getWidth();
237 | windowManager.updateViewLayout(expandableView, expandableParams);
238 | }
239 |
240 | public static final class Builder {
241 | private int sizeX;
242 | private int sizeY;
243 | private View bubbleView;
244 | private View removeBubbleView;
245 | private View expandableView;
246 | private FloatingBubbleLogger logger;
247 | private WindowManager windowManager;
248 | private FloatingBubbleTouchListener listener;
249 | private int removeBubbleSize;
250 | private FloatingBubbleTouchListener physics;
251 | private FloatingBubbleConfig config;
252 | private int padding;
253 | private int marginBottom;
254 |
255 | public Builder() {
256 | }
257 |
258 | public Builder sizeX(int val) {
259 | sizeX = val;
260 | return this;
261 | }
262 |
263 | public Builder sizeY(int val) {
264 | sizeY = val;
265 | return this;
266 | }
267 |
268 | public Builder bubbleView(View val) {
269 | bubbleView = val;
270 | return this;
271 | }
272 |
273 | public Builder removeBubbleView(View val) {
274 | removeBubbleView = val;
275 | return this;
276 | }
277 |
278 | public Builder expandableView(View val) {
279 | expandableView = val;
280 | return this;
281 | }
282 |
283 | public Builder logger(FloatingBubbleLogger val) {
284 | logger = val;
285 | return this;
286 | }
287 |
288 | public Builder windowManager(WindowManager val) {
289 | windowManager = val;
290 | return this;
291 | }
292 |
293 | public FloatingBubbleTouch build() {
294 | return new FloatingBubbleTouch(this);
295 | }
296 |
297 | public Builder removeBubbleSize(int val) {
298 | removeBubbleSize = val;
299 | return this;
300 | }
301 |
302 | public Builder physics(FloatingBubbleTouchListener val) {
303 | physics = val;
304 | return this;
305 | }
306 |
307 | public Builder listener(FloatingBubbleTouchListener val) {
308 | listener = val;
309 | return this;
310 | }
311 |
312 | public Builder config(FloatingBubbleConfig val) {
313 | config = val;
314 | return this;
315 | }
316 |
317 | public Builder padding(int val) {
318 | padding = val;
319 | return this;
320 | }
321 |
322 | public Builder marginBottom(int val) {
323 | marginBottom = val;
324 | return this;
325 | }
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/java/com/bsk/floatingbubblelib/FloatingBubbleTouchListener.java:
--------------------------------------------------------------------------------
1 | package com.bsk.floatingbubblelib;
2 |
3 | /**
4 | * Floating bubble remove listener
5 | * Created by bijoysingh on 2/19/17.
6 | */
7 |
8 | public interface FloatingBubbleTouchListener {
9 |
10 | void onDown(float x, float y);
11 |
12 | void onTap(boolean expanded);
13 |
14 | void onRemove();
15 |
16 | void onMove(float x, float y);
17 |
18 | void onUp(float x, float y);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/res/drawable/bubble_default_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/floating-bubble/src/main/res/drawable/bubble_default_icon.png
--------------------------------------------------------------------------------
/floating-bubble/src/main/res/drawable/close_default_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/floating-bubble/src/main/res/drawable/close_default_icon.png
--------------------------------------------------------------------------------
/floating-bubble/src/main/res/drawable/triangle_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/floating-bubble/src/main/res/drawable/triangle_icon.png
--------------------------------------------------------------------------------
/floating-bubble/src/main/res/layout/floating_bubble_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/res/layout/floating_expandable_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
28 |
29 |
35 |
36 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/res/layout/floating_remove_bubble_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/floating-bubble/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | FloatingBubbleLib
3 |
4 | Remove Floating Bubble
5 | Floating Bubble
6 |
7 |
--------------------------------------------------------------------------------
/sampleapp/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sampleapp/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "28.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 17
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 |
23 | dependencies {
24 | implementation fileTree(dir: 'libs', include: ['*.jar'])
25 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
26 | exclude group: 'com.android.support', module: 'support-annotations'
27 | })
28 | implementation 'com.android.support:appcompat-v7:28.0.0'
29 | implementation 'com.android.support:support-core-utils:28.0.0'
30 | implementation 'com.android.support:support-v4:28.0.0'
31 | implementation 'com.android.support:cardview-v7:28.0.0'
32 | implementation project(':floating-bubble')
33 | testImplementation 'junit:junit:4.12'
34 | }
35 |
--------------------------------------------------------------------------------
/sampleapp/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/bijoy/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sampleapp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/sampleapp/src/main/gen/com/bsk/sampleapp/BuildConfig.java:
--------------------------------------------------------------------------------
1 | /*___Generated_by_IDEA___*/
2 |
3 | package com.bsk.sampleapp;
4 |
5 | /* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
6 | public final class BuildConfig {
7 | public final static boolean DEBUG = Boolean.parseBoolean(null);
8 | }
--------------------------------------------------------------------------------
/sampleapp/src/main/gen/com/bsk/sampleapp/Manifest.java:
--------------------------------------------------------------------------------
1 | /*___Generated_by_IDEA___*/
2 |
3 | package com.bsk.sampleapp;
4 |
5 | /* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
6 | public final class Manifest {
7 | }
--------------------------------------------------------------------------------
/sampleapp/src/main/gen/com/bsk/sampleapp/R.java:
--------------------------------------------------------------------------------
1 | /*___Generated_by_IDEA___*/
2 |
3 | package com.bsk.sampleapp;
4 |
5 | /* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
6 | public final class R {
7 | }
--------------------------------------------------------------------------------
/sampleapp/src/main/java/com/bsk/sampleapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.bsk.sampleapp;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | import com.bsk.floatingbubblelib.FloatingBubblePermissions;
9 | import com.bsk.floatingbubblelib.FloatingBubbleService;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 |
18 | FloatingBubblePermissions.startPermissionRequest(this);
19 | View startBubble = findViewById(R.id.start_bubble);
20 | startBubble.setOnClickListener(new View.OnClickListener() {
21 | @Override
22 | public void onClick(View view) {
23 | startService(new Intent(getApplicationContext(), SimpleService.class));
24 | }
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sampleapp/src/main/java/com/bsk/sampleapp/SimpleService.java:
--------------------------------------------------------------------------------
1 | package com.bsk.sampleapp;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.support.v4.content.ContextCompat;
6 | import android.view.Gravity;
7 | import android.view.WindowManager;
8 |
9 | import com.bsk.floatingbubblelib.FloatingBubbleConfig;
10 | import com.bsk.floatingbubblelib.FloatingBubbleService;
11 | import com.bsk.floatingbubblelib.FloatingBubbleTouchListener;
12 |
13 | /**
14 | * Created by bijoysingh on 2/19/17.
15 | */
16 |
17 | public class SimpleService extends FloatingBubbleService {
18 | @Override
19 | protected FloatingBubbleConfig getConfig() {
20 | Context context = getApplicationContext();
21 | return new FloatingBubbleConfig.Builder()
22 | .bubbleIcon(ContextCompat.getDrawable(context, R.drawable.web_icon))
23 | .removeBubbleIcon(ContextCompat.getDrawable(context, com.bsk.floatingbubblelib.R.drawable.close_default_icon))
24 | .bubbleIconDp(54)
25 | .expandableView(getInflater().inflate(R.layout.sample_view_1, null))
26 | .removeBubbleIconDp(54)
27 | .paddingDp(4)
28 | .borderRadiusDp(0)
29 | .physicsEnabled(true)
30 | .expandableColor(Color.WHITE)
31 | .triangleColor(0xFF215A64)
32 | .gravity(Gravity.LEFT)
33 | .build();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable/close_default_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/sampleapp/src/main/res/drawable/close_default_icon.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable/cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/sampleapp/src/main/res/drawable/cover.jpg
--------------------------------------------------------------------------------
/sampleapp/src/main/res/drawable/web_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/sampleapp/src/main/res/drawable/web_icon.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/layout/sample_view_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
17 |
18 |
27 |
28 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/sampleapp/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/sampleapp/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/sampleapp/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/sampleapp/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/sampleapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SampleApp
3 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed dapibus lectus. Aenean tempor accumsan massa ut pharetra. Pellentesque ac risus quam. Nunc faucibus lectus ut tellus tempus, eu elementum ex facilisis. Quisque commodo mollis lorem nec viverra. Nulla dolor urna, faucibus ac neque a, tristique feugiat diam. Duis vel sem sed lectus consectetur interdum. Etiam sagittis velit vel molestie pharetra. Phasellus id sem id leo pretium volutpat. Nullam a lacus eget lectus lacinia feugiat sed ac mauris. Donec lobortis ut eros sit amet mollis.
4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed dapibus lectus. Aenean tempor accumsan massa ut pharetra.
5 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed dapibus lectus.
6 | Lorem ipsum dolor
7 |
8 |
--------------------------------------------------------------------------------
/sampleapp/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/screenshots/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/screenshots/demo.gif
--------------------------------------------------------------------------------
/screenshots/screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/screenshots/screenshot_1.png
--------------------------------------------------------------------------------
/screenshots/screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BijoySingh/Floating-Bubble-Library-Android/320f4c976d5192d49aac741589bfd7f98fa97ad7/screenshots/screenshot_2.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':floating-bubble', ':sampleapp'
2 |
--------------------------------------------------------------------------------