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 onCreate(@ullable Bundle savedInstanceState)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 setupEmojis()92 private void setupEmojis() { 93 TextView emoji1 = (TextView) findViewById(R.id.emoji1); 94 emoji1.setText(getEmojiByUnicode(getResources().getInteger(R.integer.emoji_ok_hand_sign))); 95 TextView emoji2 = (TextView) findViewById(R.id.emoji2); 96 emoji2.setText(getEmojiByUnicode(getResources().getInteger(R.integer.emoji_thumb_up))); 97 TextView emoji3 = (TextView) findViewById(R.id.emoji3); 98 emoji3.setText(getEmojiByUnicode(getResources().getInteger(R.integer.emoji_thumb_down))); 99 TextView emoji4 = (TextView) findViewById(R.id.emoji4); 100 emoji4.setText(getEmojiByUnicode(getResources().getInteger(R.integer.emoji_heart))); 101 TextView emoji5 = (TextView) findViewById(R.id.emoji5); 102 emoji5.setText(getEmojiByUnicode(getResources().getInteger(R.integer.emoji_smiling_face))); 103 } 104 getEmojiByUnicode(int unicode)105 private String getEmojiByUnicode(int unicode){ 106 return new String(Character.toChars(unicode)); 107 } 108 setupAutoReply()109 private void setupAutoReply() { 110 TextView cannedMessage = (TextView) findViewById(R.id.canned_message); 111 cannedMessage.setText(getString(R.string.reply_message_display_template, 112 getString(R.string.caned_message_driving_right_now))); 113 cannedMessage.setOnClickListener( 114 v -> sendReply(getString(R.string.caned_message_driving_right_now))); 115 findViewById(R.id.emoji1).setOnClickListener(this::sendReply); 116 findViewById(R.id.emoji2).setOnClickListener(this::sendReply); 117 findViewById(R.id.emoji3).setOnClickListener(this::sendReply); 118 findViewById(R.id.emoji4).setOnClickListener(this::sendReply); 119 findViewById(R.id.emoji5).setOnClickListener(this::sendReply); 120 } 121 122 /** 123 * View needs to be TextView. Leave it as View, so can take advantage of lambda syntax 124 */ sendReply(View view)125 private void sendReply(View view) { 126 sendReply(((TextView) view).getText()); 127 } 128 sendReply(CharSequence message)129 private void sendReply(CharSequence message) { 130 // send auto reply 131 Intent intent = new Intent(getBaseContext(), MessengerService.class) 132 .setAction(MessengerService.ACTION_AUTO_REPLY) 133 .putExtra(MessengerService.EXTRA_SENDER_KEY, mSenderKey) 134 .putExtra( 135 MessengerService.EXTRA_REPLY_MESSAGE, 136 message); 137 startService(intent); 138 139 String messageSent = getString( 140 R.string.message_sent_notice, 141 getIntent().getStringExtra(EXTRA_SENDER_NAME)); 142 // hide all view and show reply sent notice text 143 mContainer.invalidate(); 144 mMessageContainer.setVisibility(View.GONE); 145 mVoicePlate.setVisibility(View.GONE); 146 mReplyNotice.setText(messageSent); 147 mReplyNotice.setVisibility(View.VISIBLE); 148 ViewGroup.LayoutParams layoutParams = mContainer.getLayoutParams(); 149 layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; 150 mContainer.requestLayout(); 151 152 // read out the reply sent notice. Finish activity after TTS is done. 153 List<CharSequence> ttsMessages = new ArrayList<>(); 154 ttsMessages.add(messageSent); 155 mTTSHelper.requestPlay(ttsMessages, 156 new TTSHelper.Listener() { 157 @Override 158 public void onTTSStarted() { 159 } 160 161 @Override 162 public void onTTSStopped(boolean error) { 163 if (error) { 164 Log.w(TAG, "TTS error."); 165 } 166 finish(); 167 } 168 }); 169 } 170 showAutoReply()171 private void showAutoReply() { 172 mContainer.invalidate(); 173 mMessageContainer.setVisibility(View.VISIBLE); 174 mLeftButton.setText(getString(R.string.action_close_messages)); 175 mLeftButton.setOnClickListener(v -> finish()); 176 ViewGroup.LayoutParams layoutParams = mContainer.getLayoutParams(); 177 layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; 178 mContainer.requestLayout(); 179 } 180 hideAutoReply()181 private void hideAutoReply() { 182 mContainer.invalidate(); 183 mMessageContainer.setVisibility(View.GONE); 184 mLeftButton.setText(getString(R.string.action_reply)); 185 mLeftButton.setOnClickListener(v -> showAutoReply()); 186 ViewGroup.LayoutParams layoutParams = mContainer.getLayoutParams(); 187 layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; 188 mContainer.requestLayout(); 189 } 190 191 /** 192 * If there's a touch outside the voice plate, exit the activity. 193 */ 194 @Override onTouchEvent(MotionEvent event)195 public boolean onTouchEvent(MotionEvent event) { 196 if (event.getAction() == MotionEvent.ACTION_UP) { 197 if (event.getX() < mContainer.getX() 198 || event.getX() > mContainer.getX() + mContainer.getWidth() 199 || event.getY() < mContainer.getY() 200 || event.getY() > mContainer.getY() + mContainer.getHeight()) { 201 finish(); 202 203 } 204 } 205 return super.onTouchEvent(event); 206 } 207 208 @Override onStart()209 protected void onStart() { 210 super.onStart(); 211 212 // Bind to LocalService 213 Intent intent = new Intent(this, MessengerService.class); 214 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 215 mMessengerServiceBroadcastReceiver.start(); 216 processIntent(); 217 } 218 219 @Override onNewIntent(Intent intent)220 protected void onNewIntent(Intent intent) { 221 super.onNewIntent(intent); 222 processIntent(); 223 } 224 processIntent()225 private void processIntent() { 226 mSenderKey = getIntent().getParcelableExtra(EXTRA_MESSAGE_KEY); 227 playMessage(); 228 if (getIntent().getBooleanExtra(EXTRA_SHOW_REPLY_LIST_FLAG, false)) { 229 showAutoReply(); 230 } 231 if (getIntent().getBooleanExtra(EXTRA_REPLY_DISABLED_FLAG, false)) { 232 mLeftButton.setVisibility(View.GONE); 233 } 234 } 235 236 @Override onDestroy()237 protected void onDestroy() { 238 AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, 239 R.anim.trans_bottom_out); 240 set.setTarget(mContainer); 241 set.start(); 242 super.onDestroy(); 243 } 244 245 @Override onStop()246 protected void onStop() { 247 super.onStop(); 248 mTTSHelper.cleanup(); 249 mMessengerServiceBroadcastReceiver.cleanup(); 250 unbindService(mConnection); 251 } 252 playMessage()253 private void playMessage() { 254 Intent intent = new Intent(getBaseContext(), MessengerService.class) 255 .setAction(MessengerService.ACTION_PLAY_MESSAGES) 256 .putExtra(MessengerService.EXTRA_SENDER_KEY, mSenderKey); 257 startService(intent); 258 } 259 stopMessage()260 private void stopMessage() { 261 Intent intent = new Intent(getBaseContext(), MessengerService.class) 262 .setAction(MessengerService.ACTION_STOP_PLAYOUT) 263 .putExtra(MessengerService.EXTRA_SENDER_KEY, mSenderKey); 264 startService(intent); 265 } 266 updateViewForMessagePlaying()267 private void updateViewForMessagePlaying() { 268 mRightButton.setText(getString(R.string.action_stop)); 269 mRightButton.setOnClickListener(v -> stopMessage()); 270 mVoiceIcon.setVisibility(View.VISIBLE); 271 } 272 updateViewFoeMessageStopped()273 private void updateViewFoeMessageStopped() { 274 mRightButton.setText(getString(R.string.action_repeat)); 275 mRightButton.setOnClickListener(v -> playMessage()); 276 mVoiceIcon.setVisibility(View.INVISIBLE); 277 } 278 279 private class MessengerServiceBroadcastReceiver extends BroadcastReceiver { 280 private final IntentFilter mIntentFilter; MessengerServiceBroadcastReceiver()281 MessengerServiceBroadcastReceiver() { 282 mIntentFilter = new IntentFilter(); 283 mIntentFilter.addAction(MapMessageMonitor.ACTION_MESSAGE_PLAY_START); 284 mIntentFilter.addAction(MapMessageMonitor.ACTION_MESSAGE_PLAY_STOP); 285 } 286 start()287 void start() { 288 registerReceiver(this, mIntentFilter); 289 } 290 cleanup()291 void cleanup() { 292 unregisterReceiver(this); 293 } 294 295 @Override onReceive(Context context, Intent intent)296 public void onReceive(Context context, Intent intent) { 297 switch (intent.getAction()) { 298 case MapMessageMonitor.ACTION_MESSAGE_PLAY_START: 299 updateViewForMessagePlaying(); 300 break; 301 case MapMessageMonitor.ACTION_MESSAGE_PLAY_STOP: 302 updateViewFoeMessageStopped(); 303 break; 304 default: 305 break; 306 } 307 } 308 } 309 310 private final ServiceConnection mConnection = new ServiceConnection() { 311 @Override 312 public void onServiceConnected(ComponentName className, IBinder service) { 313 MessengerService.LocalBinder binder = (MessengerService.LocalBinder) service; 314 mMessengerService = binder.getService(); 315 if (mMessengerService.isPlaying()) { 316 updateViewForMessagePlaying(); 317 } else { 318 updateViewFoeMessageStopped(); 319 } 320 } 321 322 @Override 323 public void onServiceDisconnected(ComponentName arg0) { 324 mMessengerService = null; 325 } 326 }; 327 } 328