net.kourlas.voipms_sms.activities.ConversationsActivity.java Source code

Java tutorial

Introduction

Here is the source code for net.kourlas.voipms_sms.activities.ConversationsActivity.java

Source

/*
 * VoIP.ms SMS
 * Copyright (C) 2015 Michael Kourlas and other contributors
 *
 * 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 net.kourlas.voipms_sms.activities;

import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.InputType;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import net.kourlas.voipms_sms.*;
import net.kourlas.voipms_sms.adapters.ConversationsRecyclerViewAdapter;
import net.kourlas.voipms_sms.gcm.Gcm;
import net.kourlas.voipms_sms.model.Conversation;
import net.kourlas.voipms_sms.model.Message;
import net.kourlas.voipms_sms.receivers.SynchronizationIntervalReceiver;
import org.json.JSONObject;

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

public class ConversationsActivity extends AppCompatActivity
        implements ActionMode.Callback, View.OnClickListener, View.OnLongClickListener {
    private final ConversationsActivity conversationsActivity = this;

    private Gcm gcm;
    private Billing billing;
    private Database database;
    private Preferences preferences;

    private RecyclerView recyclerView;
    private ConversationsRecyclerViewAdapter adapter;

    private Menu menu;

    private ActionMode actionMode;
    private boolean actionModeEnabled;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.conversations);

        gcm = Gcm.getInstance(getApplicationContext());
        billing = Billing.getInstance(getApplicationContext());
        database = Database.getInstance(getApplicationContext());
        preferences = Preferences.getInstance(getApplicationContext());

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        ViewCompat.setElevation(toolbar, getResources().getDimension(R.dimen.toolbar_elevation));
        setSupportActionBar(toolbar);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        adapter = new ConversationsRecyclerViewAdapter(this, layoutManager);
        recyclerView = (RecyclerView) findViewById(R.id.list);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);

        actionMode = null;
        actionModeEnabled = false;

        SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                preFullUpdate();
            }
        });
        swipeRefreshLayout.setColorSchemeResources(R.color.accent);

        ImageButton button = (ImageButton) findViewById(R.id.new_button);
        ViewCompat.setElevation(button, getResources().getDimension(R.dimen.fab_elevation));
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!preferences.getEmail().equals("") && !preferences.getPassword().equals("")
                        && !preferences.getDid().equals("")) {
                    Intent intent = new Intent(conversationsActivity, NewConversationActivity.class);
                    startActivity(intent);
                }
            }
        });

        SynchronizationIntervalReceiver.setupSynchronizationInterval(getApplicationContext());
    }

    @Override
    public void onResume() {
        super.onResume();
        ActivityMonitor.getInstance().setCurrentActivity(this);

        if (!(preferences.getEmail().equals("") || preferences.getPassword().equals("")
                || preferences.getDid().equals(""))) {
            preRecentUpdate();
        } else {
            Utils.showAlertDialog(this, null, getString(R.string.conversations_first_run_dialog_text),
                    getString(R.string.preferences_name), new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            Intent preferencesIntent = new Intent(conversationsActivity, PreferencesActivity.class);
                            startActivity(preferencesIntent);
                        }
                    }, getString(R.string.help_name), new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            Intent helpIntent = new Intent(conversationsActivity, HelpActivity.class);
                            startActivity(helpIntent);
                        }
                    });
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        ActivityMonitor.getInstance().deleteReferenceToActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityMonitor.getInstance().deleteReferenceToActivity(this);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 1001 && resultCode == RESULT_OK) {
            try {
                String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
                JSONObject json = new JSONObject(purchaseData);
                String token = json.getString("purchaseToken");
                billing.postDonation(token, this);
            } catch (Exception ignored) {
                // Do nothing.
            }
        }
    }

    @Override
    public void onBackPressed() {
        if (actionModeEnabled) {
            actionMode.finish();
        } else if (menu != null) {
            MenuItem searchItem = menu.findItem(R.id.search_button);
            SearchView searchView = (SearchView) searchItem.getActionView();
            if (!searchView.isIconified()) {
                searchItem.collapseActionView();
            } else {
                super.onBackPressed();
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.conversations, menu);
        this.menu = menu;

        SearchView searchView = (SearchView) menu.findItem(R.id.search_button).getActionView();
        searchView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                adapter.refresh(newText);
                return true;
            }
        });

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.preferences_button:
            Intent preferencesIntent = new Intent(this, PreferencesActivity.class);
            startActivity(preferencesIntent);
            return true;
        case R.id.help_button:
            Intent helpIntent = new Intent(this, HelpActivity.class);
            startActivity(helpIntent);
            return true;
        case R.id.credits_button:
            Intent creditsIntent = new Intent(this, CreditsActivity.class);
            startActivity(creditsIntent);
            return true;
        case R.id.donate_button:
            billing.preDonation(this);
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.conversations_secondary, menu);
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
    }

    /**
     * Called when an action mode item is clicked.
     *
     * @param mode The action mode containing the item that is clicked.
     * @param item The item that is clicked.
     * @return Returns true if the method handles the item clicked.
     */
    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        switch (item.getItemId()) {
        case R.id.mark_read_unread_button:
            for (int i = 0; i < adapter.getItemCount(); i++) {
                if (adapter.isItemChecked(i)) {
                    Message[] smses = adapter.getItem(i).getMessages();
                    for (Message message : smses) {
                        message.setUnread(item.getTitle()
                                .equals(getResources().getString(R.string.conversations_action_mark_unread)));
                        database.insertMessage(message);
                    }
                }
            }
            adapter.refresh();
            mode.finish();
            return true;
        case R.id.delete_button:
            List<Long> databaseIds = new ArrayList<>();
            for (int i = 0; i < adapter.getItemCount(); i++) {
                if (adapter.isItemChecked(i)) {
                    for (Message message : adapter.getItem(i).getMessages()) {
                        if (message.getDatabaseId() != null) {
                            databaseIds.add(message.getDatabaseId());
                        }
                    }
                }
            }

            Long[] databaseIdsArray = new Long[databaseIds.size()];
            databaseIds.toArray(databaseIdsArray);
            deleteMessages(databaseIdsArray);
            mode.finish();
            return true;
        default:
            return false;
        }
    }

    /**
     * Called when the action mode is destroyed.
     *
     * @param mode The action mode to be destroyed.
     */
    @Override
    public void onDestroyActionMode(ActionMode mode) {
        for (int i = 0; i < adapter.getItemCount(); i++) {
            adapter.setItemChecked(i, false);
        }
        actionModeEnabled = false;
    }

    /**
     * Toggles the item associated with the specified view. Activates and deactivates the action mode depending on the
     * checked item count.
     *
     * @param view The specified view.
     */
    private void toggleItem(View view) {
        adapter.toggleItemChecked(recyclerView.getChildAdapterPosition(view));

        if (adapter.getCheckedItemCount() == 0) {
            if (actionMode != null) {
                actionMode.finish();
            }
            actionModeEnabled = false;
            return;
        }

        if (!actionModeEnabled) {
            actionMode = startSupportActionMode(this);
            actionModeEnabled = true;
        }
        updateButtons();
    }

    /**
     * Switches between "mark as read" and "mark as unread" buttons for the action mode depending on which items in
     * the RecyclerView are selected.
     */
    private void updateButtons() {
        int read = 0;
        int unread = 0;
        for (int i = 0; i < adapter.getItemCount(); i++) {
            if (adapter.isItemChecked(i)) {
                if (adapter.getItem(i).isUnread()) {
                    unread++;
                } else {
                    read++;
                }
            }
        }

        MenuItem item = actionMode.getMenu().findItem(R.id.mark_read_unread_button);
        if (read > unread) {
            item.setIcon(R.drawable.ic_markunread_white_24dp);
            item.setTitle(R.string.conversations_action_mark_unread);
        } else {
            item.setIcon(R.drawable.ic_drafts_white_24dp);
            item.setTitle(R.string.conversations_action_mark_read);
        }
    }

    /**
     * Called when any item in the RecyclerView is short-clicked.
     * <p/>
     * This method only toggles the selected item (if the action mode is enabled) or opens the ConversationActivity
     * for that item (if the action mode is not enabled).
     *
     * @param view The item to toggle or open.
     */
    @Override
    public void onClick(View view) {
        if (actionModeEnabled) {
            toggleItem(view);
        } else {
            Conversation conversation = adapter.getItem(recyclerView.getChildAdapterPosition(view));
            String contact = conversation.getContact();

            Intent intent = new Intent(this, ConversationActivity.class);
            intent.putExtra(getString(R.string.conversation_extra_contact), contact);
            startActivity(intent);
        }
    }

    /**
     * Called when any item in the RecyclerView is long-clicked.
     * <p/>
     * This method only toggles the selected item.
     *
     * @param view The item to toggle.
     * @return Always returns true.
     */
    @Override
    public boolean onLongClick(View view) {
        toggleItem(view);
        return true;
    }

    /**
     * Deletes the messages with the specified database IDs.
     *
     * @param databaseIds The database IDs of the messages to delete.
     */
    public void deleteMessages(final Long[] databaseIds) {
        Utils.showAlertDialog(this, getString(R.string.conversations_delete_confirm_title),
                getString(R.string.conversations_delete_confirm_message), getString(R.string.delete),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        for (long databaseId : databaseIds) {
                            Message message = database.getMessageWithDatabaseId(preferences.getDid(), databaseId);
                            if (message.getVoipId() == null) {
                                // Simply delete messages with no VoIP.ms ID from the database; no request to the
                                // VoIP.ms API will be necessary
                                database.removeMessage(databaseId);
                            } else {
                                // Otherwise, keep the message in the database but set a deleted flag, so the message's
                                // VoIP.ms ID can be accessed later if local deletions are propagated to the VoIP.ms
                                // servers
                                message.setDeleted(true);
                                database.insertMessage(message);
                            }
                            adapter.refresh();
                        }
                    }
                }, getString(R.string.cancel), null);
    }

    /**
     * Initiates a partial update of the message database. This update only retrieves messages dated after the most
     * recent message.
     */
    public void preRecentUpdate() {
        adapter.refresh();
        gcm.registerForGcm(conversationsActivity, false, false);
        database.synchronize(true, false, conversationsActivity);
    }

    /**
     * Initiates a full update of the message database. This update follows all synchronization rules set in the
     * application's settings.
     */
    public void preFullUpdate() {
        adapter.refresh();
        gcm.registerForGcm(conversationsActivity, false, false);
        database.synchronize(false, true, conversationsActivity);
    }

    /**
     * Updates this activity's user interface after a database update.
     * <p/>
     * Called by the Api class after updating the SMS database if this activity made the update request or, if the
     * update request was initiated by the GCM service, if this activity is currently visible to the user.
     */
    public void postUpdate() {
        SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
        swipeRefreshLayout.setRefreshing(false);

        adapter.refresh();
    }
}