src/com/beem/project/beem/ui/ChangeStatus.java
author Da Risk <da_risk@elyzion.net>
Mon, 05 Sep 2011 09:31:22 +0200
changeset 902 c82293948d2f
parent 875 bfb4326524f8
child 934 6b30719f4de6
permissions -rw-r--r--
Fix a NullPointerException in ChangeStatus.

/*
    BEEM is a videoconference application on the Android Platform.

    Copyright (C) 2009 by Frederic-Charles Barthelery,
                          Jean-Manuel Da Silva,
                          Nikita Kozlov,
                          Philippe Lago,
                          Jean Baptiste Vergely,
                          Vincent Veronis.

    This file is part of BEEM.

    BEEM 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.

    BEEM 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 BEEM.  If not, see <http://www.gnu.org/licenses/>.

    Please send bug reports with examples or suggestions to
    contact@beem-project.com or http://dev.beem-project.com/

    Epitech, hereby disclaims all copyright interest in the program "Beem"
    written by Frederic-Charles Barthelery,
               Jean-Manuel Da Silva,
               Nikita Kozlov,
               Philippe Lago,
               Jean Baptiste Vergely,
               Vincent Veronis.

    Nicolas Sadirac, November 26, 2009
    President of Epitech.

    Flavien Astraud, November 26, 2009
    Head of the EIP Laboratory.

*/

package com.beem.project.beem.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;

import com.beem.project.beem.BeemService;
import com.beem.project.beem.BeemApplication;
import com.beem.project.beem.R;
import com.beem.project.beem.service.aidl.IXmppFacade;
import com.beem.project.beem.service.UserInfo;
import com.beem.project.beem.providers.AvatarProvider;
import com.beem.project.beem.utils.BeemBroadcastReceiver;
import com.beem.project.beem.utils.BeemConnectivity;
import com.beem.project.beem.utils.Status;

import java.io.File;
import java.util.Date;
import java.text.SimpleDateFormat;

/**
 * This Activity is used to change the status.
 * @author nikita
 */
public class ChangeStatus extends Activity {

    private static final Intent SERVICE_INTENT = new Intent();
    static {
	SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
    }

    private static final String TAG = ChangeStatus.class.getSimpleName();
    private static final int AVAILABLE_FOR_CHAT_IDX = 0;
    private static final int AVAILABLE_IDX = 1;
    private static final int BUSY_IDX = 2;
    private static final int AWAY_IDX = 3;
    private static final int UNAVAILABLE_IDX = 4;
    private static final int DISCONNECTED_IDX = 5;

    private static final int ICON_SIZE = 80;

    private static final int SELECT_PHOTO_DLG = 0;

    private static final int CAMERA_WITH_DATA = 0;
    private static final int PHOTO_PICKED_WITH_DATA = 1;

    private static final File PHOTO_DIR = new File(
            Environment.getExternalStorageDirectory() + "/DCIM/Camera");

    private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";

    private static final Uri MY_AVATAR_URI = Uri.parse(AvatarProvider.CONTENT_URI + "/my_avatar");

    private EditText mStatusMessageEditText;
    private Toast mToast;
    private Button mOk;
    private Button mClear;
    private Button mContact;
    private Spinner mSpinner;
    private ImageButton mAvatar;
    private Uri mAvatarUri;

    private SharedPreferences mSettings;
    private ArrayAdapter<CharSequence> mAdapter;
    private IXmppFacade mXmppFacade;
    private final ServiceConnection mServConn = new BeemServiceConnection();
    private final OnClickListener mOnClickOk = new MyOnClickListener();
    private final BeemBroadcastReceiver mReceiver = new BeemBroadcastReceiver();
    private boolean mShowCurrentAvatar = true;
    private boolean mDisableAvatar;
    private File mCurrentPhotoFile;

    /**
     * Constructor.
     */
    public ChangeStatus() {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);

