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.util.Log;
22 
23 import com.android.bips.BuiltInPrintService;
24 
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.concurrent.CopyOnWriteArrayList;
31 
32 /**
33  * Parent class for all printer discovery mechanisms. Subclasses must implement onStart and onStop.
34  * While started, discovery mechanisms deliver DiscoveredPrinter objects via
35  * {@link #printerFound(DiscoveredPrinter)} when they appear, and {@link #printerLost(Uri)} when
36  * they become unavailable.
37  */
38 public abstract class Discovery {
39     private static final String TAG = Discovery.class.getSimpleName();
40     private static final boolean DEBUG = false;
41 
42     private final BuiltInPrintService mPrintService;
43     private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
44     private final Map<Uri, DiscoveredPrinter> mPrinters = new HashMap<>();
45 
46     private boolean mStarted = false;
47 
Discovery(BuiltInPrintService printService)48     Discovery(BuiltInPrintService printService) {
49         mPrintService = printService;
50     }
51 
52     /**
53      * Add a listener and begin receiving notifications from the Discovery object of any
54      * printers it finds.
55      */
start(Listener listener)56     public void start(Listener listener) {
57         mListeners.add(listener);
58         mPrinters.values().forEach(listener::onPrinterFound);
59         start();
60     }
61 
62     /**
63      * Remove a listener so that it no longer receives notifications of found printers.
64      * Discovery will continue for other listeners until the last one is removed.
65      */
stop(Listener listener)66     public void stop(Listener listener) {
67         mListeners.remove(listener);
68         if (mListeners.isEmpty()) {
69             stop();
70         }
71     }
72 
73     /**
74      * Return true if this object is in a started state
75      */
isStarted()76     boolean isStarted() {
77         return mStarted;
78     }
79 
80     /**
81      * Return the current print service instance
82      */
getPrintService()83     BuiltInPrintService getPrintService() {
84         return mPrintService;
85     }
86 
87     /**
88      * Start if not already started
89      */
start()90     private void start() {
91         if (!mStarted) {
92             mStarted = true;
93             onStart();
94         }
95     }
96 
97     /**
98      * Stop if not already stopped
99      */
stop()100     private void stop() {
101         if (mStarted) {
102             mStarted = false;
103             onStop();
104             mPrinters.clear();
105         }
106     }
107 
108     /**
109      * Start searching for printers
110      */
onStart()111     abstract void onStart();
112 
113     /**
114      * Stop searching for printers, freeing any search-reated resources.
115      */
onStop()116     abstract void onStop();
117 
118     /**
119      * Signal that a printer appeared or possibly changed state.
120      */
printerFound(DiscoveredPrinter printer)121     void printerFound(DiscoveredPrinter printer) {
122         DiscoveredPrinter current = mPrinters.get(printer.getUri());
123         if (Objects.equals(current, printer)) {
124             if (DEBUG) Log.d(TAG, "Already have the reported printer, ignoring");
125             return;
126         }
127         mPrinters.put(printer.getUri(), printer);
128         for (Listener listener : mListeners) {
129             listener.onPrinterFound(printer);
130         }
131     }
132 
133     /**
134      * Signal that a printer is no longer visible
135      */
printerLost(Uri printerUri)136     void printerLost(Uri printerUri) {
137         DiscoveredPrinter printer = mPrinters.remove(printerUri);
138         if (printer == null) return;
139         for (Listener listener : mListeners) {
140             listener.onPrinterLost(printer);
141         }
142     }
143 
144     /** Signal loss of all printers */
allPrintersLost()145     void allPrintersLost() {
146         for (DiscoveredPrinter printer : mPrinters.values()) {
147             for (Listener listener : mListeners) {
148                 listener.onPrinterLost(printer);
149             }
150         }
151         mPrinters.clear();
152     }
153 
154     /**
155      * Return the working collection of currently-found printers
156      */
getPrinters()157     Collection<DiscoveredPrinter> getPrinters() {
158         return mPrinters.values();
159     }
160 
161     public interface Listener {
onPrinterFound(DiscoveredPrinter printer)162         void onPrinterFound(DiscoveredPrinter printer);
163 
onPrinterLost(DiscoveredPrinter printer)164         void onPrinterLost(DiscoveredPrinter printer);
165     }
166 }