Java tutorial
/* * Copyright (C) 2009-2015 Felix Bechstein * * This file is part of SMSdroid. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see <http://www.gnu.org/licenses/>. */ package me.myatminsoe.myansms; import android.annotation.TargetApi; import android.app.AlertDialog.Builder; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.provider.CallLog.Calls; import android.support.v4.app.FragmentActivity; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.AppCompatImageButton; import android.text.ClipboardManager; import android.text.InputType; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.EditText; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.Toast; import de.ub0r.android.lib.Utils; import de.ub0r.android.lib.apis.Contact; import de.ub0r.android.lib.apis.ContactsWrapper; /** * {@link FragmentActivity} showing a single conversation. * * @author flx */ public class MessageListActivity extends AppCompatActivity implements OnItemClickListener, OnItemLongClickListener, OnClickListener, OnLongClickListener { private static final String TAG = "ml"; /** * {@link ContactsWrapper}. */ private static final ContactsWrapper WRAPPER = ContactsWrapper.getInstance(); /** * Number of items. */ private static final int WHICH_N = 8; /** * Index in dialog: mark view/add contact. */ private static final int WHICH_VIEW_CONTACT = 0; /** * Index in dialog: mark call contact. */ private static final int WHICH_CALL = 1; /** * Index in dialog: mark read/unread. */ private static final int WHICH_MARK_UNREAD = 2; /** * Index in dialog: reply. */ private static final int WHICH_REPLY = 3; /** * Index in dialog: forward. */ private static final int WHICH_FORWARD = 4; /** * Index in dialog: copy text. */ private static final int WHICH_COPY_TEXT = 5; /** * Index in dialog: view details. */ private static final int WHICH_VIEW_DETAILS = 6; /** * Index in dialog: delete. */ private static final int WHICH_DELETE = 7; /** * maximum number of lines in EditText */ private static final int MAX_EDITTEXT_LINES = 10; /** * Package name for System's chooser. */ private static String chooserPackage = null; /** * Used {@link Uri}. */ private Uri uri; /** * {@link Conversation} shown. */ private Conversation conv = null; /** * ORIG_URI to resolve. */ static final String URI = "content://mms-sms/conversations/"; /** * Dialog items shown if an item was long clicked. */ private final String[] longItemClickDialog = new String[WHICH_N]; /** * Marked a message unread? */ private boolean markedUnread = false; /** * {@link EditText} holding text. */ private EditText etText; /** * {@link ClipboardManager}. */ @SuppressWarnings("deprecation") private ClipboardManager cbmgr; /** * Show textfield. */ private boolean showTextField = true; /** * {@link MenuItem} holding {@link Contact}'s picture. */ private MenuItem contactItem = null; /** * True, to update {@link Contact}'s photo. */ private boolean needContactUpdate = false; /** * Get {@link ListView}. * * @return {@link ListView} */ private ListView getListView() { return (ListView) findViewById(android.R.id.list); } /** * Set {@link ListAdapter} to {@link ListView}. * * @param la ListAdapter */ private void setListAdapter(final ListAdapter la) { getListView().setAdapter(la); } @Override public final void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this); setTheme(PreferencesActivity.getTheme(this)); Utils.setLocale(this); setContentView(R.layout.messagelist); getSupportActionBar().setDisplayHomeAsUpEnabled(true); Log.d(TAG, "onCreate()"); cbmgr = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); etText = (EditText) findViewById(R.id.text); int flags = etText.getInputType(); if (p.getBoolean(PreferencesActivity.PREFS_EDIT_SHORT_TEXT, true)) { flags |= InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE; } else { flags &= ~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE; } etText.setInputType(flags); parseIntent(getIntent()); final ListView list = getListView(); list.setOnItemLongClickListener(this); list.setOnItemClickListener(this); View v = findViewById(R.id.send_); v.setOnClickListener(this); v.setOnLongClickListener(this); /* TextWatcher updating char count on writing. */ MyTextWatcher textWatcher = new MyTextWatcher(this, (AppCompatImageButton) findViewById(R.id.send_)); etText.addTextChangedListener(textWatcher); etText.setMaxLines(MAX_EDITTEXT_LINES); textWatcher.afterTextChanged(etText.getEditableText()); longItemClickDialog[WHICH_MARK_UNREAD] = getString(R.string.mark_unread_); longItemClickDialog[WHICH_REPLY] = getString(R.string.reply); longItemClickDialog[WHICH_FORWARD] = getString(R.string.forward_); longItemClickDialog[WHICH_COPY_TEXT] = getString(R.string.copy_text_); longItemClickDialog[WHICH_VIEW_DETAILS] = getString(R.string.view_details_); longItemClickDialog[WHICH_DELETE] = getString(R.string.delete_message_); etText.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { if (p.getString("output_convert", "true").equals("true")) { etText.setText(myat.uni2zg(etText.getText().toString())); } else { etText.setText(myat.zg2uni(etText.getText().toString())); } return true; } }); } @Override protected final void onNewIntent(final Intent intent) { super.onNewIntent(intent); setIntent(intent); parseIntent(intent); } /** * Parse data pushed by {@link Intent}. * * @param intent {@link Intent} */ private void parseIntent(final Intent intent) { if (intent == null) { return; } needContactUpdate = true; uri = intent.getData(); if (uri != null) { if (!uri.toString().startsWith(URI)) { uri = Uri.parse(URI + uri.getLastPathSegment()); } } else { final long tid = intent.getLongExtra("thread_id", -1L); uri = Uri.parse(URI + tid); if (tid < 0L) { try { startActivity(ConversationListActivity.getComposeIntent(this, null, false)); } catch (ActivityNotFoundException e) { Log.e(TAG, "activity not found", e); Toast.makeText(this, R.string.error_conv_null, Toast.LENGTH_LONG).show(); } finish(); return; } } int threadId; try { threadId = Integer.parseInt(uri.getLastPathSegment()); } catch (NumberFormatException e) { Log.e(TAG, "unable to parse thread id: ", e); Toast.makeText(this, R.string.error_conv_null, Toast.LENGTH_LONG).show(); finish(); return; } Conversation c; try { c = Conversation.getConversation(this, threadId, true); threadId = c.getThreadId(); // force a NPE :x } catch (NullPointerException e) { Toast.makeText(this, R.string.error_conv_null, Toast.LENGTH_LONG).show(); finish(); return; } conv = c; final Contact contact = c.getContact(); try { contact.update(this, false, true); } catch (NullPointerException e) { Log.e(TAG, "updating contact failed", e); } boolean showKeyboard = intent.getBooleanExtra("showKeyboard", false); final ListView lv = getListView(); lv.setStackFromBottom(true); MessageAdapter adapter = new MessageAdapter(this, uri); setListAdapter(adapter); String displayName = contact.getDisplayName(); setTitle(displayName); String number = contact.getNumber(); if (displayName.equals(number)) { getSupportActionBar().setSubtitle(null); } else { getSupportActionBar().setSubtitle(number); } final String body = intent.getStringExtra(Intent.EXTRA_TEXT); if (!TextUtils.isEmpty(body)) { etText.setText(body); showKeyboard = true; } if (showKeyboard) { etText.requestFocus(); } setRead(); } /** * {@inheritDoc} */ @Override protected final void onResume() { super.onResume(); final ListView lv = getListView(); lv.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL); lv.setAdapter(new MessageAdapter(this, uri)); markedUnread = false; final AppCompatImageButton btn = (AppCompatImageButton) findViewById(R.id.send_); if (showTextField) { Intent i; ActivityInfo ai = null; final PackageManager pm = getPackageManager(); try { i = buildIntent(false, false); if (pm != null && PreferenceManager.getDefaultSharedPreferences(this) .getBoolean(PreferencesActivity.PREFS_SHOWTARGETAPP, true)) { ai = i.resolveActivityInfo(pm, 0); } } catch (NullPointerException e) { Log.e(TAG, "unable to build Intent", e); } etText.setMaxLines(MAX_EDITTEXT_LINES); if (ai == null) { etText.setMinLines(1); } else { if (chooserPackage == null) { try { final ActivityInfo cai = buildIntent(false, true).resolveActivityInfo(pm, 0); if (cai != null) { chooserPackage = cai.packageName; } } catch (NullPointerException e) { Log.e(TAG, "unable to build Intent", e); } } if (ai.packageName.equals(chooserPackage)) { } else { } etText.setMinLines(3); } } else { btn.setImageResource(R.drawable.icn_send_disabled); } } @Override protected final void onPause() { if (!markedUnread) { setRead(); } super.onPause(); } @Override protected void onDestroy() { super.onDestroy(); } /** * Set all messages in a given thread as read. */ private void setRead() { if (conv != null) { ConversationListActivity.markRead(this, conv.getUri(), 1); } } @Override public final boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.messagelist, menu); return true; } @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public final boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case android.R.id.home: // app icon in Action Bar clicked; go home Intent intent = new Intent(this, ConversationListActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); return true; case R.id.item_delete_thread: ConversationListActivity.deleteMessages(this, uri, R.string.delete_thread_, R.string.delete_thread_question, this); return true; case R.id.item_answer: send(true, false); return true; case R.id.item_call: try { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("tel:" + conv.getContact().getNumber()))); } catch (ActivityNotFoundException e) { Log.e(TAG, "unable to open dailer", e); Toast.makeText(this, R.string.error_unknown, Toast.LENGTH_LONG).show(); } return true; case R.id.item_restore: etText.setText(PreferenceManager.getDefaultSharedPreferences(this) .getString(PreferencesActivity.PREFS_BACKUPLASTTEXT, null)); return true; default: return super.onOptionsItemSelected(item); } } /** * {@inheritDoc} */ public final void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id) { onItemLongClick(parent, view, position, id); } /** * {@inheritDoc} */ public final boolean onItemLongClick(final AdapterView<?> parent, final View view, final int position, final long id) { final Context context = this; final Message m = Message.getMessage(this, (Cursor) parent.getItemAtPosition(position)); final Uri target = m.getUri(); final int read = m.getRead(); final int type = m.getType(); Builder builder = new Builder(context); builder.setTitle(R.string.message_options_); final Contact contact = conv.getContact(); final String a = contact.getNumber(); final String n = contact.getName(); String[] items = longItemClickDialog; if (TextUtils.isEmpty(n)) { items[WHICH_VIEW_CONTACT] = getString(R.string.add_contact_); } else { items[WHICH_VIEW_CONTACT] = getString(R.string.view_contact_); } items[WHICH_CALL] = getString(R.string.call) + " " + contact.getDisplayName(); if (read == 0) { items = items.clone(); items[WHICH_MARK_UNREAD] = context.getString(R.string.mark_read_); } if (type == Message.SMS_DRAFT) { items = items.clone(); items[WHICH_FORWARD] = context.getString(R.string.send_draft_); } builder.setItems(items, new DialogInterface.OnClickListener() { @SuppressWarnings("deprecation") @Override public void onClick(final DialogInterface dialog, final int which) { Intent i; switch (which) { case WHICH_VIEW_CONTACT: if (n == null) { i = ContactsWrapper.getInstance().getInsertPickIntent(a); Conversation.flushCache(); } else { final Uri u = MessageListActivity.this.conv.getContact().getUri(); i = new Intent(Intent.ACTION_VIEW, u); } try { MessageListActivity.this.startActivity(i); } catch (ActivityNotFoundException e) { Toast.makeText(MessageListActivity.this, R.string.error_unknown, Toast.LENGTH_LONG).show(); } break; case WHICH_CALL: MessageListActivity.this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("tel:" + a))); break; case WHICH_MARK_UNREAD: ConversationListActivity.markRead(context, target, 1 - read); MessageListActivity.this.markedUnread = true; break; case WHICH_REPLY: MessageListActivity.this.startActivity( ConversationListActivity.getComposeIntent(MessageListActivity.this, a, false)); break; case WHICH_FORWARD: int resId; if (type == Message.SMS_DRAFT) { resId = R.string.send_draft_; i = ConversationListActivity.getComposeIntent(MessageListActivity.this, MessageListActivity.this.conv.getContact().getNumber(), false); } else { resId = R.string.forward_; i = new Intent(Intent.ACTION_SEND); i.setType("text/plain"); i.putExtra("forwarded_message", true); } CharSequence text; if (PreferencesActivity.decodeDecimalNCR(context)) { text = Converter.convertDecNCR2Char(m.getBody()); } else { text = m.getBody(); } i.putExtra(Intent.EXTRA_TEXT, text); i.putExtra("sms_body", text); context.startActivity(Intent.createChooser(i, context.getString(resId))); break; case WHICH_COPY_TEXT: final ClipboardManager cm = (ClipboardManager) context .getSystemService(Context.CLIPBOARD_SERVICE); if (PreferencesActivity.decodeDecimalNCR(context)) { cm.setText(Converter.convertDecNCR2Char(m.getBody())); } else { cm.setText(m.getBody()); } break; case WHICH_VIEW_DETAILS: final int t = m.getType(); Builder b = new Builder(context); b.setTitle(R.string.view_details_); b.setCancelable(true); StringBuilder sb = new StringBuilder(); final String a = m.getAddress(context); final long d = m.getDate(); final String ds = DateFormat.format(context.getString(R.string.DATEFORMAT_details), d) .toString(); String sentReceived; String fromTo; if (t == Calls.INCOMING_TYPE) { sentReceived = context.getString(R.string.received_); fromTo = context.getString(R.string.from_); } else if (t == Calls.OUTGOING_TYPE) { sentReceived = context.getString(R.string.sent_); fromTo = context.getString(R.string.to_); } else { sentReceived = "ukwn:"; fromTo = "ukwn:"; } sb.append(sentReceived).append(" "); sb.append(ds); sb.append("\n"); sb.append(fromTo).append(" "); sb.append(a); sb.append("\n"); sb.append(context.getString(R.string.type_)); if (m.isMMS()) { sb.append(" MMS"); } else { sb.append(" SMS"); } b.setMessage(sb.toString()); b.setPositiveButton(android.R.string.ok, null); b.show(); break; case WHICH_DELETE: ConversationListActivity.deleteMessages(context, target, R.string.delete_message_, R.string.delete_message_question, null); break; default: break; } } }); builder.show(); return true; } /** * {@inheritDoc} */ @SuppressWarnings("deprecation") public final void onClick(final View v) { switch (v.getId()) { case R.id.send_: send(true, false); return; default: // should never happen } } /** * {@inheritDoc} */ public final boolean onLongClick(final View v) { switch (v.getId()) { case R.id.send_: send(false, true); return true; default: return true; } } /** * Build an {@link Intent} for sending it. * * @param autosend autosend * @param showChooser show chooser * @return {@link Intent} */ private Intent buildIntent(final boolean autosend, final boolean showChooser) { //noinspection ConstantConditions if (conv == null || conv.getContact() == null) { throw new NullPointerException("conv and conv.getContact() must be not null"); } final SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this); String text = etText.getText().toString().trim(); if (p.getBoolean("auto_convert", false)) { if (p.getString("output_convert", "true").equals("true")) { text = myat.uni2zg(text); } else { text = myat.zg2uni(text); } } final Intent i = ConversationListActivity.getComposeIntent(this, conv.getContact().getNumber(), showChooser); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.putExtra(Intent.EXTRA_TEXT, text); i.putExtra("sms_body", text); if (showChooser) { return Intent.createChooser(i, getString(R.string.reply)); } else { return i; } } /** * Answer/send message. * * @param autosend enable autosend * @param showChooser show chooser */ private void send(final boolean autosend, final boolean showChooser) { try { final Intent i = buildIntent(autosend, showChooser); startActivity(i); //noinspection ConstantConditions PreferenceManager.getDefaultSharedPreferences(this).edit() .putString(PreferencesActivity.PREFS_BACKUPLASTTEXT, etText.getText().toString()).commit(); etText.setText(""); } catch (ActivityNotFoundException | NullPointerException e) { Log.e(TAG, "unable to launch sender app", e); Toast.makeText(this, R.string.error_sending_failed, Toast.LENGTH_LONG).show(); } } }