com.cyanogenmod.messaging.quickmessage.QuickMessagePopup.java Source code

Java tutorial

Introduction

Here is the source code for com.cyanogenmod.messaging.quickmessage.QuickMessagePopup.java

Source

/*
 * Copyright (C) 2012 The CyanogenMod Project (DvTonder)
 *
 * 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.cyanogenmod.messaging.quickmessage;

import android.app.Activity;
import android.app.DialogFragment;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;

import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
import com.android.messaging.R;
import com.android.messaging.datamodel.NoConfirmationSmsSendService;
import com.android.messaging.datamodel.action.MarkAsReadAction;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.ui.PlainTextEditText;
import com.android.messaging.ui.conversation.ComposeMessageView;
import com.android.messaging.util.PhoneUtils;
import com.android.messaging.util.UnicodeFilter;
import com.cyanogenmod.messaging.ui.QuickMessageView;
import com.cyanogenmod.messaging.util.PrefsUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class QuickMessagePopup extends Activity {
    private static final String LOG_TAG = "QuickMessagePopup";

    private boolean DEBUG = false;

    // Intent bungle fields
    public static final String SMS_NOTIFICATION_OBJECT_EXTRA = "com.cyanaogenmod.messaging.NOTIFICATION_OBJECT";
    public static final String QR_SHOW_KEYBOARD_EXTRA = "com.cyanaogenmod.messaging.QR_SHOW_KEYBOARD";

    // View items
    private Button mCloseButton;
    private Button mViewButton;

    // General items
    private Context mContext;

    // Message list items
    private ArrayList<QuickMessage> mMessageList;
    private QuickMessage mCurrentQm = null;
    private int mCurrentPage = -1; // Set to an invalid index

    // Configuration
    private boolean mCloseClosesAll = false;

    private UnicodeFilter mUnicodeFilter;

    // Message pager
    private ViewPager mMessagePager;
    private MessagePagerAdapter mPagerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Initialise the message list and other variables
        mContext = this;
        mMessageList = new ArrayList<QuickMessage>();

        mUnicodeFilter = PrefsUtils.getUnicodeFilterIfEnabled();

        mCloseClosesAll = PrefsUtils.isQuickMessagingCloseAllEnabled();

        // Set the window features and layout
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.dialog_quickmessage);

        // Turn on the Options Menu
        invalidateOptionsMenu();

        // Load the views and Parse the intent to show the QuickMessage
        setupViews();
        parseIntent(getIntent().getExtras(), false);
    }

    private void setupViews() {

        // Load the main views
        mCloseButton = (Button) findViewById(R.id.button_close);
        mViewButton = (Button) findViewById(R.id.button_view);

        // ViewPager Support
        mPagerAdapter = new MessagePagerAdapter();
        mMessagePager = (ViewPager) findViewById(R.id.message_pager);
        mMessagePager.setAdapter(mPagerAdapter);
        mMessagePager.setOnPageChangeListener(mPagerAdapter);

        // Close button
        mCloseButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // If not closing all, close the current QM and move on
                int numMessages = mMessageList.size();
                if (mCloseClosesAll || numMessages == 1) {
                    clearNotification(true);
                    finish();
                } else {
                    // Dismiss the keyboard if it is shown
                    QuickMessage qm = mMessageList.get(mCurrentPage);
                    if (qm != null) {
                        dismissKeyboard(qm);

                        if (mCurrentPage < numMessages - 1) {
                            showNextMessageWithRemove(qm);
                        } else {
                            showPreviousMessageWithRemove(qm);
                        }
                    }
                }
            }
        });

        // View button
        mViewButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // Trigger the view intent
                mCurrentQm = mMessageList.get(mCurrentPage);
                Intent vi = mCurrentQm.getViewIntent(mContext);
                if (vi != null) {
                    mCurrentQm.saveReplyText();
                    vi.putExtra("sms_body", mCurrentQm.getReplyText());

                    startActivity(vi);
                }
                clearNotification(false);
                finish();
            }
        });
    }

    private void parseIntent(Bundle extras, boolean newMessage) {
        if (extras == null) {
            return;
        }

        // Parse the intent and ensure we have a notification object to work with
        NotificationInfo nm = extras.getParcelable(SMS_NOTIFICATION_OBJECT_EXTRA);
        if (nm != null) {
            QuickMessage qm = new QuickMessage(nm);
            markCurrentMessageRead(qm);
            mMessageList.add(qm);
            mPagerAdapter.notifyDataSetChanged();

            // If triggered from Quick Reply the keyboard should be visible immediately
            if (extras.getBoolean(QR_SHOW_KEYBOARD_EXTRA, false)) {
                getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
            }

            if (newMessage && mCurrentPage != -1) {
                // There is already a message showing
                // Stay on the current message
                mMessagePager.setCurrentItem(mCurrentPage);
            } else {
                // Set the current message to the last message received
                mCurrentPage = mMessageList.size() - 1;
                mMessagePager.setCurrentItem(mCurrentPage);
            }

            if (DEBUG)
                Log.d(LOG_TAG,
                        "parseIntent(): New message from " + qm.getFromName().toString()
                                + " added. Number of messages = " + mMessageList.size() + ". Displaying page #"
                                + (mCurrentPage + 1));

        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (DEBUG)
            Log.d(LOG_TAG, "onNewIntent() called");

        // Set new intent
        setIntent(intent);

        // Load and display the new message
        parseIntent(intent.getExtras(), true);
    }

    //==========================================================
    // Utility methods
    //==========================================================

    /**
     * This method dismisses the on screen keyboard if it is visible for the supplied qm
     *
     * @param qm - qm to check against
     */
    private void dismissKeyboard(QuickMessage qm) {
        if (qm != null) {
            EditText editView = qm.getEditText();
            if (editView != null) {
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(editView.getApplicationWindowToken(), 0);
            }
        }
    }

    /**
     * Remove the supplied qm from the ViewPager and show the previous/older message
     *
     * @param qm
     */
    public void showPreviousMessageWithRemove(QuickMessage qm) {
        if (qm != null) {
            if (DEBUG)
                Log.d(LOG_TAG, "showPreviousMessageWithRemove()");

            markCurrentMessageRead(qm);
            if (mCurrentPage > 0) {
                updatePages(mCurrentPage - 1, qm);
            }
        }
    }

    /**
     * Remove the supplied qm from the ViewPager and show the next/newer message
     *
     * @param qm
     */
    public void showNextMessageWithRemove(QuickMessage qm) {
        if (qm != null) {
            if (DEBUG)
                Log.d(LOG_TAG, "showNextMessageWithRemove()");

            markCurrentMessageRead(qm);
            if (mCurrentPage < (mMessageList.size() - 1)) {
                updatePages(mCurrentPage, qm);
            }
        }
    }

    /**
     * Handle qm removal and the move to and display of the appropriate page
     *
     * @param gotoPage - page number to display after the removal
     * @param removeMsg - qm to remove from ViewPager
     */
    private void updatePages(int gotoPage, QuickMessage removeMsg) {
        mMessageList.remove(removeMsg);
        mPagerAdapter.notifyDataSetChanged();
        mMessagePager.setCurrentItem(gotoPage);

        if (DEBUG)
            Log.d(LOG_TAG, "updatePages(): Removed message " + removeMsg.getConversationId()
                    + " and changed to page #" + (gotoPage + 1) + ". Remaining messages = " + mMessageList.size());
    }

    /**
     * Marks the supplied qm as read
     *
     * @param qm
     */
    private void markCurrentMessageRead(QuickMessage qm) {
        MarkAsReadAction.markAsRead(qm.getConversationId());
    }

    /**
     * Marks all qm's in the message list as read
     */
    private void markAllMessagesRead() {
        // This iterates through our MessageList and marks the contained threads as read
        for (QuickMessage qm : mMessageList) {
            markCurrentMessageRead(qm);
        }
    }

    private CharSequence stripUnicodeIfRequested(CharSequence text) {
        if (mUnicodeFilter != null) {
            text = mUnicodeFilter.filter(text);
        }
        return text;
    }

    /**
     * Use standard api to send the supplied message
     *
     * @param message - message to send
     * @param qm - qm to reply to (for sender details)
     * @param subId - the subscription to use
     */
    private void sendQuickMessage(String message, QuickMessage qm, int subId) {
        if (message != null && qm != null) {
            Intent sendIntent = new Intent(this, NoConfirmationSmsSendService.class);
            sendIntent.setAction(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE);
            sendIntent.putExtra(NoConfirmationSmsSendService.EXTRA_SUBSCRIPTION, subId);
            sendIntent.putExtra(Intent.EXTRA_TEXT, stripUnicodeIfRequested(message));
            sendIntent.setData(qm.getRecipientsUri());
            startService(sendIntent);
            Toast.makeText(mContext, R.string.toast_sending_message, Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Clears the status bar notification and, optionally, mark all messages as read
     * This is used to clean up when we are done with all qm's
     *
     * @param markAsRead - should remaining qm's be maked as read?
     */
    private void clearNotification(boolean markAsRead) {
        // Dismiss the notification that brought us here.
        NotificationManager notificationManager = (NotificationManager) getSystemService(
                Context.NOTIFICATION_SERVICE);

        StatusBarNotification[] statusBarNotifications = notificationManager.getActiveNotifications();
        final String packageName = getPackageName();
        for (StatusBarNotification statusBarNotification : statusBarNotifications) {
            if (packageName.equals(statusBarNotification.getPackageName())) {
                notificationManager.cancel(statusBarNotification.getTag(), statusBarNotification.getId());
                break;
            }
        }

        // Mark all contained conversations as seen
        if (markAsRead) {
            markAllMessagesRead();
        }

        // Clear the messages list
        mMessageList.clear();

        if (DEBUG)
            Log.d(LOG_TAG, "clearNotification(): Message list cleared. Size = " + mMessageList.size());
    }

    /**
     * display the sim select dialog for multi sim phones
     */
    private void showSimSelector(Activity activity, final ComposeMessageView.OnSimSelectedCallback cb) {
        final TelecomManager telecomMgr = (TelecomManager) activity.getSystemService(Context.TELECOM_SERVICE);
        final List<PhoneAccountHandle> handles = telecomMgr.getCallCapablePhoneAccounts();
        final List<PhoneAccountHandle> filteredHandles = new ArrayList<>();

        //trim out SIP accounts
        for (PhoneAccountHandle handle : handles) {
            PhoneAccount phoneAccount = PhoneUtils.getAccountOrNull(activity, handle);
            if (phoneAccount != null) {
                Uri address = phoneAccount.getAddress();
                if (address != null && !TextUtils.equals(address.getScheme(), PhoneAccount.SCHEME_SIP)) {
                    filteredHandles.add(handle);
                }
            }
        }

        final SelectPhoneAccountDialogFragment.SelectPhoneAccountListener listener = new SelectPhoneAccountDialogFragment.SelectPhoneAccountListener() {
            @Override
            public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle, boolean setDefault) {
                // we need the subId and we only have a PhoneAccountHandle
                TelephonyManager telephonyManager = (TelephonyManager) mContext
                        .getSystemService(Context.TELEPHONY_SERVICE);
                Iterator<PhoneAccountHandle> phoneAccounts = telecomMgr.getCallCapablePhoneAccounts()
                        .listIterator();
                int subId = 0; // defaulting to 0, just in case
                while (phoneAccounts.hasNext()) {
                    PhoneAccountHandle p = phoneAccounts.next();
                    if (p.getId().equals(selectedAccountHandle.getId())) {
                        PhoneAccount phoneAccount = telecomMgr.getPhoneAccount(p);
                        subId = telephonyManager.getSubIdForPhoneAccount(phoneAccount);
                    }
                }
                cb.onSimSelected(subId);
            }

            @Override
            public void onDialogDismissed() {
            }
        };

        DialogFragment dialogFragment = SelectPhoneAccountDialogFragment.newInstance(
                R.string.select_phone_account_title, false /* canSetDefault */, filteredHandles, listener);
        dialogFragment.show(activity.getFragmentManager(), "SELECT_PHONE_ACCOUNT_DIALOG_FRAGMENT");
    }

    private boolean isSMSPromptEnabled() {
        return PhoneUtils.getDefault().getActiveSubscriptionCount() > 1
                && !PhoneUtils.getDefault().getHasPreferredSmsSim();
    }

    /**
     * Message Pager class, used to display and navigate through the ViewPager pages
     */
    private class MessagePagerAdapter extends PagerAdapter
            implements ViewPager.OnPageChangeListener, OnEditorActionListener, OnClickListener {

        protected LinearLayout mCurrentPrimaryLayout = null;

        @Override
        public int getCount() {
            return mMessageList.size();
        }

        @Override
        public Object instantiateItem(ViewGroup collection, int position) {

            // Load the layout to be used
            LayoutInflater inflater = LayoutInflater.from(mContext);
            QuickMessageView quickMessageView;
            quickMessageView = (QuickMessageView) inflater.inflate(R.layout.quickmessage_view, null);

            // Load the main views
            PlainTextEditText qmReplyText = (PlainTextEditText) quickMessageView
                    .findViewById(R.id.embedded_text_editor);

            // Retrieve the current message
            QuickMessage qm = mMessageList.get(position);
            if (qm != null) {
                if (DEBUG)
                    Log.d(LOG_TAG, "instantiateItem(): Creating page #" + (position + 1) + " for message from "
                            + qm.getFromName() + ". Number of pages to create = " + getCount());

                quickMessageView.bind(qm, this, this, position, getCount());

                // Add the context menu
                registerForContextMenu(qmReplyText);

                // Store the EditText object for future use
                qm.setEditText(qmReplyText);

                // Add the layout to the viewpager
                collection.addView(quickMessageView);

                if (PrefsUtils.isShowEmoticonsEnabled()) {
                    qmReplyText
                            .setInputType(qmReplyText.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
                } else {
                    qmReplyText.setInputType(
                            qmReplyText.getInputType() & ~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
                }
            }
            return quickMessageView;
        }

        @Override
        public void onClick(View v) {
            final QuickMessage qm = mMessageList.get(mCurrentPage);
            if (qm != null) {
                final EditText editView = qm.getEditText();
                if (editView != null) {
                    if (isSMSPromptEnabled()) {
                        showSimSelector(QuickMessagePopup.this, new ComposeMessageView.OnSimSelectedCallback() {
                            @Override
                            public void onSimSelected(int subId) {
                                sendMessageAndMoveOn(editView.getText().toString(), qm, subId);
                            }
                        });
                    } else {
                        sendMessageAndMoveOn(editView.getText().toString(), qm,
                                ParticipantData.DEFAULT_SELF_SUB_ID);
                    }
                }
            }
        }

        @Override
        public boolean onEditorAction(final TextView v, int actionId, KeyEvent event) {
            if (event != null) {
                // event != null means enter key pressed
                if (!event.isShiftPressed()) {
                    // if shift is not pressed then move focus to send button
                    if (v != null) {
                        View focusableView = v.focusSearch(View.FOCUS_RIGHT);
                        if (focusableView != null) {
                            focusableView.requestFocus();
                            return true;
                        }
                    }
                }
                return false;
            }
            if (actionId == EditorInfo.IME_ACTION_SEND) {
                if (v != null) {
                    final QuickMessage qm = mMessageList.get(mCurrentPage);
                    if (qm != null) {
                        if (isSMSPromptEnabled()) {
                            showSimSelector(QuickMessagePopup.this, new ComposeMessageView.OnSimSelectedCallback() {
                                @Override
                                public void onSimSelected(int subId) {
                                    sendMessageAndMoveOn(v.getText().toString(), qm, subId);
                                }
                            });
                        } else {
                            sendMessageAndMoveOn(v.getText().toString(), qm, ParticipantData.DEFAULT_SELF_SUB_ID);
                        }
                    }
                }
                return true;
            }
            return true;
        }

        /**
         * This method sends the supplied message in reply to the supplied qm and then
         * moves to the next or previous message as appropriate. If this is the last qm
         * in the MessageList, we end by clearing the notification and calling finish()
         *
         * @param message - message to send
         * @param qm - qm we are replying to (for sender details)
         * @param subId - the subscription to use
         */
        private void sendMessageAndMoveOn(String message, QuickMessage qm, int subId) {
            sendQuickMessage(message, qm, subId);
            // Close the current QM and move on
            int numMessages = mMessageList.size();
            if (numMessages == 1) {
                // No more messages
                clearNotification(true);
                finish();
            } else {
                // Dismiss the keyboard if it is shown
                dismissKeyboard(qm);

                if (mCurrentPage < numMessages - 1) {
                    showNextMessageWithRemove(qm);
                } else {
                    showPreviousMessageWithRemove(qm);
                }
            }
        }

        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            LinearLayout view = ((LinearLayout) object);
            if (view != mCurrentPrimaryLayout) {
                mCurrentPrimaryLayout = view;
            }
        }

        @Override
        public void onPageSelected(int position) {
            // The user had scrolled to a new message
            if (mCurrentQm != null) {
                mCurrentQm.saveReplyText();
            }

            // Set the new 'active' QuickMessage
            mCurrentPage = position;
            mCurrentQm = mMessageList.get(position);
            markCurrentMessageRead(mCurrentQm);
            if (DEBUG)
                Log.d(LOG_TAG, "onPageSelected(): Current page is #" + (position + 1) + " of " + getCount()
                        + " pages. Currenty visible message is from " + mCurrentQm.getFromName());

            notifyDataSetChanged();
        }

        @Override
        public int getItemPosition(Object object) {
            // This is needed to force notifyDatasetChanged() to rebuild the pages
            return PagerAdapter.POSITION_NONE;
        }

        @Override
        public void destroyItem(View collection, int position, Object view) {
            ((ViewPager) collection).removeView((LinearLayout) view);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == ((LinearLayout) object);
        }

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

        @Override
        public void onPageScrollStateChanged(int state) {
        }
    }
}