Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b8ec3434 authored by John Spurlock's avatar John Spurlock
Browse files

Settings: Carve out app-level notifcation settings dialog.

Bug:16208321
Change-Id: I46574618518e8fe4cbef70e80204cc7bb7cb76e9
parent 3a166613
Loading
Loading
Loading
Loading
+11 −5
Original line number Original line Diff line number Diff line
@@ -1836,17 +1836,23 @@
                android:label="@string/app_notifications_title"
                android:label="@string/app_notifications_title"
                android:exported="true"
                android:exported="true"
                android:taskAffinity="">
                android:taskAffinity="">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.settings.ACTION_APP_NOTIFICATION_SETTINGS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                android:value="com.android.settings.notification.AppNotificationSettings" />
                android:value="com.android.settings.notification.AppNotificationSettings" />
            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
                android:resource="@id/notification_settings" />
                android:resource="@id/notification_settings" />
        </activity>
        </activity>


        <activity android:name=".notification.AppNotificationDialog"
                android:theme="@style/Theme.AlertDialog"
                android:launchMode="singleTop"
                android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.settings.APP_NOTIFICATION_SETTINGS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <!-- Show regulatory info (from settings item or dialing "*#07#") -->
        <!-- Show regulatory info (from settings item or dialing "*#07#") -->
        <activity android:name="RegulatoryInfoDisplayActivity"
        <activity android:name="RegulatoryInfoDisplayActivity"
                  android:label="@string/regulatory_information"
                  android:label="@string/regulatory_information"
+180 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2014 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 android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.CompoundButton.OnCheckedChangeListener;

import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.settings.R;
import com.android.settings.notification.AppNotificationSettings.Backend;
import com.android.settings.notification.AppNotificationSettings.AppRow;

public class AppNotificationDialog extends AlertActivity {
    private static final String TAG = "AppNotificationDialog";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    /**
     * Show a checkbox in the per-app notification control dialog to allow the user to
     * selectively redact this app's notifications on the lockscreen.
     */
    private static final boolean ENABLE_APP_NOTIFICATION_PRIVACY_OPTION = false;

