| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * Copyright (C) 2016 Mopria Alliance, Inc. |
| * |
| * 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.bips.discovery; |
| |
| import android.net.Uri; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.util.Log; |
| |
| import com.android.bips.BuiltInPrintService; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| |
| /** |
| * Parent class for all printer discovery mechanisms. Subclasses must implement onStart and onStop. |
| * While started, discovery mechanisms deliver DiscoveredPrinter objects via |
| * {@link #printerFound(DiscoveredPrinter)} when they appear, and {@link #printerLost(Uri)} when |
| * they become unavailable. |
| */ |
| public abstract class Discovery { |
| private static final String TAG = Discovery.class.getSimpleName(); |
| private static final boolean DEBUG = false; |
| |
| private final BuiltInPrintService mPrintService; |
| private final List<Listener> mListeners = new CopyOnWriteArrayList<>(); |
| private final Map<Uri, DiscoveredPrinter> mPrinters = new HashMap<>(); |
| private final Handler mHandler = new Handler(Looper.getMainLooper()); |
| |
| private boolean mStarted = false; |
| |
| Discovery(BuiltInPrintService printService) { |
| mPrintService = printService; |
| } |
| |
| /** |
| * Add a listener and begin receiving notifications from the Discovery object of any |
| * printers it finds. |
| */ |
| public void start(Listener listener) { |
| mListeners.add(listener); |
| |
| // If printers are already present, signal them to the listener |
| if (!mPrinters.isEmpty()) { |
| if (!mListeners.contains(listener)) { |
| return; |
| } |
| for (DiscoveredPrinter printer : new ArrayList<>(mPrinters.values())) { |
| listener.onPrinterFound(printer); |
| } |
| } |
| |
| start(); |
| } |
| |
| /** |
| * Remove a listener so that it no longer receives notifications of found printers. |
| * Discovery will continue for other listeners until the last one is removed. |
| */ |
| public void stop(Listener listener) { |
| mListeners.remove(listener); |
| if (mListeners.isEmpty()) { |
| stop(); |
| } |
| } |
| |
| /** |
| * Return true if this object is in a started state |
| */ |
| boolean isStarted() { |
| return mStarted; |
| } |
| |
| /** |
| * Return the current print service instance |
| */ |
| BuiltInPrintService getPrintService() { |
| return mPrintService; |
| } |
| |
| /** |
| * Return a handler to defer actions to the main (UI) thread while started. All delayed actions |
| * scheduled on this handler are cancelled at {@link #stop()} time. |
| */ |
| Handler getHandler() { |
| return mHandler; |
| } |
| |
| /** |
| * Start if not already started |
| */ |
| private void start() { |
| if (!mStarted) { |
| mStarted = true; |
| onStart(); |
| } |
| } |
| |
| /** |
| * Stop if not already stopped |
| */ |
| private void stop() { |
| if (mStarted) { |
| mStarted = false; |
| onStop(); |
| mPrinters.clear(); |
| mHandler.removeCallbacksAndMessages(null); |
| } |
| } |
| |
| /** |
| * Start searching for printers |
| */ |
| abstract void onStart(); |
| |
| /** |
| * Stop searching for printers, freeing any search-related resources |
| */ |
| abstract void onStop(); |
| |
| /** |
| * Signal that a printer appeared or possibly changed state. |
| */ |
| void printerFound(DiscoveredPrinter printer) { |
| DiscoveredPrinter current = mPrinters.get(printer.getUri()); |
| if (Objects.equals(current, printer)) { |
| if (DEBUG) Log.d(TAG, "Already have the reported printer, ignoring"); |
| return; |
| } |
| mPrinters.put(printer.getUri(), printer); |
| for (Listener listener : mListeners) { |
| listener.onPrinterFound(printer); |
| } |
| } |
| |
| /** |
| * Signal that a printer is no longer visible |
| */ |
| void printerLost(Uri printerUri) { |
| DiscoveredPrinter printer = mPrinters.remove(printerUri); |
| if (printer == null) { |
| return; |
| } |
| for (Listener listener : mListeners) { |
| listener.onPrinterLost(printer); |
| } |
| } |
| |
| /** Signal loss of all printers */ |
| void allPrintersLost() { |
| for (Uri uri: new ArrayList<>(mPrinters.keySet())) { |
| printerLost(uri); |
| } |
| } |
| |
| /** |
| * Return the working collection of currently-found printers |
| */ |
| public Collection<DiscoveredPrinter> getPrinters() { |
| return mPrinters.values(); |
| } |
| |
| /** |
| * Return printer matching the uri, or null if none |
| */ |
| public DiscoveredPrinter getPrinter(Uri uri) { |
| return mPrinters.get(uri); |
| } |
| |
| /** |
| * Return a collection of leaf objects. By default returns a collection containing this object. |
| * Subclasses wrapping other {@link Discovery} objects should override this method. |
| */ |
| Collection<Discovery> getChildren() { |
| return Collections.singleton(this); |
| } |
| |
| /** |
| * Return a collection of saved printers. Subclasses supporting saved printers should override |
| * this method. |
| */ |
| public Collection<DiscoveredPrinter> getSavedPrinters() { |
| List<DiscoveredPrinter> printers = new ArrayList<>(); |
| for (Discovery child : getChildren()) { |
| if (child != this) { |
| printers.addAll(child.getSavedPrinters()); |
| } |
| } |
| return printers; |
| } |
| |
| /** |
| * Remove a saved printer by its path. Subclasses supporting saved printers should override |
| * this method. |
| */ |
| public void removeSavedPrinter(Uri printerPath) { |
| for (Discovery child : getChildren()) { |
| if (child != this) { |
| child.removeSavedPrinter(printerPath); |
| } |
| } |
| } |
| |
| public interface Listener { |
| /** |
| * A new printer has been discovered, or an existing printer has been updated |
| */ |
| void onPrinterFound(DiscoveredPrinter printer); |
| |
| /** |
| * A previously-found printer is no longer discovered. |
| */ |
| void onPrinterLost(DiscoveredPrinter printer); |
| } |
| } |