Skip to content

Most visited

Recently visited

navigation
Quiz / Wearable / src / com.example.android.wearable.quiz /

QuizListenerService.java

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
 
17
package com.example.android.wearable.quiz;
18
 
19
import static com.example.android.wearable.quiz.Constants.ANSWERS;
20
import static com.example.android.wearable.quiz.Constants.CONNECT_TIMEOUT_MS;
21
import static com.example.android.wearable.quiz.Constants.CORRECT_ANSWER_INDEX;
22
import static com.example.android.wearable.quiz.Constants.NUM_CORRECT;
23
import static com.example.android.wearable.quiz.Constants.NUM_INCORRECT;
24
import static com.example.android.wearable.quiz.Constants.NUM_SKIPPED;
25
import static com.example.android.wearable.quiz.Constants.QUESTION;
26
import static com.example.android.wearable.quiz.Constants.QUESTION_INDEX;
27
import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_ANSWERED;
28
import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_DELETED;
29
import static com.example.android.wearable.quiz.Constants.QUIZ_ENDED_PATH;
30
import static com.example.android.wearable.quiz.Constants.QUIZ_EXITED_PATH;
31
 
32
import android.app.Notification;
33
import android.app.NotificationManager;
34
import android.app.PendingIntent;
35
import android.content.Intent;
36
import android.net.Uri;
37
import android.text.SpannableStringBuilder;
38
import android.text.style.ForegroundColorSpan;
39
import android.util.Log;
40
 
41
import com.google.android.gms.common.ConnectionResult;
42
import com.google.android.gms.common.api.GoogleApiClient;
43
import com.google.android.gms.wearable.DataEvent;
44
import com.google.android.gms.wearable.DataEventBuffer;
45
import com.google.android.gms.wearable.DataItem;
46
import com.google.android.gms.wearable.DataMap;
47
import com.google.android.gms.wearable.DataMapItem;
48
import com.google.android.gms.wearable.MessageEvent;
49
import com.google.android.gms.wearable.Wearable;
50
import com.google.android.gms.wearable.WearableListenerService;
51
 
52
import java.util.Collections;
53
import java.util.HashMap;
54
import java.util.List;
55
import java.util.Map;
56
import java.util.concurrent.TimeUnit;
57
 
58
/**
59
 * Listens to changes in DataItems, which represent quiz questions.
60
 * If a new question is created, this builds a new notification for it.
61
 * Otherwise, if a question is deleted, this cancels the corresponding notification.
62
 *
63
 * When the quiz ends, this listener receives a message telling it to create an end-of-quiz report.
64
 */
