Home | History | Annotate | Download | only in browse
      1 /*
      2  * Copyright (C) 2012 Google Inc.
      3  * Licensed to The Android Open Source Project.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.mail.browse;
     19 
     20 import android.app.FragmentManager;
     21 import android.content.ActivityNotFoundException;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.graphics.Bitmap;
     25 import android.util.AttributeSet;
     26 import android.view.LayoutInflater;
     27 import android.view.View;
     28 import android.view.View.OnClickListener;
     29 import android.view.ViewGroup;
     30 import android.view.ViewParent;
     31 
     32 import com.android.ex.photo.util.ImageUtils;
     33 import com.android.mail.R;
     34 import com.android.mail.analytics.Analytics;
     35 import com.android.mail.providers.Attachment;
     36 import com.android.mail.providers.UIProvider;
     37 import com.android.mail.providers.UIProvider.AttachmentDestination;
     38 import com.android.mail.providers.UIProvider.AttachmentRendition;
     39 import com.android.mail.ui.AttachmentTile;
     40 import com.android.mail.ui.AttachmentTileGrid;
     41 import com.android.mail.utils.AttachmentUtils;
     42 import com.android.mail.utils.LogTag;
     43 import com.android.mail.utils.LogUtils;
     44 import com.android.mail.utils.Utils;
     45 
     46 import java.util.Comparator;
     47 import java.util.PriorityQueue;
     48 
     49 /**
     50  * View for a single attachment in conversation view. Shows download status and allows launching
     51  * intents to act on an attachment.
     52  *
     53  */
     54 public class MessageAttachmentTile extends AttachmentTile implements OnClickListener,
     55         AttachmentViewInterface {
     56 
     57     private int mPhotoIndex;
     58     private View mTextContainer;
     59 
     60     private final AttachmentActionHandler mActionHandler;
     61 
     62     private PhotoViewHandler mPhotoViewHandler;
     63 
     64     private static final String LOG_TAG = LogTag.getLogTag();
     65 
     66     /**
     67      * Let someone else do this work, since it typically requires broader visibility of context,
     68      * like what other photos to also show alongside this one.
     69      */
     70     public interface PhotoViewHandler {
     71         void viewPhoto(MessageAttachmentTile source);
     72     }
     73 
     74     public MessageAttachmentTile(Context context) {
     75         this(context, null);
     76     }
     77 
     78     public MessageAttachmentTile(Context context, AttributeSet attrs) {
     79         super(context, attrs);
     80 
     81         mActionHandler = new AttachmentActionHandler(context, this);
     82     }
     83 
     84     public void initialize(FragmentManager fragmentManager) {
     85         mActionHandler.initialize(fragmentManager);
     86     }
     87 
     88     public void setPhotoViewHandler(PhotoViewHandler pvh) {
     89         mPhotoViewHandler = pvh;
     90     }
     91 
     92     /**
     93      * Render or update an attachment's view. This happens immediately upon instantiation, and
     94      * repeatedly as status updates stream in, so only properties with new or changed values will
     95      * cause sub-views to update.
     96      */
     97     public void render(Attachment attachment, int index,
     98             AttachmentPreviewCache attachmentPreviewCache, boolean loaderResult) {
     99         render(attachment, attachmentPreviewCache);
    100 
    101         mPhotoIndex = index;
    102 
    103         mActionHandler.setAttachment(mAttachment);
    104         mActionHandler.updateStatus(loaderResult);
    105     }
    106 
    107     public static MessageAttachmentTile inflate(LayoutInflater inflater, ViewGroup parent) {
    108         MessageAttachmentTile view = (MessageAttachmentTile) inflater.inflate(
    109                 R.layout.conversation_message_attachment_tile, parent, false);
    110         return view;
    111     }
    112 
    113 
    114     @Override
    115     protected void onFinishInflate() {
    116         super.onFinishInflate();
    117 
    118         mTextContainer = findViewById(R.id.attachment_tile_text_container);
    119 
    120         setOnClickListener(this);
    121     }
    122 
    123     @Override
    124     public void onClick(View v) {
    125         onClick();
    126     }
    127 
    128     private boolean onClick() {
    129         showAndDownloadAttachments();
    130         return true;
    131     }
    132 
    133     private void showAndDownloadAttachments() {
    134         // TODO: clean this up, it seems like it should live in AttachmentTileGrid since it keeps
    135         // inappropriately touching this view's peers
    136         AttachmentTileGrid tileGrid = ((AttachmentTileGrid) getParent());
    137         int childCount = tileGrid.getChildCount();
    138 
    139         PriorityQueue<MessageAttachmentTile> queue = new PriorityQueue<MessageAttachmentTile>(
    140                 childCount, new ViewIndexDistanceComparator(mPhotoIndex));
    141         for (int i = 0; i < childCount; i++) {
    142             MessageAttachmentTile tile = (MessageAttachmentTile) tileGrid.getChildAt(i);
    143             queue.add(tile);
    144         }
    145 
    146         // we want our downloads to have higher priority than the highest background downloads
    147         int maxAdditionalPriority = childCount;
    148         for (int i = 0; i < childCount; i++) {
    149             // higher priority tiles are returned first
    150             MessageAttachmentTile tile = queue.remove();
    151             tile.downloadAttachment(maxAdditionalPriority - i, i != 0);
    152         }
    153 
    154         viewAttachment();
    155     }
    156 
    157     public void downloadAttachment(int additionalPriority, boolean delayDownload) {
    158         if (!mAttachment.isPresentLocally()) {
    159             mActionHandler.startDownloadingAttachment(AttachmentDestination.CACHE,
    160                     UIProvider.AttachmentRendition.BEST, additionalPriority, delayDownload);
    161         }
    162     }
    163 
    164     @Override
    165     public void viewAttachment() {
    166         final String mime = Utils.normalizeMimeType(mAttachment.getContentType());
    167 
    168         Analytics.getInstance()
    169                 .sendEvent("view_attachment", mime, "attachment_tile", mAttachment.size);
    170 
    171         if (ImageUtils.isImageMimeType(mime)) {
    172             if (mPhotoViewHandler != null) {
    173                 mPhotoViewHandler.viewPhoto(this);
    174             } else {
    175                 LogUtils.e(LOG_TAG, "unable to view image attachment b/c handler is null");
    176             }
    177             return;
    178         }
    179 
    180         Intent intent = new Intent(Intent.ACTION_VIEW);
    181         intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
    182                 | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    183         Utils.setIntentDataAndTypeAndNormalize(
    184                 intent, mAttachment.contentUri, mime);
    185         try {
    186             getContext().startActivity(intent);
    187         } catch (ActivityNotFoundException e) {
    188             // couldn't find activity for View intent
    189             LogUtils.e(LOG_TAG, "Couldn't find Activity for intent", e);
    190         }
    191     }
    192 
    193     @Override
    194     public void updateProgress(boolean showDeterminateProgress) {
    195         // do not show progress for image tiles
    196     }
    197 
    198     @Override
    199     public void onUpdateStatus() {
    200     }
    201 
    202     @Override
    203     public void setThumbnailToDefault() {
    204         super.setThumbnailToDefault();
    205         mTextContainer.setVisibility(VISIBLE);
    206     }
    207 
    208     @Override
    209     public void setThumbnail(Bitmap result) {
    210         super.setThumbnail(result);
    211         mTextContainer.setVisibility(GONE);
    212     }
    213 
    214     @Override
    215     public void thumbnailLoadFailed() {
    216         super.thumbnailLoadFailed();
    217 
    218         if (AttachmentUtils.canDownloadAttachment(getContext(), null)) {
    219             // Download if there is network. This check prevents the attachment
    220             // download from failing and making the error toast show
    221             mActionHandler.startDownloadingAttachment(
    222                     AttachmentDestination.CACHE, AttachmentRendition.SIMPLE, 0, false);
    223         }
    224     }
    225 
    226     /**
    227      * Given two child views, figure out whose index is closest to the specified
    228      * index.
    229      */
    230     public static class ViewIndexDistanceComparator implements Comparator<View>{
    231         final private int mIndex;
    232         /**
    233          * @param index Compare based on each view's distance to this index
    234          */
    235         public ViewIndexDistanceComparator(int index) {
    236             mIndex = index;
    237         }
    238 
    239         @Override
    240         public int compare(View lhs, View rhs) {
    241             ViewParent parent = lhs.getParent();
    242             if (parent == rhs.getParent()) {
    243                 if (parent instanceof ViewGroup) {
    244                     ViewGroup p = (ViewGroup) parent;
    245                     int lhsIndex = p.indexOfChild(lhs);
    246                     int rhsIndex = p.indexOfChild(rhs);
    247                     int lhsDistance = Math.abs(mIndex - lhsIndex);
    248                     int rhsDistance = Math.abs(mIndex - rhsIndex);
    249                     // prefer shorter distance since they are the next ones to be swiped to
    250                     int result = lhsDistance - rhsDistance;
    251                     if (result == 0) {
    252                         // prefer higher index since they are to the right in the photoviewer
    253                         return rhsIndex - lhsIndex;
    254                     }
    255                     return result;
    256                 }
    257             }
    258             return 0;
    259         }
    260     }
    261 }
    262