blob: 6c5f2a5ea5700fc7be087b62b3f6fef9f9c0aa73 [file] [log] [blame]
/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.wearable.wear.messaging.chat;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.wearable.view.WearableRecyclerView;
import android.support.wearable.view.drawer.WearableActionDrawer;
import android.support.wearable.view.drawer.WearableDrawerLayout;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import com.example.android.wearable.wear.messaging.GoogleSignedInActivity;
import com.example.android.wearable.wear.messaging.R;
import com.example.android.wearable.wear.messaging.mock.MockDatabase;
import com.example.android.wearable.wear.messaging.model.Chat;
import com.example.android.wearable.wear.messaging.model.Message;
import com.example.android.wearable.wear.messaging.util.Constants;
import com.example.android.wearable.wear.messaging.util.DividerItemDecoration;
import com.example.android.wearable.wear.messaging.util.MenuTinter;
import com.example.android.wearable.wear.messaging.util.PrescrollToBottom;
import com.example.android.wearable.wear.messaging.util.SchedulerHelper;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
/**
* Handles chat functionality. Activity uses chat id (passed as extra) to load chat details
* (messages, etc.) and displays them.
*
* <p>Action drawer allows several inputs (speech, text) and can be easily extended to offer more
* (gif, etc.).
*/
public class ChatActivity extends GoogleSignedInActivity {
private static final String TAG = "ChatActivity";
private static final int SPEECH_REQUEST_CODE = 9001;
private WearableRecyclerView mRecyclerView;
private WearableDrawerLayout mDrawerLayout;
private EditText mInput;
private TextView mNoMessagesView;
private ChatAdapter mAdapter;
private InputMethodManager mInputMethodManager;
private Chat mChat;
private FindChatsAsyncTask mFindChatsTask;
private SendMessageAsyncTask mSendMessageTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
if (!getIntent().hasExtra(Constants.EXTRA_CHAT)) {
finish();
return;
}
mChat = getIntent().getParcelableExtra(Constants.EXTRA_CHAT);
mNoMessagesView = (TextView) findViewById(R.id.no_messages_view);
mInput = (EditText) findViewById(R.id.edit_text_input);
mDrawerLayout = (WearableDrawerLayout) findViewById(R.id.drawer_layout);
mRecyclerView = (WearableRecyclerView) findViewById(R.id.recycler_list);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, R.drawable.divider));
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
mAdapter = new ChatAdapter(this, mChat, getUser());
mRecyclerView.setAdapter(mAdapter);
mRecyclerView
.getViewTreeObserver()
.addOnPreDrawListener(new PrescrollToBottom(mRecyclerView, mAdapter));
WearableActionDrawer actionDrawer =
(WearableActionDrawer) findViewById(R.id.bottom_action_drawer);
configureWearableActionDrawer(actionDrawer);
mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
// Automatically sends updated content as a message when input changes to streamline
// communication.
mInput.setOnEditorActionListener(
new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView editText, int actionId, KeyEvent event) {
if (actionId != EditorInfo.IME_ACTION_NONE) {
mInputMethodManager.hideSoftInputFromWindow(
editText.getWindowToken(), 0);
editText.setVisibility(View.INVISIBLE);
String text = editText.getText().toString();
if (!text.isEmpty()) {
sendMessage(text);
mInputMethodManager.hideSoftInputFromInputMethod(
mInput.getWindowToken(), 0);
}
return true;
}
return false;
}
});
}
private void configureWearableActionDrawer(WearableActionDrawer actionDrawer) {
Menu menu = actionDrawer.getMenu();
MenuTinter.tintMenu(this, menu, R.color.blue_15);
actionDrawer.setOnMenuItemClickListener(
new WearableActionDrawer.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return handleMenuItems(menuItem);
}
});
actionDrawer.setBackgroundColor(ContextCompat.getColor(this, R.color.blue_65));
View peek = getLayoutInflater().inflate(R.layout.drawer_chat_action, mDrawerLayout, false);
actionDrawer.setPeekContent(peek);
// Handles peek interactions.
peek.findViewById(R.id.button_speech)
.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
displayVoiceToTextInput();
}
});
peek.findViewById(R.id.button_keyboard)
.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
showKeyboard();
}
});
}
@Override
protected void onStart() {
super.onStart();
// Peeks drawer on page start.
mDrawerLayout.peekDrawer(Gravity.BOTTOM);
// After messages are added, re-scroll to the bottom.
mRecyclerView
.getViewTreeObserver()
.addOnPreDrawListener(new PrescrollToBottom(mRecyclerView, mAdapter));
if (mFindChatsTask != null) {
mFindChatsTask.cancel(true);
}
mFindChatsTask = new FindChatsAsyncTask(this, mChat);
mFindChatsTask.execute();
}
@Override
protected void onPause() {
super.onPause();
if (mFindChatsTask != null) {
mFindChatsTask.cancel(true);
}
if (mSendMessageTask != null) {
mSendMessageTask.cancel(true);
}
}
/*
* Takes a menu item and delegates to the appropriate function based on
* the menu item id. Used with click handlers.
*/
private boolean handleMenuItems(MenuItem menuItem) {
int id = menuItem.getItemId();
if (id == R.id.item_voice) {
displayVoiceToTextInput();
} else if (id == R.id.item_keyboard) {
showKeyboard();
}
mDrawerLayout.closeDrawer(Gravity.BOTTOM);
return false;
}
private void showKeyboard() {
Log.d(TAG, "Showing keyboard");
mInput.setText("");
mInput.setVisibility(View.VISIBLE);
mInput.requestFocus();
mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_FORCED);
}
private void displayVoiceToTextInput() {
Log.d(TAG, "Starting speech recognizer");
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
startActivityForResult(intent, SPEECH_REQUEST_CODE);
}
private void sendMessage(String text) {
Message message =
new Message.Builder()
.id(UUID.randomUUID().toString())
.senderId(getUser().getId())
.text(text)
.build();
sendMessage(message);
}
private void sendMessage(Message message) {
if (mSendMessageTask != null) {
mSendMessageTask.cancel(true);
}
mSendMessageTask = new SendMessageAsyncTask(this, mChat);
mSendMessageTask.execute(message);
}
class FindChatsAsyncTask extends AsyncTask<Void, Void, Collection<Message>> {
final Context mContext;
final Chat mChat;
FindChatsAsyncTask(Context context, Chat chat) {
this.mContext = context;
this.mChat = chat;
}
@Override
protected Collection<Message> doInBackground(Void... params) {
return MockDatabase.getAllMessagesForChat(mContext, mChat.getId());
}
@Override
protected void onPostExecute(Collection<Message> chats) {
super.onPostExecute(chats);
mAdapter.addMessages(chats);
// Displays welcome message if no messages in chat.
if (mAdapter.getItemCount() == 0) {
mRecyclerView.setVisibility(View.GONE);
mNoMessagesView.setVisibility(View.VISIBLE);
} else {
mRecyclerView.setVisibility(View.VISIBLE);
mNoMessagesView.setVisibility(View.GONE);
}
}
}
class SendMessageAsyncTask extends AsyncTask<Message, Void, Message> {
final Context mContext;
final Chat mChat;
SendMessageAsyncTask(Context context, Chat chat) {
this.mContext = context;
this.mChat = chat;
}
@Override
protected Message doInBackground(Message... params) {
List<Message> parameters = Arrays.asList(params);
if (parameters.isEmpty()) {
throw new IllegalArgumentException("Messages are required for sending.");
}
for (Message message : parameters) {
MockDatabase.saveMessage(mContext, mChat, message);
SchedulerHelper.scheduleMockNotification(mContext, mChat, message);
}
return parameters.get(parameters.size() - 1);
}
@Override
protected void onPostExecute(Message message) {
super.onPostExecute(message);
mAdapter.addMessage(message);
mRecyclerView.smoothScrollToPosition(mAdapter.getItemCount());
}
}
}