App-level notification configuration settings page.
New sub-page off of notification settings to ban/unban apps and navigate to an app's notification configuration activity (if configured). Centralized all notification settings artifacts under a new settings subpackage. Bug: 13935172 Change-Id: I53b75c02f0091900734d17dc9217035d0df9b466
This commit is contained in:
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
* 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.animation.LayoutTransition;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.ListFragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.Signature;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.shapes.PathShape;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcelable;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.SectionIndexer;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class AppNotificationSettings extends ListFragment {
|
||||
private static final String TAG = "AppNotificationSettings";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private static final String SECTION_BEFORE_A = "*";
|
||||
private static final String SECTION_AFTER_Z = "**";
|
||||
private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
|
||||
= new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_NOTIFICATION_PREFERENCES);
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private final ArrayMap<String, AppRow> mRows = new ArrayMap<String, AppRow>();
|
||||
private final ArrayList<AppRow> mSortedRows = new ArrayList<AppRow>();
|
||||
private final ArrayList<String> mSections = new ArrayList<String>();
|
||||
|
||||
private Context mContext;
|
||||
private LayoutInflater mInflater;
|
||||
private NotificationAppAdapter mAdapter;
|
||||
private Signature[] mSystemSignature;
|
||||
private Parcelable mListViewState;
|
||||
private Backend mBackend = new Backend();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mContext = getActivity();
|
||||
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mAdapter = new NotificationAppAdapter(mContext);
|
||||
getActivity().setTitle(R.string.app_notifications_title);
|
||||
}
|
||||
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.notification_app_list, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
repositionScrollbar();
|
||||
getListView().setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (DEBUG) Log.d(TAG, "Saving listView state");
|
||||
mListViewState = getListView().onSaveInstanceState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
loadAppsList();
|
||||
}
|
||||
|
||||
public void setBackend(Backend backend) {
|
||||
mBackend = backend;
|
||||
}
|
||||
|
||||
private void loadAppsList() {
|
||||
AsyncTask.execute(mCollectAppsRunnable);
|
||||
}
|
||||
|
||||
private String getSection(CharSequence label) {
|
||||
if (label == null || label.length() == 0) return SECTION_BEFORE_A;
|
||||
final char c = Character.toUpperCase(label.charAt(0));
|
||||
if (c < 'A') return SECTION_BEFORE_A;
|
||||
if (c > 'Z') return SECTION_AFTER_Z;
|
||||
return Character.toString(c);
|
||||
}
|
||||
|
||||
private void repositionScrollbar() {
|
||||
final int sbWidthPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||
getListView().getScrollBarSize(),
|
||||
getResources().getDisplayMetrics());
|
||||
final View parent = (View)getView().getParent();
|
||||
final int eat = Math.min(sbWidthPx, parent.getPaddingEnd());
|
||||
if (eat <= 0) return;
|
||||
if (DEBUG) Log.d(TAG, String.format("Eating %dpx into %dpx padding for %dpx scroll, ld=%d",
|
||||
eat, parent.getPaddingEnd(), sbWidthPx, getListView().getLayoutDirection()));
|
||||
parent.setPaddingRelative(parent.getPaddingStart(), parent.getPaddingTop(),
|
||||
parent.getPaddingEnd() - eat, parent.getPaddingBottom());
|
||||
}
|
||||
|
||||
private boolean isSystemApp(PackageInfo pkg) {
|
||||
if (mSystemSignature == null) {
|
||||
mSystemSignature = new Signature[]{ getSystemSignature() };
|
||||
}
|
||||
return mSystemSignature[0] != null && mSystemSignature[0].equals(getFirstSignature(pkg));
|
||||
}
|
||||
|
||||
private static Signature getFirstSignature(PackageInfo pkg) {
|
||||
if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
|
||||
return pkg.signatures[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Signature getSystemSignature() {
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
try {
|
||||
final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
|
||||
return getFirstSignature(sys);
|
||||
} catch (NameNotFoundException e) {
|
||||
}
|
||||
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);
|
||||
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*/);
|
||||
} else {
|
||||
showBox.setOnCheckedChangeListener(null);
|
||||
showBox.setChecked(!isChecked);
|
||||
showBox.setOnCheckedChangeListener(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
showBox.setOnCheckedChangeListener(showListener);
|
||||
final AlertDialog d = new AlertDialog.Builder(mContext)
|
||||
.setView(layout)
|
||||
.setPositiveButton(R.string.app_notifications_dialog_done, null)
|
||||
.create();
|
||||
d.show();
|
||||
}
|
||||
|
||||
private static class ViewHolder {
|
||||
ViewGroup row;
|
||||
ViewGroup appButton;
|
||||
ImageView icon;
|
||||
ImageView banBadge;
|
||||
ImageView priBadge;
|
||||
TextView title;
|
||||
View settingsDivider;
|
||||
ImageView settingsButton;
|
||||
View rowDivider;
|
||||
}
|
||||
|
||||
private class NotificationAppAdapter extends ArrayAdapter<Row> implements SectionIndexer {
|
||||
private final ShapeDrawable mBanShape, mPriShape;
|
||||
|
||||
public NotificationAppAdapter(Context context) {
|
||||
super(context, 0, 0);
|
||||
final int s = context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.notification_app_icon_badge_size);
|
||||
mBanShape = shape(banPath(s), s);
|
||||
mPriShape = shape(priPath(s), s);
|
||||
}
|
||||
|
||||
private ShapeDrawable shape(Path path, int s) {
|
||||
final ShapeDrawable sd = new ShapeDrawable(new PathShape(path, s, s));
|
||||
sd.getPaint().setStyle(Paint.Style.STROKE);
|
||||
sd.getPaint().setColor(0xffffffff);
|
||||
sd.getPaint().setStrokeWidth(s / 12);
|
||||
sd.setIntrinsicWidth(s);
|
||||
sd.setIntrinsicHeight(s);
|
||||
return sd;
|
||||
}
|
||||
|
||||
private Path banPath(int s) {
|
||||
final Path p = new Path();
|
||||
final int d = s / 5;
|
||||
p.moveTo(d, d); p.lineTo(s - d, s - d);
|
||||
p.moveTo(d, s - d); p.lineTo(s - d, d);
|
||||
return p;
|
||||
}
|
||||
|
||||
private Path priPath(int s) {
|
||||
final Path p = new Path();
|
||||
final int d = s / 5;
|
||||
p.moveTo(s / 2, d); p.lineTo(s / 2, s - d);
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
Row r = getItem(position);
|
||||
return r instanceof AppRow ? 1 : 0;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
Row r = getItem(position);
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(parent, r);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, r, false /*animate*/);
|
||||
return v;
|
||||
}
|
||||
|
||||
public View newView(ViewGroup parent, Row r) {
|
||||
if (!(r instanceof AppRow)) {
|
||||
return mInflater.inflate(R.layout.notification_app_section, parent, false);
|
||||
}
|
||||
final View v = mInflater.inflate(R.layout.notification_app, parent, false);
|
||||
final ViewHolder vh = new ViewHolder();
|
||||
vh.row = (ViewGroup) v;
|
||||
vh.row.setLayoutTransition(new LayoutTransition());
|
||||
vh.appButton = (ViewGroup) v.findViewById(android.R.id.button1);
|
||||
vh.appButton.setLayoutTransition(new LayoutTransition());
|
||||
vh.icon = (ImageView) v.findViewById(android.R.id.icon);
|
||||
vh.banBadge = (ImageView) v.findViewById(android.R.id.icon1);
|
||||
vh.banBadge.setImageDrawable(mBanShape);
|
||||
vh.priBadge = (ImageView) v.findViewById(android.R.id.icon2);
|
||||
vh.priBadge.setImageDrawable(mPriShape);
|
||||
vh.title = (TextView) v.findViewById(android.R.id.title);
|
||||
vh.settingsDivider = v.findViewById(R.id.settings_divider);
|
||||
vh.settingsButton = (ImageView) v.findViewById(android.R.id.button2);
|
||||
vh.rowDivider = v.findViewById(R.id.row_divider);
|
||||
v.setTag(vh);
|
||||
return v;
|
||||
}
|
||||
|
||||
private void enableLayoutTransitions(ViewGroup vg, boolean enabled) {
|
||||
if (enabled) {
|
||||
vg.getLayoutTransition().enableTransitionType(LayoutTransition.APPEARING);
|
||||
vg.getLayoutTransition().enableTransitionType(LayoutTransition.DISAPPEARING);
|
||||
} else {
|
||||
vg.getLayoutTransition().disableTransitionType(LayoutTransition.APPEARING);
|
||||
vg.getLayoutTransition().disableTransitionType(LayoutTransition.DISAPPEARING);
|
||||
}
|
||||
}
|
||||
|
||||
public void bindView(final View view, Row r, boolean animate) {
|
||||
if (!(r instanceof AppRow)) {
|
||||
TextView tv = (TextView)view;
|
||||
tv.setText(r.section);
|
||||
return;
|
||||
}
|
||||
|
||||
final AppRow row = (AppRow)r;
|
||||
final ViewHolder vh = (ViewHolder) view.getTag();
|
||||
enableLayoutTransitions(vh.row, animate);
|
||||
vh.rowDivider.setVisibility(row.first ? View.GONE : View.VISIBLE);
|
||||
vh.appButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showDialog(view, row);
|
||||
}
|
||||
});
|
||||
enableLayoutTransitions(vh.appButton, animate);
|
||||
vh.icon.setImageDrawable(row.icon);
|
||||
vh.banBadge.setVisibility(row.banned ? View.VISIBLE : View.GONE);
|
||||
vh.priBadge.setVisibility(row.priority ? View.VISIBLE : View.GONE);
|
||||
vh.title.setText(row.label);
|
||||
final boolean showSettings = !row.banned && row.settingsIntent != null;
|
||||
vh.settingsDivider.setVisibility(showSettings ? View.VISIBLE : View.GONE);
|
||||
vh.settingsButton.setVisibility(showSettings ? View.VISIBLE : View.GONE);
|
||||
vh.settingsButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (row.settingsIntent != null) {
|
||||
getContext().startActivity(row.settingsIntent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getSections() {
|
||||
return mSections.toArray(new Object[mSections.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionForSection(int sectionIndex) {
|
||||
final String section = mSections.get(sectionIndex);
|
||||
final int n = getCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final Row r = getItem(i);
|
||||
if (r.section.equals(section)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionForPosition(int position) {
|
||||
Row row = getItem(position);
|
||||
return mSections.indexOf(row.section);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Row {
|
||||
public String section;
|
||||
}
|
||||
|
||||
private static class AppRow extends Row {
|
||||
public String pkg;
|
||||
public int uid;
|
||||
public Drawable icon;
|
||||
public CharSequence label;
|
||||
public Intent settingsIntent;
|
||||
public boolean banned;
|
||||
public boolean priority;
|
||||
public boolean first;
|
||||
}
|
||||
|
||||
private static final Comparator<AppRow> mRowComparator = new Comparator<AppRow>() {
|
||||
private final Collator sCollator = Collator.getInstance();
|
||||
@Override
|
||||
public int compare(AppRow lhs, AppRow rhs) {
|
||||
return sCollator.compare(lhs.label, rhs.label);
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mCollectAppsRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (mRows) {
|
||||
final long start = SystemClock.uptimeMillis();
|
||||
if (DEBUG) Log.d(TAG, "Collecting apps...");
|
||||
mRows.clear();
|
||||
mSortedRows.clear();
|
||||
|
||||
// collect all non-system apps
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
for (PackageInfo pkg : pm.getInstalledPackages(PackageManager.GET_SIGNATURES)) {
|
||||
if (pkg.applicationInfo == null || isSystemApp(pkg)) {
|
||||
if (DEBUG) Log.d(TAG, "Skipping " + pkg.packageName);
|
||||
continue;
|
||||
}
|
||||
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 = mBackend.getNotificationsBanned(row.pkg, row.uid);
|
||||
row.priority = mBackend.getHighPriority(row.pkg, row.uid);
|
||||
mRows.put(row.pkg, row);
|
||||
}
|
||||
// collect config activities
|
||||
Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is " + APP_NOTIFICATION_PREFS_CATEGORY_INTENT);
|
||||
final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
|
||||
APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
|
||||
PackageManager.MATCH_DEFAULT_ONLY);
|
||||
if (DEBUG) Log.d(TAG, "Found " + resolveInfos.size() + " preference activities");
|
||||
for (ResolveInfo ri : resolveInfos) {
|
||||
final ActivityInfo activityInfo = ri.activityInfo;
|
||||
final ApplicationInfo appInfo = activityInfo.applicationInfo;
|
||||
final AppRow row = mRows.get(appInfo.packageName);
|
||||
if (row == null) {
|
||||
Log.v(TAG, "Ignoring notification preference activity ("
|
||||
+ activityInfo.name + ") for unknown package "
|
||||
+ activityInfo.packageName);
|
||||
continue;
|
||||
}
|
||||
if (row.settingsIntent != null) {
|
||||
Log.v(TAG, "Ignoring duplicate notification preference activity ("
|
||||
+ activityInfo.name + ") for package "
|
||||
+ activityInfo.packageName);
|
||||
continue;
|
||||
}
|
||||
row.settingsIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.setClassName(activityInfo.packageName, activityInfo.name);
|
||||
}
|
||||
// sort rows
|
||||
mSortedRows.addAll(mRows.values());
|
||||
Collections.sort(mSortedRows, mRowComparator);
|
||||
// compute sections
|
||||
mSections.clear();
|
||||
String section = null;
|
||||
for (AppRow r : mSortedRows) {
|
||||
r.section = getSection(r.label);
|
||||
if (!r.section.equals(section)) {
|
||||
section = r.section;
|
||||
mSections.add(section);
|
||||
}
|
||||
}
|
||||
mHandler.post(mRefreshAppsListRunnable);
|
||||
final long elapsed = SystemClock.uptimeMillis() - start;
|
||||
if (DEBUG) Log.d(TAG, "Collected " + mRows.size() + " apps in " + elapsed + "ms");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void refreshDisplayedItems() {
|
||||
if (DEBUG) Log.d(TAG, "Refreshing apps...");
|
||||
mAdapter.clear();
|
||||
synchronized (mSortedRows) {
|
||||
String section = null;
|
||||
final int N = mSortedRows.size();
|
||||
boolean first = true;
|
||||
for (int i = 0; i < N; i++) {
|
||||
final AppRow row = mSortedRows.get(i);
|
||||
if (!row.section.equals(section)) {
|
||||
section = row.section;
|
||||
Row r = new Row();
|
||||
r.section = section;
|
||||
mAdapter.add(r);
|
||||
first = true;
|
||||
}
|
||||
row.first = first;
|
||||
mAdapter.add(row);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
if (mListViewState != null) {
|
||||
if (DEBUG) Log.d(TAG, "Restoring listView state");
|
||||
getListView().onRestoreInstanceState(mListViewState);
|
||||
mListViewState = null;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "Refreshed " + mSortedRows.size() + " displayed items");
|
||||
}
|
||||
|
||||
private final Runnable mRefreshAppsListRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshDisplayedItems();
|
||||
}
|
||||
};
|
||||
|
||||
public static class Backend {
|
||||
public boolean setNotificationsBanned(String pkg, int uid, boolean banned) {
|
||||
INotificationManager nm = INotificationManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||
try {
|
||||
nm.setNotificationsEnabledForPackage(pkg, uid, !banned);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Error calling NoMan", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getNotificationsBanned(String pkg, int uid) {
|
||||
INotificationManager nm = INotificationManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||
try {
|
||||
final boolean enabled = nm.areNotificationsEnabledForPackage(pkg, uid);
|
||||
return !enabled;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Error calling NoMan", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getHighPriority(String pkg, int uid) {
|
||||
// TODO get high-pri state from NoMan
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean setHighPriority(String pkg, int uid, boolean priority) {
|
||||
// TODO save high-pri state to NoMan
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.ListFragment;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageItemInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.util.Slog;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class NotificationAccessSettings extends ListFragment {
|
||||
static final String TAG = NotificationAccessSettings.class.getSimpleName();
|
||||
private static final boolean SHOW_PACKAGE_NAME = false;
|
||||
|
||||
private PackageManager mPM;
|
||||
private ContentResolver mCR;
|
||||
|
||||
private final HashSet<ComponentName> mEnabledListeners = new HashSet<ComponentName>();
|
||||
private ListenerListAdapter mList;
|
||||
|
||||
private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
|
||||
= Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
|
||||
|
||||
private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
updateList();
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateList();
|
||||
}
|
||||
};
|
||||
|
||||
public class ListenerWarningDialogFragment extends DialogFragment {
|
||||
static final String KEY_COMPONENT = "c";
|
||||
static final String KEY_LABEL = "l";
|
||||
|
||||
public ListenerWarningDialogFragment setListenerInfo(ComponentName cn, String label) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_COMPONENT, cn.flattenToString());
|
||||
args.putString(KEY_LABEL, label);
|
||||
setArguments(args);
|
||||
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final Bundle args = getArguments();
|
||||
final String label = args.getString(KEY_LABEL);
|
||||
final ComponentName cn = ComponentName.unflattenFromString(args.getString(KEY_COMPONENT));
|
||||
|
||||
final String title = getResources().getString(
|
||||
R.string.notification_listener_security_warning_title, label);
|
||||
final String summary = getResources().getString(
|
||||
R.string.notification_listener_security_warning_summary, label);
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setMessage(summary)
|
||||
.setTitle(title)
|
||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
mEnabledListeners.add(cn);
|
||||
saveEnabledListeners();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// pass
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
mPM = getActivity().getPackageManager();
|
||||
mCR = getActivity().getContentResolver();
|
||||
mList = new ListenerListAdapter(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.notification_access_settings, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateList();
|
||||
|
||||
// listen for package changes
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
||||
filter.addDataScheme("package");
|
||||
getActivity().registerReceiver(mPackageReceiver, filter);
|
||||
|
||||
mCR.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI, false, mSettingsObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
getActivity().unregisterReceiver(mPackageReceiver);
|
||||
mCR.unregisterContentObserver(mSettingsObserver);
|
||||
}
|
||||
|
||||
void loadEnabledListeners() {
|
||||
mEnabledListeners.clear();
|
||||
final String flat = Settings.Secure.getString(mCR,
|
||||
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
|
||||
if (flat != null && !"".equals(flat)) {
|
||||
final String[] names = flat.split(":");
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
final ComponentName cn = ComponentName.unflattenFromString(names[i]);
|
||||
if (cn != null) {
|
||||
mEnabledListeners.add(cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void saveEnabledListeners() {
|
||||
StringBuilder sb = null;
|
||||
for (ComponentName cn : mEnabledListeners) {
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder();
|
||||
} else {
|
||||
sb.append(':');
|
||||
}
|
||||
sb.append(cn.flattenToString());
|
||||
}
|
||||
Settings.Secure.putString(mCR,
|
||||
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
|
||||
sb != null ? sb.toString() : "");
|
||||
}
|
||||
|
||||
void updateList() {
|
||||
loadEnabledListeners();
|
||||
|
||||
getListeners(mList, mPM);
|
||||
mList.sort(new PackageItemInfo.DisplayNameComparator(mPM));
|
||||
|
||||
getListView().setAdapter(mList);
|
||||
}
|
||||
|
||||
static int getListenersCount(PackageManager pm) {
|
||||
return getListeners(null, pm);
|
||||
}
|
||||
|
||||
private static int getListeners(ArrayAdapter<ServiceInfo> adapter, PackageManager pm) {
|
||||
int listeners = 0;
|
||||
if (adapter != null) {
|
||||
adapter.clear();
|
||||
}
|
||||
final int user = ActivityManager.getCurrentUser();
|
||||
|
||||
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
|
||||
new Intent(NotificationListenerService.SERVICE_INTERFACE),
|
||||
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
|
||||
user);
|
||||
|
||||
for (int i = 0, count = installedServices.size(); i < count; i++) {
|
||||
ResolveInfo resolveInfo = installedServices.get(i);
|
||||
ServiceInfo info = resolveInfo.serviceInfo;
|
||||
|
||||
if (!android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE.equals(
|
||||
info.permission)) {
|
||||
Slog.w(TAG, "Skipping notification listener service "
|
||||
+ info.packageName + "/" + info.name
|
||||
+ ": it does not require the permission "
|
||||
+ android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
|
||||
continue;
|
||||
}
|
||||
if (adapter != null) {
|
||||
adapter.add(info);
|
||||
}
|
||||
listeners++;
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
boolean isListenerEnabled(ServiceInfo info) {
|
||||
final ComponentName cn = new ComponentName(info.packageName, info.name);
|
||||
return mEnabledListeners.contains(cn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
ServiceInfo info = mList.getItem(position);
|
||||
final ComponentName cn = new ComponentName(info.packageName, info.name);
|
||||
if (mEnabledListeners.contains(cn)) {
|
||||
// the simple version: disabling
|
||||
mEnabledListeners.remove(cn);
|
||||
saveEnabledListeners();
|
||||
} else {
|
||||
// show a scary dialog
|
||||
new ListenerWarningDialogFragment()
|
||||
.setListenerInfo(cn, info.loadLabel(mPM).toString())
|
||||
.show(getFragmentManager(), "dialog");
|
||||
}
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
ImageView icon;
|
||||
TextView name;
|
||||
CheckBox checkbox;
|
||||
TextView description;
|
||||
}
|
||||
|
||||
class ListenerListAdapter extends ArrayAdapter<ServiceInfo> {
|
||||
final LayoutInflater mInflater;
|
||||
|
||||
ListenerListAdapter(Context context) {
|
||||
super(context, 0, 0);
|
||||
mInflater = (LayoutInflater)
|
||||
getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, position);
|
||||
return v;
|
||||
}
|
||||
|
||||
public View newView(ViewGroup parent) {
|
||||
View v = mInflater.inflate(R.layout.notification_listener_item, parent, false);
|
||||
ViewHolder h = new ViewHolder();
|
||||
h.icon = (ImageView) v.findViewById(R.id.icon);
|
||||
h.name = (TextView) v.findViewById(R.id.name);
|
||||
h.checkbox = (CheckBox) v.findViewById(R.id.checkbox);
|
||||
h.description = (TextView) v.findViewById(R.id.description);
|
||||
v.setTag(h);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void bindView(View view, int position) {
|
||||
ViewHolder vh = (ViewHolder) view.getTag();
|
||||
ServiceInfo info = getItem(position);
|
||||
|
||||
vh.icon.setImageDrawable(info.loadIcon(mPM));
|
||||
vh.name.setText(info.loadLabel(mPM));
|
||||
if (SHOW_PACKAGE_NAME) {
|
||||
vh.description.setText(info.packageName);
|
||||
vh.description.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
vh.description.setVisibility(View.GONE);
|
||||
}
|
||||
vh.checkbox.setChecked(isListenerEnabled(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
252
src/com/android/settings/notification/NotificationSettings.java
Normal file
252
src/com/android/settings/notification/NotificationSettings.java
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.ContentObserver;
|
||||
import android.media.RingtoneManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceGroup;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.preference.TwoStatePreference;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.SoundSettings;
|
||||
|
||||
public class NotificationSettings extends SettingsPreferenceFragment implements
|
||||
Preference.OnPreferenceChangeListener, OnPreferenceClickListener {
|
||||
private static final String TAG = "NotificationSettings";
|
||||
|
||||
private static final String KEY_NOTIFICATION_SOUND = "notification_sound";
|
||||
private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
|
||||
private static final String KEY_HEADS_UP = "heads_up";
|
||||
private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "toggle_lock_screen_notifications";
|
||||
private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access";
|
||||
|
||||
private static final String KEY_TWEAKS_CATEGORY = "category_tweaks"; // power toys, eng only
|
||||
|
||||
private static final int MSG_UPDATE_SOUND_SUMMARY = 2;
|
||||
|
||||
private Context mContext;
|
||||
private PackageManager mPM;
|
||||
|
||||
private Preference mNotificationSoundPreference;
|
||||
private Preference mNotificationAccess;
|
||||
private TwoStatePreference mLockscreenNotifications;
|
||||
private TwoStatePreference mHeadsUp;
|
||||
private TwoStatePreference mNotificationPulse;
|
||||
|
||||
private final Runnable mRingtoneLookupRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mNotificationSoundPreference != null) {
|
||||
final CharSequence summary = SoundSettings.updateRingtoneName(
|
||||
mContext, RingtoneManager.TYPE_NOTIFICATION);
|
||||
if (summary != null) {
|
||||
mHandler.sendMessage(
|
||||
mHandler.obtainMessage(MSG_UPDATE_SOUND_SUMMARY, summary));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_SOUND_SUMMARY:
|
||||
mNotificationSoundPreference.setSummary((CharSequence) msg.obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mContext = getActivity();
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
|
||||
mPM = mContext.getPackageManager();
|
||||
|
||||
addPreferencesFromResource(R.xml.notification_settings);
|
||||
|
||||
final PreferenceScreen root = getPreferenceScreen();
|
||||
|
||||
PreferenceGroup tweaksCategory = (PreferenceGroup)
|
||||
root.findPreference(KEY_TWEAKS_CATEGORY);
|
||||
|
||||
if (tweaksCategory != null
|
||||
&& !(Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"))) {
|
||||
root.removePreference(tweaksCategory);
|
||||
tweaksCategory = null;
|
||||
}
|
||||
|
||||
mNotificationSoundPreference = findPreference(KEY_NOTIFICATION_SOUND);
|
||||
|
||||
mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
|
||||
refreshNotificationListeners();
|
||||
|
||||
mLockscreenNotifications
|
||||
= (TwoStatePreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
|
||||
if (mLockscreenNotifications != null) {
|
||||
if (!getDeviceLockscreenNotificationsEnabled()) {
|
||||
root.removePreference(mLockscreenNotifications);
|
||||
} else {
|
||||
mLockscreenNotifications.setChecked(getLockscreenAllowPrivateNotifications());
|
||||
}
|
||||
}
|
||||
|
||||
mHeadsUp = (TwoStatePreference) findPreference(KEY_HEADS_UP);
|
||||
if (mHeadsUp != null) {
|
||||
updateHeadsUpMode(resolver);
|
||||
mHeadsUp.setOnPreferenceChangeListener(this);
|
||||
resolver.registerContentObserver(
|
||||
Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
|
||||
false, new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateHeadsUpMode(resolver);
|
||||
}
|
||||
});
|
||||
}
|
||||
mNotificationPulse = (TwoStatePreference) findPreference(KEY_NOTIFICATION_PULSE);
|
||||
|
||||
if (mNotificationPulse != null
|
||||
&& getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_intrusiveNotificationLed) == false) {
|
||||
getPreferenceScreen().removePreference(mNotificationPulse);
|
||||
} else {
|
||||
try {
|
||||
mNotificationPulse.setChecked(Settings.System.getInt(resolver,
|
||||
Settings.System.NOTIFICATION_LIGHT_PULSE) == 1);
|
||||
mNotificationPulse.setOnPreferenceChangeListener(this);
|
||||
} catch (Settings.SettingNotFoundException snfe) {
|
||||
Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
refreshNotificationListeners();
|
||||
lookupRingtoneNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
|
||||
final String key = preference.getKey();
|
||||
|
||||
if (KEY_LOCK_SCREEN_NOTIFICATIONS.equals(key)) {
|
||||
Settings.Secure.putInt(getContentResolver(),
|
||||
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
|
||||
mLockscreenNotifications.isChecked() ? 1 : 0);
|
||||
} else if (KEY_HEADS_UP.equals(key)) {
|
||||
setHeadsUpMode(getContentResolver(), mHeadsUp.isChecked());
|
||||
} else if (KEY_NOTIFICATION_PULSE.equals(key)) {
|
||||
Settings.System.putInt(getContentResolver(),
|
||||
Settings.System.NOTIFICATION_LIGHT_PULSE,
|
||||
mNotificationPulse.isChecked() ? 1 : 0);
|
||||
} else {
|
||||
return super.onPreferenceTreeClick(preferenceScreen, preference);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object objValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// === Heads-up notifications ===
|
||||
|
||||
private void updateHeadsUpMode(ContentResolver resolver) {
|
||||
mHeadsUp.setChecked(Settings.Global.HEADS_UP_ON == Settings.Global.getInt(resolver,
|
||||
Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HEADS_UP_OFF));
|
||||
}
|
||||
|
||||
private void setHeadsUpMode(ContentResolver resolver, boolean value) {
|
||||
Settings.Global.putInt(resolver, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
|
||||
value ? Settings.Global.HEADS_UP_ON : Settings.Global.HEADS_UP_OFF);
|
||||
}
|
||||
|
||||
// === Lockscreen (public / private) notifications ===
|
||||
|
||||
private boolean getDeviceLockscreenNotificationsEnabled() {
|
||||
return 0 != Settings.Global.getInt(getContentResolver(),
|
||||
Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
|
||||
}
|
||||
|
||||
private boolean getLockscreenAllowPrivateNotifications() {
|
||||
return 0 != Settings.Secure.getInt(getContentResolver(),
|
||||
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
|
||||
}
|
||||
|
||||
// === Notification listeners ===
|
||||
|
||||
private int getNumEnabledNotificationListeners() {
|
||||
final String flat = Settings.Secure.getString(getContentResolver(),
|
||||
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
|
||||
if (flat == null || "".equals(flat)) return 0;
|
||||
final String[] components = flat.split(":");
|
||||
return components.length;
|
||||
}
|
||||
|
||||
private void refreshNotificationListeners() {
|
||||
if (mNotificationAccess != null) {
|
||||
final int total = NotificationAccessSettings.getListenersCount(mPM);
|
||||
if (total == 0) {
|
||||
getPreferenceScreen().removePreference(mNotificationAccess);
|
||||
} else {
|
||||
final int n = getNumEnabledNotificationListeners();
|
||||
if (n == 0) {
|
||||
mNotificationAccess.setSummary(getResources().getString(
|
||||
R.string.manage_notification_access_summary_zero));
|
||||
} else {
|
||||
mNotificationAccess.setSummary(String.format(getResources().getQuantityString(
|
||||
R.plurals.manage_notification_access_summary_nonzero,
|
||||
n, n)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Ringtone ===
|
||||
|
||||
private void lookupRingtoneNames() {
|
||||
new Thread(mRingtoneLookupRunnable).start();
|
||||
}
|
||||
}
|
||||
408
src/com/android/settings/notification/NotificationStation.java
Normal file
408
src/com/android/settings/notification/NotificationStation.java
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.INotificationListener;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.DateTimeView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class NotificationStation extends SettingsPreferenceFragment {
|
||||
private static final String TAG = NotificationStation.class.getSimpleName();
|
||||
static final boolean DEBUG = true;
|
||||
private static final String PACKAGE_SCHEME = "package";
|
||||
private static final boolean SHOW_HISTORICAL_NOTIFICATIONS = true;
|
||||
|
||||
private final PackageReceiver mPackageReceiver = new PackageReceiver();
|
||||
private PackageManager mPm;
|
||||
private INotificationManager mNoMan;
|
||||
|
||||
private Runnable mRefreshListRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshList();
|
||||
}
|
||||
};
|
||||
|
||||
private INotificationListener.Stub mListener = new INotificationListener.Stub() {
|
||||
@Override
|
||||
public void onListenerConnected(String[] notificationKeys) throws RemoteException {
|
||||
// noop
|
||||
}
|
||||
@Override
|
||||
public void onNotificationPosted(StatusBarNotification notification) throws RemoteException {
|
||||
Log.v(TAG, "onNotificationPosted: " + notification);
|
||||
final Handler h = getListView().getHandler();
|
||||
h.removeCallbacks(mRefreshListRunnable);
|
||||
h.postDelayed(mRefreshListRunnable, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationRemoved(StatusBarNotification notification) throws RemoteException {
|
||||
final Handler h = getListView().getHandler();
|
||||
h.removeCallbacks(mRefreshListRunnable);
|
||||
h.postDelayed(mRefreshListRunnable, 100);
|
||||
}
|
||||
};
|
||||
|
||||
private NotificationHistoryAdapter mAdapter;
|
||||
private Context mContext;
|
||||
|
||||
private final Comparator<HistoricalNotificationInfo> mNotificationSorter
|
||||
= new Comparator<HistoricalNotificationInfo>() {
|
||||
@Override
|
||||
public int compare(HistoricalNotificationInfo lhs,
|
||||
HistoricalNotificationInfo rhs) {
|
||||
return (int)(rhs.timestamp - lhs.timestamp);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
logd("onAttach(%s)", activity.getClass().getSimpleName());
|
||||
super.onAttach(activity);
|
||||
mContext = activity;
|
||||
mPm = mContext.getPackageManager();
|
||||
mNoMan = INotificationManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||
try {
|
||||
mNoMan.registerListener(mListener,
|
||||
new ComponentName(mContext.getPackageName(),
|
||||
this.getClass().getCanonicalName()),
|
||||
ActivityManager.getCurrentUser());
|
||||
} catch (RemoteException e) {
|
||||
// well, that didn't work out
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
logd("onCreate(%s)", icicle);
|
||||
super.onCreate(icicle);
|
||||
Activity activity = getActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
logd("onActivityCreated(%s)", savedInstanceState);
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
ListView listView = getListView();
|
||||
|
||||
// TextView emptyView = (TextView) getView().findViewById(android.R.id.empty);
|
||||
// emptyView.setText(R.string.screensaver_settings_disabled_prompt);
|
||||
// listView.setEmptyView(emptyView);
|
||||
|
||||
mAdapter = new NotificationHistoryAdapter(mContext);
|
||||
listView.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
logd("onPause()");
|
||||
super.onPause();
|
||||
mContext.unregisterReceiver(mPackageReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
logd("onResume()");
|
||||
super.onResume();
|
||||
refreshList();
|
||||
|
||||
// listen for package changes
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
||||
filter.addDataScheme(PACKAGE_SCHEME);
|
||||
mContext.registerReceiver(mPackageReceiver , filter);
|
||||
}
|
||||
|
||||
private void refreshList() {
|
||||
List<HistoricalNotificationInfo> infos = loadNotifications();
|
||||
if (infos != null) {
|
||||
logd("adding %d infos", infos.size());
|
||||
mAdapter.clear();
|
||||
mAdapter.addAll(infos);
|
||||
mAdapter.sort(mNotificationSorter);
|
||||
}
|
||||
}
|
||||
|
||||
private static void logd(String msg, Object... args) {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args));
|
||||
}
|
||||
|
||||
private static class HistoricalNotificationInfo {
|
||||
public String pkg;
|
||||
public Drawable pkgicon;
|
||||
public CharSequence pkgname;
|
||||
public Drawable icon;
|
||||
public CharSequence title;
|
||||
public int priority;
|
||||
public int user;
|
||||
public long timestamp;
|
||||
public boolean active;
|
||||
}
|
||||
|
||||
private List<HistoricalNotificationInfo> loadNotifications() {
|
||||
final int currentUserId = ActivityManager.getCurrentUser();
|
||||
try {
|
||||
StatusBarNotification[] active = mNoMan.getActiveNotifications(mContext.getPackageName());
|
||||
StatusBarNotification[] dismissed = mNoMan.getHistoricalNotifications(mContext.getPackageName(), 50);
|
||||
|
||||
List<HistoricalNotificationInfo> list
|
||||
= new ArrayList<HistoricalNotificationInfo>(active.length + dismissed.length);
|
||||
|
||||
for (StatusBarNotification[] resultset
|
||||
: new StatusBarNotification[][] { active, dismissed }) {
|
||||
for (StatusBarNotification sbn : resultset) {
|
||||
final HistoricalNotificationInfo info = new HistoricalNotificationInfo();
|
||||
info.pkg = sbn.getPackageName();
|
||||
info.user = sbn.getUserId();
|
||||
info.icon = loadIconDrawable(info.pkg, info.user, sbn.getNotification().icon);
|
||||
info.pkgicon = loadPackageIconDrawable(info.pkg, info.user);
|
||||
info.pkgname = loadPackageName(info.pkg);
|
||||
if (sbn.getNotification().extras != null) {
|
||||
info.title = sbn.getNotification().extras.getString(Notification.EXTRA_TITLE);
|
||||
if (info.title == null || "".equals(info.title)) {
|
||||
info.title = sbn.getNotification().extras.getString(Notification.EXTRA_TEXT);
|
||||
}
|
||||
}
|
||||
if (info.title == null || "".equals(info.title)) {
|
||||
info.title = sbn.getNotification().tickerText;
|
||||
}
|
||||
// still nothing? come on, give us something!
|
||||
if (info.title == null || "".equals(info.title)) {
|
||||
info.title = info.pkgname;
|
||||
}
|
||||
info.timestamp = sbn.getPostTime();
|
||||
info.priority = sbn.getNotification().priority;
|
||||
logd(" [%d] %s: %s", info.timestamp, info.pkg, info.title);
|
||||
|
||||
info.active = (resultset == active);
|
||||
|
||||
if (info.user == UserHandle.USER_ALL
|
||||
|| info.user == currentUserId) {
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Resources getResourcesForUserPackage(String pkg, int userId) {
|
||||
Resources r = null;
|
||||
|
||||
if (pkg != null) {
|
||||
try {
|
||||
if (userId == UserHandle.USER_ALL) {
|
||||
userId = UserHandle.USER_OWNER;
|
||||
}
|
||||
r = mPm.getResourcesForApplicationAsUser(pkg, userId);
|
||||
} catch (PackageManager.NameNotFoundException ex) {
|
||||
Log.e(TAG, "Icon package not found: " + pkg);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
r = mContext.getResources();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private Drawable loadPackageIconDrawable(String pkg, int userId) {
|
||||
Drawable icon = null;
|
||||
try {
|
||||
icon = mPm.getApplicationIcon(pkg);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private CharSequence loadPackageName(String pkg) {
|
||||
try {
|
||||
ApplicationInfo info = mPm.getApplicationInfo(pkg,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
if (info != null) return mPm.getApplicationLabel(info);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
private Drawable loadIconDrawable(String pkg, int userId, int resId) {
|
||||
Resources r = getResourcesForUserPackage(pkg, userId);
|
||||
|
||||
if (resId == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return r.getDrawable(resId);
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(TAG, "Icon not found in "
|
||||
+ (pkg != null ? resId : "<system>")
|
||||
+ ": " + Integer.toHexString(resId));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private class NotificationHistoryAdapter extends ArrayAdapter<HistoricalNotificationInfo> {
|
||||
private final LayoutInflater mInflater;
|
||||
|
||||
public NotificationHistoryAdapter(Context context) {
|
||||
super(context, 0);
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final HistoricalNotificationInfo info = getItem(position);
|
||||
logd("getView(%s/%s)", info.pkg, info.title);
|
||||
final View row = convertView != null ? convertView : createRow(parent);
|
||||
row.setTag(info);
|
||||
|
||||
// bind icon
|
||||
if (info.icon != null) {
|
||||
((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(info.icon);
|
||||
}
|
||||
if (info.pkgicon != null) {
|
||||
((ImageView) row.findViewById(R.id.pkgicon)).setImageDrawable(info.pkgicon);
|
||||
}
|
||||
|
||||
((DateTimeView) row.findViewById(R.id.timestamp)).setTime(info.timestamp);
|
||||
|
||||
// bind caption
|
||||
((TextView) row.findViewById(android.R.id.title)).setText(info.title);
|
||||
|
||||
// app name
|
||||
((TextView) row.findViewById(R.id.pkgname)).setText(info.pkgname);
|
||||
|
||||
// extra goodies -- not implemented yet
|
||||
// ((TextView) row.findViewById(R.id.extra)).setText(
|
||||
// ...
|
||||
// );
|
||||
row.findViewById(R.id.extra).setVisibility(View.GONE);
|
||||
|
||||
row.setAlpha(info.active ? 1.0f : 0.5f);
|
||||
|
||||
// set up click handler
|
||||
row.setOnClickListener(new OnClickListener(){
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
v.setPressed(true);
|
||||
startApplicationDetailsActivity(info.pkg);
|
||||
}});
|
||||
|
||||
// // bind radio button
|
||||
// RadioButton radioButton = (RadioButton) row.findViewById(android.R.id.button1);
|
||||
// radioButton.setChecked(dreamInfo.isActive);
|
||||
// radioButton.setOnTouchListener(new OnTouchListener() {
|
||||
// @Override
|
||||
// public boolean onTouch(View v, MotionEvent event) {
|
||||
// row.onTouchEvent(event);
|
||||
// return false;
|
||||
// }});
|
||||
|
||||
// bind settings button + divider
|
||||
// boolean showSettings = info.
|
||||
// settingsComponentName != null;
|
||||
// View settingsDivider = row.findViewById(R.id.divider);
|
||||
// settingsDivider.setVisibility(false ? View.VISIBLE : View.INVISIBLE);
|
||||
//
|
||||
// ImageView settingsButton = (ImageView) row.findViewById(android.R.id.button2);
|
||||
// settingsButton.setVisibility(false ? View.VISIBLE : View.INVISIBLE);
|
||||
// settingsButton.setAlpha(info.isActive ? 1f : Utils.DISABLED_ALPHA);
|
||||
// settingsButton.setEnabled(info.isActive);
|
||||
// settingsButton.setOnClickListener(new OnClickListener(){
|
||||
// @Override
|
||||
// public void onClick(View v) {
|
||||
// mBackend.launchSettings((DreamInfo) row.getTag());
|
||||
// }});
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
private View createRow(ViewGroup parent) {
|
||||
final View row = mInflater.inflate(R.layout.notification_log_row, parent, false);
|
||||
return row;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void startApplicationDetailsActivity(String packageName) {
|
||||
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.fromParts("package", packageName, null));
|
||||
intent.setComponent(intent.resolveActivity(mPm));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private class PackageReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
logd("PackageReceiver.onReceive");
|
||||
//refreshList();
|
||||
}
|
||||
}
|
||||
}
|
||||
359
src/com/android/settings/notification/ZenModeSettings.java
Normal file
359
src/com/android/settings/notification/ZenModeSettings.java
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* 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.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings.Global;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ZenModeSettings extends SettingsPreferenceFragment implements Indexable {
|
||||
private static final String TAG = "ZenModeSettings";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private final SettingsObserver mSettingsObserver = new SettingsObserver();
|
||||
|
||||
private ZenModeConfigView mConfig;
|
||||
private Switch mSwitch;
|
||||
private Activity mActivity;
|
||||
private MenuItem mSearch;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
mActivity = getActivity();
|
||||
mSwitch = new Switch(mActivity.getActionBar().getThemedContext());
|
||||
final int p = getResources().getDimensionPixelSize(R.dimen.content_margin_left);
|
||||
mSwitch.setPadding(0, 0, p, 0);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
mSearch = menu.findItem(R.id.search);
|
||||
if (mSearch != null) mSearch.setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateState();
|
||||
mSettingsObserver.register();
|
||||
mActivity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
|
||||
ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||
mActivity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams(
|
||||
ActionBar.LayoutParams.WRAP_CONTENT,
|
||||
ActionBar.LayoutParams.WRAP_CONTENT,
|
||||
Gravity.CENTER_VERTICAL | Gravity.END));
|
||||
if (mSearch != null) mSearch.setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mSettingsObserver.unregister();
|
||||
mActivity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||
if (mSearch != null) mSearch.setVisible(true);
|
||||
}
|
||||
|
||||
private final class SettingsObserver extends ContentObserver {
|
||||
private final Uri ZEN_MODE_URI = Global.getUriFor(Global.ZEN_MODE);
|
||||
|
||||
public SettingsObserver() {
|
||||
super(mHandler);
|
||||
}
|
||||
|
||||
public void register() {
|
||||
getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
|
||||
}
|
||||
|
||||
public void unregister() {
|
||||
getContentResolver().unregisterContentObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
super.onChange(selfChange, uri);
|
||||
if (ZEN_MODE_URI.equals(uri)) {
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void updateState() {
|
||||
mSwitch.setOnCheckedChangeListener(null);
|
||||
final boolean zenMode = Global.getInt(getContentResolver(),
|
||||
Global.ZEN_MODE, Global.ZEN_MODE_OFF) != Global.ZEN_MODE_OFF;
|
||||
mSwitch.setChecked(zenMode);
|
||||
mSwitch.setOnCheckedChangeListener(mSwitchListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
final Context context = getActivity();
|
||||
final ScrollView sv = new ScrollView(context);
|
||||
sv.setVerticalScrollBarEnabled(false);
|
||||
sv.setHorizontalScrollBarEnabled(false);
|
||||
mConfig = new ZenModeConfigView(context);
|
||||
sv.addView(mConfig);
|
||||
return sv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mConfig.resetBackground();
|
||||
}
|
||||
|
||||
private final OnCheckedChangeListener mSwitchListener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
|
||||
AsyncTask.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final int v = isChecked ? Global.ZEN_MODE_ON : Global.ZEN_MODE_OFF;
|
||||
Global.putInt(getContentResolver(), Global.ZEN_MODE, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public static final class ZenModeConfigView extends LinearLayout {
|
||||
private static final Typeface LIGHT =
|
||||
Typeface.create("sans-serif-light", Typeface.NORMAL);
|
||||
private static final int BG_COLOR = 0xffe7e8e9;
|
||||
private final Context mContext;
|
||||
|
||||
private Drawable mOldBackground;
|
||||
|
||||
public ZenModeConfigView(Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
setOrientation(VERTICAL);
|
||||
|
||||
int p = getResources().getDimensionPixelSize(R.dimen.content_margin_left);
|
||||
TextView tv = addHeader("When on");
|
||||
tv.setPadding(0, p / 2, 0, p / 4);
|
||||
addBuckets();
|
||||
tv = addHeader("Automatically turn on");
|
||||
tv.setPadding(0, p / 2, 0, p / 4);
|
||||
addTriggers();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
mOldBackground = getParentView().getBackground();
|
||||
if (DEBUG) Log.d(TAG, "onAttachedToWindow mOldBackground=" + mOldBackground);
|
||||
getParentView().setBackgroundColor(BG_COLOR);
|
||||
}
|
||||
|
||||
public void resetBackground() {
|
||||
if (DEBUG) Log.d(TAG, "resetBackground");
|
||||
getParentView().setBackground(mOldBackground);
|
||||
}
|
||||
|
||||
private View getParentView() {
|
||||
return (View)getParent().getParent();
|
||||
}
|
||||
|
||||
private TextView addHeader(String text) {
|
||||
TextView tv = new TextView(mContext);
|
||||
tv.setTypeface(LIGHT);
|
||||
tv.setTextColor(0x7f000000);
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getTextSize() * 1.5f);
|
||||
tv.setText(text);
|
||||
addView(tv);
|
||||
return tv;
|
||||
}
|
||||
|
||||
private void addTriggers() {
|
||||
addView(new TriggerView("Never"));
|
||||
}
|
||||
|
||||
private void addBuckets() {
|
||||
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
BucketView callView = new BucketView("Phone calls", 0,
|
||||
"Block all", "Block all except...", "Allow all");
|
||||
addView(callView, lp);
|
||||
lp.topMargin = 4;
|
||||
BucketView msgView = new BucketView("Texts, SMS, & other calls", 0,
|
||||
"Block all", "Block all except...", "Allow all");
|
||||
addView(msgView, lp);
|
||||
BucketView alarmView = new BucketView("Alarms & timers", 2,
|
||||
"Block all", "Block all except...", "Allow all");
|
||||
addView(alarmView, lp);
|
||||
BucketView otherView = new BucketView("Other interruptions", 0,
|
||||
"Block all", "Block all except...", "Allow all");
|
||||
addView(otherView, lp);
|
||||
}
|
||||
|
||||
private class BucketView extends RelativeLayout {
|
||||
public BucketView(String category, int defaultValue, String... values) {
|
||||
super(ZenModeConfigView.this.mContext);
|
||||
|
||||
setBackgroundColor(0xffffffff);
|
||||
final int p = getResources().getDimensionPixelSize(R.dimen.content_margin_left);
|
||||
final int lm = p * 3 / 4;
|
||||
TextView title = new TextView(mContext);
|
||||
title.setId(android.R.id.title);
|
||||
title.setTextColor(0xff000000);
|
||||
title.setTypeface(LIGHT);
|
||||
title.setText(category);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_PX, title.getTextSize() * 1.5f);
|
||||
LayoutParams lp =
|
||||
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
lp.topMargin = p / 2;
|
||||
lp.leftMargin = lm;
|
||||
addView(title, lp);
|
||||
|
||||
TextView subtitle = new TextView(mContext);
|
||||
subtitle.setTextColor(0xff000000);
|
||||
subtitle.setTypeface(LIGHT);
|
||||
subtitle.setText(values[defaultValue]);
|
||||
lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
lp.addRule(BELOW, title.getId());
|
||||
lp.leftMargin = lm;
|
||||
lp.bottomMargin = p / 2;
|
||||
addView(subtitle, lp);
|
||||
}
|
||||
}
|
||||
|
||||
private class TriggerView extends RelativeLayout {
|
||||
public TriggerView(String text) {
|
||||
super(ZenModeConfigView.this.mContext);
|
||||
|
||||
setBackgroundColor(0xffffffff);
|
||||
final int p = getResources().getDimensionPixelSize(R.dimen.content_margin_left);
|
||||
|
||||
final TextView tv = new TextView(mContext);
|
||||
tv.setText(text);
|
||||
tv.setTypeface(LIGHT);
|
||||
tv.setTextColor(0xff000000);
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getTextSize() * 1.5f);
|
||||
LayoutParams lp =
|
||||
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
lp.addRule(CENTER_VERTICAL);
|
||||
lp.bottomMargin = p / 2;
|
||||
lp.topMargin = p / 2;
|
||||
lp.leftMargin = p * 3 / 4;
|
||||
addView(tv, lp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable indexing of searchable data
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
|
||||
final Resources res = context.getResources();
|
||||
|
||||
SearchIndexableRaw data = new SearchIndexableRaw(context);
|
||||
data.title = res.getString(R.string.zen_mode_settings_title);
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "When on";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "Calls";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "Text & SMS Messages";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "Alarms & Timers";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "Other Interruptions";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "Automatically turn on";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "While driving";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "While in meetings";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
data = new SearchIndexableRaw(context);
|
||||
data.title = "During a set time period";
|
||||
data.screenTitle = res.getString(R.string.zen_mode_settings_title);
|
||||
result.add(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user