Android之通知使用权

Posted zhchoutai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android之通知使用权相关的知识,希望对你有一定的参考价值。

通知使用权打开方式

设置——提示音和通知——通知使用权。

具体界面如图:

技术分享图片

存在须要拥有通知使用权应用时:

技术分享图片

存在须要拥有通知使用权应用时:

技术分享图片

用户为应用勾选复选框后系统弹dialog须要用户进一步确认时:

技术分享图片

主要涉及文件:

/packages/apps/Settings/src/com/android/settings/notification/NotificationAccessSettings.java

?/packages/apps/Settings/src/com/android/settings/notification/ManagedServiceSettings.java

涉及数据库:

data/data/com.android.providers.settings

具体解说:

public class NotificationAccessSettings extends ManagedServiceSettings {
    private static final String TAG = NotificationAccessSettings.class.getSimpleName();
    private static final Config CONFIG = getNotificationListenerConfig();

    private static Config getNotificationListenerConfig() {
        final Config c = new Config();
        c.tag = TAG;
        /*Settings.Secure.ENABLED_NOTIFICATION_LISTENERS = 
         *数据库字段。通过读取数据库中字段推断应用是否有通知使用权。有则界面中应用相应的checkbox为勾选状态,
         *应用相应checkbox不勾选时,时用户勾选后会有提示框弹出。确定后通过此字段向数据库写入此应用信息。
         * */
        c.setting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
        c.intentAction = NotificationListenerService.SERVICE_INTERFACE;
        /*
         * 应用须要在manifest文件里声明这个服务权限才会被检測到, 才会显示到同一时候使用权界面
         * */
        c.permission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
        c.noun = "notification listener";
        /*用户勾选后弹出的dialog中的标题*/
        c.warningDialogTitle = R.string.notification_listener_security_warning_title;
        /*用户勾选后弹出的dialog中的内容*/
        c.warningDialogSummary = R.string.notification_listener_security_warning_summary;
        /*当前系统中不存在不论什么须要使用通知使用权的应用时,通知使用权界面会有相应提示*/
        c.emptyText = R.string.no_notification_listeners;
        return c;
    }

    @Override
    protected Config getConfig() {
        return CONFIG;
    }

    public static int getListenersCount(PackageManager pm) {
        return getServicesCount(CONFIG, pm);
    }

    public static int getEnabledListenersCount(Context context) {
        return getEnabledServicesCount(CONFIG, context);
    }
}


/packages/apps/Settings/src/com/android/settings/notification/ManagedServiceSettings.java

public abstract class ManagedServiceSettings extends ListFragment {
    private static final boolean SHOW_PACKAGE_NAME = false;

    private final Config mConfig;
    private PackageManager mPM;
    private ContentResolver mCR;

    private final HashSet<ComponentName> mEnabledServices = new HashSet<ComponentName>();
    private ServiceListAdapter mListAdapter;

    abstract protected Config getConfig();