65
public class QuizListenerService extends WearableListenerService {
66
 
67
    private static final String TAG = "QuizSample";
68
    private static final int QUIZ_REPORT_NOTIF_ID = -1; // Never used by question notifications.
69
    private static final Map<Integer, Integer> questionNumToDrawableId;
70
 
71
    static {
72
        Map<Integer, Integer> temp = new HashMap<Integer, Integer>(4);
73
        temp.put(0, R.drawable.ic_choice_a);
74
        temp.put(1, R.drawable.ic_choice_b);
75
        temp.put(2, R.drawable.ic_choice_c);
76
        temp.put(3, R.drawable.ic_choice_d);
77
        questionNumToDrawableId = Collections.unmodifiableMap(temp);
78
    }
79
 
80
    @Override
81
    public void onDataChanged(DataEventBuffer dataEvents) {
82
        GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
83
                .addApi(Wearable.API)
84
                .build();
85
 
86
        ConnectionResult connectionResult = googleApiClient.blockingConnect(CONNECT_TIMEOUT_MS,
87
                TimeUnit.MILLISECONDS);
88
        if (!connectionResult.isSuccess()) {
89
            Log.e(TAG, "QuizListenerService failed to connect to GoogleApiClient.");
90
            return;
91
        }
92
 
93
        for (DataEvent event : dataEvents) {
94
            if (event.getType() == DataEvent.TYPE_CHANGED) {
95
                DataItem dataItem = event.getDataItem();
96
                DataMap dataMap = DataMapItem.fromDataItem(dataItem).getDataMap();
97
                if (dataMap.getBoolean(QUESTION_WAS_ANSWERED)
98
                        || dataMap.getBoolean(QUESTION_WAS_DELETED)) {
99
                    // Ignore the change in data; it is used in MainActivity to update
100
                    // the question's status (i.e. was the answer right or wrong or left blank).
101
                    continue;
102
                }
103
                String question = dataMap.getString(QUESTION);
104
                int questionIndex = dataMap.getInt(QUESTION_INDEX);
105
                int questionNum = questionIndex + 1;
106
                String[] answers = dataMap.getStringArray(ANSWERS);
107
                int correctAnswerIndex = dataMap.getInt(CORRECT_ANSWER_INDEX);
108
                Intent deleteOperation = new Intent(this, DeleteQuestionService.class);
109
                deleteOperation.setData(dataItem.getUri());
110
                PendingIntent deleteIntent = PendingIntent.getService(this, 0,
111
                        deleteOperation, PendingIntent.FLAG_UPDATE_CURRENT);
112
                // First page of notification contains question as Big Text.
113
                Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle()
114
                        .setBigContentTitle(getString(R.string.question, questionNum))
115
                        .bigText(question);
116
                Notification.Builder builder = new Notification.Builder(this)
117
                        .setStyle(bigTextStyle)
118
                        .setSmallIcon(R.drawable.ic_launcher)
119
                        .setLocalOnly(true)
120
                        .setDeleteIntent(deleteIntent);
121
 
122
                // Add answers as actions.
123
                Notification.WearableExtender wearableOptions = new Notification.WearableExtender();
124
                for (int i = 0; i < answers.length; i++) {
125
                    Notification answerPage = new Notification.Builder(this)
126
                            .setContentTitle(question)
127
                            .setContentText(answers[i])
128
                            .extend(new Notification.WearableExtender()
129
                                    .setContentAction(i))
130
                            .build();
131
 
132
                    boolean correct = (i == correctAnswerIndex);
133
                    Intent updateOperation = new Intent(this, UpdateQuestionService.class);
134
                    // Give each intent a unique action.
135
                    updateOperation.setAction("question_" + questionIndex + "_answer_" + i);
136
                    updateOperation.setData(dataItem.getUri());
137
                    updateOperation.putExtra(UpdateQuestionService.EXTRA_QUESTION_INDEX,
138
                            questionIndex);
139
                    updateOperation.putExtra(UpdateQuestionService.EXTRA_QUESTION_CORRECT, correct);
140
                    PendingIntent updateIntent = PendingIntent.getService(this, 0, updateOperation,
141
                            PendingIntent.FLAG_UPDATE_CURRENT);
142
                    Notification.Action action = new Notification.Action.Builder(
143
                            questionNumToDrawableId.get(i), null, updateIntent)
144
                            .build();
145
                    wearableOptions.addAction(action).addPage(answerPage);
146
                }
147
                builder.extend(wearableOptions);
148
                Notification notification = builder.build();
149
                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
150
                        .notify(questionIndex, notification);
151
            } else if (event.getType() == DataEvent.TYPE_DELETED) {
152
                Uri uri = event.getDataItem().getUri();
153
                // URI's are of the form "/question/0", "/question/1" etc.
154
                // We use the question index as the notification id.
155
                int notificationId = Integer.parseInt(uri.getLastPathSegment());
156
                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
157
                        .cancel(notificationId);
158
            }
159
            // Delete the quiz report, if it exists.
160
            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
161
                    .cancel(QUIZ_REPORT_NOTIF_ID);
162
        }
163
        googleApiClient.disconnect();
164
    }
165
 
166
    @Override
167
    public void onMessageReceived(MessageEvent messageEvent) {
168
        String path = messageEvent.getPath();
169
        if (path.equals(QUIZ_EXITED_PATH)) {
170
            // Remove any lingering question notifications.
171
            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancelAll();
172
        }
173
        if (path.equals(QUIZ_ENDED_PATH) || path.equals(QUIZ_EXITED_PATH)) {
174
            // Quiz ended - display overall results.
175
            DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
176
            int numCorrect = dataMap.getInt(NUM_CORRECT);
177
            int numIncorrect = dataMap.getInt(NUM_INCORRECT);
178
            int numSkipped = dataMap.getInt(NUM_SKIPPED);
179
 
180
            Notification.Builder builder = new Notification.Builder(this)
181
                    .setContentTitle(getString(R.string.quiz_report))
182
                    .setSmallIcon(R.drawable.ic_launcher)
183
                    .setLocalOnly(true);
184
            SpannableStringBuilder quizReportText = new SpannableStringBuilder();
185
            appendColored(quizReportText, String.valueOf(numCorrect), R.color.dark_green);
186
            quizReportText.append(" " + getString(R.string.correct) + "\n");
187
            appendColored(quizReportText, String.valueOf(numIncorrect), R.color.dark_red);
188
            quizReportText.append(" " + getString(R.string.incorrect) + "\n");
189
            appendColored(quizReportText, String.valueOf(numSkipped), R.color.dark_yellow);
190
            quizReportText.append(" " + getString(R.string.skipped) + "\n");
191
 
192
            builder.setContentText(quizReportText);
193
            if (!path.equals(QUIZ_EXITED_PATH)) {
194
                // Don't add reset option if user exited quiz (there might not be a quiz to reset!).
195
                builder.addAction(R.drawable.ic_launcher,
196
                        getString(R.string.reset_quiz), getResetQuizPendingIntent());
197
            }
198
            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
199
                    .notify(QUIZ_REPORT_NOTIF_ID, builder.build());
200
        }
201
    }
202
 
203
    private void appendColored(SpannableStringBuilder builder, String text, int colorResId) {
204
        builder.append(text).setSpan(new ForegroundColorSpan(getResources().getColor(colorResId)),
205
                builder.length() - text.length(), builder.length(), 0);
206
    }
207
 
208
    /**
209
     * Returns a PendingIntent that will send a message to the phone to reset the quiz when fired.
210
     */
211
    private PendingIntent getResetQuizPendingIntent() {
212
        Intent intent = new Intent(QuizReportActionService.ACTION_RESET_QUIZ)
213
                .setClass(this, QuizReportActionService.class);
214
        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
215
    }
216
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!