1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.notification;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.pm.PackageInfo;
22import android.content.pm.PackageManager;
23import android.content.pm.PackageManager.NameNotFoundException;
24import android.os.Bundle;
25import android.preference.Preference;
26import android.preference.Preference.OnPreferenceChangeListener;
27import android.preference.SwitchPreference;
28import android.provider.Settings;
29import android.text.TextUtils;
30import android.util.ArrayMap;
31import android.util.Log;
32import android.view.View;
33import android.view.View.OnClickListener;
34import android.view.ViewGroup;
35import android.widget.ImageView;
36import android.widget.TextView;
37import android.widget.Toast;
38
39import com.android.internal.widget.LockPatternUtils;
40import com.android.settings.R;
41import com.android.settings.SettingsPreferenceFragment;
42import com.android.settings.Utils;
43import com.android.settings.notification.NotificationAppList.AppRow;
44import com.android.settings.notification.NotificationAppList.Backend;
45
46/** These settings are per app, so should not be returned in global search results. */
47public class AppNotificationSettings extends SettingsPreferenceFragment {
48    private static final String TAG = "AppNotificationSettings";
49    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
50
51    private static final String KEY_BLOCK = "block";
52    private static final String KEY_PRIORITY = "priority";
53    private static final String KEY_SENSITIVE = "sensitive";
54
55    static final String EXTRA_HAS_SETTINGS_INTENT = "has_settings_intent";
56    static final String EXTRA_SETTINGS_INTENT = "settings_intent";
57
58    private final Backend mBackend = new Backend();
59
60    private Context mContext;
61    private SwitchPreference mBlock;
62    private SwitchPreference mPriority;
63    private SwitchPreference mSensitive;
64    private AppRow mAppRow;
65    private boolean mCreated;
66
67    @Override
68    public void onActivityCreated(Bundle savedInstanceState) {
69        super.onActivityCreated(savedInstanceState);
70        if (DEBUG) Log.d(TAG, "onActivityCreated mCreated=" + mCreated);
71        if (mCreated) {
72            Log.w(TAG, "onActivityCreated: ignoring duplicate call");
73            return;
74        }
75        mCreated = true;
76        if (mAppRow == null) return;
77        final View content = getActivity().findViewById(R.id.main_content);
78        final ViewGroup contentParent = (ViewGroup) content.getParent();
79        final View bar = getActivity().getLayoutInflater().inflate(R.layout.app_notification_header,
80                contentParent, false);
81
82        final ImageView appIcon = (ImageView) bar.findViewById(R.id.app_icon);
83        appIcon.setImageDrawable(mAppRow.icon);
84
85        final TextView appName = (TextView) bar.findViewById(R.id.app_name);
86        appName.setText(mAppRow.label);
87
88        final View appSettings = bar.findViewById(R.id.app_settings);
89        if (mAppRow.settingsIntent == null) {
90            appSettings.setVisibility(View.GONE);
91        } else {
92            appSettings.setClickable(true);
93            appSettings.setOnClickListener(new OnClickListener() {
94                @Override
95                public void onClick(View v) {
96                    mContext.startActivity(mAppRow.settingsIntent);
97                }
98            });
99        }
100        contentParent.addView(bar, 0);
101    }
102
103    @Override
104    public void onCreate(Bundle savedInstanceState) {
105        super.onCreate(savedInstanceState);
106        mContext = getActivity();
107        Intent intent = getActivity().getIntent();
108        if (DEBUG) Log.d(TAG, "onCreate getIntent()=" + intent);
109        if (intent == null) {
110            Log.w(TAG, "No intent");
111            toastAndFinish();
112            return;
113        }
114
115        final int uid = intent.getIntExtra(Settings.EXTRA_APP_UID, -1);
116        final String pkg = intent.getStringExtra(Settings.EXTRA_APP_PACKAGE);
117        if (uid == -1 || TextUtils.isEmpty(pkg)) {
118            Log.w(TAG, "Missing extras: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg + ", "
119                    + Settings.EXTRA_APP_UID + " was " + uid);
120            toastAndFinish();
121            return;
122        }
123
124        if (DEBUG) Log.d(TAG, "Load details for pkg=" + pkg + " uid=" + uid);
125        final PackageManager pm = getPackageManager();
126        final PackageInfo info = findPackageInfo(pm, pkg, uid);
127        if (info == null) {
128            Log.w(TAG, "Failed to find package info: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg
129                    + ", " + Settings.EXTRA_APP_UID + " was " + uid);
130            toastAndFinish();
131            return;
132        }
133
134        addPreferencesFromResource(R.xml.app_notification_settings);
135        mBlock = (SwitchPreference) findPreference(KEY_BLOCK);
136        mPriority = (SwitchPreference) findPreference(KEY_PRIORITY);
137        mSensitive = (SwitchPreference) findPreference(KEY_SENSITIVE);
138
139        final boolean secure = new LockPatternUtils(getActivity()).isSecure();
140        final boolean enabled = getLockscreenNotificationsEnabled();
141        final boolean allowPrivate = getLockscreenAllowPrivateNotifications();
142        if (!secure || !enabled || !allowPrivate) {
143            getPreferenceScreen().removePreference(mSensitive);
144        }
145
146        mAppRow = NotificationAppList.loadAppRow(pm, info.applicationInfo, mBackend);
147        if (intent.hasExtra(EXTRA_HAS_SETTINGS_INTENT)) {
148            // use settings intent from extra
149            if (intent.getBooleanExtra(EXTRA_HAS_SETTINGS_INTENT, false)) {
150                mAppRow.settingsIntent = intent.getParcelableExtra(EXTRA_SETTINGS_INTENT);
151            }
152        } else {
153            // load settings intent
154            ArrayMap<String, AppRow> rows = new ArrayMap<String, AppRow>();
155            rows.put(mAppRow.pkg, mAppRow);
156            NotificationAppList.collectConfigActivities(getPackageManager(), rows);
157        }
158
159        mBlock.setChecked(mAppRow.banned);
160        mPriority.setChecked(mAppRow.priority);
161        if (mSensitive != null) {
162            mSensitive.setChecked(mAppRow.sensitive);
163        }
164
165        mBlock.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
166            @Override
167            public boolean onPreferenceChange(Preference preference, Object newValue) {
168                final boolean block = (Boolean) newValue;
169                return mBackend.setNotificationsBanned(pkg, uid, block);
170            }
171        });
172
173        mPriority.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
174            @Override
175            public boolean onPreferenceChange(Preference preference, Object newValue) {
176                final boolean priority = (Boolean) newValue;
177                return mBackend.setHighPriority(pkg, uid, priority);
178            }
179        });
180
181        if (mSensitive != null) {
182            mSensitive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
183                @Override
184                public boolean onPreferenceChange(Preference preference, Object newValue) {
185                    final boolean sensitive = (Boolean) newValue;
186                    return mBackend.setSensitive(pkg, uid, sensitive);
187                }
188            });
189        }
190
191        // Users cannot block notifications from system/signature packages
192        if (Utils.isSystemPackage(pm, info)) {
193            getPreferenceScreen().removePreference(mBlock);
194            mPriority.setDependency(null); // don't have it depend on a preference that's gone
195        }
196    }
197
198    private boolean getLockscreenNotificationsEnabled() {
199        return Settings.Secure.getInt(getContentResolver(),
200                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
201    }
202
203    private boolean getLockscreenAllowPrivateNotifications() {
204        return Settings.Secure.getInt(getContentResolver(),
205                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
206    }
207
208    private void toastAndFinish() {
209        Toast.makeText(mContext, R.string.app_not_found_dlg_text, Toast.LENGTH_SHORT).show();
210        getActivity().finish();
211    }
212
213    private static PackageInfo findPackageInfo(PackageManager pm, String pkg, int uid) {
214        final String[] packages = pm.getPackagesForUid(uid);
215        if (packages != null && pkg != null) {
216            final int N = packages.length;
217            for (int i = 0; i < N; i++) {
218                final String p = packages[i];
219                if (pkg.equals(p)) {
220                    try {
221                        return pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
222                    } catch (NameNotFoundException e) {
223                        Log.w(TAG, "Failed to load package " + pkg, e);
224                    }
225                }
226            }
227        }
228        return null;
229    }
230}
231