1 /*
2  * Copyright (c) 2016, 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.car.stream;
17 
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.ServiceConnection;
22 import android.os.IBinder;
23 import android.support.annotation.CallSuper;
24 import android.util.Log;
25 
26 import java.util.Iterator;
27 import java.util.LinkedList;
28 import java.util.Queue;
29 
30 /**
31  * A base class that produces {@link StreamCard} for the StreamService
32  */
33 public abstract class StreamProducer {
34     private static final String TAG = "StreamProducer";
35 
36     protected final Context mContext;
37 
38     /**
39      * A queue that holds {@link StreamCard}s that were added before this {@link StreamProducer}
40      * has connected to the {@link StreamService}. After connecting, these cards are posted to
41      * the StreamService.
42      */
43     private final Queue<StreamCard> mQueuedCards = new LinkedList<>();
44 
45     private StreamService mStreamService;
46 
StreamProducer(Context context)47     public StreamProducer(Context context) {
48         mContext = context;
49     }
50 
51     /**
52      * Posts the given card to the {@link StreamService} for rendering by stream consumers.
53      *
54      * @return {@code true} if the card was successfully posted. {@code false} is returned if the
55      * {@link StreamService} is not available. The given card will be queued and posted when the
56      * {@link StreamService} becomes available.
57      */
postCard(StreamCard card)58     public final boolean postCard(StreamCard card) {
59         if (mStreamService != null) {
60             mStreamService.addStreamCard(card);
61             return true;
62         }
63 
64         if (Log.isLoggable(TAG, Log.DEBUG)) {
65             Log.d(TAG, "StreamService not found, adding card to queue for later addition.");
66         }
67 
68         mQueuedCards.add(card);
69         return false;
70     }
71 
72     /**
73      * Removes the given card from the {@link StreamService}. If this {@link StreamProducer} has not
74      * connected to the {@link StreamService}, then {@link #mQueuedCards} is checked to see if it
75      * contains the given card.
76      *
77      * @return {@code true} if the card is successfully removed from either the
78      * {@link StreamService} or {@link #mQueuedCards}.
79      */
removeCard(StreamCard card)80     public final boolean removeCard(StreamCard card) {
81         if (card == null) {
82             return false;
83         }
84 
85         if (mStreamService != null) {
86             mStreamService.removeStreamCard(card);
87             return true;
88         }
89 
90         if (Log.isLoggable(TAG, Log.DEBUG)) {
91             Log.d(TAG, "StreamService not found, checking if it exists in the queue.");
92         }
93 
94         for (Iterator<StreamCard> iterator = mQueuedCards.iterator(); iterator.hasNext();) {
95             StreamCard queuedCard = iterator.next();
96             if (queuedCard.getType() == card.getType() && queuedCard.getId() == card.getId()) {
97                 iterator.remove();
98                 return true;
99             }
100         }
101 
102         return false;
103     }
104 
onCardDismissed(StreamCard card)105     public void onCardDismissed(StreamCard card) {
106         // Handle when a StreamCard is dismissed.
107         if (Log.isLoggable(TAG, Log.DEBUG)) {
108             Log.d(TAG, "Stream Card dismissed: " + card);
109         }
110     }
111 
112     /**
113      * Start the producer and connect to the {@link StreamService}
114      */
115     @CallSuper
start()116     public void start() {
117         Intent streamServiceIntent = new Intent(mContext, StreamService.class);
118         streamServiceIntent.setAction(StreamConstants.STREAM_PRODUCER_BIND_ACTION);
119         mContext.bindService(streamServiceIntent, mServiceConnection, 0 /* flags */);
120     }
121 
122     /**
123      * Stop the producer.
124      */
125     @CallSuper
stop()126     public void stop() {
127         mContext.unbindService(mServiceConnection);
128         mQueuedCards.clear();
129     }
130 
131     private ServiceConnection mServiceConnection = new ServiceConnection() {
132         @Override
133         public void onServiceConnected(ComponentName name, IBinder service) {
134             StreamService.StreamProducerBinder binder
135                     = (StreamService.StreamProducerBinder) service;
136             mStreamService = binder.getService();
137 
138             while (!mQueuedCards.isEmpty()) {
139                 mStreamService.addStreamCard(mQueuedCards.remove());
140             }
141         }
142 
143         @Override
144         public void onServiceDisconnected(ComponentName name) {
145             mStreamService = null;
146         }
147     };
148 }
149