	Log.d(TAG, "oncreate");
	setContentView(R.layout.changestatus);

	mOk = (Button) findViewById(R.id.ChangeStatusOk);
	mOk.setOnClickListener(mOnClickOk);

	mClear = (Button) findViewById(R.id.ChangeStatusClear);
	mClear.setOnClickListener(mOnClickOk);

	mContact = (Button) findViewById(R.id.OpenContactList);
	mContact.setOnClickListener(mOnClickOk);

	BeemApplication app = (BeemApplication) getApplication();
	mAvatar = (ImageButton) findViewById(R.id.avatarButton);
	mAvatar.setOnClickListener(mOnClickOk);
	if (!app.isPepEnabled()) {
	    View avatarPanel = findViewById(R.id.avatar_panel);
	    avatarPanel.setVisibility(View.GONE);
	}


	mSettings = PreferenceManager.getDefaultSharedPreferences(this);
	mStatusMessageEditText = (EditText) findViewById(R.id.ChangeStatusMessage);
	mStatusMessageEditText.setText(mSettings.getString(BeemApplication.STATUS_TEXT_KEY, ""));

	mSpinner = (Spinner) findViewById(R.id.ChangeStatusSpinner);
	mAdapter = ArrayAdapter.createFromResource(this, R.array.status_types, android.R.layout.simple_spinner_item);
	mAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
	mSpinner.setAdapter(mAdapter);

	mToast = Toast.makeText(this, R.string.ChangeStatusOk, Toast.LENGTH_LONG);
	mSpinner.setSelection(getPreferenceStatusIndex());

