I have spend like 2 hours,
I'm unable to figure out what is the issue in service. I'm not calling stopService() or stopSelf from anywhere else. Below is the code ,
public class FloatingViewService extends Service {
private WindowManager mWindowManager;
private View mFloatingView;
WindowManager.LayoutParams params,landscapeParams,nonTouchableParams;
public FloatingViewService() {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
//Inflate the floating view layout we created
mFloatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating_widget, null);
//Add the view to the window.
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
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
params.x = 0;
params.y = 100;
//Add the view to the window.
landscapeParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
//Specify the view position
landscapeParams.gravity = Gravity.TOP | Gravity.LEFT; //Initially view will be added to top-left corner
landscapeParams.x = 0;
landscapeParams.y = 100;
//Add the view to the window.
nonTouchableParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
//Specify the view position
nonTouchableParams.gravity = Gravity.TOP | Gravity.LEFT; //Initially view will be added to top-left corner
nonTouchableParams.x = 0;
nonTouchableParams.y = 100;
//Add the view to the window
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowManager.addView(mFloatingView, params);
//The root element of the collapsed view layout
final View collapsedView = mFloatingView.findViewById(R.id.collapse_view);
//The root element of the expanded view layout
final View expandedView = mFloatingView.findViewById(R.id.expanded_container);
//Set the close button
ImageView closeButtonCollapsed = (ImageView) mFloatingView.findViewById(R.id.close_btn);
closeButtonCollapsed.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//close the service and remove the from from the window
stopSelf();
}
});
//Set the close button
ImageView closeButton = (ImageView) mFloatingView.findViewById(R.id.close_button);
closeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
collapsedView.setVisibility(View.VISIBLE);
expandedView.setVisibility(View.GONE);
}
});
//Set the close button
ImageView lock = (ImageView) mFloatingView.findViewById(R.id.lock_button);
lock.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mWindowManager.updateViewLayout(mFloatingView, nonTouchableParams);
}
});
//Set the close button
ImageView expand = (ImageView) mFloatingView.findViewById(R.id.expand);
expand.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mWindowManager.updateViewLayout(mFloatingView, landscapeParams);
}
});
//Drag and move floating view using user's touch action.
mFloatingView.findViewById(R.id.root_container).setOnTouchListener(new View.OnTouchListener() {
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//remember the initial position.
initialX = params.x;
initialY = params.y;
//get the touch location
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return true;
case MotionEvent.ACTION_UP:
int Xdiff = (int) (event.getRawX() - initialTouchX);
int Ydiff = (int) (event.getRawY() - initialTouchY);
//The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
//So that is click event.
if (Xdiff < 10 && Ydiff < 10) {
if (isViewCollapsed()) {
//When user clicks on the image view of the collapsed layout,
//visibility of the collapsed layout will be changed to "View.GONE"
//and expanded view will become visible.
collapsedView.setVisibility(View.GONE);
expandedView.setVisibility(View.VISIBLE);
}
}
return true;
case MotionEvent.ACTION_MOVE:
//Calculate the X and Y coordinates of the view.
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
//Update the layout with new X & Y coordinate
mWindowManager.updateViewLayout(mFloatingView, params);
return true;
}
return false;
}
});
}
/**
* Detect if the floating view is collapsed or expanded.
*
* #return true if the floating view is collapsed.
*/
private boolean isViewCollapsed() {
return mFloatingView == null || mFloatingView.findViewById(R.id.collapse_view).getVisibility() == View.VISIBLE;
}
#Override
public void onDestroy() {
super.onDestroy();
if (mFloatingView != null) mWindowManager.removeView(mFloatingView);
}
}
I am not able to figure out anomalous behaviour help me out.
Start your service as forground.
https://developer.android.com/reference/android/app/Service.html#startForeground(int, android.app.Notification)
Since you want your service to run continuously you will have to override the onStartCommand method and return START_STICKY
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
For started services, there are two additional major modes of operation they can decide to run in, depending on the value they return from onStartCommand(): START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them. See the linked documentation for more detail on the semantics.
https://developer.android.com/reference/android/app/Service.html
Related
This question already has answers here:
SYSTEM_ALERT_WINDOW PERMISSION on API 26 not working as expected. Permission denied for window type 2002
(6 answers)
Closed 3 years ago.
I was working in Floating Widget service icon ,the code is working fine till android version 6.1,i am able to get the notification icon from the server when it send the notification.
but while testing it in android version 8.1 the application getting crushed when the notification is send from the server,
but i am not able to find the solution for it.i dont have any idea why it is happening.
LOGCAT
beginning of crash
2019-03-02 11:41:21.416 12616-12616/com.progressive_solution.parttimeforce E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.progressive_solution.parttimeforce, PID: 12616
java.lang.RuntimeException: Unable to create service com.progressive_solution.parttimeforce.FloatingWidgetService: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W#ac5309 -- permission denied for window type 2002
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3568)
at android.app.ActivityThread.-wrap4(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1812)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:7000)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:441)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W#ac5309 -- permission denied for window type 2002
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1026)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:384)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:101)
at com.progressive_solution.parttimeforce.FloatingWidgetService.addRemoveView(FloatingWidgetService.java:86)
at com.progressive_solution.parttimeforce.FloatingWidgetService.onCreate(FloatingWidgetService.java:58)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3558)
at android.app.ActivityThread.-wrap4(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1812)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:7000)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:441)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
MYCODE
FloatingWidgetService.java
public class FloatingWidgetService extends Service implements View.OnClickListener
{
private WindowManager mWindowManager;
private View mFloatingWidgetView, collapsedView, expandedView;
private ImageView remove_image_view;
private Point szWindow = new Point();
private View removeFloatingWidgetView;
private int x_init_cord, y_init_cord, x_init_margin, y_init_margin;
//Variable to check if the Floating widget view is on left side or in right side
// initially we are displaying Floating widget view to Left side so set it to true
private boolean isLeft = true;
public FloatingWidgetService() {
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
//init WindowManager
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
getWindowManagerDefaultDisplay();
//Init LayoutInflater
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
addRemoveView(inflater);
addFloatingWidgetView(inflater);
implementClickListeners();
implementTouchListenerToFloatingWidgetView();
}
/* Add Remove View to Window Manager */
private View addRemoveView(LayoutInflater inflater) {
//Inflate the removing view layout we created
removeFloatingWidgetView = inflater.inflate(R.layout.remove_floating_widget_layout, null);
//Add the view to the window.
WindowManager.LayoutParams paramRemove = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
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);
return remove_image_view;
}
/* Add Floating Widget View to Window Manager */
private void addFloatingWidgetView(LayoutInflater inflater) {
//Inflate the floating view layout we created
mFloatingWidgetView = inflater.inflate(R.layout.activity_floating_widget_service, null);
//Add the view to the window.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
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);
//find id of collapsed view layout
collapsedView = mFloatingWidgetView.findViewById(R.id.collapse_view);
//find id of the expanded view layout
expandedView = mFloatingWidgetView.findViewById(R.id.expanded_container);
}
private void getWindowManagerDefaultDisplay() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
mWindowManager.getDefaultDisplay().getSize(szWindow);
else {
int w = mWindowManager.getDefaultDisplay().getWidth();
int h = mWindowManager.getDefaultDisplay().getHeight();
szWindow.set(w, h);
}
}
/* Implement Touch Listener to Floating Widget Root View */
private void implementTouchListenerToFloatingWidgetView() {
//Drag and move floating view using user's touch action.
mFloatingWidgetView.findViewById(R.id.root_container).setOnTouchListener(new View.OnTouchListener() {
long time_start = 0, time_end = 0;
boolean isLongClick = false;//variable to judge if user click long press
boolean inBounded = false;//variable to judge if floating view is bounded to remove view
int remove_img_width = 0, remove_img_height = 0;
Handler handler_longClick = new Handler();
Runnable runnable_longClick = new Runnable() {
#Override
public void run() {
//On Floating Widget Long Click
//Set isLongClick as true
isLongClick = true;
//Set remove widget view visibility to VISIBLE
removeFloatingWidgetView.setVisibility(View.VISIBLE);
onFloatingWidgetLongClick();
}
};
#Override
public boolean onTouch(View v, MotionEvent event) {
//Get Floating widget view params
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
//get the touch location coordinates
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();
handler_longClick.postDelayed(runnable_longClick, 600);
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;
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;
}
//Get the difference between initial coordinate and current coordinate
int x_diff = x_cord - x_init_cord;
int y_diff = y_cord - y_init_cord;
//The check for x_diff <5 && y_diff< 5 because sometime elements moves a little while clicking.
//So that is click event.
if (Math.abs(x_diff) < 5 && Math.abs(y_diff) < 5) {
time_end = System.currentTimeMillis();
//Also check the difference between start time and end time should be less than 300ms
if ((time_end - time_start) < 300)
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 if user drags the floating view
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) {
remove_image_view.getLayoutParams().height = (int) (remove_img_height * 1.5);
remove_image_view.getLayoutParams().width = (int) (remove_img_width * 1.5);
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;
}
});
}
private void implementClickListeners() {
mFloatingWidgetView.findViewById(R.id.close_floating_view).setOnClickListener(this);
mFloatingWidgetView.findViewById(R.id.close_expanded_view).setOnClickListener(this);
mFloatingWidgetView.findViewById(R.id.open_activity_button).setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.close_floating_view:
//close the service and remove the from from the window
stopSelf();
break;
case R.id.close_expanded_view:
collapsedView.setVisibility(View.VISIBLE);
expandedView.setVisibility(View.GONE);
break;
case R.id.open_activity_button:
//open the activity and stop service
Intent intent = new Intent(FloatingWidgetService.this, Get_Permission.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
//close the service and remove view from the view hierarchy
stopSelf();
break;
}
}
/* on Floating Widget Long Click, increase the size of remove view as it look like taking focus */
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 (x_cord_now <= szWindow.x / 2) {
isLeft = true;
moveToLeft(x_cord_now);
} else {
isLeft = false;
moveToRight(x_cord_now);
}
}
/* Method to move the Floating widget view to Left */
private void moveToLeft(final int current_x_cord) {
final int x = szWindow.x - current_x_cord;
new CountDownTimer(500, 5) {
//get params of Floating Widget view
WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
public void onTick(long t) {
long step = (500 - t) / 5;
mParams.x = 0 - (int) (current_x_cord * current_x_cord * step);
//If you want bounce effect uncomment below line and comment above line
// mParams.x = 0 - (int) (double) bounceValue(step, x);
//Update window manager for Floating Widget
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
}
public void onFinish() {
mParams.x = 0;
//Update window manager for Floating Widget
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
}
}.start();
}
/* Method to move the Floating widget view to Right */
private void moveToRight(final int current_x_cord) {
new CountDownTimer(500, 5) {
//get params of Floating Widget view
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 you want bounce effect uncomment below line and comment above line
// mParams.x = szWindow.x + (int) (double) bounceValue(step, x_cord_now) - mFloatingWidgetView.getWidth();
//Update window manager for Floating Widget
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
}
public void onFinish() {
mParams.x = szWindow.x - mFloatingWidgetView.getWidth();
//Update window manager for Floating Widget
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
}
}.start();
}
/* Get Bounce value if you want to make bounce effect to your Floating Widget */
private double bounceValue(long step, long scale) {
double value = scale * java.lang.Math.exp(-0.055 * step) * java.lang.Math.cos(0.08 * step);
return value;
}
/* Detect if the floating view is collapsed or expanded */
private boolean isViewCollapsed() {
return mFloatingWidgetView == null || mFloatingWidgetView.findViewById(R.id.collapse_view).getVisibility() == View.VISIBLE;
}
/* return status bar height on basis of device display metrics */
private int getStatusBarHeight() {
return (int) Math.ceil(25 * getApplicationContext().getResources().getDisplayMetrics().density);
}
/* Update Floating Widget view coordinates on Configuration change */
#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);
}
}
}
/* on Floating widget click show expanded view */
private void onFloatingWidgetClick() {
if (isViewCollapsed()) {
//When user clicks on the image view of the collapsed layout,
//visibility of the collapsed layout will be changed to "View.GONE"
//and expanded view will become visible.
collapsedView.setVisibility(View.GONE);
expandedView.setVisibility(View.VISIBLE);
}
}
#Override
public void onDestroy() {
super.onDestroy();
/* on destroy remove both view from window manager */
if (mFloatingWidgetView != null)
mWindowManager.removeView(mFloatingWidgetView);
if (removeFloatingWidgetView != null)
mWindowManager.removeView(removeFloatingWidgetView);
}
}
Permissioncheck.java
private void writeCalendarEvent()
{
Log.d(Utils.LogTag, "lst_StartService -> Utils.canDrawOverlays(Main.this): " + Utils.canDrawOverlays(Permissioncheck.this));
if(Utils.canDrawOverlays(Permissioncheck.this))
{
checkPermission_Contact();
startService();
}
else
{
requestPermission(OVERLAY_PERMISSION_REQ_CODE_CHATHEAD);
}
}
public void startService()
{
boolean results = checkPermission_Contact();
if (results)
{
Intent k = new Intent(context, Login.class);
startActivity(k);
}
}
private void needPermissionDialog(final int requestCode)
{
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(Permissioncheck.this);
builder.setMessage("You need to allow permission");
builder.setPositiveButton("OK",
new android.content.DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
// TODO Auto-generated method stub
requestPermission(requestCode);
}
});
builder.setNegativeButton("Cancel", new android.content.DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
// TODO Auto-generated method stub
}
});
builder.setCancelable(false);
builder.show();
}
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
}
private void requestPermission(int requestCode)
{
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, requestCode);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == OVERLAY_PERMISSION_REQ_CODE_CHATHEAD)
{
if (!Utils.canDrawOverlays(Permissioncheck.this))
{
needPermissionDialog(requestCode);
} else
{
startService();
}
}
}
Help me to fix the problem.
add WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
reference
I have an incomming call Broadcast receiver, it is working on android 7.1 nut not on android 4.2.
Broadcast Receiver
public class CallReceiver extends BroadcastReceiver {
private MainActivity activity;
public static final String PREFS_PHONE = "Phone";
public static final String PREFS_EMAIL = "Email";
#Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
SharedPreferences settings = context.getSharedPreferences(context.getResources().getString(R.string.app_name),0);
if(settings.contains(PREFS_EMAIL)) {
if (extras != null) {
String state = extras.getString(TelephonyManager.EXTRA_STATE);
Log.w("MY_DEBUG_TAG", state);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
DatabaseHandler db = new DatabaseHandler(context);
User user;
String phone = extras.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
try {
user = db.getUserByPhone(phone);
Log.d("USER", user.getEmail());
context.startActivity(new Intent(context, FloatingActivity.class)
.putExtra("PHONE", TelephonyManager.EXTRA_INCOMING_NUMBER));
String phoneNumber = extras
.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
settings.edit().putString(PREFS_PHONE, phone).apply();
Log.d("MY_DEBUG_TAG", phoneNumber);
} catch (IndexOutOfBoundsException e) {
Log.i(phone, "Not Found!");
}
}
}
}
}
}
Floating activity
public class FloatingActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chathead_layout);
startService(new Intent(this, FloatingViewService.class)
.putExtra("PHONE", this.getIntent().getStringExtra("PHONE")));
finish();
}
}
Floating Service
public class FloatingViewService extends Service {
private View chatHead;
private View closeChatHead;
private WindowManager windowManager;
private DatabaseHandler db = new DatabaseHandler(this);
private String phone;
private SharedPreferences settings;
private boolean mayRemove;
private Rect chatHeadRect;
private Rect closeHeadRect;
private boolean isHover;
public static final String PREFS_PHONE = "Phone";
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
//Inflate the floating view layout we created
settings = getSharedPreferences(getResources().getString(R.string.app_name),0);
phone = settings.getString(PREFS_PHONE, null);
User user = db.getUserByPhone(phone);
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
chatHead = LayoutInflater.from(this).inflate(R.layout.chathead_layout, null);
closeChatHead = LayoutInflater.from(this).inflate(R.layout.close_chathead_layout, null);
closeChatHead.setVisibility(View.INVISIBLE);
//Add the view to the window.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
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
params.x = 0;
params.y = 500;
final WindowManager.LayoutParams params2 = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params2.gravity = Gravity.BOTTOM | Gravity.CENTER;
//Add the view to the window
windowManager.addView(chatHead, params);
windowManager.addView(closeChatHead, params2);
((CircleImageView) chatHead.findViewById(R.id.profile_image)).setImageDrawable(new ColorDrawable(Color.parseColor("#"+user.getColour())));
((TextView) chatHead.findViewById(R.id.pts)).setText(user.getPts()+"");
switch (user.getDirection()) {
case 1:
chatHead.findViewById(R.id.up).setVisibility(View.VISIBLE);
break;
case 0:
chatHead.findViewById(R.id.equal).setVisibility(View.VISIBLE);
break;
case -1:
chatHead.findViewById(R.id.down).setVisibility(View.VISIBLE);
break;
}
final CircleImageView remover = ((CircleImageView) chatHead.findViewById(R.id.remover));
//Set the close button
/*ImageView closeButton = (ImageView) chatHead.findViewById(R.id.close_btn);
closeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//close the service and remove the from from the window
stopSelf();
}
});*/
//Drag and move floating view using user's touch action.
chatHead.findViewById(R.id.chat_head).setOnTouchListener(new View.OnTouchListener() {
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//remember the initial position.
initialX = params.x;
initialY = params.y;
//get the touch location
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
closeChatHead.setVisibility(View.VISIBLE);
return true;
case MotionEvent.ACTION_MOVE:
//show remover
//Calculate the X and Y coordinates of the view.
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
//Update the layout with new X & Y coordinate
windowManager.updateViewLayout(chatHead, params);
int[] a = new int[2];
int[] b = new int[2];
chatHead.getLocationOnScreen(a);
View remover = closeChatHead.findViewById(R.id.remover);
remover.getLocationOnScreen(b);
chatHeadRect = new Rect(a[0], a[1], a[0] + chatHead.getWidth(), a[1] + chatHead.getHeight());
closeHeadRect = new Rect(b[0], b[1], b[0] + remover.getWidth(), b[1] + remover.getHeight());
if (checkToRemove(Math.round((chatHeadRect.left)) + Math.round((chatHeadRect.width()) / 2),
Math.round((chatHeadRect.top)) + Math.round((chatHeadRect.height()) / 2),
closeHeadRect.left, closeHeadRect.top, closeHeadRect.left + closeHeadRect.width())) {
//Log.d("motion move","intersect = yes");
mayRemove = true;
if (!isHover) {
isHover = !isHover;
windowManager.removeView(closeChatHead);
closeChatHead = LayoutInflater.from(FloatingViewService.this).inflate(R.layout.close_chathead_layout, null);
windowManager.addView(closeChatHead, params2);
}
} else {
//Log.d("motion move","intersect = no");
mayRemove = false;
if (isHover) {
//Log.d("MOTION MOVE2","PASSOU");
isHover = !isHover;
windowManager.removeView(closeChatHead);
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
closeChatHead = inflater.inflate(R.layout.close_chathead_layout, null);
windowManager.addView(closeChatHead, params2);
}
}
closeChatHead.setVisibility(View.VISIBLE);
return true;
case MotionEvent.ACTION_UP:
int Xdiff = (int) (event.getRawX() - initialTouchX);
int Ydiff = (int) (event.getRawY() - initialTouchY);
//The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
//So that is click event.
if (Xdiff < 10 && Ydiff < 10) {
Intent intent = new Intent(FloatingViewService.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
//close the service and remove view from the view hierarchy
stopSelf();
}
if (mayRemove) {
stopSelf();
}
closeChatHead.setVisibility(View.INVISIBLE);
return true;
}
return false;
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
if (chatHead != null) windowManager.removeView(chatHead);
}
private boolean checkToRemove(int x,int y,int left,int top,int left_width){
return (y>(top-50) && x>(left-50) && x<(left_width-50));
}
}
Why not working:
In 7.1 everything is working fine i get the call and the chathead apears.
In 4.4 [LG] I receive the call but nothing happens.
In 4.4 [BQ] Aplication crashes.
Any help?
Found the problem.
Where I'm calling startActivity from outside an activity like:
context.startActivity(new Intent(context, FloatingActivity.class)
.putExtra("PHONE", TelephonyManager.EXTRA_INCOMING_NUMBER));
in android 4.4 it is required to set the flag Intent.FLAG_ACTIVITY_NEW_TASK
o in all ocurrencies i need to call it like:
context.startActivity(new Intent(context, FloatingActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("PHONE", TelephonyManager.EXTRA_INCOMING_NUMBER));
I have an Android app with a Service to Overlay a button on Android (like Facebook chat bubble).
I want to show a layout (overlay.xml) when the user click on the icon but I can't show the layout with the good dimension.
This is my result :
This is my OverlayShowingService.class :
public class OverlayShowingService extends Service {
private WindowManager windowManager;
private ImageView chatHead;
private Boolean _enable = true;
#Override
public IBinder onBind(Intent intent) {
_enable=false;
return null;
}
#Override
public void onCreate() {
super.onCreate();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
chatHead = new ImageView(this);
chatHead.setImageResource(R.mipmap.ic_launcher);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = 0;
params.y = 100;
windowManager.addView(chatHead, params);
chatHead.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
LayoutInflater layoutInflater = (LayoutInflater)getBaseContext()
.getSystemService(LAYOUT_INFLATER_SERVICE);
View popupView = layoutInflater.inflate(R.layout.overlay, null);
final PopupWindow popupWindow = new PopupWindow(popupView,
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
popupWindow.update();
Button btnDismiss = (Button)popupView.findViewById(R.id.dismiss);
Button endService= (Button) popupView.findViewById(R.id.endService);
endService.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
stopSelf();
Toast.makeText(getApplicationContext(), "Service Terminated", Toast.LENGTH_LONG).show();
}
});
btnDismiss.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(), "Popup Terminated", Toast.LENGTH_LONG).show();
popupWindow.dismiss();
_enable=true;
}
});
if(_enable){
popupWindow.showAsDropDown(chatHead, 50, -30);
_enable=false;
}
else if(!_enable) {
Log.d("FALSE", "FALSE");
}
}
});
try {
chatHead.setOnTouchListener(new View.OnTouchListener() {
private WindowManager.LayoutParams paramsF = params;
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
#Override public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = paramsF.x;
initialY = paramsF.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
paramsF.x = initialX + (int) (event.getRawX() - initialTouchX);
paramsF.y = initialY + (int) (event.getRawY() - initialTouchY);
windowManager.updateViewLayout(chatHead, paramsF);
break;
}
return false;
}
});
} catch (Exception ignored) {}
}
#Override
public void onDestroy() {
super.onDestroy();
if (chatHead != null) windowManager.removeView(chatHead);
}
}
This is the layout overlay.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:orientation="vertical"
android:layout_margin="5dp"
android:background="#545454">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="It's a PopupWindow"
android:textSize="40sp"
android:textColor="#ffffff" />
<Button
android:id="#+id/dismiss"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Dismiss Popup"
android:textColor="#ffffff" />
<Button
android:id="#+id/endService"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Finish Service"
android:textColor="#ffffff" />
</LinearLayout>
Edit: This is an alternate approach that mimics a popup window. I have kept the first answer with a altert dialog approach after the following explanation and code. Either way will work, but this new way may be preferable since it looks more like a popup window.
This approach simply adds the overlay.xml layout into a view that is place on the screen just like the chatHead view is.
OverlayShowingService.java
public class OverlayShowingService extends Service {
private WindowManager windowManager;
private ImageView chatHead;
private Boolean _enable = true;
#Override
public IBinder onBind(Intent intent) {
_enable = false;
return null;
}
#Override
public void onCreate() {
super.onCreate();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
chatHead = new ImageView(this);
chatHead.setImageResource(R.mipmap.ic_launcher);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
windowManager.addView(chatHead, params);
chatHead.post(new Runnable() {
#Override
public void run() {
params.x = (int) chatHead.getBottom() + 50;
params.y = (int) chatHead.getRight() - 30;
}
});
chatHead.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
LayoutInflater layoutInflater = (LayoutInflater) getBaseContext()
.getSystemService(LAYOUT_INFLATER_SERVICE);
final View popupView = layoutInflater.inflate(R.layout.overlay, null);
final PopupWindow popupWindow = new PopupWindow(popupView,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.update();
Button btnDismiss = (Button) popupView.findViewById(R.id.dismiss);
Button endService = (Button) popupView.findViewById(R.id.endService);
endService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
windowManager.removeViewImmediate(popupView);
stopSelf();
Toast.makeText(getApplicationContext(), "Service Terminated", Toast.LENGTH_LONG).show();
}
});
btnDismiss.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(), "Popup Terminated", Toast.LENGTH_LONG).show();
// popupWindow.dismiss();
windowManager.removeViewImmediate(popupView);
_enable = true;
}
});
if (_enable) {
windowManager.addView(popupView, params);
// popupWindow.showAsDropDown(chatHead, 50, -30);
_enable = false;
} else if (!_enable) {
Log.d("FALSE", "FALSE");
}
}
});
try {
chatHead.setOnTouchListener(new View.OnTouchListener() {
private WindowManager.LayoutParams paramsF = params;
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = paramsF.x;
initialY = paramsF.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
paramsF.x = initialX + (int) (event.getRawX() - initialTouchX);
paramsF.y = initialY + (int) (event.getRawY() - initialTouchY);
windowManager.updateViewLayout(chatHead, paramsF);
break;
}
return false;
}
});
} catch (Exception ignored) {
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (chatHead != null) windowManager.removeView(chatHead);
}
}
The first approach using an alert dialog.
Your code works as-is for API 21 and below. Something changed with API 22 that is prohibiting what you want to do.
I think the primary issue is with the popup and the service environment. I have modified your code to use an AlertDialog with the type TYPE_SYSTEM_ALERT. Although the styling is not what you want, it does work otherwise. You may be able to get it to style differently if you play with it a little.
Another alternative would be to start an activity from your service. An activity-based approach will present you with a more pliable (and forgiving) environment to do what you want with the UI. (This is not as straight-forward as one may think.)
Below is the code that works as a system-level alert. Here is a screen shot.
OverlayShowingService
chatHead.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
final AlertDialog alertDialog = new AlertDialog.Builder(arg0.getContext(),
R.style.Theme_AppCompat_Dialog_Alert).create();
LayoutInflater layoutInflater = (LayoutInflater) getBaseContext()
.getSystemService(LAYOUT_INFLATER_SERVICE);
final View popupView = layoutInflater.inflate(R.layout.overlay, null);
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog.setView(popupView);
Button btnDismiss = (Button) popupView.findViewById(R.id.dismiss);
Button endService = (Button) popupView.findViewById(R.id.endService);
endService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
alertDialog.dismiss();
stopSelf();
Toast.makeText(getApplicationContext(), "Service Terminated", Toast.LENGTH_LONG).show();
}
});
btnDismiss.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(), "Popup Terminated", Toast.LENGTH_LONG).show();
alertDialog.dismiss();
}
});
alertDialog.show();
}
});
As I stated in my first answer, you are having this issue with API 22 and later. Your code works OK for API 21 and earlier.
You are running afoul of changes made to PopupWindow in API level 22. See this documentation.
Fix issue #17789629: PopupWindow overlaps with navigation bar.
The Lollipop release introduced a feature that allowed
apps to extend under the navigation bar. This also means
any popup window that is anchored to the bottom of its
parent window will overlap with the navigation bar if the
parent window is extending underneath the navigation bar.
This change introduces a new window flag
(FLAG_LAYOUT_ATTACHED_IN_DECOR) that allows the app to
specify if the popup window should be attached to the decor
frame of the parent window thereby avoiding an overlap
with the screen decorations.
By default the flag is set on SDK version LOLLIPOP_MR1 or
greater and cleared on lesser SDK versions.
You can turn off this flag with the setAttachedInDecor method of PopupWindow.
Add the following code to OverlayShowingService.java after popupWindow.update() in the OnClickListener and you should be good to go.
// Turn off this functionality introduced in API 22.
// See documentation for FLAG_LAYOUT_ATTACHED_IN_DECOR in
// WindowManager.LayoutParams
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
popupWindow.setAttachedInDecor(false);
}
I have an Overlay Button in my Android app. I want to show a layout and interact with the views of my Layout when the user click on the button.
For the moment, I show a Toast. How can I do that ?
This is my OverlayShowingService.class :
public class OverlayShowingService extends Service implements OnTouchListener, OnClickListener {
private View topLeftView;
private Button overlayedButton;
private float offsetX;
private float offsetY;
private int originalXPos;
private int originalYPos;
private boolean moving;
private WindowManager wm;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
overlayedButton = new Button(this);
overlayedButton.setText("Overlay button");
overlayedButton.setOnTouchListener(this);
overlayedButton.setBackgroundColor(Color.BLACK);
overlayedButton.setOnClickListener(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.TOP;
params.x = 0;
params.y = 0;
wm.addView(overlayedButton, params);
topLeftView = new View(this);
WindowManager.LayoutParams topLeftParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT);
topLeftParams.gravity = Gravity.LEFT | Gravity.TOP;
topLeftParams.x = 0;
topLeftParams.y = 0;
topLeftParams.width = 0;
topLeftParams.height = 0;
wm.addView(topLeftView, topLeftParams);
}
#Override
public void onDestroy() {
super.onDestroy();
if (overlayedButton != null) {
wm.removeView(overlayedButton);
wm.removeView(topLeftView);
overlayedButton = null;
topLeftView = null;
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
float x = event.getRawX();
float y = event.getRawY();
moving = false;
int[] location = new int[2];
overlayedButton.getLocationOnScreen(location);
originalXPos = location[0];
originalYPos = location[1];
offsetX = originalXPos - x;
offsetY = originalYPos - y;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
int[] topLeftLocationOnScreen = new int[2];
topLeftView.getLocationOnScreen(topLeftLocationOnScreen);
System.out.println("topLeftY="+topLeftLocationOnScreen[1]);
System.out.println("originalY="+originalYPos);
float x = event.getRawX();
float y = event.getRawY();
WindowManager.LayoutParams params = (LayoutParams) overlayedButton.getLayoutParams();
int newX = (int) (offsetX + x);
int newY = (int) (offsetY + y);
if (Math.abs(newX - originalXPos) < 1 && Math.abs(newY - originalYPos) < 1 && !moving) {
return false;
}
params.x = newX - (topLeftLocationOnScreen[0]);
params.y = newY - (topLeftLocationOnScreen[1]);
wm.updateViewLayout(overlayedButton, params);
moving = true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (moving) {
return true;
}
}
return false;
}
#Override
public void onClick(View v) {
Toast.makeText(this, "Here I want to show a layout with some options in a box", Toast.LENGTH_SHORT).show();
}
}
Send a broadcast when then the button is clicked and start activity from Broadcast receivers onReceive method.
Create a broadcast receiver,Register a Broadcast reciever onCreate() of your service then. Broadcast intent when the button is clicked. Finally the receiver will open a Activity(layout) when the overlay button is clicked.
If you only wanted to open a dialog this is a decent answer. https://stackoverflow.com/a/31221355/4179914
Turns out you can also theme a activity like a dialog as mentioned here
https://stackoverflow.com/a/7918720/4179914
public class OverlayClickReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent toDialog = new Intent(context, Main2Activity.class);
toDialog.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(toDialog);
}
}
Send Broadcast on overlay button clicked.
#Override
public void onClick(View v) {
broadcastClick();
}
private void broadcastClick() {
final Intent intent = new Intent("user.clicked.overlay.button");
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
broadcastManager.sendBroadcast(intent);
}
Register Broadcast receiver onCreate()
#Override
public void onCreate() {
super.onCreate();
overlayShowing = new OverlayClickReceiver();
.......
}
private void registerClickReceiver() {
LocalBroadcastManager.getInstance(this).registerReceiver(overlayShowing,
new IntentFilter("user.clicked.overlay.button"));
}
Unregister Broadcast receiver onDestroy()
#Override
public void onDestroy() {
super.onDestroy();
..........
unregisterClickReceriver();
}
private void unregisterClickReceriver() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(overlayShowing);
}
From what i understood from the question, you want to get a view form xml files and add it to a window.
You can use View view = LayoutInflater.from(context).inflate(r.layout.overlay,null,false); and add that view with WindowManager.addView(view) to the window.
I am trying to create a draggable imageView ("chatHead" in the code, kinda like the chat head of Facebook)
I followed the instruction on the android website but whereever I drag the imageview, it always ends up at the left upper corner. (with x,y cordinate being (0,0) ~ (90,90))
Later on I found out the problem is that the case: MotionEvent.ACTION_MOVE is only entered when my touch is around the imageView location.
And when I drag a bit further away from the imageView, ACTION_MOVE is no longer entered. And instead the case DragEvent.ACTION_DRAG_EXIT is executed.
Can someone tell me how can I expand the area for the ACTION_MOVE to be executed or what is the problem. I would like to locate the imageView at whichever location I want on the screen.
( you may just focus on onDragListener, the MyDragShadowBuilder is a customized extending DragShawdowBuilder and onLongClickListener is just to trigger the drag)
How to keep the imageView even the app is closed?
public class ChatHead extends Service {
private WindowManager windowManager;
private ImageView chatHead;
private WindowManager.LayoutParams params;
private final String CHAT_TAG ="CHAT";
private int cor_x = 0;
private int cor_y = 0;
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override public void onCreate() {
super.onCreate();
ImageView image = new ImageView(this);
Log.d("", "created");
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
chatHead = new ImageView(this);
chatHead.setTag(CHAT_TAG);
chatHead.setImageResource(R.drawable.chat_icon);
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = 300;
params.y = 300;
chatHead.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
ClipData.Item item = new ClipData.Item(v.getTag().toString());
String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
ClipData dragData = new ClipData(v.getTag().toString(), mimeTypes,item);
View.DragShadowBuilder myShadow = new MyDragShadowBuilder(chatHead);
// Starts the drag
v.startDrag(dragData, // the data to be dragged
myShadow, // the drag shadow builder
null, // no need to use local data
0 // flags (not currently used, set to 0)
);
return false;}
});
chatHead.setOnDragListener(new View.OnDragListener() {
#Override
public boolean onDrag(View v, DragEvent event) {
//cor_x = ((int) chatHead.getX());
//cor_y = ((int) chatHead.getY());
switch (event.getAction()){
case DragEvent.ACTION_DRAG_ENTERED : { Log.d("", "x:"+ event.getX()+"y:"+event.getY());break;}
case DragEvent.ACTION_DRAG_STARTED : { Log.d("", "x:"+ event.getX()+" y:"+event.getY());break;}
case MotionEvent.ACTION_MOVE: {
cor_x = ((int) event.getX());
cor_y = ((int) event.getX());
Log.d("", "x:"+ cor_x+" y:"+cor_y);
break;}
case DragEvent.ACTION_DRAG_EXITED:{
Log.d("", "x:"+ cor_x+" y:"+cor_y);
break;
}
case DragEvent.ACTION_DRAG_ENDED : {
if(windowManager!=null && params!=null){
params.x = cor_x;
params.y = cor_y;
Log.d("", "x:"+ cor_x+" y:"+cor_y);
windowManager.removeView(chatHead);
windowManager.addView(chatHead, params);
}
return false;
}
}
return false;
}
});
windowManager.addView(chatHead, params);
}
If I understand what you mean, your problem is that you don't get the MotionEvent.ACTION_MOVE unless your finger is above your image.
Well that is because the concept of DragListener is that the View that register that listener, accept the events. So the only view in your layout that register to those events is chatHead, and he gets only the events that occur above him.
So if you want to get all the events in layout you must register view that take the whole screen space. So the quickest solution is to wrap the view with container that take the whole screen, and register it instead the chatHead to the DragListener. Somethong like:
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
RelativeLayout container = new RelativeLayout(this);
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
chatHead = new ImageView(this);
chatHead.setTag(CHAT_TAG);
chatHead.setImageResource(R.drawable.chat_icon);
RelativeLayout.LayoutParams chatHeadParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
chatHeadParams.addRule(Gravity.TOP | Gravity.LEFT) ;
chatHeadParams.leftMargin = 300;
chatHeadParams.topMargin = 300;
container.addView(chatHead, chatHeadParams);
windowManager.addView(container, params);
And changing the line:
chatHead.setOnLongClickListener(new View.OnLongClickListener() {
to:
container.setOnLongClickListener(new View.OnLongClickListener() {
You may also adjust additional things because the DragEvent event.getX() and event.getX() will return different values according to the View itself and the DragEvent.Action type.