API for notification listener for Companioon apps
Test: 1. Trigger the confitrmation dialog.
Ensure it looks exactly like the one from settings.
2. Call an API without associating the appa first
Ensure exception is thrown with a message mentioning the need to associate 1st
Change-Id: I0b280e9bcef7f9d21ce7c9dfeaf2703945481efd
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b38cea7..02351e5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2645,6 +2645,10 @@
android:value="true" />
</activity>
+ <!-- Confirmation dialog for enabling notification access from CompanionDeviceManager -->
+ <activity android:name=".notification.NotificationAccessConfirmationActivity"
+ android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+
<receiver android:name=".widget.SettingsAppWidgetProvider"
android:label="@string/gadget_title"
android:exported="false"
diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
new file mode 100644
index 0000000..a78fed7
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.settings.notification;
+
+import static com.android.internal.notification.NotificationAccessConfirmationActivityContract
+ .EXTRA_COMPONENT_NAME;
+import static com.android.internal.notification.NotificationAccessConfirmationActivityContract
+ .EXTRA_PACKAGE_TITLE;
+import static com.android.internal.notification.NotificationAccessConfirmationActivityContract
+ .EXTRA_USER_ID;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.SettingsStringUtil;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.settings.R;
+import com.android.settings.core.TouchOverlayManager;
+
+/** @hide */
+public class NotificationAccessConfirmationActivity extends Activity
+ implements DialogInterface {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "NotificationAccessConfirmationActivity";
+
+ private int mUserId;
+ private ComponentName mComponentName;
+ private TouchOverlayManager mTouchOverlayManager;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mTouchOverlayManager = new TouchOverlayManager(this);
+
+ mComponentName = getIntent().getParcelableExtra(EXTRA_COMPONENT_NAME);
+ mUserId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL);
+ String pkgTitle = getIntent().getStringExtra(EXTRA_PACKAGE_TITLE);
+
+ AlertController.AlertParams p = new AlertController.AlertParams(this);
+ p.mTitle = getString(
+ R.string.notification_listener_security_warning_title,
+ pkgTitle);
+ p.mMessage = getString(
+ R.string.notification_listener_security_warning_summary,
+ pkgTitle);
+ p.mPositiveButtonText = getString(R.string.allow);
+ p.mPositiveButtonListener = (a, b) -> onAllow();
+ p.mNegativeButtonText = getString(R.string.deny);
+ p.mNegativeButtonListener = (a, b) -> cancel();
+ AlertController
+ .create(this, this, getWindow())
+ .installContent(p);
+ }
+
+ private void onAllow() {
+ String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
+ try {
+ ServiceInfo serviceInfo = getPackageManager().getServiceInfo(mComponentName, 0);
+ if (!requiredPermission.equals(serviceInfo.permission)) {
+ Slog.e(LOG_TAG,
+ "Service " + mComponentName + " lacks permission " + requiredPermission);
+ return;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(LOG_TAG, "Failed to get service info for " + mComponentName, e);
+ return;
+ }
+
+ final SettingsStringUtil.SettingStringHelper setting =
+ new SettingsStringUtil.SettingStringHelper(
+ getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+ mUserId);
+ setting.write(SettingsStringUtil.ComponentNameSet.add(setting.read(), mComponentName));
+
+ finish();
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ return AlertActivity.dispatchPopulateAccessibilityEvent(this, event);
+ }
+
+ @Override
+ public void cancel() {
+ finish();
+ }
+
+ @Override
+ public void dismiss() {
+ // This is called after the click, since we finish when handling the
+ // click, don't do that again here.
+ if (!isFinishing()) {
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mTouchOverlayManager.setOverlayAllowed(false);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mTouchOverlayManager.setOverlayAllowed(true);
+ }
+}