1 /* 2 * Copyright (C) 2017 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.car.messenger; 18 19 import android.animation.AnimatorInflater; 20 import android.animation.AnimatorSet; 21 import android.annotation.Nullable; 22 import android.app.Activity; 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.ServiceConnection; 29 import android.os.Bundle; 30 import android.os.IBinder; 31 import android.util.Log; 32 import android.view.MotionEvent; 33 import android.view.View; 34 import android.view.ViewGroup; 35 import android.widget.ImageView; 36 import android.widget.TextView; 37 38 import com.android.car.messenger.tts.TTSHelper; 39 40 import java.util.ArrayList; 41 import java.util.List; 42 43 /** 44 * Controls the TTS of the message received. 45 */ 46 public class PlayMessageActivity extends Activity { 47 private static final String TAG = "PlayMessageActivity"; 48 49 public static final String EXTRA_MESSAGE_KEY = "car.messenger.EXTRA_MESSAGE_KEY"; 50 public static final String EXTRA_SENDER_NAME = "car.messenger.EXTRA_SENDER_NAME"; 51 public static final String EXTRA_SHOW_REPLY_LIST_FLAG = 52 "car.messenger.EXTRA_SHOW_REPLY_LIST_FLAG"; 53 public static final String EXTRA_REPLY_DISABLED_FLAG = 54 "car.messenger.EXTRA_REPLY_DISABLED_FLAG"; 55 private View mContainer; 56 private View mMessageContainer; 57 private View mVoicePlate; 58 private TextView mReplyNotice; 59 private TextView mLeftButton; 60 private TextView mRightButton; 61 private ImageView mVoiceIcon; 62 private MessengerService mMessengerService; 63 private MessengerServiceBroadcastReceiver mMessengerServiceBroadcastReceiver = 64 new MessengerServiceBroadcastReceiver(); 65 private MapMessageMonitor.SenderKey mSenderKey; 66 private TTSHelper mTTSHelper; 67 68 @Override 69 protected void onCreate(@Nullable Bundle savedInstanceState) { 70 super.onCreate(savedInstanceState); 71 setContentView(R.layout.play_message_layout); 72 mContainer = findViewById(R.id.container); 73 mMessageContainer = findViewById(R.id.message_container); 74 mReplyNotice = (TextView) findViewById(R.id.reply_notice); 75 mVoicePlate = findViewById(R.id.voice_plate); 76 mLeftButton = (TextView) findViewById(R.id.left_btn); 77 mRightButton = (TextView) findViewById(R.id.right_btn); 78 mVoiceIcon = (ImageView) findViewById(R.id.voice_icon); 79 80 mTTSHelper = new TTSHelper(this); 81 setupEmojis(); 82 hideAutoReply(); 83 setupAutoReply(); 84 updateViewForMessagePlaying(); 85 86 AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, 87 R.anim.trans_bottom_in); 88 set.setTarget(mContainer); 89 set.start(); 90 } 91 92 private void setupEmojis() { 93 TextView emoji1 = (TextView) findViewById(R.id.emoji1); 94 emoji1.setText(getEmojiByUnicode(getResources().getInteger(R.integer.emoji_thumb_up))); 95 TextView emoji2 = (TextView) findViewById(R.id.emoji2); 96 emoji2.setText(getEmojiByUnicode(getResources().getInteger(R.integer.emoji_thumb_down))); 97 TextView emoji3 = (TextView) findViewById(R.id.emoji3); 98 emoji3.setText(getEmojiByUnicode(getResources().getInteger(R.integer.emoji_smiling_face))); 99 } 100 101 private String getEmojiByUnicode(int unicode){ 102 return new String(Character.toChars(unicode)); 103 } 104 105 private void setupAutoReply() { 106 TextView cannedMessage = (TextView) findViewById(R.id.canned_message); 107 cannedMessage.setText(getString(R.string.reply_message_display_template, 108 getString(R.string.caned_message_driving_right_now))); 109 cannedMessage.setOnClickListener( 110 v -> sendReply(getString(R.string.caned_message_driving_right_now))); 111 findViewById(R.id.emoji1).setOnClickListener(this::sendReply); 112 findViewById(R.id.emoji2).setOnClickListener(this::sendReply); 113 findViewById(R.id.emoji3).setOnClickListener(this::sendReply); 114 } 115 116 /** 117 * View needs to be TextView. Leave it as View, so can take advantage of lambda syntax 118 */ 119 private void sendReply(View view) { 120 sendReply(((TextView) view).getText()); 121 } 122 123 private void sendReply(CharSequence message) { 124 // send auto reply 125 Intent intent = new Intent(getBaseContext(), MessengerService.class) 126 .setAction(MessengerService.ACTION_AUTO_REPLY) 127 .putExtra(MessengerService.EXTRA_SENDER_KEY, mSenderKey) 128 .putExtra( 129 MessengerService.EXTRA_REPLY_MESSAGE, 130 message); 131 startService(intent); 132 133 String messageSent = getString( 134 R.string.message_sent_notice, 135 getIntent().getStringExtra(EXTRA_SENDER_NAME)); 136 // hide all view and show reply sent notice text 137 mContainer.invalidate(); 138 mMessageContainer.setVisibility(View.GONE); 139 mVoicePlate.setVisibility(View.GONE); 140 mReplyNotice.setText(messageSent); 141 mReplyNotice.setVisibility(View.VISIBLE); 142 ViewGroup.LayoutParams layoutParams = mContainer.getLayoutParams(); 143 layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; 144 mContainer.requestLayout(); 145 146 // read out the reply sent notice. Finish activity after TTS is done. 147 List<CharSequence> ttsMessages = new ArrayList<>(); 148 ttsMessages.add(messageSent); 149 mTTSHelper.requestPlay(ttsMessages, 150 new TTSHelper.Listener() { 151 @Override 152 public void onTTSStarted() { 153 } 154 155 @Override 156 public void onTTSStopped(boolean error) { 157 if (error) { 158 Log.w(TAG, "TTS error."); 159 } 160 finish(); 161 } 162 163 @Override 164 public void onAudioFocusFailed() { 165 Log.w(TAG, "failed to require audio focus."); 166 } 167 }); 168 } 169 170 private void showAutoReply() { 171 mContainer.invalidate(); 172 mMessageContainer.setVisibility(View.VISIBLE); 173 mLeftButton.setText(getString(R.string.action_close_messages)); 174 mLeftButton.setOnClickListener(v -> finish()); 175 ViewGroup.LayoutParams layoutParams = mContainer.getLayoutParams(); 176 layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; 177 mContainer.requestLayout(); 178 } 179 180 private void hideAutoReply() { 181 mContainer.invalidate(); 182 mMessageContainer.setVisibility(View.GONE); 183 mLeftButton.setText(getString(R.string.action_reply)); 184 mLeftButton.setOnClickListener(v -> showAutoReply()); 185 ViewGroup.LayoutParams layoutParams = mContainer.getLayoutParams(); 186 layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; 187 mContainer.requestLayout(); 188 } 189 190 /** 191 * If there's a touch outside the voice plate, exit the activity. 192 */ 193 @Override 194 public boolean onTouchEvent(MotionEvent event) { 195 if (event.getAction() == MotionEvent.ACTION_UP) { 196 if (event.getX() < mContainer.getX() 197 || event.getX() > mContainer.getX() + mContainer.getWidth() 198 || event.getY() < mContainer.getY() 199 || event.getY() > mContainer.getY() + mContainer.getHeight()) { 200 finish(); 201 } 202 } 203 return super.onTouchEvent(event); 204 } 205 206 @Override 207 protected void onStart() { 208 super.onStart(); 209 210 // Bind to LocalService 211 Intent intent = new Intent(this, MessengerService.class); 212 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 213 mMessengerServiceBroadcastReceiver.start(); 214 processIntent(); 215 } 216 217 @Override 218 protected void onNewIntent(Intent intent) { 219 super.onNewIntent(intent); 220 processIntent(); 221 } 222 223 private void processIntent() { 224 mSenderKey = getIntent().getParcelableExtra(EXTRA_MESSAGE_KEY); 225 playMessage(); 226 if (getIntent().getBooleanExtra(EXTRA_SHOW_REPLY_LIST_FLAG, false)) { 227 showAutoReply(); 228 } 229 if (getIntent().getBooleanExtra(EXTRA_REPLY_DISABLED_FLAG, false)) { 230 mLeftButton.setVisibility(View.GONE); 231 } 232 } 233 234 @Override 235 protected void onDestroy() { 236 AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, 237 R.anim.trans_bottom_out); 238 set.setTarget(mContainer); 239 set.start(); 240 super.onDestroy(); 241 } 242 243 @Override 244 protected void onStop() { 245 super.onStop(); 246 stopMessage(); 247 mTTSHelper.cleanup(); 248 mMessengerServiceBroadcastReceiver.cleanup(); 249 unbindService(mConnection); 250 } 251 252 private void playMessage() { 253 Intent intent = new Intent(getBaseContext(), MessengerService.class) 254 .setAction(MessengerService.ACTION_PLAY_MESSAGES) 255 .putExtra(MessengerService.EXTRA_SENDER_KEY, mSenderKey); 256 startService(intent); 257 } 258 259 private void stopMessage() { 260 Intent intent = new Intent(getBaseContext(), MessengerService.class) 261 .setAction(MessengerService.ACTION_STOP_PLAYOUT) 262 .putExtra(MessengerService.EXTRA_SENDER_KEY, mSenderKey); 263 startService(intent); 264 } 265 266 private void updateViewForMessagePlaying() { 267 mRightButton.setText(getString(R.string.action_stop)); 268 mRightButton.setOnClickListener(v -> stopMessage()); 269 mVoiceIcon.setVisibility(View.VISIBLE); 270 } 271 272 private void updateViewForMessageStopped() { 273 mRightButton.setText(getString(R.string.action_repeat)); 274 mRightButton.setOnClickListener(v -> playMessage()); 275 mVoiceIcon.setVisibility(View.INVISIBLE); 276 } 277 278 private class MessengerServiceBroadcastReceiver extends BroadcastReceiver { 279 private final IntentFilter mIntentFilter; 280 MessengerServiceBroadcastReceiver() { 281 mIntentFilter = new IntentFilter(); 282 mIntentFilter.addAction(MapMessageMonitor.ACTION_MESSAGE_PLAY_START); 283 mIntentFilter.addAction(MapMessageMonitor.ACTION_MESSAGE_PLAY_STOP); 284 } 285 286 void start() { 287 registerReceiver(this, mIntentFilter); 288 } 289 290 void cleanup() { 291 unregisterReceiver(this); 292 } 293 294 @Override 295 public void onReceive(Context context, Intent intent) { 296 switch (intent.getAction()) { 297 case MapMessageMonitor.ACTION_MESSAGE_PLAY_START: 298 updateViewForMessagePlaying(); 299 break; 300 case MapMessageMonitor.ACTION_MESSAGE_PLAY_STOP: 301 updateViewForMessageStopped(); 302 break; 303 default: 304 break; 305 } 306 } 307 } 308 309 private final ServiceConnection mConnection = new ServiceConnection() { 310 @Override 311 public void onServiceConnected(ComponentName className, IBinder service) { 312 MessengerService.LocalBinder binder = (MessengerService.LocalBinder) service; 313 mMessengerService = binder.getService(); 314 if (mMessengerService.isPlaying()) { 315 updateViewForMessagePlaying(); 316 } else { 317 updateViewForMessageStopped(); 318 } 319 } 320 321 @Override 322 public void onServiceDisconnected(ComponentName arg0) { 323 mMessengerService = null; 324 } 325 }; 326 } 327