    private final Context mContext = this;
    private final Backend mBackend = new Backend();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (DEBUG) Log.d(TAG, "onCreate getIntent()=" + getIntent());
        if (!buildDialog()) {
            Toast.makeText(mContext, R.string.app_not_found_dlg_text, Toast.LENGTH_SHORT).show();
            finish();
        }
    }

    private boolean buildDialog() {
        final Intent intent = getIntent();
        if (intent != null) {
            final int uid = intent.getIntExtra(Settings.EXTRA_APP_UID, -1);
            final String pkg = intent.getStringExtra(Settings.EXTRA_APP_PACKAGE);
            if (uid != -1 && !TextUtils.isEmpty(pkg)) {
                if (DEBUG) Log.d(TAG, "Load details for pkg=" + pkg + " uid=" + uid);
                final PackageManager pm = getPackageManager();
                final PackageInfo info = findPackageInfo(pm, pkg, uid);
                if (info != null) {
                    final AppRow row = AppNotificationSettings.loadAppRow(pm, info, mBackend);
                    final AlertController.AlertParams p = mAlertParams;
                    p.mView = getLayoutInflater().inflate(R.layout.notification_app_dialog,
                            null, false);
                    p.mPositiveButtonText = getString(R.string.app_notifications_dialog_done);
                    bindDialog(p.mView, row);
                    setupAlert();
                    return true;
                } else {
                    Log.w(TAG, "Failed to find package info");
                }
            } else {
                Log.w(TAG, "Missing extras: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg + ", "
                        + Settings.EXTRA_APP_UID + " was " + uid);
            }
        } else {
            Log.w(TAG, "No intent");
        }
        return false;
    }

    private static PackageInfo findPackageInfo(PackageManager pm, String pkg, int uid) {
        final String[] packages = pm.getPackagesForUid(uid);
        if (packages != null && pkg != null) {
            final int N = packages.length;
            for (int i = 0; i < N; i++) {
                final String p = packages[i];
                if (pkg.equals(p)) {
                    try {
                        return pm.getPackageInfo(pkg, 0);
                    } catch (NameNotFoundException e) {
                        Log.w(TAG, "Failed to load package " + pkg, e);
                    }
                }
            }
        }
        return null;
    }

    private void bindDialog(final View v, final AppRow row) {
        final ImageView icon = (ImageView) v.findViewById(android.R.id.icon);
        icon.setImageDrawable(row.icon);
        final TextView title = (TextView) v.findViewById(android.R.id.title);
        title.setText(row.label);
        final CheckBox showNotifications = (CheckBox) v.findViewById(android.R.id.button1);
        final CheckBox highPriority = (CheckBox) v.findViewById(android.R.id.button2);
        final CheckBox sensitive = (CheckBox) v.findViewById(android.R.id.button3);

        if (!ENABLE_APP_NOTIFICATION_PRIVACY_OPTION) {
            sensitive.setVisibility(View.GONE);
        }

        showNotifications.setChecked(!row.banned);
        final OnCheckedChangeListener showListener = new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                boolean success = mBackend.setNotificationsBanned(row.pkg, row.uid, !isChecked);
                if (success) {
                    row.banned = !isChecked;
                    highPriority.setEnabled(!row.banned);
                    sensitive.setEnabled(!row.banned);
                } else {
                    showNotifications.setOnCheckedChangeListener(null);
                    showNotifications.setChecked(!isChecked);
                    showNotifications.setOnCheckedChangeListener(this);
                }
            }
        };
        showNotifications.setOnCheckedChangeListener(showListener);

        highPriority.setChecked(row.priority);
        final OnCheckedChangeListener priListener = new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                boolean success = mBackend.setHighPriority(row.pkg, row.uid, isChecked);
                if (success) {
                    row.priority = isChecked;
                } else {
                    highPriority.setOnCheckedChangeListener(null);
                    highPriority.setChecked(!isChecked);
                    highPriority.setOnCheckedChangeListener(this);
                }
            }
        };
        highPriority.setOnCheckedChangeListener(priListener);

        sensitive.setChecked(row.sensitive);
        final OnCheckedChangeListener senListener = new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                boolean success = mBackend.setSensitive(row.pkg, row.uid, isChecked);
                if (success) {
                    row.sensitive = isChecked;
                } else {
                    sensitive.setOnCheckedChangeListener(null);
                    sensitive.setChecked(!isChecked);
                    sensitive.setOnCheckedChangeListener(this);
                }
            }
        };
        sensitive.setOnCheckedChangeListener(senListener);

        highPriority.setEnabled(!row.banned);
        sensitive.setEnabled(!row.banned);
    }
}
+27 −117
Original line number Original line Diff line number Diff line
@@ -17,9 +17,7 @@
package com.android.settings.notification;
package com.android.settings.notification;


import android.animation.LayoutTransition;
import android.animation.LayoutTransition;
import android.app.AlertDialog;
import android.app.INotificationManager;
import android.app.INotificationManager;
import android.app.ListFragment;
import android.app.Notification;
import android.app.Notification;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -37,6 +35,7 @@ import android.os.Handler;
import android.os.Parcelable;
import android.os.Parcelable;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Log;
import android.util.TypedValue;
import android.util.TypedValue;
@@ -45,11 +44,7 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.SectionIndexer;
import android.widget.SectionIndexer;
import android.widget.TextView;
import android.widget.TextView;


