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.android.test.voiceinteraction; 18 19 import android.app.ActivityManager; 20 import android.app.VoiceInteractor; 21 import android.app.assist.AssistContent; 22 import android.app.assist.AssistStructure; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.graphics.Bitmap; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.service.voice.VoiceInteractionSession; 29 import android.util.Log; 30 import android.view.View; 31 import android.widget.Button; 32 import android.widget.CheckBox; 33 import android.widget.ImageView; 34 import android.widget.TextView; 35 36 public class MainInteractionSession extends VoiceInteractionSession 37 implements View.OnClickListener { 38 static final String TAG = "MainInteractionSession"; 39 40 Intent mStartIntent; 41 View mContentView; 42 AssistVisualizer mAssistVisualizer; 43 View mTopContent; 44 View mBottomContent; 45 TextView mText; 46 Button mTreeButton; 47 Button mTextButton; 48 Button mStartButton; 49 CheckBox mOptionsCheck; 50 View mOptionsContainer; 51 CheckBox mDisallowAssist; 52 CheckBox mDisallowScreenshot; 53 TextView mOptionsText; 54 ImageView mScreenshot; 55 ImageView mFullScreenshot; 56 Button mConfirmButton; 57 Button mCompleteButton; 58 Button mAbortButton; 59 60 AssistStructure mAssistStructure; 61 62 static final int STATE_IDLE = 0; 63 static final int STATE_LAUNCHING = 1; 64 static final int STATE_CONFIRM = 2; 65 static final int STATE_PICK_OPTION = 3; 66 static final int STATE_COMMAND = 4; 67 static final int STATE_ABORT_VOICE = 5; 68 static final int STATE_COMPLETE_VOICE = 6; 69 static final int STATE_DONE = 7; 70 71 int mState = STATE_IDLE; 72 VoiceInteractor.PickOptionRequest.Option[] mPendingOptions; 73 CharSequence mPendingPrompt; 74 Request mPendingRequest; 75 int mCurrentTask = -1; 76 MainInteractionSession(Context context)77 MainInteractionSession(Context context) { 78 super(context); 79 } 80 81 @Override onCreate()82 public void onCreate() { 83 super.onCreate(); 84 ActivityManager am = getContext().getSystemService(ActivityManager.class); 85 am.setWatchHeapLimit(40 * 1024 * 1024); 86 } 87 88 @Override onShow(Bundle args, int showFlags)89 public void onShow(Bundle args, int showFlags) { 90 super.onShow(args, showFlags); 91 Log.i(TAG, "onShow: flags=0x" + Integer.toHexString(showFlags) + " args=" + args); 92 mState = STATE_IDLE; 93 mStartIntent = args != null ? (Intent)args.getParcelable("intent") : null; 94 if (mStartIntent == null) { 95 mStartIntent = new Intent(getContext(), TestInteractionActivity.class); 96 } 97 if (mAssistVisualizer != null) { 98 mAssistVisualizer.clearAssistData(); 99 } 100 onHandleScreenshot(null); 101 updateState(); 102 refreshOptions(); 103 } 104 105 @Override onHide()106 public void onHide() { 107 super.onHide(); 108 if (mAssistVisualizer != null) { 109 mAssistVisualizer.clearAssistData(); 110 } 111 mState = STATE_DONE; 112 updateState(); 113 } 114 115 @Override onCreateContentView()116 public View onCreateContentView() { 117 mContentView = getLayoutInflater().inflate(R.layout.voice_interaction_session, null); 118 mAssistVisualizer = (AssistVisualizer)mContentView.findViewById(R.id.assist_visualizer); 119 if (mAssistStructure != null) { 120 mAssistVisualizer.setAssistStructure(mAssistStructure); 121 } 122 mTopContent = mContentView.findViewById(R.id.top_content); 123 mBottomContent = mContentView.findViewById(R.id.bottom_content); 124 mText = (TextView)mContentView.findViewById(R.id.text); 125 mTreeButton = (Button)mContentView.findViewById(R.id.do_tree); 126 mTreeButton.setOnClickListener(this); 127 mTextButton = (Button)mContentView.findViewById(R.id.do_text); 128 mTextButton.setOnClickListener(this); 129 mStartButton = (Button)mContentView.findViewById(R.id.start); 130 mStartButton.setOnClickListener(this); 131 mScreenshot = (ImageView)mContentView.findViewById(R.id.screenshot); 132 mScreenshot.setOnClickListener(this); 133 mFullScreenshot = (ImageView)mContentView.findViewById(R.id.full_screenshot); 134 mOptionsCheck = (CheckBox)mContentView.findViewById(R.id.show_options); 135 mOptionsCheck.setOnClickListener(this); 136 mOptionsContainer = mContentView.findViewById(R.id.options); 137 mDisallowAssist = (CheckBox)mContentView.findViewById(R.id.disallow_structure); 138 mDisallowAssist.setOnClickListener(this); 139 mDisallowScreenshot = (CheckBox)mContentView.findViewById(R.id.disallow_screenshot); 140 mDisallowScreenshot.setOnClickListener(this); 141 mOptionsText = (TextView)mContentView.findViewById(R.id.options_text); 142 mConfirmButton = (Button)mContentView.findViewById(R.id.confirm); 143 mConfirmButton.setOnClickListener(this); 144 mCompleteButton = (Button)mContentView.findViewById(R.id.complete); 145 mCompleteButton.setOnClickListener(this); 146 mAbortButton = (Button)mContentView.findViewById(R.id.abort); 147 mAbortButton.setOnClickListener(this); 148 refreshOptions(); 149 return mContentView; 150 } 151 refreshOptions()152 void refreshOptions() { 153 if (mOptionsContainer != null) { 154 if (mOptionsCheck.isChecked()) { 155 mOptionsContainer.setVisibility(View.VISIBLE); 156 int flags = getDisabledShowContext(); 157 mDisallowAssist.setChecked((flags & SHOW_WITH_ASSIST) != 0); 158 mDisallowScreenshot.setChecked((flags & SHOW_WITH_SCREENSHOT) != 0); 159 int disabled = getUserDisabledShowContext(); 160 mOptionsText.setText("Disabled: 0x" + Integer.toHexString(disabled)); 161 } else { 162 mOptionsContainer.setVisibility(View.GONE); 163 } 164 } 165 } 166 onHandleAssist(Bundle assistBundle)167 public void onHandleAssist(Bundle assistBundle) { 168 } 169 170 @Override onHandleAssist(Bundle data, AssistStructure structure, AssistContent content)171 public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) { 172 mAssistStructure = structure; 173 if (mAssistStructure != null) { 174 if (mAssistVisualizer != null) { 175 mAssistVisualizer.setAssistStructure(mAssistStructure); 176 } 177 } else { 178 if (mAssistVisualizer != null) { 179 mAssistVisualizer.clearAssistData(); 180 } 181 } 182 if (content != null) { 183 Log.i(TAG, "Assist intent: " + content.getIntent()); 184 Log.i(TAG, "Assist clipdata: " + content.getClipData()); 185 } 186 if (data != null) { 187 Uri referrer = data.getParcelable(Intent.EXTRA_REFERRER); 188 if (referrer != null) { 189 Log.i(TAG, "Referrer: " + referrer); 190 } 191 } 192 } 193 194 @Override onHandleScreenshot(Bitmap screenshot)195 public void onHandleScreenshot(Bitmap screenshot) { 196 if (screenshot != null) { 197 mScreenshot.setImageBitmap(screenshot); 198 mScreenshot.setAdjustViewBounds(true); 199 mScreenshot.setMaxWidth(screenshot.getWidth() / 3); 200 mScreenshot.setMaxHeight(screenshot.getHeight() / 3); 201 mFullScreenshot.setImageBitmap(screenshot); 202 } else { 203 mScreenshot.setImageDrawable(null); 204 mFullScreenshot.setImageDrawable(null); 205 } 206 } 207 updateState()208 void updateState() { 209 if (mState == STATE_IDLE) { 210 mTopContent.setVisibility(View.VISIBLE); 211 mBottomContent.setVisibility(View.GONE); 212 mAssistVisualizer.setVisibility(View.VISIBLE); 213 } else if (mState == STATE_DONE) { 214 mTopContent.setVisibility(View.GONE); 215 mBottomContent.setVisibility(View.GONE); 216 mAssistVisualizer.setVisibility(View.GONE); 217 } else { 218 mTopContent.setVisibility(View.GONE); 219 mBottomContent.setVisibility(View.VISIBLE); 220 mAssistVisualizer.setVisibility(View.GONE); 221 } 222 mStartButton.setEnabled(mState == STATE_IDLE); 223 mConfirmButton.setEnabled(mState == STATE_CONFIRM || mState == STATE_PICK_OPTION 224 || mState == STATE_COMMAND); 225 mAbortButton.setEnabled(mState == STATE_ABORT_VOICE); 226 mCompleteButton.setEnabled(mState == STATE_COMPLETE_VOICE); 227 } 228 onClick(View v)229 public void onClick(View v) { 230 if (v == mTreeButton) { 231 if (mAssistVisualizer != null) { 232 mAssistVisualizer.logTree(); 233 } 234 } else if (v == mTextButton) { 235 if (mAssistVisualizer != null) { 236 mAssistVisualizer.logText(); 237 } 238 } else if (v == mOptionsCheck) { 239 refreshOptions(); 240 } else if (v == mDisallowAssist) { 241 int flags = getDisabledShowContext(); 242 if (mDisallowAssist.isChecked()) { 243 flags |= SHOW_WITH_ASSIST; 244 } else { 245 flags &= ~SHOW_WITH_ASSIST; 246 } 247 setDisabledShowContext(flags); 248 } else if (v == mDisallowScreenshot) { 249 int flags = getDisabledShowContext(); 250 if (mDisallowScreenshot.isChecked()) { 251 flags |= SHOW_WITH_SCREENSHOT; 252 } else { 253 flags &= ~SHOW_WITH_SCREENSHOT; 254 } 255 setDisabledShowContext(flags); 256 } else if (v == mStartButton) { 257 mState = STATE_LAUNCHING; 258 updateState(); 259 startVoiceActivity(mStartIntent); 260 } else if (v == mConfirmButton) { 261 if (mPendingRequest instanceof ConfirmationRequest) { 262 ((ConfirmationRequest)mPendingRequest).sendConfirmationResult(true, null); 263 mPendingRequest = null; 264 mState = STATE_LAUNCHING; 265 } else if (mPendingRequest instanceof PickOptionRequest) { 266 PickOptionRequest pick = (PickOptionRequest)mPendingRequest; 267 int numReturn = mPendingOptions.length/2; 268 if (numReturn <= 0) { 269 numReturn = 1; 270 } 271 VoiceInteractor.PickOptionRequest.Option[] picked 272 = new VoiceInteractor.PickOptionRequest.Option[numReturn]; 273 for (int i=0; i<picked.length; i++) { 274 picked[i] = mPendingOptions[i*2]; 275 } 276 mPendingOptions = picked; 277 if (picked.length <= 1) { 278 pick.sendPickOptionResult(picked, null); 279 mPendingRequest = null; 280 mState = STATE_LAUNCHING; 281 } else { 282 pick.sendIntermediatePickOptionResult(picked, null); 283 updatePickText(); 284 } 285 } else if (mPendingRequest instanceof CommandRequest) { 286 Bundle result = new Bundle(); 287 result.putString("key", "a result!"); 288 ((CommandRequest)mPendingRequest).sendResult(result); 289 mPendingRequest = null; 290 mState = STATE_LAUNCHING; 291 } 292 } else if (v == mAbortButton && mPendingRequest instanceof AbortVoiceRequest) { 293 ((AbortVoiceRequest)mPendingRequest).sendAbortResult(null); 294 mPendingRequest = null; 295 } else if (v == mCompleteButton && mPendingRequest instanceof CompleteVoiceRequest) { 296 ((CompleteVoiceRequest)mPendingRequest).sendCompleteResult(null); 297 mPendingRequest = null; 298 } else if (v == mScreenshot) { 299 if (mFullScreenshot.getVisibility() != View.VISIBLE) { 300 mFullScreenshot.setVisibility(View.VISIBLE); 301 } else { 302 mFullScreenshot.setVisibility(View.INVISIBLE); 303 } 304 } 305 updateState(); 306 } 307 308 @Override onComputeInsets(Insets outInsets)309 public void onComputeInsets(Insets outInsets) { 310 super.onComputeInsets(outInsets); 311 if (mState != STATE_IDLE) { 312 outInsets.contentInsets.top = mBottomContent.getTop(); 313 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_CONTENT; 314 } 315 } 316 317 @Override onTaskStarted(Intent intent, int taskId)318 public void onTaskStarted(Intent intent, int taskId) { 319 super.onTaskStarted(intent, taskId); 320 mCurrentTask = taskId; 321 } 322 323 @Override onTaskFinished(Intent intent, int taskId)324 public void onTaskFinished(Intent intent, int taskId) { 325 super.onTaskFinished(intent, taskId); 326 if (mCurrentTask == taskId) { 327 mCurrentTask = -1; 328 } 329 } 330 331 @Override onLockscreenShown()332 public void onLockscreenShown() { 333 if (mCurrentTask < 0) { 334 hide(); 335 } 336 } 337 338 @Override onGetSupportedCommands(String[] commands)339 public boolean[] onGetSupportedCommands(String[] commands) { 340 boolean[] res = new boolean[commands.length]; 341 for (int i=0; i<commands.length; i++) { 342 if ("com.android.test.voiceinteraction.COMMAND".equals(commands[i])) { 343 res[i] = true; 344 } 345 } 346 return res; 347 } 348 setPrompt(VoiceInteractor.Prompt prompt)349 void setPrompt(VoiceInteractor.Prompt prompt) { 350 if (prompt == null) { 351 mText.setText("(null)"); 352 mPendingPrompt = ""; 353 } else { 354 mText.setText(prompt.getVisualPrompt()); 355 mPendingPrompt = prompt.getVisualPrompt(); 356 } 357 } 358 359 @Override onRequestConfirmation(ConfirmationRequest request)360 public void onRequestConfirmation(ConfirmationRequest request) { 361 Log.i(TAG, "onConfirm: prompt=" + request.getVoicePrompt() + " extras=" 362 + request.getExtras()); 363 setPrompt(request.getVoicePrompt()); 364 mConfirmButton.setText("Confirm"); 365 mPendingRequest = request; 366 mState = STATE_CONFIRM; 367 updateState(); 368 } 369 370 @Override onRequestPickOption(PickOptionRequest request)371 public void onRequestPickOption(PickOptionRequest request) { 372 Log.i(TAG, "onPickOption: prompt=" + request.getVoicePrompt() + " options=" 373 + request.getOptions() + " extras=" + request.getExtras()); 374 mConfirmButton.setText("Pick Option"); 375 mPendingRequest = request; 376 setPrompt(request.getVoicePrompt()); 377 mPendingOptions = request.getOptions(); 378 mState = STATE_PICK_OPTION; 379 updatePickText(); 380 updateState(); 381 } 382 updatePickText()383 void updatePickText() { 384 StringBuilder sb = new StringBuilder(); 385 sb.append(mPendingPrompt); 386 sb.append(": "); 387 for (int i=0; i<mPendingOptions.length; i++) { 388 if (i > 0) { 389 sb.append(", "); 390 } 391 sb.append(mPendingOptions[i].getLabel()); 392 } 393 mText.setText(sb.toString()); 394 } 395 396 @Override onRequestCompleteVoice(CompleteVoiceRequest request)397 public void onRequestCompleteVoice(CompleteVoiceRequest request) { 398 Log.i(TAG, "onCompleteVoice: message=" + request.getVoicePrompt() + " extras=" 399 + request.getExtras()); 400 setPrompt(request.getVoicePrompt()); 401 mPendingRequest = request; 402 mState = STATE_COMPLETE_VOICE; 403 updateState(); 404 } 405 406 @Override onRequestAbortVoice(AbortVoiceRequest request)407 public void onRequestAbortVoice(AbortVoiceRequest request) { 408 Log.i(TAG, "onAbortVoice: message=" + request.getVoicePrompt() + " extras=" 409 + request.getExtras()); 410 setPrompt(request.getVoicePrompt()); 411 mPendingRequest = request; 412 mState = STATE_ABORT_VOICE; 413 updateState(); 414 } 415 416 @Override onRequestCommand(CommandRequest request)417 public void onRequestCommand(CommandRequest request) { 418 Bundle extras = request.getExtras(); 419 if (extras != null) { 420 extras.getString("arg"); 421 } 422 Log.i(TAG, "onCommand: command=" + request.getCommand() + " extras=" + extras); 423 mText.setText("Command: " + request.getCommand() + ", " + extras); 424 mConfirmButton.setText("Finish Command"); 425 mPendingRequest = request; 426 mState = STATE_COMMAND; 427 updateState(); 428 } 429 430 @Override onCancelRequest(Request request)431 public void onCancelRequest(Request request) { 432 Log.i(TAG, "onCancel"); 433 if (mPendingRequest == request) { 434 mPendingRequest = null; 435 mState = STATE_LAUNCHING; 436 updateState(); 437 } 438 request.cancel(); 439 } 440 } 441