Initial checkin of AOSP Messaging app.
b/23110861 Change-Id: I9aa980d7569247d6b2ca78f5dcb4502e1eaadb8a
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.photoviewer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.support.rastermill.FrameSequenceDrawable;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
|
||||
import com.android.ex.photo.PhotoViewController;
|
||||
import com.android.ex.photo.loaders.PhotoBitmapLoaderInterface;
|
||||
import com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult;
|
||||
import com.android.messaging.datamodel.media.ImageRequestDescriptor;
|
||||
import com.android.messaging.datamodel.media.ImageResource;
|
||||
import com.android.messaging.datamodel.media.MediaRequest;
|
||||
import com.android.messaging.datamodel.media.MediaResourceManager;
|
||||
import com.android.messaging.datamodel.media.UriImageRequestDescriptor;
|
||||
import com.android.messaging.util.ImageUtils;
|
||||
|
||||
/**
|
||||
* Loader for the bitmap of a photo.
|
||||
*/
|
||||
public class BuglePhotoBitmapLoader extends AsyncTaskLoader<BitmapResult>
|
||||
implements PhotoBitmapLoaderInterface {
|
||||
private String mPhotoUri;
|
||||
private ImageResource mImageResource;
|
||||
// The drawable that is currently "in use" and being presented to the user. This drawable
|
||||
// should never exist without the image resource backing it.
|
||||
private Drawable mDrawable;
|
||||
|
||||
public BuglePhotoBitmapLoader(Context context, String photoUri) {
|
||||
super(context);
|
||||
mPhotoUri = photoUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPhotoUri(String photoUri) {
|
||||
mPhotoUri = photoUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitmapResult loadInBackground() {
|
||||
final BitmapResult result = new BitmapResult();
|
||||
final Context context = getContext();
|
||||
if (context != null && mPhotoUri != null) {
|
||||
final ImageRequestDescriptor descriptor =
|
||||
new UriImageRequestDescriptor(Uri.parse(mPhotoUri),
|
||||
PhotoViewController.sMaxPhotoSize, PhotoViewController.sMaxPhotoSize,
|
||||
true /* allowCompression */, false /* isStatic */,
|
||||
false /* cropToCircle */,
|
||||
ImageUtils.DEFAULT_CIRCLE_BACKGROUND_COLOR /* circleBackgroundColor */,
|
||||
ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */);
|
||||
final MediaRequest<ImageResource> imageRequest =
|
||||
descriptor.buildSyncMediaRequest(context);
|
||||
final ImageResource imageResource =
|
||||
MediaResourceManager.get().requestMediaResourceSync(imageRequest);
|
||||
if (imageResource != null) {
|
||||
setImageResource(imageResource);
|
||||
result.status = BitmapResult.STATUS_SUCCESS;
|
||||
result.drawable = mImageResource.getDrawable(context.getResources());
|
||||
} else {
|
||||
releaseImageResource();
|
||||
result.status = BitmapResult.STATUS_EXCEPTION;
|
||||
}
|
||||
} else {
|
||||
result.status = BitmapResult.STATUS_EXCEPTION;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is new data to deliver to the client. The super class will take care of
|
||||
* delivering it; the implementation here just adds a little more logic.
|
||||
*/
|
||||
@Override
|
||||
public void deliverResult(BitmapResult result) {
|
||||
final Drawable drawable = result != null ? result.drawable : null;
|
||||
if (isReset()) {
|
||||
// An async query came in while the loader is stopped. We don't need the result.
|
||||
releaseDrawable(drawable);
|
||||
return;
|
||||
}
|
||||
|
||||
// We are now going to display this drawable so set to mDrawable
|
||||
mDrawable = drawable;
|
||||
|
||||
if (isStarted()) {
|
||||
// If the Loader is currently started, we can immediately deliver its results.
|
||||
super.deliverResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to start the Loader.
|
||||
*/
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
if (mDrawable != null) {
|
||||
// If we currently have a result available, deliver it
|
||||
// immediately.
|
||||
final BitmapResult result = new BitmapResult();
|
||||
result.status = BitmapResult.STATUS_SUCCESS;
|
||||
result.drawable = mDrawable;
|
||||
deliverResult(result);
|
||||
}
|
||||
|
||||
if (takeContentChanged() || (mImageResource == null)) {
|
||||
// If the data has changed since the last time it was loaded
|
||||
// or is not currently available, start a load.
|
||||
forceLoad();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to stop the Loader.
|
||||
*/
|
||||
@Override
|
||||
protected void onStopLoading() {
|
||||
// Attempt to cancel the current load task if possible.
|
||||
cancelLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to cancel a load.
|
||||
*/
|
||||
@Override
|
||||
public void onCanceled(BitmapResult result) {
|
||||
super.onCanceled(result);
|
||||
|
||||
// At this point we can release the resources associated with 'drawable' if needed.
|
||||
if (result != null) {
|
||||
releaseDrawable(result.drawable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to completely reset the Loader.
|
||||
*/
|
||||
@Override
|
||||
protected void onReset() {
|
||||
super.onReset();
|
||||
|
||||
// Ensure the loader is stopped
|
||||
onStopLoading();
|
||||
|
||||
releaseImageResource();
|
||||
}
|
||||
|
||||
private void releaseDrawable(Drawable drawable) {
|
||||
if (drawable != null && drawable instanceof FrameSequenceDrawable
|
||||
&& !((FrameSequenceDrawable) drawable).isDestroyed()) {
|
||||
((FrameSequenceDrawable) drawable).destroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setImageResource(final ImageResource resource) {
|
||||
if (mImageResource != resource) {
|
||||
// Clear out any information for what is currently used
|
||||
releaseImageResource();
|
||||
mImageResource = resource;
|
||||
// No need to add ref since a ref is already reserved as a result of
|
||||
// requestMediaResourceSync.
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseImageResource() {
|
||||
// If we are getting rid of the imageResource backing the drawable, we must also
|
||||
// destroy the drawable before releasing it.
|
||||
releaseDrawable(mDrawable);
|
||||
mDrawable = null;
|
||||
|
||||
if (mImageResource != null) {
|
||||
mImageResource.release();
|
||||
}
|
||||
mImageResource = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.photoviewer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
|
||||
import com.android.ex.photo.adapters.PhotoPagerAdapter;
|
||||
import com.android.ex.photo.fragments.PhotoViewFragment;
|
||||
|
||||
public class BuglePhotoPageAdapter extends PhotoPagerAdapter {
|
||||
|
||||
public BuglePhotoPageAdapter(Context context, FragmentManager fm, Cursor c, float maxScale,
|
||||
boolean thumbsFullScreen) {
|
||||
super(context, fm, c, maxScale, thumbsFullScreen);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PhotoViewFragment createPhotoViewFragment(Intent intent, int position,
|
||||
boolean onlyShowSpinner) {
|
||||
return BuglePhotoViewFragment.newInstance(intent, position, onlyShowSpinner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.photoviewer;
|
||||
|
||||
import com.android.ex.photo.PhotoViewActivity;
|
||||
import com.android.ex.photo.PhotoViewController;
|
||||
|
||||
/**
|
||||
* Activity to display the conversation images in full-screen. Most of the customization is in
|
||||
* {@link BuglePhotoViewController}.
|
||||
*/
|
||||
public class BuglePhotoViewActivity extends PhotoViewActivity {
|
||||
@Override
|
||||
public PhotoViewController createController() {
|
||||
return new BuglePhotoViewController(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.photoviewer;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.ShareActionProvider;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.ex.photo.PhotoViewController;
|
||||
import com.android.ex.photo.adapters.PhotoPagerAdapter;
|
||||
import com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult;
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.datamodel.ConversationImagePartsView.PhotoViewQuery;
|
||||
import com.android.messaging.datamodel.MediaScratchFileProvider;
|
||||
import com.android.messaging.ui.conversation.ConversationFragment;
|
||||
import com.android.messaging.util.Dates;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.OsUtil;
|
||||
|
||||
/**
|
||||
* Customizations for the photoviewer to display conversation images in full screen.
|
||||
*/
|
||||
public class BuglePhotoViewController extends PhotoViewController {
|
||||
private ShareActionProvider mShareActionProvider;
|
||||
private MenuItem mShareItem;
|
||||
private MenuItem mSaveItem;
|
||||
|
||||
public BuglePhotoViewController(final ActivityInterface activity) {
|
||||
super(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<BitmapResult> onCreateBitmapLoader(
|
||||
final int id, final Bundle args, final String uri) {
|
||||
switch (id) {
|
||||
case BITMAP_LOADER_AVATAR:
|
||||
case BITMAP_LOADER_THUMBNAIL:
|
||||
case BITMAP_LOADER_PHOTO:
|
||||
return new BuglePhotoBitmapLoader(getActivity().getContext(), uri);
|
||||
default:
|
||||
LogUtil.e(LogUtil.BUGLE_TAG,
|
||||
"Photoviewer unable to open bitmap loader with unknown id: " + id);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActionBar() {
|
||||
final Cursor cursor = getCursorAtProperPosition();
|
||||
|
||||
if (mSaveItem == null || cursor == null) {
|
||||
// Load not finished, called from framework code before ready
|
||||
return;
|
||||
}
|
||||
// Show the name as the title
|
||||
mActionBarTitle = cursor.getString(PhotoViewQuery.INDEX_SENDER_FULL_NAME);
|
||||
if (TextUtils.isEmpty(mActionBarTitle)) {
|
||||
// If the name is not known, fall back to the phone number
|
||||
mActionBarTitle = cursor.getString(PhotoViewQuery.INDEX_DISPLAY_DESTINATION);
|
||||
}
|
||||
|
||||
// Show the timestamp as the subtitle
|
||||
final long receivedTimestamp = cursor.getLong(PhotoViewQuery.INDEX_RECEIVED_TIMESTAMP);
|
||||
mActionBarSubtitle = Dates.getMessageTimeString(receivedTimestamp).toString();
|
||||
|
||||
setActionBarTitles(getActivity().getActionBarInterface());
|
||||
mSaveItem.setVisible(!isTempFile());
|
||||
|
||||
updateShareActionProvider();
|
||||
}
|
||||
|
||||
private void updateShareActionProvider() {
|
||||
final PhotoPagerAdapter adapter = getAdapter();
|
||||
final Cursor cursor = getCursorAtProperPosition();
|
||||
if (mShareActionProvider == null || mShareItem == null || adapter == null ||
|
||||
cursor == null) {
|
||||
// Not enough stuff loaded to update the share action
|
||||
return;
|
||||
}
|
||||
final String photoUri = adapter.getPhotoUri(cursor);
|
||||
if (isTempFile()) {
|
||||
mShareItem.setVisible(false);
|
||||
return;
|
||||
}
|
||||
final String contentType = adapter.getContentType(cursor);
|
||||
|
||||
final Intent shareIntent = new Intent();
|
||||
shareIntent.setAction(Intent.ACTION_SEND);
|
||||
shareIntent.setType(contentType);
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(photoUri));
|
||||
mShareActionProvider.setShareIntent(shareIntent);
|
||||
mShareItem.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current photo is a temp file. A temp file can be deleted at any time, so
|
||||
* we need to disable share and save options because the file may no longer be there.
|
||||
*/
|
||||
private boolean isTempFile() {
|
||||
final Cursor cursor = getCursorAtProperPosition();
|
||||
final Uri photoUri = Uri.parse(getAdapter().getPhotoUri(cursor));
|
||||
return MediaScratchFileProvider.isMediaScratchSpaceUri(photoUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||
((Activity) getActivity()).getMenuInflater().inflate(R.menu.photo_view_menu, menu);
|
||||
|
||||
// Get the ShareActionProvider
|
||||
mShareItem = menu.findItem(R.id.action_share);
|
||||
mShareActionProvider = (ShareActionProvider) mShareItem.getActionProvider();
|
||||
updateShareActionProvider();
|
||||
|
||||
mSaveItem = menu.findItem(R.id.action_save);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(final Menu menu) {
|
||||
return !mIsEmpty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_save) {
|
||||
if (OsUtil.hasStoragePermission()) {
|
||||
final PhotoPagerAdapter adapter = getAdapter();
|
||||
final Cursor cursor = getCursorAtProperPosition();
|
||||
if (cursor == null) {
|
||||
final Context context = getActivity().getContext();
|
||||
final String error = context.getResources().getQuantityString(
|
||||
R.plurals.attachment_save_error, 1, 1);
|
||||
Toast.makeText(context, error, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
final String photoUri = adapter.getPhotoUri(cursor);
|
||||
new ConversationFragment.SaveAttachmentTask(((Activity) getActivity()),
|
||||
Uri.parse(photoUri), adapter.getContentType(cursor)).executeOnThreadPool();
|
||||
} else {
|
||||
((Activity)getActivity()).requestPermissions(
|
||||
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PhotoPagerAdapter createPhotoPagerAdapter(final Context context,
|
||||
final FragmentManager fm, final Cursor c, final float maxScale) {
|
||||
return new BuglePhotoPageAdapter(context, fm, c, maxScale, mDisplayThumbsFullScreen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.photoviewer;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.rastermill.FrameSequenceDrawable;
|
||||
import android.support.v4.content.Loader;
|
||||
|
||||
import com.android.ex.photo.PhotoViewCallbacks;
|
||||
import com.android.ex.photo.fragments.PhotoViewFragment;
|
||||
import com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult;
|
||||
|
||||
public class BuglePhotoViewFragment extends PhotoViewFragment {
|
||||
|
||||
/** Public no-arg constructor for allowing the framework to handle orientation changes */
|
||||
public BuglePhotoViewFragment() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public static PhotoViewFragment newInstance(Intent intent, int position,
|
||||
boolean onlyShowSpinner) {
|
||||
final PhotoViewFragment f = new BuglePhotoViewFragment();
|
||||
initializeArguments(intent, position, onlyShowSpinner, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<BitmapResult> loader, BitmapResult result) {
|
||||
super.onLoadFinished(loader, result);
|
||||
// Need to check for the first time when we load the photos
|
||||
if (PhotoViewCallbacks.BITMAP_LOADER_PHOTO == loader.getId()
|
||||
&& result.status == BitmapResult.STATUS_SUCCESS
|
||||
&& mCallback.isFragmentActive(this)) {
|
||||
startGif();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
startGif();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
stopGif();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewActivated() {
|
||||
super.onViewActivated();
|
||||
startGif();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetViews() {
|
||||
super.resetViews();
|
||||
stopGif();
|
||||
}
|
||||
|
||||
private void stopGif() {
|
||||
final Drawable drawable = getDrawable();
|
||||
if (drawable != null && drawable instanceof FrameSequenceDrawable) {
|
||||
((FrameSequenceDrawable) drawable).stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void startGif() {
|
||||
final Drawable drawable = getDrawable();
|
||||
if (drawable != null && drawable instanceof FrameSequenceDrawable) {
|
||||
((FrameSequenceDrawable) drawable).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user