    public ManagedServiceSettings() {
        mConfig = getConfig();
    }

    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();
        }
    };
    /*用户勾选后弹出相应dialog等待用户进一步确认要为此应用打开通知使用权*/
    public class ScaryWarningDialogFragment extends DialogFragment {
        static final String KEY_COMPONENT = "c";
        static final String KEY_LABEL = "l";

        public ScaryWarningDialogFragment setServiceInfo(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(mConfig.warningDialogTitle, label);
            final String summary = getResources().getString(mConfig.warningDialogSummary, label);
            return new AlertDialog.Builder(getActivity())
                    .setMessage(summary)
                    .setTitle(title)
                    .setCancelable(true)
                    .setPositiveButton(android.R.string.ok,//
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    mEnabledServices.add(cn);//加入应用信息到HashSet<ComponentName>中
                                    saveEnabledServices();//数据库写操作
                                }
                            })
                    .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();
        mListAdapter = new ServiceListAdapter(getActivity());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View v =  inflater.inflate(R.layout.managed_service_settings, container, false);
        TextView empty = (TextView) v.findViewById(android.R.id.empty);
        empty.setText(mConfig.emptyText);
        return v;
    }

    @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(Settings.Secure.getUriFor(mConfig.setting),
                false, mSettingsObserver);
    }

    @Override
    public void onPause() {
        super.onPause();

        getActivity().unregisterReceiver(mPackageReceiver);
        mCR.unregisterContentObserver(mSettingsObserver);
    }
    
    /*从数据库中载入拥有通知使用权的应用,并将其信息存入到HashSet<ComponentName>中。*/
    private void loadEnabledServices() {
        mEnabledServices.clear();//首先清空HashSet<ComponentName>。确保数据最新从数据库读取
        final String flat = Settings.Secure.getString(mCR, mConfig.setting);//数据库读操作
        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) {
                    mEnabledServices.add(cn);
                }
            }
        }
    }
    /*数据库存操作*/
    private void saveEnabledServices() {
        StringBuilder sb = null;
        for (ComponentName cn : mEnabledServices) {
            if (sb == null) {
                sb = new StringBuilder();
            } else {
                sb.append(‘:‘);
            }
            sb.append(cn.flattenToString());
        }
        /*数据库存操作*/
        Settings.Secure.putString(mCR,
                mConfig.setting,
                sb != null ? sb.toString() : "");
    }
    /*更新应用显示列表*/
    private void updateList() {
        loadEnabledServices();

        getServices(mConfig, mListAdapter, mPM);
        mListAdapter.sort(new PackageItemInfo.DisplayNameComparator(mPM));

        getListView().setAdapter(mListAdapter);
    }

    protected static int getEnabledServicesCount(Config config, Context context) {
        final String flat = Settings.Secure.getString(context.getContentResolver(), config.setting);
        if (flat == null || "".equals(flat)) return 0;
        final String[] components = flat.split(":");
        return components.length;
    }

    protected static int getServicesCount(Config c, PackageManager pm) {
        return getServices(c, null, pm);
    }

    private static int getServices(Config c, ArrayAdapter<ServiceInfo> adapter, PackageManager pm) {
        int services = 0;
        if (adapter != null) {
            adapter.clear();
        }
        final int user = ActivityManager.getCurrentUser();

        List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
                new Intent(c.intentAction),
                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 (!c.permission.equals(info.permission)) {
                Slog.w(c.tag, "Skipping " + c.noun + " service "
                        + info.packageName + "/" + info.name
                        + ": it does not require the permission "
                        + c.permission);
                continue;
            }
            if (adapter != null) {
                adapter.add(info);
            }
            services++;
        }
        return services;
    }

    private boolean isServiceEnabled(ServiceInfo info) {
        final ComponentName cn = new ComponentName(info.packageName, info.name);
        return mEnabledServices.contains(cn);
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        ServiceInfo info = mListAdapter.getItem(position);
        final ComponentName cn = new ComponentName(info.packageName, info.name);
        if (mEnabledServices.contains(cn)) {
        	//取消勾选
            // the simple version: disabling
            mEnabledServices.remove(cn);
            saveEnabledServices();
        } else {
        	//选择勾选后填出dialog
            // show a scary dialog
            new ScaryWarningDialogFragment()
                .setServiceInfo(cn, info.loadLabel(mPM).toString())
                .show(getFragmentManager(), "dialog");
        }
    }

    private static class ViewHolder {
        ImageView icon;
        TextView name;
        CheckBox checkbox;
        TextView description;
    }
    
    /*用于应用列表载入显示*/
    private class ServiceListAdapter extends ArrayAdapter<ServiceInfo> {
        final LayoutInflater mInflater;

        ServiceListAdapter(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.managed_service_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(isServiceEnabled(info));
        }
    }

    protected static class Config {
        String tag;
        String setting;
        String intentAction;
        String permission;
        String noun;
        int warningDialogTitle;
        int warningDialogSummary;
        int emptyText;
    }
<p>}
</p>

数据库相关信息

技术分享图片

数据库字段相应应用信息格式:包名/service:包名/service。两应用间信息用”:“隔开。

以上是关于Android之通知使用权的主要内容,如果未能解决你的问题,请参考以下文章

在android中按下通知时如何打开片段页面

Android 错误从通知转到片段

当通知进入android时如何获取Bundle数据

Android 通知导航到现有活动

推送通知停止在parse.com android项目中工作

从通知导航到带有 NavController 的片段