This commit is contained in:
Hamza-Ayed
2024-07-31 21:19:19 +03:00
parent dea83d970c
commit 2bc71355c3
106 changed files with 4600 additions and 727 deletions

8
bubble-master/android/.gitignore vendored Executable file
View File

@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

View File

@@ -0,0 +1,35 @@
group 'com.dsaved.bubblehead.bubble'
version '1.0'
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
android {
namespace 'com.dsaved.bubblehead.bubble'
compileSdkVersion 30
defaultConfig {
minSdkVersion 16
}
}
dependencies {
implementation 'com.google.android.material:material:1.4.0'
}

View File

@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

View File

@@ -0,0 +1 @@
rootProject.name = 'bubble'

View File

@@ -0,0 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dsaved.bubblehead.bubble">
</manifest>

View File

@@ -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);
}
}
}

View File

@@ -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() {
}
}

View 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>

View 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,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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View 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>

View 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>

View 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>

View 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>