package akechi.projectl; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v4.content.LocalBroadcastManager; import android.text.Editable; import android.text.Selection; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.google.api.client.extensions.android.http.AndroidHttp; import com.google.api.client.repackaged.com.google.common.base.Strings; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import java.io.EOFException; import java.io.IOException; import java.util.List; import java.util.Queue; import java.util.regex.Pattern; import akechi.projectl.async.LingrTaskLoader; import jp.michikusa.chitose.lingr.LingrClient; import jp.michikusa.chitose.lingr.LingrClientFactory; import jp.michikusa.chitose.lingr.LingrException; import jp.michikusa.chitose.lingr.Room; public class SayFragment extends Fragment implements Button.OnClickListener, LoaderManager.LoaderCallbacks<Room.Message>, View.OnKeyListener { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View v= inflater.inflate(R.layout.fragment_say, container, false); this.inputText= (EditText) v.findViewById(R.id.messageView); this.sayButton= (Button) v.findViewById(R.id.sayButton); this.sayButton.setOnClickListener(this); this.inputText.setOnKeyListener(this); this.getLoaderManager().initLoader(LOADER_SAY_FROM_WIDGET, null, this); this.getLoaderManager().initLoader(LOADER_SAY_FROM_TEXT, null, this); // handle implicit intent { final Intent intent= this.getActivity().getIntent(); final String text= intent.getStringExtra(Intent.EXTRA_TEXT); if(!Strings.isNullOrEmpty(text)) { this.inputText.getText().append(text); } } return v; } @Override public void onAttach(Activity activity) { super.onAttach(activity); final LocalBroadcastManager lbMan= LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()); { final IntentFilter ifilter= new IntentFilter("akechi.projectl.ReplyAction"); final BroadcastReceiver receiver= new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { final String replyText= intent.getStringExtra("text"); SayFragment.this.reply(replyText); } }; lbMan.registerReceiver(receiver, ifilter); this.receivers.add(receiver); } { final IntentFilter ifilter= new IntentFilter(Event.RoomChange.ACTION); final BroadcastReceiver receiver= new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { final String roomId= intent.getStringExtra(Event.RoomChange.KEY_ROOM_ID); SayFragment.this.onRoomSelected(roomId); } }; lbMan.registerReceiver(receiver, ifilter); this.receivers.add(receiver); } { final IntentFilter ifilter= new IntentFilter(Event.PostMessage.ACTION); final BroadcastReceiver receiver= new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { final String text= intent.getStringExtra(Event.PostMessage.KEY_TEXT); SayFragment.this.say(text); } }; lbMan.registerReceiver(receiver, ifilter); this.receivers.add(receiver); } } @Override public void onDetach() { super.onDetach(); final LocalBroadcastManager lbMan= LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()); for(final BroadcastReceiver receiver : this.receivers) { lbMan.unregisterReceiver(receiver); } this.receivers.clear(); } @Override public boolean onKey(View v, int keyCode, KeyEvent event) { switch(keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT:{ // avoid left key to swipe fragment final int pos= Selection.getSelectionStart(this.inputText.getText()); if(pos <= 0) { return true; } break; } case KeyEvent.KEYCODE_DPAD_RIGHT:{ // avoid right key to swipe fragment final int pos= Selection.getSelectionEnd(this.inputText.getText()); final int length= this.inputText.getText().length(); if(pos >= length) { return true; } break; } } return false; } public void reply(String text) { if(Strings.isNullOrEmpty(text)) { return; } final Iterable<String> lines= Splitter.on(Pattern.compile("\\r?\\n")).split(text); for(final String line : lines) { this.inputText.getText().append("> " + line + System.getProperty("line.separator")); } } public void say(String text) { if(text == null) { return; } this.texts.add(text); this.getLoaderManager().getLoader(LOADER_SAY_FROM_TEXT).forceLoad(); } @Override public void onClick(View v) { Log.i("SayFragment", "input area and say button are disabled"); this.sayButton.setEnabled(false); this.inputText.setEnabled(false); this.getLoaderManager().getLoader(LOADER_SAY_FROM_WIDGET).forceLoad(); } private void onRoomSelected(CharSequence roomId) { Log.i("SayFragment", "input area and say button are enabled"); this.sayButton.setEnabled(true); this.inputText.setEnabled(true); } @Override public Loader<Room.Message> onCreateLoader(int id, Bundle args) { switch(id) { case LOADER_SAY_FROM_WIDGET: return new MessagePostLoader(this.getActivity(), new Supplier<String>(){ @Override public String get() { return inputText.getText().toString(); } }); case LOADER_SAY_FROM_TEXT: return new MessagePostLoader(this.getActivity(), new Supplier<String>(){ @Override public String get() { return texts.poll(); } }); default: throw new AssertionError("Unknown loader id " + id); } } @Override public void onLoadFinished(Loader<Room.Message> loader, Room.Message data) { // TODO: graceful implementations if(loader.getId() == LOADER_SAY_FROM_TEXT) { Toast.makeText(this.getActivity(), "Posted", Toast.LENGTH_SHORT).show(); return; } try { if(data == null) { return; } this.inputText.setText(""); // close keypad final InputMethodManager imeManager= (InputMethodManager)this.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); imeManager.hideSoftInputFromWindow(this.inputText.getWindowToken(), 0); Toast.makeText(this.getActivity(), "Posted", Toast.LENGTH_SHORT).show(); } finally { Log.i("SayFragment", "input area and say button are enabled"); this.sayButton.setEnabled(true); this.inputText.setEnabled(true); } } @Override public void onLoaderReset(Loader<Room.Message> loader) { } private static final class MessagePostLoader extends LingrTaskLoader<Room.Message> { public MessagePostLoader(Context context, Supplier<String> textSupplier) { super(context); this.textSupplier= textSupplier; } @Override public Room.Message loadInBackground(CharSequence authToken, LingrClient lingr) throws IOException, LingrException { final String text= this.textSupplier.get(); Log.i("SayFragment", "loadInBackground(), text = " + text); if(Strings.isNullOrEmpty(text)) { return null; } final AppContext appContext= this.getApplicationContext(); final Account account= appContext.getAccount(); final String roomId= appContext.getRoomId(account); return lingr.say(authToken, roomId, text); } private final Supplier<String> textSupplier; } private static final LingrClientFactory lingrFactory= LingrClientFactory.newLingrClientFactory(AndroidHttp.newCompatibleTransport()); private static final int LOADER_SAY_FROM_WIDGET= 0; private static final int LOADER_SAY_FROM_TEXT= 1; private EditText inputText; private Button sayButton; // for LOADER_SAY_FROM_TEXT private Queue<String> texts= Queues.newArrayDeque(); private List<BroadcastReceiver> receivers= Lists.newLinkedList(); }