Amir Amir - 3 months ago 23
Android Question

Android Chathead Crashing on Start (No Permission)

I have added permission to the manifest as below:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.appbubble">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>


It is strange when I go to app setting I dont see any permission?

But as soon as I open the app I get this crash message.

08-28 00:59:53.945 31761-31761/app.appbubble E/AndroidRuntime: FATAL EXCEPTION: main
Process: app.appbubble, PID: 31761
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@2d54966 -- permission denied for this window type
at android.view.ViewRootImpl.setView(ViewRootImpl.java:875)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:337)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
at com.txusballesteros.bubbles.BubblesService$2.run(BubblesService.java:120)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)


This is the MainActivity:

package app.appbubble;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;

import com.txusballesteros.bubbles.BubbleLayout;
import com.txusballesteros.bubbles.BubblesManager;
import com.txusballesteros.bubbles.OnInitializedCallback;

public class MainActivity extends AppCompatActivity {

private BubblesManager bubblesManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SYSTEM_ALERT_WINDOW) != PackageManager.PERMISSION_GRANTED ){//Can add more as per requirement

ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW},
123);

}
}
setContentView(R.layout.activity_main);


// configure bublle manager
initializeBubbleManager();

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addNewNotification();
}
});
}

/*
* Inflate notifation layout into bubble layout
*/
private void addNewNotification() {
BubbleLayout bubbleView = (BubbleLayout) LayoutInflater.from(MainActivity.this)
.inflate(R.layout.notification_layout, null);
// this method call when user remove notification layout
bubbleView.setOnBubbleRemoveListener(new BubbleLayout.OnBubbleRemoveListener() {
@Override
public void onBubbleRemoved(BubbleLayout bubble) {
Toast.makeText(getApplicationContext(), "Bubble removed !",
Toast.LENGTH_SHORT).show();
}
});
// this methoid call when cuser click on the notification layout( bubble layout)
bubbleView.setOnBubbleClickListener(new BubbleLayout.OnBubbleClickListener() {

@Override
public void onBubbleClick(BubbleLayout bubble) {
Toast.makeText(getApplicationContext(), "Clicked !",
Toast.LENGTH_SHORT).show();
}
});

// add bubble view into bubble manager
bubblesManager.addBubble(bubbleView, 60, 20);
}

/**
* Configure the trash layout with your BubblesManager builder.
*/
private void initializeBubbleManager() {
bubblesManager = new BubblesManager.Builder(this)
.setTrashLayout(R.layout.notification_trash_layout)
.build();
bubblesManager.initialize();
}

@Override
protected void onDestroy() {
super.onDestroy();
bubblesManager.recycle();
}
}

Answer

I'm going to take a wild guess(due to lack of code) that you run on API 23 and never requested permission:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.SYSTEM_ALERT_WINDOW) != PackageManager.PERMISSION_GRANTED ){//Can add more as per requirement

        ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW},
                    123);

    }
}

For development and release, it is best practice as it is signature protection level(in release)

What does "Signature protection level" mean?

A permission that the system grants only if the requesting application is signed with the same certificate as the application that declared the permission. If the certificates match, the system automatically grants the permission without notifying the user or asking for the user's explicit approval.

And as it says in the documentation:

Note: If the app targets API level 23 or higher, the app user must explicitly grant this permission to the app through a permission management screen. The app requests the user's approval by sending an intent with action ACTION_MANAGE_OVERLAY_PERMISSION. The app can check whether it has this authorization by calling Settings.canDrawOverlays().

Best practice for requesting;

This is a rather disputed topic, as Google recommends requesting when they are being used but I have seen Google apps requesting before they are being used. And a lot of developers have meanings about it as well.

What I do is request when the app starts. That means the first thing that happens, request permissions.

Though you can request as a separate screen where you give the user an explanation why you need the permissions.

Or as Google recommends, request when you need it.

EDIT 2:

public final static int REQUEST_CODE = 6341; 

public void checkDrawOverlayPermission() {
    /** check if we already  have permission to draw over other apps */
    if (!Settings.canDrawOverlays(Context)) {
        /** if not construct intent to request permission */
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName()));
        /** request permission via start activity for result */
        startActivityForResult(intent, REQUEST_CODE);
    }
}

Why like that?

There are a couple of permissions that don't behave like normal and dangerous permissions. SYSTEM_ALERT_WINDOW and WRITE_SETTINGS are particularly sensitive, so most apps should not use them. If an app needs one of these permissions, it must declare the permission in the manifest, and send an intent requesting the user's authorization. The system responds to the intent by showing a detailed management screen to the user.

Ignore the above code(requesting permission code) it is the edit 2 code that is valid.