com.layer.atlas.messenger.AtlasMessagesScreen.java Source code

Java tutorial

Introduction

Here is the source code for com.layer.atlas.messenger.AtlasMessagesScreen.java

Source

/*
 * Copyright (c) 2015 Layer. All rights reserved.
 *
 * 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.layer.atlas.messenger;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Locale;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.database.Cursor;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.layer.atlas.Atlas;
import com.layer.atlas.Atlas.Participant;
import com.layer.atlas.Atlas.Tools;
import com.layer.atlas.AtlasMessageComposer;
import com.layer.atlas.AtlasMessagesList;
import com.layer.atlas.AtlasMessagesList.Cell;
import com.layer.atlas.AtlasMessagesList.ItemClickListener;
import com.layer.atlas.AtlasParticipantPicker;
import com.layer.atlas.AtlasTypingIndicator;
import com.layer.atlas.cells.ImageCell;
import com.layer.atlas.messenger.MessengerApp.keys;
import com.layer.sdk.LayerClient;
import com.layer.sdk.changes.LayerChangeEvent;
import com.layer.sdk.listeners.LayerChangeEventListener;
import com.layer.sdk.messaging.Conversation;
import com.layer.sdk.messaging.ConversationOptions;
import com.layer.sdk.messaging.Message;
import com.layer.sdk.messaging.MessagePart;
import com.layer.sdk.query.Predicate;
import com.layer.sdk.query.Query;
import com.layer.sdk.query.SortDescriptor;

/**
 * @author Oleg Orlov
 * @since 14 Apr 2015
 */
public class AtlasMessagesScreen extends Activity {

    private static final String TAG = AtlasMessagesScreen.class.getSimpleName();
    private static final boolean debug = false;

    public static final String EXTRA_CONVERSATION_IS_NEW = "conversation.new";
    public static final String EXTRA_CONVERSATION_URI = keys.CONVERSATION_URI;

    public static final int REQUEST_CODE_SETTINGS = 101;
    public static final int REQUEST_CODE_GALLERY = 111;
    public static final int REQUEST_CODE_CAMERA = 112;

    /** Switch it to <code>true</code> to see {@link #AtlasMessagesScreen()} Query support in action */
    private static final boolean USE_QUERY = false;

    private volatile Conversation conv;

    private LocationManager locationManager;
    private Location lastKnownLocation;
    private Handler uiHandler;

    private AtlasMessagesList messagesList;
    private AtlasMessageComposer messageComposer;
    private AtlasParticipantPicker participantsPicker;
    private AtlasTypingIndicator typingIndicator;

    private MessengerApp app;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.uiHandler = new Handler();
        this.app = (MessengerApp) getApplication();

        setContentView(R.layout.atlas_screen_messages);