@@ -65,18 +60,7 @@ import java.util.List;
/** Just a sectioned list of installed applications, nothing else to index **/
/** Just a sectioned list of installed applications, nothing else to index **/
public class AppNotificationSettings extends PinnedHeaderListFragment {
public class AppNotificationSettings extends PinnedHeaderListFragment {
    private static final String TAG = "AppNotificationSettings";
    private static final String TAG = "AppNotificationSettings";
    private static final boolean DEBUG = true;
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    /**
     * Show a checkbox in the per-app notification control dialog to allow the user
     * to promote this app's notifications to higher priority.
     */
    private static final boolean ENABLE_APP_NOTIFICATION_PRIORITY_OPTION = true;
    /**
     * Show a checkbox in the per-app notification control dialog to allow the user to
     * selectively redact this app's notifications on the lockscreen.
     */
    private static final boolean ENABLE_APP_NOTIFICATION_PRIVACY_OPTION = false;


    private static final String SECTION_BEFORE_A = "*";
    private static final String SECTION_BEFORE_A = "*";
    private static final String SECTION_AFTER_Z = "**";
    private static final String SECTION_AFTER_Z = "**";
@@ -189,89 +173,6 @@ public class AppNotificationSettings extends PinnedHeaderListFragment {
        return null;
        return null;
    }
    }



