Home | History | Annotate | Download | only in notificationlistener
      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 package com.android.example.notificationlistener;
     17 
     18 import android.app.AlertDialog;
     19 import android.app.ListActivity;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.os.Bundle;
     26 import android.provider.Settings.Secure;
     27 import android.service.notification.StatusBarNotification;
     28 import android.support.v4.content.LocalBroadcastManager;
     29 import android.util.Log;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.widget.BaseAdapter;
     33 import android.widget.Button;
     34 import android.widget.FrameLayout;
     35 import android.widget.TextView;
     36 
     37 import java.util.HashMap;
     38 import java.util.HashSet;
     39 import java.util.List;
     40 
     41 public class NotificationListenerActivity extends ListActivity {
     42     private static final String LISTENER_PATH = "com.android.example.notificationlistener/" +
     43             "com.android.example.notificationlistener.Listener";
     44     private static final String TAG = "NotificationListenerActivity";
     45 
     46     private Button mLaunchButton;
     47     private TextView mEmptyText;
     48     private StatusAdaptor mStatusAdaptor;
     49     private final BroadcastReceiver mRefreshListener = new BroadcastReceiver() {
     50         @Override
     51         public void onReceive(Context context, Intent intent) {
     52             Log.i(TAG, "update tickle");
     53             updateList(intent.getStringExtra(Listener.EXTRA_KEY));
     54         }
     55     };
     56 
     57     @Override
     58     public void onCreate(Bundle savedInstanceState) {
     59         super.onCreate(savedInstanceState);
     60         setTitle(R.string.long_app_name);
     61         setContentView(R.layout.main);
     62         mLaunchButton = (Button) findViewById(R.id.launch_settings);
     63         mEmptyText = (TextView) findViewById(android.R.id.empty);
     64         mStatusAdaptor = new StatusAdaptor(this);
     65         setListAdapter(mStatusAdaptor);
     66     }
     67 
     68     @Override
     69     protected void onStop() {
     70         LocalBroadcastManager.getInstance(this).unregisterReceiver(mRefreshListener);
     71         super.onStop();
     72     }
     73 
     74     @Override
     75     public void onStart() {
     76         super.onStart();
     77         final IntentFilter intentFilter = new IntentFilter(Listener.ACTION_REFRESH);
     78         LocalBroadcastManager.getInstance(this).registerReceiver(mRefreshListener, intentFilter);
     79         updateList(null);
     80     }
     81 
     82     @Override
     83     public void onResume() {
     84         super.onResume();
     85         checkEnabled();
     86     }
     87 
     88     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     89         checkEnabled();
     90     }
     91 
     92     private void checkEnabled() {
     93         String listeners = Secure.getString(getContentResolver(),
     94                 "enabled_notification_listeners");
     95         if (listeners != null && listeners.contains(LISTENER_PATH)) {
     96             mLaunchButton.setText(R.string.launch_to_disable);
     97             mEmptyText.setText(R.string.waiting_for_content);
     98         } else {
     99             mLaunchButton.setText(R.string.launch_to_enable);
    100             mEmptyText.setText(R.string.nothing_to_see);
    101             AlertDialog.Builder builder = new AlertDialog.Builder(this);
    102             builder.setMessage(R.string.explanation)
    103                     .setTitle(R.string.disabled);
    104             builder.setPositiveButton(R.string.enable_it, new DialogInterface.OnClickListener() {
    105                 public void onClick(DialogInterface dialog, int id) {
    106                     launchSettings(null);
    107                 }
    108             });
    109             builder.setNegativeButton(R.string.cancel, null);
    110             builder.create().show();
    111         }
    112     }
    113 
    114     public void launchSettings(View v) {
    115         startActivityForResult(
    116                 new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"), 0);
    117     }
    118 
    119     public void dismiss(View v) {
    120         Log.d(TAG, "clicked dismiss ");
    121         Object tag = v.getTag();
    122         if (tag instanceof StatusBarNotification) {
    123             StatusBarNotification sbn = (StatusBarNotification) tag;
    124             Log.d(TAG, "  on " + sbn.getKey());
    125             LocalBroadcastManager.getInstance(this).
    126                     sendBroadcast(new Intent(Listener.ACTION_DISMISS)
    127                             .putExtra(Listener.EXTRA_KEY, sbn.getKey()));
    128         }
    129     }
    130 
    131     public void launch(View v) {
    132         Log.d(TAG, "clicked launch");
    133         Object tag = v.getTag();
    134         if (tag instanceof StatusBarNotification) {
    135             StatusBarNotification sbn = (StatusBarNotification) tag;
    136             Log.d(TAG, "  on " + sbn.getKey());
    137             LocalBroadcastManager.getInstance(this).
    138                     sendBroadcast(new Intent(Listener.ACTION_LAUNCH)
    139                             .putExtra(Listener.EXTRA_KEY, sbn.getKey()));
    140         }
    141     }
    142 
    143     private void updateList(String key) {
    144         if (mStatusAdaptor.requiresInitialization()) {
    145             final List<StatusBarNotification> notifications = Listener.getNotifications();
    146             if (notifications != null) {
    147                 mStatusAdaptor.init(notifications);
    148             }
    149         }
    150         mStatusAdaptor.update(key);
    151     }
    152 
    153     private class StatusAdaptor extends BaseAdapter {
    154         private final Context mContext;
    155         private List<StatusBarNotification> mNotifications;
    156         private HashMap<String, Long> mKeyToId;
    157         private HashSet<String> mKeys;
    158         private long mNextId;
    159         private HashMap<String, View> mRecycledViews;
    160         private String mUpdateKey;
    161 
    162         public StatusAdaptor(Context context) {
    163             mContext = context;
    164             mKeyToId = new HashMap<String, Long>();
    165             mKeys = new HashSet<String>();
    166             mNextId = 0;
    167             mRecycledViews = new HashMap<String, View>();
    168         }
    169 
    170         @Override
    171         public int getCount() {
    172             return mNotifications == null ? 0 : mNotifications.size();
    173         }
    174 
    175         @Override
    176         public Object getItem(int position) {
    177             return mNotifications.get(position);
    178         }
    179 
    180         @Override
    181         public boolean hasStableIds() {
    182             return true;
    183         }
    184 
    185         @Override
    186         public long getItemId(int position) {
    187             final StatusBarNotification sbn = mNotifications.get(position);
    188             final String key = sbn.getKey();
    189             if (!mKeyToId.containsKey(key)) {
    190                 mKeyToId.put(key, mNextId);
    191                 mNextId ++;
    192             }
    193             return mKeyToId.get(key);
    194         }
    195 
    196         @Override
    197         public View getView(int position, View view, ViewGroup list) {
    198             if (view == null) {
    199                 view = View.inflate(mContext, R.layout.item, null);
    200             }
    201             FrameLayout container = (FrameLayout) view.findViewById(R.id.remote_view);
    202             View dismiss = view.findViewById(R.id.dismiss);
    203             StatusBarNotification sbn = mNotifications.get(position);
    204             View child;
    205             if (container.getTag() instanceof StatusBarNotification &&
    206                     container.getChildCount() > 0) {
    207                 // recycle the view
    208                 StatusBarNotification old = (StatusBarNotification) container.getTag();
    209                 if (sbn.getKey().equals(mUpdateKey)) {
    210                     //this view is out of date, discard it
    211                     mUpdateKey = null;
    212                 } else {
    213                     View content = container.getChildAt(0);
    214                     container.removeView(content);
    215                     mRecycledViews.put(old.getKey(), content);
    216                 }
    217             }
    218             child = mRecycledViews.get(sbn.getKey());
    219             if (child == null) {
    220                 child = sbn.getNotification().contentView.apply(mContext, null);
    221             }
    222             container.setTag(sbn);
    223             container.removeAllViews();
    224             container.addView(child);
    225             dismiss.setVisibility(sbn.isClearable() ? View.VISIBLE : View.GONE);
    226             dismiss.setTag(sbn);
    227             return view;
    228         }
    229 
    230         public void update(String key) {
    231             if (mNotifications != null) {
    232                 synchronized (mNotifications) {
    233                     mKeys.clear();
    234                     for (int i = 0; i < mNotifications.size(); i++) {
    235                         mKeys.add(mNotifications.get(i).getKey());
    236                     }
    237                     mKeyToId.keySet().retainAll(mKeys);
    238                 }
    239                 if (key == null) {
    240                     mRecycledViews.clear();
    241                 } else {
    242                     mUpdateKey = key;
    243                     mRecycledViews.remove(key);
    244                 }
    245                 Log.d(TAG, "notifyDataSetChanged");
    246                 notifyDataSetChanged();
    247             } else {
    248                 Log.d(TAG, "missed and update");
    249             }
    250         }
    251 
    252         public boolean requiresInitialization() {
    253             return mNotifications == null;
    254         }
    255 
    256         public void init(List<StatusBarNotification> notifications) {
    257             mNotifications = notifications;
    258         }
    259     }
    260 }
    261