電話のロックを解除せずにロック画面(KeyGuard)に情報ONを含むウィンドウを表示する必要があるアプリで作業しています。 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG でおそらくできると思った
しかし、次のエラーでアプリがクラッシュするたびに:
Android.view.WindowManager $ BadTokenException:ウィンドウAndroid.view.ViewRootImpl$W@40ec8528を追加できません-このウィンドウタイプの許可は拒否されました
これらの投稿( here 、 here および here )は、すべて同じ答えを提供します。マニフェストファイルに次のアクセス許可を追加します。
Android.permission.SYSTEM_ALERT_WINDOW
実装したソリューションですが、同じエラーが引き続き発生します。私が間違っていることのアイデアはありますか?
マニフェストファイル内の権限は次のとおりです。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
package="com.droidpilot.keyguardwindow" >
<uses-sdk
Android:minSdkVersion="16"
Android:targetSdkVersion="21" />
<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission Android:name="Android.permission.INTERNET" />
<uses-permission Android:name="Android.permission.ACCESS_NETWORK_STATE" />
<uses-permission Android:name="Android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission Android:name="Android.permission.READ_PHONE_STATE" />
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission Android:name="Android.permission.VIBRATE" />
これは、ロック画面にウィンドウを追加するために使用するコードです
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mView = mInflater.inflate(R.layout.lock_screen_notif, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
PixelFormat.TRANSLUCENT
);
wm.addView(mView, params);
誰もがアイデアを思いつきましたか?
追伸HTC Desire 620 DS実行中Android 4.4.2でテストしています
完全に明らかな理由のために、通常のアプリはロック画面の上に任意のウィンドウを作成することはできません。実際のロック画面を完全に模倣できるように、ロック画面にウィンドウを作成して、違いがわからない場合、私は何ができると思いますか?
エラーの技術的な理由はTYPE_KEYGUARD_DIALOG
フラグの使用です-署名レベルの許可であるAndroid.permission.INTERNAL_SYSTEM_WINDOW
が必要です。これは、権限の作成者が同じ証明書で署名したアプリのみが使用できることを意味します。
Android.permission.INTERNAL_SYSTEM_WINDOW
の作成者はAndroidシステム自体なので、アプリがOSの一部でない限り、チャンスはありません。
ロック画面からの情報をユーザーに通知する の、明確に定義され、文書化された方法があります。ロック画面に表示するカスタマイズされた通知を作成でき、ユーザーはそれらと対話できます。
apiLevel >= 19
を使用する場合は、使用しないでください
WindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
次のエラーが発生します。
Android.view.WindowManager $ BadTokenException:ウィンドウAndroid.view.ViewRootImpl$W@40ec8528を追加できません-このウィンドウタイプの許可は拒否されました
代わりにこれを使用してください:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
ターゲットを区別する必要があると思います(オレオの前後)
int LAYOUT_FLAG;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
}
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
LAYOUT_FLAG,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
この問題で利用可能なすべての例を試してみました。最終的に私はこれに対する答えを得ましたが、どれだけ信頼できるかわかりませんが、私のアプリは現在クラッシュしていません。
windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
//here is all the science of params
final LayoutParams myParams = new LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
LayoutParams.TYPE_SYSTEM_ERROR,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
PixelFormat.TRANSLUCENT
);
マニフェストファイルで許可を与えるだけです
<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW"/>
これに加えて、> = 23の場合はAPIレベルを確認することもできます
if(Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(Activity.this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1234);
}
}
else
{
Intent intent = new Intent(Activity.this, Service.class);
startService(intent);
}
どこかで役立つことを願っています。完全な例 https://anam-Android-codes.blogspot.in/?m=1
8.0.0のAndroid APIレベルの場合、使用する必要があります
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
の代わりに
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
またはSYSTEM_ALERT
。
このコードを完全に試してみてください
int layout_parms;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
layout_parms = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}
else {
layout_parms = WindowManager.LayoutParams.TYPE_PHONE;
}
yourparams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
layout_parms,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
サーチ
他のアプリの上に描画
設定でアプリを有効にします。 Android 8 Oreoの場合、試してください
設定>アプリと通知>アプリ情報>他のアプリの上に表示>有効
Android Oとの互換性を確保するために、プロジェクトのWindowmangerフラグ「TYPE_SYSTEM_OVERLAY」を「TYPE_APPLICATION_OVERLAY」に変更します
WindowManager.LayoutParams.TYPE_PHONE
からWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
TYPE_SYSTEM_OVERLAY
の代わりにTYPE_KEYGUARD_DIALOG
を使用して、最終的にロック画面にウィンドウを表示することに成功しました。これは期待どおりに機能し、ロック画面にウィンドウを追加します。
これに伴う問題は、可能な限りすべての上にウィンドウが追加されることです。つまり、ウィンドウはキーパッド/安全なロック画面の場合のパターンロック。セキュリティで保護されていないロック画面の場合、ロック画面から開くと通知トレイの上部に表示されます。
私にとっては、それは受け入れられません。これがこの問題に直面している他の人の助けになることを願っています。
LayoutParams.TYPE_PHONEは非推奨です。これらのようにコードを更新しました。
parameters = if (Build.VERSION.SDK_INT > 25) {
LayoutParams(
minHW * 2 / 3, minHW * 2 / 3,
LayoutParams.TYPE_APPLICATION_OVERLAY,
LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT)
}else {
LayoutParams(
minHW * 2 / 3, minHW * 2 / 3,
LayoutParams.TYPE_PHONE,
LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT)
}
LayoutParams.TYPE_APPLICATION_OVERLAYには、APIレベル26以上が必要です。
まず、マニフェストファイルに追加権限があることを確認します。
<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW"/>
アプリケーションが他のアプリの描画許可を持っているかどうかを確認しますか?この許可は、デフォルトでAPI <23で使用可能です。ただし、API> 23の場合、実行時に許可を要求する必要があります。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
}
このコードを使用:
public class ChatHeadService extends Service {
private WindowManager mWindowManager;
private View mChatHeadView;
WindowManager.LayoutParams params;
public ChatHeadService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Language language = new Language();
//Inflate the chat head layout we created
mChatHeadView = LayoutInflater.from(this).inflate(R.layout.dialog_incoming_call, null);
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
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
params.x = 0;
params.y = 100;
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowManager.addView(mChatHeadView, params);
} else {
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
params.x = 0;
params.y = 100;
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowManager.addView(mChatHeadView, params);
}
TextView tvTitle=mChatHeadView.findViewById(R.id.tvTitle);
tvTitle.setText("Incoming Call");
//Set the close button.
Button btnReject = (Button) mChatHeadView.findViewById(R.id.btnReject);
btnReject.setText(language.getText(R.string.reject));
btnReject.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//close the service and remove the chat head from the window
stopSelf();
}
});
//Drag and move chat head using user's touch action.
final Button btnAccept = (Button) mChatHeadView.findViewById(R.id.btnAccept);
btnAccept.setText(language.getText(R.string.accept));
LinearLayout linearLayoutMain=mChatHeadView.findViewById(R.id.linearLayoutMain);
linearLayoutMain.setOnTouchListener(new View.OnTouchListener() {
private int lastAction;
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();
lastAction = event.getAction();
return true;
case MotionEvent.ACTION_UP:
//As we implemented on touch listener with ACTION_MOVE,
//we have to check if the previous action was ACTION_DOWN
//to identify if the user clicked the view or not.
if (lastAction == MotionEvent.ACTION_DOWN) {
//Open the chat conversation click.
Intent intent = new Intent(ChatHeadService.this, HomeActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
//close the service and remove the chat heads
stopSelf();
}
lastAction = event.getAction();
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(mChatHeadView, params);
lastAction = event.getAction();
return true;
}
return false;
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
if (mChatHeadView != null) mWindowManager.removeView(mChatHeadView);
}
}