	this.registerReceiver(mReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onResume() {
	super.onResume();
	if (!BeemConnectivity.isConnected(getApplicationContext())) {
	    Intent i = new Intent(this, Login.class);
	    startActivity(i);
	    finish();
	}
	bindService(new Intent(this, BeemService.class), mServConn, BIND_AUTO_CREATE);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onPause() {
	super.onPause();
	unbindService(mServConn);
    }

    /* (non-Javadoc)
     * @see android.app.Activity#onDestroy()
     */
    @Override
    protected void onDestroy() {
	super.onDestroy();
	this.unregisterReceiver(mReceiver);
    }

    /*
     * The activity is often reclaimed by the system memory.
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        if (mCurrentPhotoFile != null) {
            outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
        }
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        String fileName = savedInstanceState.getString(KEY_CURRENT_PHOTO_FILE);
        if (fileName != null) {
            mCurrentPhotoFile = new File(fileName);
        }
        super.onRestoreInstanceState(savedInstanceState);
    }



    @Override
    protected Dialog onCreateDialog(int id) {
	if (id == SELECT_PHOTO_DLG)
	    return createPickPhotoDialog();
	return null;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	  // Ignore failed requests
        if (resultCode != RESULT_OK) return;

        switch (requestCode) {
            case PHOTO_PICKED_WITH_DATA:
		mAvatarUri = Uri.parse(data.getAction());
		Log.d(TAG, "selected avatar uri " + mAvatarUri);
		if (mAvatarUri != null) {
		    mAvatar.setImageURI(mAvatarUri);
		    mDisableAvatar = false;
		    mShowCurrentAvatar = false;
		}
                break;

            case CAMERA_WITH_DATA:
                doCropPhoto(mCurrentPhotoFile);
                break;
	    default:
		Log.w(TAG, "onActivityResult : invalid request code");

        }
    }

    /**
     * Return the status index from status the settings.
     * @return the status index from status the settings.
     */
    private int getPreferenceStatusIndex() {
	return mSettings.getInt(BeemApplication.STATUS_KEY, AVAILABLE_IDX);
    }

    /**
     * Return the status text from status the settings.
     * @param id status text id.
     * @return the status text from status the settings.
     */
    private String getPreferenceString(int id) {
	return mSettings.getString(getString(id), "");
    }

    /**
     * convert status text to.
     * @param item selected item text.
     * @return item position in the array.
     */
    private int getStatusForService(String item) {
	int result;
	switch (mAdapter.getPosition(item)) {
	    case ChangeStatus.DISCONNECTED_IDX:
		result = Status.CONTACT_STATUS_DISCONNECT;
		break;
	    case ChangeStatus.AVAILABLE_FOR_CHAT_IDX:
		result = Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT;
		break;
	    case ChangeStatus.AVAILABLE_IDX:
		result = Status.CONTACT_STATUS_AVAILABLE;
		break;
	    case ChangeStatus.AWAY_IDX:
		result = Status.CONTACT_STATUS_AWAY;
		break;
	    case ChangeStatus.BUSY_IDX:
		result = Status.CONTACT_STATUS_BUSY;
		break;
	    case ChangeStatus.UNAVAILABLE_IDX:
		result = Status.CONTACT_STATUS_UNAVAILABLE;
		break;
	    default:
		result = Status.CONTACT_STATUS_AVAILABLE;
		break;
	}
	return result;
    }

    /**
     * ClickListener for the avatarButton.
     *
     * @param button the avatar button
     */
    private void onAvatarButton(View button) {
	showDialog(SELECT_PHOTO_DLG);
    }

    /**
     * Publish the selected avatar.
     */
    private void publishAvatar() {
	try {
	    if (mDisableAvatar)
		mXmppFacade.disableAvatarPublishing();
	    else if (mAvatarUri != null)
		mXmppFacade.publishAvatar(mAvatarUri);
	} catch (RemoteException e) {
	    Log.e(TAG, "Error while publishing avatar", e);
	}
    }

    /**
     * Display the current avatar in the button.
     */
    private void displayCurrentAvatar() {
	try {
	    UserInfo ui = mXmppFacade.getUserInfo();
	    if (ui == null)
		return;
	    String avatarId = ui.getAvatarId();
	    Log.d(TAG, "User info : avatar id " + avatarId);
	    if (avatarId != null) {
		Uri uri = AvatarProvider.CONTENT_URI.buildUpon().appendPath(avatarId).build();
		mAvatar.setImageURI(uri);
	    }
	} catch (RemoteException e) {
	    Log.e(TAG, "Error while displaying current avatar", e);
	}
	mShowCurrentAvatar = false;
    }

    /*
     * Some codes from AOSP (platform/packages/apps/Contacts)
     * to select and crop an image.
     */

    /**
     * Creates a dialog offering two options: take a photo or pick a photo from the gallery.
     * @return the dialog
     */
    private Dialog createPickPhotoDialog() {
	 // Wrap our context to inflate list items using correct theme
        final Context dialogContext = new ContextThemeWrapper(this,
                android.R.style.Theme_Light);

        final ListAdapter adapter = ArrayAdapter.createFromResource(dialogContext,
		R.array.pick_photo_items,
                android.R.layout.simple_list_item_1);

        final AlertDialog.Builder builder = new AlertDialog.Builder(dialogContext);
        builder.setTitle(R.string.select_avatar);
        builder.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                switch(which) {
                    case 0:
                        doTakePhoto();
                        break;
                    case 1:
                        doPickPhotoFromGallery();
                        break;
                    case 2:
			mDisableAvatar = true;
			mAvatar.setImageURI(null);
			break;
		    default:
			Log.w(TAG, "DialogInterface onClick : invalid which code");
                }
            }
        });
        return builder.create();
    }

    /**
     * Create a file name for the icon photo using current time.
     * @return the filename
     */
    private String getPhotoFileName() {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
        return dateFormat.format(date) + ".jpg";
    }

    /**
     * Launches Camera to take a picture and store it in a file.
     */
    protected void doTakePhoto() {
        try {
            // Launch camera to take photo for selected contact
            PHOTO_DIR.mkdirs();
            mCurrentPhotoFile = new File(PHOTO_DIR, getPhotoFileName());
            final Intent intent = getTakePickIntent(mCurrentPhotoFile);
            startActivityForResult(intent, CAMERA_WITH_DATA);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Constructs an intent for capturing a photo and storing it in a temporary file.
     * @param f the temporary file to use to store the picture
     * @return the intent
     */
    public static Intent getTakePickIntent(File f) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
        return intent;
    }

    /**
     * Sends a newly acquired photo to Gallery for cropping.
     * @param f the image file to crop
     */
    protected void doCropPhoto(final File f) {
        try {

            // Add the image to the media store
	    // level 8
	    /*
            MediaScannerConnection.scanFile(
                    this,
                    new String[] { f.getAbsolutePath() },
                    new String[] { null },
                    null);
	    */

            // Launch gallery to crop the photo
            final Intent intent = getCropImageIntent(Uri.fromFile(f));
            startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
        } catch (ActivityNotFoundException e) {
            Log.e(TAG, "Cannot crop image", e);
            Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Constructs an intent for image cropping.
     * @param photoUri the uri of the photo to crop
     * @return the intent
     */
    public static Intent getCropImageIntent(Uri photoUri) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(photoUri, "image/*");
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", ICON_SIZE);
        intent.putExtra("outputY", ICON_SIZE);
	intent.putExtra(MediaStore.EXTRA_OUTPUT, MY_AVATAR_URI);
        return intent;
    }

    /**
     * Launches Gallery to pick a photo.
     */
    protected void doPickPhotoFromGallery() {
        try {
            // Launch picker to choose photo for selected contact
            final Intent intent = getPhotoPickIntent();
            startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
     * @return the intent
     */
    public static Intent getPhotoPickIntent() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
        intent.setType("image/*");
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", ICON_SIZE);
        intent.putExtra("outputY", ICON_SIZE);
	intent.putExtra(MediaStore.EXTRA_OUTPUT, MY_AVATAR_URI);
	// use this to get the bitmap in the intent
//         intent.putExtra("return-data", true);
        return intent;
    }



    /**
     * connection to service.
     * @author nikita
     */
    private class BeemServiceConnection implements ServiceConnection {

	/**
	 * constructor.
	 */
	public BeemServiceConnection() {
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
	    mXmppFacade = IXmppFacade.Stub.asInterface(service);
	    if (mShowCurrentAvatar)
		displayCurrentAvatar();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onServiceDisconnected(ComponentName name) {
	    mXmppFacade = null;
	}
    }

    /**
     * User have clicked on ok.
     * @author nikita
     */
    private class MyOnClickListener implements OnClickListener {

	/**
	 * constructor.
	 */
	public MyOnClickListener() {
	}

	@Override
	public void onClick(View v) {
	    if (v == mOk) {
		String msg = mStatusMessageEditText.getText().toString();
		int status = getStatusForService((String) mSpinner.getSelectedItem());
		Editor edit = mSettings.edit();
		edit.putString(BeemApplication.STATUS_TEXT_KEY, msg);
		if (status == Status.CONTACT_STATUS_DISCONNECT) {
		    stopService(new Intent(ChangeStatus.this, BeemService.class));
		} else {
		    try {
			mXmppFacade.changeStatus(status, msg.toString());
			edit.putInt(BeemApplication.STATUS_KEY, mSpinner.getSelectedItemPosition());
			publishAvatar();
		    } catch (RemoteException e) {
			e.printStackTrace();
		    }
		    mToast.show();
		}
		edit.commit();
		ChangeStatus.this.finish();
	    } else if (v == mClear) {
		mStatusMessageEditText.setText(null);
	    } else if (v == mContact) {
		startActivity(new Intent(ChangeStatus.this, ContactList.class));
		ChangeStatus.this.finish();
	    } else if (v == mAvatar)
		onAvatarButton(v);
	}
    }
}