        boolean convIsNew = getIntent().getBooleanExtra(EXTRA_CONVERSATION_IS_NEW, false);
        String convUri = getIntent().getStringExtra(EXTRA_CONVERSATION_URI);
        if (convUri != null) {
            Uri uri = Uri.parse(convUri);
            conv = app.getLayerClient().getConversation(uri);
            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).cancel(convUri.hashCode()); // Clear notifications for this Conversation
        }

        participantsPicker = (AtlasParticipantPicker) findViewById(R.id.atlas_screen_messages_participants_picker);
        participantsPicker.init(new String[] { app.getLayerClient().getAuthenticatedUserId() },
                app.getParticipantProvider());
        if (convIsNew) {
            participantsPicker.setVisibility(View.VISIBLE);
        }

        messageComposer = (AtlasMessageComposer) findViewById(R.id.atlas_screen_messages_message_composer);
        messageComposer.init(app.getLayerClient(), conv);
        messageComposer.setListener(new AtlasMessageComposer.Listener() {
            public boolean beforeSend(Message message) {
                boolean conversationReady = ensureConversationReady();
                if (!conversationReady)
                    return false;

                // push
                preparePushMetadata(message);
                return true;
            }

        });

        messageComposer.registerMenuItem("Photo", new OnClickListener() {
            public void onClick(View v) {
                if (!ensureConversationReady())
                    return;

                Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                String fileName = "cameraOutput" + System.currentTimeMillis() + ".jpg";
                photoFile = new File(getExternalFilesDir(android.os.Environment.DIRECTORY_PICTURES), fileName);
                final Uri outputUri = Uri.fromFile(photoFile);
                cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
                if (debug)
                    Log.w(TAG, "onClick() requesting photo to file: " + fileName + ", uri: " + outputUri);
                startActivityForResult(cameraIntent, REQUEST_CODE_CAMERA);
            }
        });

        messageComposer.registerMenuItem("Image", new OnClickListener() {
            public void onClick(View v) {
                if (!ensureConversationReady())
                    return;

                // in onCreate or any event where your want the user to select a file
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CODE_GALLERY);
            }
        });

        messageComposer.registerMenuItem("Location", new OnClickListener() {
            public void onClick(View v) {
                if (!ensureConversationReady())
                    return;

                if (lastKnownLocation == null) {
                    Toast.makeText(v.getContext(), "Inserting Location: Location is unknown yet",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                String locationString = "{\"lat\":" + lastKnownLocation.getLatitude() + ", \"lon\":"
                        + lastKnownLocation.getLongitude() + "}";
                MessagePart part = app.getLayerClient().newMessagePart(Atlas.MIME_TYPE_ATLAS_LOCATION,
                        locationString.getBytes());
                Message message = app.getLayerClient().newMessage(Arrays.asList(part));

                preparePushMetadata(message);
                conv.send(message);

                if (debug)
                    Log.w(TAG, "onSendLocation() loc:  " + locationString);
            }
        });

        messagesList = (AtlasMessagesList) findViewById(R.id.atlas_screen_messages_messages_list);
        messagesList.init(app.getLayerClient(), app.getParticipantProvider());
        if (USE_QUERY) {
            Query<Message> query = Query.builder(Message.class)
                    .predicate(new Predicate(Message.Property.CONVERSATION, Predicate.Operator.EQUAL_TO, conv))
                    .sortDescriptor(new SortDescriptor(Message.Property.POSITION, SortDescriptor.Order.ASCENDING))
                    .build();
            messagesList.setQuery(query);
        } else {
            messagesList.setConversation(conv);
        }

        messagesList.setItemClickListener(new ItemClickListener() {
            public void onItemClick(Cell cell) {
                if (Atlas.MIME_TYPE_ATLAS_LOCATION.equals(cell.messagePart.getMimeType())) {
                    String jsonLonLat = new String(cell.messagePart.getData());
                    JSONObject json;
                    try {
                        json = new JSONObject(jsonLonLat);
                        double lon = json.getDouble("lon");
                        double lat = json.getDouble("lat");
                        Intent openMapIntent = new Intent(Intent.ACTION_VIEW);
                        String uriString = String.format(Locale.ENGLISH, "geo:%f,%f?z=%d&q=%f,%f", lat, lon, 18,
                                lat, lon);
                        final Uri geoUri = Uri.parse(uriString);
                        openMapIntent.setData(geoUri);
                        if (openMapIntent.resolveActivity(getPackageManager()) != null) {
                            startActivity(openMapIntent);
                            if (debug)
                                Log.w(TAG, "onItemClick() starting Map: " + uriString);
                        } else {
                            if (debug)
                                Log.w(TAG, "onItemClick() No Activity to start Map: " + geoUri);
                        }
                    } catch (JSONException ignored) {
                    }
                } else if (cell instanceof ImageCell) {
                    Intent intent = new Intent(AtlasMessagesScreen.this.getApplicationContext(),
                            AtlasImageViewScreen.class);
                    app.setParam(intent, cell);
                    startActivity(intent);
                }
            }
        });

        typingIndicator = (AtlasTypingIndicator) findViewById(R.id.atlas_screen_messages_typing_indicator);
        typingIndicator.init(conv,
                new AtlasTypingIndicator.DefaultTypingIndicatorCallback(app.getParticipantProvider()));

        // location manager for inserting locations:
        this.locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        prepareActionBar();
    }

    private void updateValues() {
        if (conv == null) {
            Log.e(TAG, "updateValues() no conversation set");
            return;
        }

        TextView titleText = (TextView) findViewById(R.id.atlas_actionbar_title_text);
        titleText.setText(
                Atlas.getTitle(conv, app.getParticipantProvider(), app.getLayerClient().getAuthenticatedUserId()));
    }

    private boolean ensureConversationReady() {
        if (conv != null)
            return true;

        // create new one
        String[] userIds = participantsPicker.getSelectedUserIds();

        // no people, no conversation
        if (userIds.length == 0) {
            Toast.makeText(this, "Conversation cannot be created without participants", Toast.LENGTH_SHORT).show();
            return false;
        }

        conv = app.getLayerClient().newConversation(new ConversationOptions().distinct(false), userIds);
        participantsPicker.setVisibility(View.GONE);
        messageComposer.setConversation(conv);
        messagesList.setConversation(conv);
        typingIndicator.setConversation(conv);
        updateValues();
        return true;
    }

    private void preparePushMetadata(Message message) {
        Participant me = app.getParticipantProvider().getParticipant(app.getLayerClient().getAuthenticatedUserId());
        String senderName = Atlas.getFullName(me);
        String text = Atlas.Tools.toString(message);
        if (!text.isEmpty()) {
            if (senderName != null && !senderName.isEmpty()) {
                message.getOptions().pushNotificationMessage(senderName + ": " + text);
            } else {
                message.getOptions().pushNotificationMessage(text);
            }
        }
    }

    /** used to take photos from camera */
    private File photoFile = null;

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (debug)
            Log.w(TAG,
                    "onActivityResult() requestCode: " + requestCode + ", resultCode: " + resultCode + ", uri: "
                            + (data == null ? "" : data.getData()) + ", data: "
                            + (data == null ? "" : MessengerApp.toString(data.getExtras())));

        if (resultCode != Activity.RESULT_OK)
            return;

        final LayerClient layerClient = ((MessengerApp) getApplication()).getLayerClient();

        switch (requestCode) {
        case REQUEST_CODE_CAMERA:

            if (photoFile == null) {
                if (debug)
                    Log.w(TAG, "onActivityResult() taking photo, but output is undefined... ");
                return;
            }
            if (!photoFile.exists()) {
                if (debug)
                    Log.w(TAG, "onActivityResult() taking photo, but photo file doesn't exist: "
                            + photoFile.getPath());
                return;
            }
            if (photoFile.length() == 0) {
                if (debug)
                    Log.w(TAG, "onActivityResult() taking photo, but photo file is empty: " + photoFile.getPath());
                return;
            }

            try {
                // prepare original
                final File originalFile = photoFile;
                FileInputStream fisOriginal = new FileInputStream(originalFile) {
                    public void close() throws IOException {
                        super.close();
                        boolean deleted = originalFile.delete();
                        if (debug)
                            Log.w(TAG, "close() original file is" + (!deleted ? " not" : "") + " removed: "
                                    + originalFile.getName());
                        photoFile = null;
                    }
                };
                final MessagePart originalPart = layerClient.newMessagePart(Atlas.MIME_TYPE_IMAGE_JPEG, fisOriginal,
                        originalFile.length());
                File tempDir = getCacheDir();

                MessagePart[] previewAndSize = Atlas.buildPreviewAndSize(originalFile, layerClient, tempDir);
                if (previewAndSize == null) {
                    Log.e(TAG, "onActivityResult() cannot build preview, cancel send...");
                    return;
                }
                Message msg = layerClient.newMessage(originalPart, previewAndSize[0], previewAndSize[1]);
                if (debug)
                    Log.w(TAG, "onActivityResult() sending photo... ");
                preparePushMetadata(msg);
                conv.send(msg);
            } catch (Exception e) {
                Log.e(TAG, "onActivityResult() cannot insert photo" + e);
            }
            break;
        case REQUEST_CODE_GALLERY:
            if (data == null) {
                if (debug)
                    Log.w(TAG, "onActivityResult() insert from gallery: no data... :( ");
                return;
            }
            // first check media gallery
            Uri selectedImageUri = data.getData();
            // TODO: Mi4 requires READ_EXTERNAL_STORAGE permission for such operation
            String selectedImagePath = getGalleryImagePath(selectedImageUri);
            String resultFileName = selectedImagePath;
            if (selectedImagePath != null) {
                if (debug)
                    Log.w(TAG, "onActivityResult() image from gallery selected: " + selectedImagePath);
            } else if (selectedImageUri.getPath() != null) {
                if (debug)
                    Log.w(TAG,
                            "onActivityResult() image from file picker appears... " + selectedImageUri.getPath());
                resultFileName = selectedImageUri.getPath();
            }

            if (resultFileName != null) {
                String mimeType = Atlas.MIME_TYPE_IMAGE_JPEG;
                if (resultFileName.endsWith(".png"))
                    mimeType = Atlas.MIME_TYPE_IMAGE_PNG;
                if (resultFileName.endsWith(".gif"))
                    mimeType = Atlas.MIME_TYPE_IMAGE_GIF;

                // test file copy locally
                try {
                    // create message and upload content
                    InputStream fis = null;
                    File fileToUpload = new File(resultFileName);
                    if (fileToUpload.exists()) {
                        fis = new FileInputStream(fileToUpload);
                    } else {
                        if (debug)
                            Log.w(TAG, "onActivityResult() file to upload doesn't exist, path: " + resultFileName
                                    + ", trying ContentResolver");
                        fis = getContentResolver().openInputStream(data.getData());
                        if (fis == null) {
                            if (debug)
                                Log.w(TAG, "onActivityResult() cannot open stream with ContentResolver, uri: "
                                        + data.getData());
                        }
                    }

                    String fileName = "galleryFile" + System.currentTimeMillis() + ".jpg";
                    final File originalFile = new File(
                            getExternalFilesDir(android.os.Environment.DIRECTORY_PICTURES), fileName);

                    OutputStream fos = new FileOutputStream(originalFile);
                    int totalBytes = Tools.streamCopyAndClose(fis, fos);

                    if (debug)
                        Log.w(TAG,
                                "onActivityResult() copied " + totalBytes + " to file: " + originalFile.getName());

                    FileInputStream fisOriginal = new FileInputStream(originalFile) {
                        public void close() throws IOException {
                            super.close();
                            boolean deleted = originalFile.delete();
                            if (debug)
                                Log.w(TAG, "close() original file is" + (!deleted ? " not" : "") + " removed: "
                                        + originalFile.getName());
                        }
                    };
                    final MessagePart originalPart = layerClient.newMessagePart(mimeType, fisOriginal,
                            originalFile.length());
                    File tempDir = getCacheDir();

                    MessagePart[] previewAndSize = Atlas.buildPreviewAndSize(originalFile, layerClient, tempDir);
                    if (previewAndSize == null) {
                        Log.e(TAG, "onActivityResult() cannot build preview, cancel send...");
                        return;
                    }
                    Message msg = layerClient.newMessage(originalPart, previewAndSize[0], previewAndSize[1]);
                    if (debug)
                        Log.w(TAG, "onActivityResult() uploaded " + originalFile.length() + " bytes");
                    preparePushMetadata(msg);
                    conv.send(msg);
                } catch (Exception e) {
                    Log.e(TAG, "onActivityResult() cannot upload file: " + resultFileName, e);
                    return;
                }
            }
            break;

        default:
            break;
        }
    }

    /**
     * pick file name from content provider with Gallery-flavor format
     */
    public String getGalleryImagePath(Uri uri) {
        String[] projection = { MediaStore.Images.Media.DATA };
        Cursor cursor = managedQuery(uri, projection, null, null, null);
        if (cursor == null) {
            return null; // uri could be not suitable for ContentProviders, i.e. points to file 
        }
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    }

    @Override
    protected void onResume() {
        super.onResume();

        updateValues();
        messagesList.jumpToLastMessage();

        // restore location tracking
        int requestLocationTimeout = 1 * 1000; // every second
        int distance = 100;
        Location loc = null;
        if (locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
            loc = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            if (debug)
                Log.w(TAG, "onResume() location from gps: " + loc);
        }
        if (loc == null && locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
            loc = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            if (debug)
                Log.w(TAG, "onResume() location from network: " + loc);
        }
        if (loc != null && loc.getTime() < System.currentTimeMillis() + LOCATION_EXPIRATION_TIME) {
            locationTracker.onLocationChanged(loc);
        }
        if (locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, requestLocationTimeout, distance,
                    locationTracker);
        }
        if (locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
            locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, requestLocationTimeout,
                    distance, locationTracker);
        }

        app.getLayerClient().registerEventListener(messagesList);
        app.getLayerClient().registerTypingIndicator(typingIndicator.clear());

        // when something changed
        app.getLayerClient().registerEventListener(new LayerChangeEventListener.MainThread() {
            public void onEventMainThread(LayerChangeEvent event) {
                updateValues();
            }
        });
    }

    private static final int LOCATION_EXPIRATION_TIME = 60 * 1000; // 1 minute 

    LocationListener locationTracker = new LocationListener() {
        @Override
        public void onLocationChanged(final Location location) {
            uiHandler.post(new Runnable() {
                @Override
                public void run() {
                    lastKnownLocation = location;
                    if (debug)
                        Log.d(TAG, "onLocationChanged() location: " + location);
                }
            });
        }

        public void onStatusChanged(String provider, int status, Bundle extras) {
        }

        public void onProviderEnabled(String provider) {
        }

        public void onProviderDisabled(String provider) {
        }
    };

    @Override
    protected void onPause() {
        super.onPause();

        locationManager.removeUpdates(locationTracker);

        app.getLayerClient().unregisterEventListener(messagesList);
        app.getLayerClient().unregisterTypingIndicator(typingIndicator.clear());
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (debug)
            Log.w(TAG, "onConfigurationChanged() newConfig: " + newConfig);
        updateValues();
        messagesList.jumpToLastMessage();
    }

    private void prepareActionBar() {
        ImageView menuBtn = (ImageView) findViewById(R.id.atlas_actionbar_left_btn);
        menuBtn.setImageResource(R.drawable.atlas_ctl_btn_back);
        menuBtn.setVisibility(View.VISIBLE);
        menuBtn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                finish();
            }
        });
        ((TextView) findViewById(R.id.atlas_actionbar_title_text)).setText("Messages");
        ((TextView) findViewById(R.id.atlas_actionbar_title_text)).setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                messagesList.requestRefreshValues(true, false);
            }
        });
        ImageView settingsBtn = (ImageView) findViewById(R.id.atlas_actionbar_right_btn);
        settingsBtn.setImageResource(R.drawable.atlas_ctl_btn_detail);
        settingsBtn.setVisibility(View.VISIBLE);
        settingsBtn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                if (conv == null)
                    return;
                AtlasConversationSettingsScreen.conv = conv;
                Intent intent = new Intent(v.getContext(), AtlasConversationSettingsScreen.class);
                startActivityForResult(intent, REQUEST_CODE_SETTINGS);
            }
        });
        Tools.setStatusBarColor(getWindow(), getResources().getColor(R.color.atlas_background_blue_dark));
    }

}