    private void showDialog(final View v, final AppRow row) {
        final RelativeLayout layout = (RelativeLayout)
                mInflater.inflate(R.layout.notification_app_dialog, null);
        final ImageView icon = (ImageView) layout.findViewById(android.R.id.icon);
        icon.setImageDrawable(row.icon);
        final TextView title = (TextView) layout.findViewById(android.R.id.title);
        title.setText(row.label);
        final CheckBox showBox = (CheckBox) layout.findViewById(android.R.id.button1);
        final CheckBox priBox = (CheckBox) layout.findViewById(android.R.id.button2);
        final CheckBox senBox = (CheckBox) layout.findViewById(android.R.id.button3);

        if (!ENABLE_APP_NOTIFICATION_PRIORITY_OPTION) {
            priBox.setVisibility(View.GONE);
        }

        if (!ENABLE_APP_NOTIFICATION_PRIVACY_OPTION) {
            senBox.setVisibility(View.GONE);
        }

        showBox.setChecked(!row.banned);
        final OnCheckedChangeListener showListener = new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                boolean success = mBackend.setNotificationsBanned(row.pkg, row.uid, !isChecked);
                if (success) {
                    row.banned = !isChecked;
                    mAdapter.bindView(v, row, true /*animate*/);
                    priBox.setEnabled(!row.banned);
                    senBox.setEnabled(!row.banned);
                } else {
                    showBox.setOnCheckedChangeListener(null);
                    showBox.setChecked(!isChecked);
                    showBox.setOnCheckedChangeListener(this);
                }
            }
        };
        showBox.setOnCheckedChangeListener(showListener);

        priBox.setChecked(row.priority);
        final OnCheckedChangeListener priListener = new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                boolean success = mBackend.setHighPriority(row.pkg, row.uid, isChecked);
                if (success) {
                    row.priority = isChecked;
                    mAdapter.bindView(v, row, true /*animate*/);
                } else {
                    priBox.setOnCheckedChangeListener(null);
                    priBox.setChecked(!isChecked);
                    priBox.setOnCheckedChangeListener(this);
                }
            }
        };
        priBox.setOnCheckedChangeListener(priListener);

        senBox.setChecked(row.sensitive);
        final OnCheckedChangeListener senListener = new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                boolean success = mBackend.setSensitive(row.pkg, row.uid, isChecked);
                if (success) {
                    row.sensitive = isChecked;
                    mAdapter.bindView(v, row, true /*animate*/);
                } else {
                    senBox.setOnCheckedChangeListener(null);
                    senBox.setChecked(!isChecked);
                    senBox.setOnCheckedChangeListener(this);
                }
            }
        };
        senBox.setOnCheckedChangeListener(senListener);

        priBox.setEnabled(!row.banned);
        senBox.setEnabled(!row.banned);

        final AlertDialog d = new AlertDialog.Builder(mContext)
            .setView(layout)
            .setPositiveButton(R.string.app_notifications_dialog_done, null)
            .create();
        d.show();
    }

    private static class ViewHolder {
    private static class ViewHolder {
        ViewGroup row;
        ViewGroup row;
        ViewGroup appButton;
        ViewGroup appButton;
@@ -366,7 +267,10 @@ public class AppNotificationSettings extends PinnedHeaderListFragment {
            vh.appButton.setOnClickListener(new OnClickListener() {
            vh.appButton.setOnClickListener(new OnClickListener() {
                @Override
                @Override
                public void onClick(View v) {
                public void onClick(View v) {
                    showDialog(view, row);
                    mContext.startActivity(new Intent(mContext, AppNotificationDialog.class)
                            .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
                            .putExtra(Settings.EXTRA_APP_PACKAGE, row.pkg)
                            .putExtra(Settings.EXTRA_APP_UID, row.uid));
                }
                }
            });
            });
            enableLayoutTransitions(vh.appButton, animate);
            enableLayoutTransitions(vh.appButton, animate);
@@ -428,7 +332,7 @@ public class AppNotificationSettings extends PinnedHeaderListFragment {
        public String section;
        public String section;
    }
    }


    private static class AppRow extends Row {
    public static class AppRow extends Row {
        public String pkg;
        public String pkg;
        public int uid;
        public int uid;
        public Drawable icon;
        public Drawable icon;
@@ -448,6 +352,23 @@ public class AppNotificationSettings extends PinnedHeaderListFragment {
        }
        }
    };
    };


    public static AppRow loadAppRow(PackageManager pm, PackageInfo pkg, Backend backend) {
        final AppRow row = new AppRow();
        row.pkg = pkg.packageName;
        row.uid = pkg.applicationInfo.uid;
        try {
            row.label = pkg.applicationInfo.loadLabel(pm);
        } catch (Throwable t) {
            Log.e(TAG, "Error loading application label for " + row.pkg, t);
            row.label = row.pkg;
        }
        row.icon = pkg.applicationInfo.loadIcon(pm);
        row.banned = backend.getNotificationsBanned(row.pkg, row.uid);
        row.priority = backend.getHighPriority(row.pkg, row.uid);
        row.sensitive = backend.getSensitive(row.pkg, row.uid);
        return row;
    }

    private final Runnable mCollectAppsRunnable = new Runnable() {
    private final Runnable mCollectAppsRunnable = new Runnable() {
        @Override
        @Override
        public void run() {
        public void run() {
@@ -464,23 +385,12 @@ public class AppNotificationSettings extends PinnedHeaderListFragment {
                        if (DEBUG) Log.d(TAG, "Skipping " + pkg.packageName);
                        if (DEBUG) Log.d(TAG, "Skipping " + pkg.packageName);
                        continue;
                        continue;
                    }
                    }
                    final AppRow row = new AppRow();
                    final AppRow row = loadAppRow(pm, pkg, mBackend);
                    row.pkg = pkg.packageName;
                    row.uid = pkg.applicationInfo.uid;
                    try {
                        row.label = pkg.applicationInfo.loadLabel(pm);
                    } catch (Throwable t) {
                        Log.e(TAG, "Error loading application label for " + row.pkg, t);
                        row.label = row.pkg;
                    }
                    row.icon = pkg.applicationInfo.loadIcon(pm);
                    row.banned = mBackend.getNotificationsBanned(row.pkg, row.uid);
                    row.priority = mBackend.getHighPriority(row.pkg, row.uid);
                    row.sensitive = mBackend.getSensitive(row.pkg, row.uid);
                    mRows.put(row.pkg, row);
                    mRows.put(row.pkg, row);
                }
                }
                // collect config activities
                // collect config activities
                Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is " + APP_NOTIFICATION_PREFS_CATEGORY_INTENT);
                if (DEBUG) Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is "
                        + APP_NOTIFICATION_PREFS_CATEGORY_INTENT);
                final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
                final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
                        APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
                        APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
                        PackageManager.MATCH_DEFAULT_ONLY);
                        PackageManager.MATCH_DEFAULT_ONLY);