Initial checkin of AOSP Messaging app.

b/23110861

Change-Id: I9aa980d7569247d6b2ca78f5dcb4502e1eaadb8a
This commit is contained in:
Mike Dodd
2015-08-12 08:58:28 -07:00
parent 8b3e2b9c1b
commit 461a34b466
1645 changed files with 186271 additions and 0 deletions
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.messaging.ui.attachmentchooser;
import android.app.Fragment;
import android.os.Bundle;
import com.android.messaging.R;
import com.android.messaging.ui.BugleActionBarActivity;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.ui.attachmentchooser.AttachmentChooserFragment.AttachmentChooserFragmentHost;
import com.android.messaging.util.Assert;
public class AttachmentChooserActivity extends BugleActionBarActivity implements
AttachmentChooserFragmentHost {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.attachment_chooser_activity);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
@Override
public void onAttachFragment(final Fragment fragment) {
if (fragment instanceof AttachmentChooserFragment) {
final String conversationId =
getIntent().getStringExtra(UIIntents.UI_INTENT_EXTRA_CONVERSATION_ID);
Assert.notNull(conversationId);
final AttachmentChooserFragment chooserFragment =
(AttachmentChooserFragment) fragment;
chooserFragment.setConversationId(conversationId);
chooserFragment.setHost(this);
}
}
@Override
public void onConfirmSelection() {
setResult(RESULT_OK);
finish();
}
}
@@ -0,0 +1,183 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.messaging.ui.attachmentchooser;
import android.app.Fragment;
import android.content.Context;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import com.android.messaging.R;
import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.datamodel.binding.Binding;
import com.android.messaging.datamodel.binding.BindingBase;
import com.android.messaging.datamodel.data.DraftMessageData;
import com.android.messaging.datamodel.data.DraftMessageData.DraftMessageDataListener;
import com.android.messaging.datamodel.data.MessagePartData;
import com.android.messaging.ui.BugleActionBarActivity;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.ui.attachmentchooser.AttachmentGridView.AttachmentGridHost;
import com.google.common.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
public class AttachmentChooserFragment extends Fragment implements DraftMessageDataListener,
AttachmentGridHost {
public interface AttachmentChooserFragmentHost {
void onConfirmSelection();
}
private AttachmentGridView mAttachmentGridView;
private AttachmentGridAdapter mAdapter;
private AttachmentChooserFragmentHost mHost;
@VisibleForTesting
Binding<DraftMessageData> mBinding = BindingBase.createBinding(this);
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.attachment_chooser_fragment, container, false);
mAttachmentGridView = (AttachmentGridView) view.findViewById(R.id.grid);
mAdapter = new AttachmentGridAdapter(getActivity());
mAttachmentGridView.setAdapter(mAdapter);
mAttachmentGridView.setHost(this);
setHasOptionsMenu(true);
return view;
}
@Override
public void onDestroy() {
super.onDestroy();
mBinding.unbind();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.attachment_chooser_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_confirm_selection:
confirmSelection();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@VisibleForTesting
void confirmSelection() {
if (mBinding.isBound()) {
mBinding.getData().removeExistingAttachments(
mAttachmentGridView.getUnselectedAttachments());
mBinding.getData().saveToStorage(mBinding);
mHost.onConfirmSelection();
}
}
public void setConversationId(final String conversationId) {
mBinding.bind(DataModel.get().createDraftMessageData(conversationId));
mBinding.getData().addListener(this);
mBinding.getData().loadFromStorage(mBinding, null, false);
}
public void setHost(final AttachmentChooserFragmentHost host) {
mHost = host;
}
@Override
public void onDraftChanged(final DraftMessageData data, final int changeFlags) {
mBinding.ensureBound(data);
if ((changeFlags & DraftMessageData.ATTACHMENTS_CHANGED) ==
DraftMessageData.ATTACHMENTS_CHANGED) {
mAdapter.onAttachmentsLoaded(data.getReadOnlyAttachments());
}
}
@Override
public void onDraftAttachmentLimitReached(final DraftMessageData data) {
// Do nothing since the user is in the process of unselecting attachments.
}
@Override
public void onDraftAttachmentLoadFailed() {
// Do nothing since the user is in the process of unselecting attachments.
}
@Override
public void displayPhoto(final Rect viewRect, final Uri photoUri) {
final Uri imagesUri = MessagingContentProvider.buildDraftImagesUri(
mBinding.getData().getConversationId());
UIIntents.get().launchFullScreenPhotoViewer(
getActivity(), photoUri, viewRect, imagesUri);
}
@Override
public void updateSelectionCount(int count) {
if (getActivity() instanceof BugleActionBarActivity) {
final ActionBar actionBar = ((BugleActionBarActivity) getActivity())
.getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(getResources().getString(
R.string.attachment_chooser_selection, count));
}
}
}
class AttachmentGridAdapter extends ArrayAdapter<MessagePartData> {
public AttachmentGridAdapter(final Context context) {
super(context, R.layout.attachment_grid_item_view, new ArrayList<MessagePartData>());
}
public void onAttachmentsLoaded(final List<MessagePartData> attachments) {
clear();
addAll(attachments);
notifyDataSetChanged();
}
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
AttachmentGridItemView itemView;
final MessagePartData item = getItem(position);
if (convertView != null && convertView instanceof AttachmentGridItemView) {
itemView = (AttachmentGridItemView) convertView;
} else {
final LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
itemView = (AttachmentGridItemView) inflater.inflate(
R.layout.attachment_grid_item_view, parent, false);
}
itemView.bind(item, mAttachmentGridView);
return itemView;
}
}
}
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.messaging.ui.attachmentchooser;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.TouchDelegate;
import android.view.View;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import com.android.messaging.R;
import com.android.messaging.datamodel.data.MessagePartData;
import com.android.messaging.ui.AttachmentPreviewFactory;
import com.android.messaging.util.Assert;
import com.google.common.annotations.VisibleForTesting;
/**
* Shows an item in the attachment picker grid.
*/
public class AttachmentGridItemView extends FrameLayout {
public interface HostInterface {
boolean isItemSelected(MessagePartData attachment);
void onItemCheckedChanged(AttachmentGridItemView view, MessagePartData attachment);
void onItemClicked(AttachmentGridItemView view, MessagePartData attachment);
}
@VisibleForTesting
MessagePartData mAttachmentData;
private FrameLayout mAttachmentViewContainer;
private CheckBox mCheckBox;
private HostInterface mHostInterface;
public AttachmentGridItemView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mAttachmentViewContainer = (FrameLayout) findViewById(R.id.attachment_container);
mCheckBox = (CheckBox) findViewById(R.id.checkbox);
mCheckBox.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
mHostInterface.onItemCheckedChanged(AttachmentGridItemView.this, mAttachmentData);
}
});
setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
mHostInterface.onItemClicked(AttachmentGridItemView.this, mAttachmentData);
}
});
addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
// Enlarge the clickable region for the checkbox.
final int touchAreaIncrease = getResources().getDimensionPixelOffset(
R.dimen.attachment_grid_checkbox_area_increase);
final Rect region = new Rect();
mCheckBox.getHitRect(region);
region.inset(-touchAreaIncrease, -touchAreaIncrease);
setTouchDelegate(new TouchDelegate(region, mCheckBox));
}
});
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
// The grid view auto-fits the columns, so we want to let the height match the width
// to make the attachment preview square.
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
public void bind(final MessagePartData attachment, final HostInterface hostInterface) {
Assert.isTrue(attachment.isAttachment());
mHostInterface = hostInterface;
updateSelectedState();
if (mAttachmentData == null || !mAttachmentData.equals(attachment)) {
mAttachmentData = attachment;
updateAttachmentView();
}
}
@VisibleForTesting
HostInterface testGetHostInterface() {
return mHostInterface;
}
public void updateSelectedState() {
mCheckBox.setChecked(mHostInterface.isItemSelected(mAttachmentData));
}
private void updateAttachmentView() {
mAttachmentViewContainer.removeAllViews();
final LayoutInflater inflater = LayoutInflater.from(getContext());
final View attachmentView = AttachmentPreviewFactory.createAttachmentPreview(inflater,
mAttachmentData, mAttachmentViewContainer,
AttachmentPreviewFactory.TYPE_CHOOSER_GRID, true /* startImageRequest */, null);
mAttachmentViewContainer.addView(attachmentView);
}
}
@@ -0,0 +1,172 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.messaging.ui.attachmentchooser;
import android.content.Context;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.widget.GridView;
import com.android.messaging.datamodel.data.MessagePartData;
import com.android.messaging.ui.attachmentchooser.AttachmentChooserFragment.AttachmentGridAdapter;
import com.android.messaging.util.Assert;
import com.android.messaging.util.UiUtils;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Displays a grid of attachment previews for the user to choose which to select/unselect
*/
public class AttachmentGridView extends GridView implements
AttachmentGridItemView.HostInterface {
public interface AttachmentGridHost {
void displayPhoto(final Rect viewRect, final Uri photoUri);
void updateSelectionCount(final int count);
}
// By default everything is selected so only need to keep track of the unselected set.
private final Set<MessagePartData> mUnselectedSet;
private AttachmentGridHost mHost;
public AttachmentGridView(final Context context, final AttributeSet attrs) {
super(context, attrs);
mUnselectedSet = new HashSet<>();
}
public void setHost(final AttachmentGridHost host) {
mHost = host;
}
@Override
public boolean isItemSelected(final MessagePartData attachment) {
return !mUnselectedSet.contains(attachment);
}
@Override
public void onItemClicked(final AttachmentGridItemView view, final MessagePartData attachment) {
// If the item is an image, show the photo viewer. All the other types (video, audio,
// vcard) have internal click handling for showing previews so we don't need to handle them
if (attachment.isImage()) {
mHost.displayPhoto(UiUtils.getMeasuredBoundsOnScreen(view), attachment.getContentUri());
}
}
@Override
public void onItemCheckedChanged(AttachmentGridItemView view, MessagePartData attachment) {
// Toggle selection.
if (isItemSelected(attachment)) {
mUnselectedSet.add(attachment);
} else {
mUnselectedSet.remove(attachment);
}
view.updateSelectedState();
updateSelectionCount();
}
public Set<MessagePartData> getUnselectedAttachments() {
return Collections.unmodifiableSet(mUnselectedSet);
}
private void updateSelectionCount() {
final int count = getAdapter().getCount() - mUnselectedSet.size();
Assert.isTrue(count >= 0);
mHost.updateSelectionCount(count);
}
private void refreshViews() {
if (getAdapter() instanceof AttachmentGridAdapter) {
((AttachmentGridAdapter) getAdapter()).notifyDataSetChanged();
}
}
@Override
public Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
final SavedState savedState = new SavedState(superState);
savedState.unselectedParts = mUnselectedSet
.toArray(new MessagePartData[mUnselectedSet.size()]);
return savedState;
}
@Override
public void onRestoreInstanceState(final Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
final SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
mUnselectedSet.clear();
for (int i = 0; i < savedState.unselectedParts.length; i++) {
final MessagePartData unselectedPart = savedState.unselectedParts[i];
mUnselectedSet.add(unselectedPart);
}
refreshViews();
}
/**
* Persists the item selection state to saved instance state so we can restore on activity
* recreation
*/
public static class SavedState extends BaseSavedState {
MessagePartData[] unselectedParts;
SavedState(final Parcelable superState) {
super(superState);
}
private SavedState(final Parcel in) {
super(in);
// Read parts
final int partCount = in.readInt();
unselectedParts = new MessagePartData[partCount];
for (int i = 0; i < partCount; i++) {
unselectedParts[i] = ((MessagePartData) in.readParcelable(
MessagePartData.class.getClassLoader()));
}
}
@Override
public void writeToParcel(final Parcel out, final int flags) {
super.writeToParcel(out, flags);
// Write parts
out.writeInt(unselectedParts.length);
for (final MessagePartData image : unselectedParts) {
out.writeParcelable(image, flags);
}
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(final Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(final int size) {
return new SavedState[size];
}
};
}
}