Home | History | Annotate | Download | only in discovery
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  * Copyright (C) 2016 Mopria Alliance, Inc.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.bips.discovery;
     19 
     20 import android.net.Uri;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.util.Log;
     24 
     25 import com.android.bips.BuiltInPrintService;
     26 
     27 import java.util.ArrayList;
     28 import java.util.Collection;
     29 import java.util.Collections;
     30 import java.util.HashMap;
     31 import java.util.List;
     32 import java.util.Map;
     33 import java.util.Objects;
     34 import java.util.concurrent.CopyOnWriteArrayList;
     35 
     36 /**
     37  * Parent class for all printer discovery mechanisms. Subclasses must implement onStart and onStop.
     38  * While started, discovery mechanisms deliver DiscoveredPrinter objects via
     39  * {@link #printerFound(DiscoveredPrinter)} when they appear, and {@link #printerLost(Uri)} when
     40  * they become unavailable.
     41  */
     42 public abstract class Discovery {
     43     private static final String TAG = Discovery.class.getSimpleName();
     44     private static final boolean DEBUG = false;
     45 
     46     private final BuiltInPrintService mPrintService;
     47     private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
     48     private final Map<Uri, DiscoveredPrinter> mPrinters = new HashMap<>();
     49     private final Handler mHandler = new Handler(Looper.getMainLooper());
     50 
     51     private boolean mStarted = false;
     52 
     53     Discovery(BuiltInPrintService printService) {
     54         mPrintService = printService;
     55     }
     56 
     57     /**
     58      * Add a listener and begin receiving notifications from the Discovery object of any
     59      * printers it finds.
     60      */
     61     public void start(Listener listener) {
     62         mListeners.add(listener);
     63 
     64         // If printers are already present, signal them to the listener
     65         if (!mPrinters.isEmpty()) {
     66             if (!mListeners.contains(listener)) {
     67                 return;
     68             }
     69             for (DiscoveredPrinter printer : new ArrayList<>(mPrinters.values())) {
     70                 listener.onPrinterFound(printer);
     71             }
     72         }
     73 
     74         start();
     75     }
     76 
     77     /**
     78      * Remove a listener so that it no longer receives notifications of found printers.
     79      * Discovery will continue for other listeners until the last one is removed.
     80      */
     81     public void stop(Listener listener) {
     82         mListeners.remove(listener);
     83         if (mListeners.isEmpty()) {
     84             stop();
     85         }
     86     }
     87 
     88     /**
     89      * Return true if this object is in a started state
     90      */
     91     boolean isStarted() {
     92         return mStarted;
     93     }
     94 
     95     /**
     96      * Return the current print service instance
     97      */
     98     BuiltInPrintService getPrintService() {
     99         return mPrintService;
    100     }
    101 
    102     /**
    103      * Return a handler to defer actions to the main (UI) thread while started. All delayed actions
    104      * scheduled on this handler are cancelled at {@link #stop()} time.
    105      */
    106     Handler getHandler() {
    107         return mHandler;
    108     }
    109 
    110     /**
    111      * Start if not already started
    112      */
    113     private void start() {
    114         if (!mStarted) {
    115             mStarted = true;
    116             onStart();
    117         }
    118     }
    119 
    120     /**
    121      * Stop if not already stopped
    122      */
    123     private void stop() {
    124         if (mStarted) {
    125             mStarted = false;
    126             onStop();
    127             mPrinters.clear();
    128             mHandler.removeCallbacksAndMessages(null);
    129         }
    130     }
    131 
    132     /**
    133      * Start searching for printers
    134      */
    135     abstract void onStart();
    136 
    137     /**
    138      * Stop searching for printers, freeing any search-related resources
    139      */
    140     abstract void onStop();
    141 
    142     /**
    143      * Signal that a printer appeared or possibly changed state.
    144      */
    145     void printerFound(DiscoveredPrinter printer) {
    146         DiscoveredPrinter current = mPrinters.get(printer.getUri());
    147         if (Objects.equals(current, printer)) {
    148             if (DEBUG) Log.d(TAG, "Already have the reported printer, ignoring");
    149             return;
    150         }
    151         mPrinters.put(printer.getUri(), printer);
    152         for (Listener listener : mListeners) {
    153             listener.onPrinterFound(printer);
    154         }
    155     }
    156 
    157     /**
    158      * Signal that a printer is no longer visible
    159      */
    160     void printerLost(Uri printerUri) {
    161         DiscoveredPrinter printer = mPrinters.remove(printerUri);
    162         if (printer == null) {
    163             return;
    164         }
    165         for (Listener listener : mListeners) {
    166             listener.onPrinterLost(printer);
    167         }
    168     }
    169 
    170     /** Signal loss of all printers */
    171     void allPrintersLost() {
    172         for (Uri uri: new ArrayList<>(mPrinters.keySet())) {
    173             printerLost(uri);
    174         }
    175     }
    176 
    177     /**
    178      * Return the working collection of currently-found printers
    179      */
    180     public Collection<DiscoveredPrinter> getPrinters() {
    181         return mPrinters.values();
    182     }
    183 
    184     /**
    185      * Return printer matching the uri, or null if none
    186      */
    187     public DiscoveredPrinter getPrinter(Uri uri) {
    188         return mPrinters.get(uri);
    189     }
    190 
    191     /**
    192      * Return a collection of leaf objects. By default returns a collection containing this object.
    193      * Subclasses wrapping other {@link Discovery} objects should override this method.
    194      */
    195     Collection<Discovery> getChildren() {
    196         return Collections.singleton(this);
    197     }
    198 
    199     /**
    200      * Return a collection of saved printers. Subclasses supporting saved printers should override
    201      * this method.
    202      */
    203     public Collection<DiscoveredPrinter> getSavedPrinters() {
    204         List<DiscoveredPrinter> printers = new ArrayList<>();
    205         for (Discovery child : getChildren()) {
    206             if (child != this) {
    207                 printers.addAll(child.getSavedPrinters());
    208             }
    209         }
    210         return printers;
    211     }
    212 
    213     /**
    214      * Remove a saved printer by its path. Subclasses supporting saved printers should override
    215      * this method.
    216      */
    217     public void removeSavedPrinter(Uri printerPath) {
    218         for (Discovery child : getChildren()) {
    219             if (child != this) {
    220                 child.removeSavedPrinter(printerPath);
    221             }
    222         }
    223     }
    224 
    225     public interface Listener {
    226         /**
    227          * A new printer has been discovered, or an existing printer has been updated
    228          */
    229         void onPrinterFound(DiscoveredPrinter printer);
    230 
    231         /**
    232          * A previously-found printer is no longer discovered.
    233          */
    234         void onPrinterLost(DiscoveredPrinter printer);
    235     }
    236 }
    237