Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2013 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.ui;
     19 
     20 import android.app.Activity;
     21 import android.content.ActivityNotFoundException;
     22 import android.content.ContentResolver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.pm.ActivityInfo;
     26 import android.content.pm.PackageManager;
     27 import android.content.pm.ResolveInfo;
     28 import android.database.Cursor;
     29 import android.net.Uri;
     30 import android.os.ParcelFileDescriptor;
     31 import android.provider.Browser;
     32 import android.webkit.WebResourceResponse;
     33 import android.webkit.WebView;
     34 import android.webkit.WebViewClient;
     35 
     36 import com.android.mail.browse.ConversationMessage;
     37 import com.android.mail.providers.Account;
     38 import com.android.mail.providers.Attachment;
     39 import com.android.mail.providers.UIProvider;
     40 import com.android.mail.utils.LogTag;
     41 import com.android.mail.utils.LogUtils;
     42 import com.android.mail.utils.Utils;
     43 
     44 import java.io.FileInputStream;
     45 import java.io.FileNotFoundException;
     46 import java.io.InputStream;
     47 import java.util.List;
     48 
     49 /**
     50  * Base implementation of a web view client for the conversation views.
     51  * Handles proxying the view intent so that additional information can
     52  * be sent with the intent when links are clicked.
     53  */
     54 public class AbstractConversationWebViewClient extends WebViewClient {
     55     private static final String LOG_TAG = LogTag.getLogTag();
     56 
     57     private Account mAccount;
     58     private Activity mActivity;
     59 
     60     public AbstractConversationWebViewClient(Account account) {
     61         mAccount = account;
     62     }
     63 
     64     public void setAccount(Account account) {
     65         mAccount = account;
     66     }
     67 
     68     public void setActivity(Activity activity) {
     69         mActivity = activity;
     70     }
     71 
     72     public Activity getActivity() {
     73         return mActivity;
     74     }
     75 
     76     /**
     77      * Translates Content ID urls (CID urls) into provider queries for the associated attachment.
     78      * With the attachment in hand, it's trivial to open a stream to the file containing the content
     79      * of the attachment.
     80      *
     81      * @param uri the raw URI from the HTML document in the Webview
     82      * @param message the message containing the HTML that is being rendered
     83      * @return a response if a stream to the attachment file can be created from the CID URL;
     84      *      <tt>null</tt> if it cannot for any reason
     85      */
     86     protected final WebResourceResponse loadCIDUri(Uri uri, ConversationMessage message) {
     87         // if the url is not a CID url, we do nothing
     88         if (!"cid".equals(uri.getScheme())) {
     89             return null;
     90         }
     91 
     92         // cid urls can be translated to content urls
     93         final String cid = uri.getSchemeSpecificPart();
     94         if (cid == null) {
     95             return null;
     96         }
     97 
     98         if (message.attachmentByCidUri == null) {
     99             return null;
    100         }
    101 
    102         final Uri queryUri = Uri.withAppendedPath(message.attachmentByCidUri, cid);
    103         if (queryUri == null) {
    104             return null;
    105         }
    106 
    107         // query for the attachment using its cid
    108         final ContentResolver cr = getActivity().getContentResolver();
    109         final Cursor c = cr.query(queryUri, UIProvider.ATTACHMENT_PROJECTION, null, null, null);
    110         if (c == null) {
    111             return null;
    112         }
    113 
    114         // create the attachment from the cursor, if one was found
    115         final Attachment target;
    116         try {
    117             if (!c.moveToFirst()) {
    118                 return null;
    119             }
    120             target = new Attachment(c);
    121         } finally {
    122             c.close();
    123         }
    124 
    125         // try to return a response that includes a stream to the attachment data
    126         try {
    127             final ParcelFileDescriptor fd = cr.openFileDescriptor(target.contentUri, "r");
    128             final InputStream stream = new FileInputStream(fd.getFileDescriptor());
    129             return new WebResourceResponse(target.getContentType(), null, stream);
    130         } catch (FileNotFoundException e) {
    131             // if no attachment file was found return null to let webview handle it
    132             return null;
    133         }
    134     }
    135 
    136     @Override
    137     public boolean shouldOverrideUrlLoading(WebView view, String url) {
    138         if (mActivity == null) {
    139             return false;
    140         }
    141 
    142         final Uri uri = Uri.parse(url);
    143         if (Utils.divertMailtoUri(mActivity, uri, mAccount)) {
    144             return true;
    145         }
    146 
    147         final Intent intent;
    148         if (mAccount != null && !Utils.isEmpty(mAccount.viewIntentProxyUri)) {
    149             intent = generateProxyIntent(uri);
    150         } else {
    151             intent = new Intent(Intent.ACTION_VIEW, uri);
    152             intent.putExtra(Browser.EXTRA_APPLICATION_ID, mActivity.getPackageName());
    153             intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
    154         }
    155 
    156         boolean result = false;
    157         try {
    158             intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
    159                     | Intent.FLAG_ACTIVITY_NO_ANIMATION);
    160             mActivity.startActivity(intent);
    161             result = true;
    162         } catch (ActivityNotFoundException ex) {
    163             // If no application can handle the URL, assume that the
    164             // caller can handle it.
    165         }
    166 
    167         return result;
    168     }
    169 
    170     private Intent generateProxyIntent(Uri uri) {
    171         return generateProxyIntent(
    172                 mActivity, mAccount.viewIntentProxyUri, uri, mAccount.getEmailAddress());
    173     }
    174 
    175     public static Intent generateProxyIntent(
    176             Context context, Uri proxyUri, Uri uri, String accountName) {
    177         final Intent intent = new Intent(Intent.ACTION_VIEW, proxyUri);
    178         intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ORIGINAL_URI, uri);
    179         intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ACCOUNT_NAME, accountName);
    180 
    181         PackageManager manager = null;
    182         // We need to catch the exception to make CanvasConversationHeaderView
    183         // test pass.  Bug: http://b/issue?id=3470653.
    184         try {
    185             manager = context.getPackageManager();
    186         } catch (UnsupportedOperationException e) {
    187             LogUtils.e(LOG_TAG, e, "Error getting package manager");
    188         }
    189 
    190         if (manager != null) {
    191             // Try and resolve the intent, to find an activity from this package
    192             final List<ResolveInfo> resolvedActivities = manager.queryIntentActivities(
    193                     intent, PackageManager.MATCH_DEFAULT_ONLY);
    194 
    195             final String packageName = context.getPackageName();
    196 
    197             // Now try and find one that came from this package, if one is not found, the UI
    198             // provider must have specified an intent that is to be handled by a different apk.
    199             // In that case, the class name will not be set on the intent, so the default
    200             // intent resolution will be used.
    201             for (ResolveInfo resolveInfo: resolvedActivities) {
    202                 final ActivityInfo activityInfo = resolveInfo.activityInfo;
    203                 if (packageName.equals(activityInfo.packageName)) {
    204                     intent.setClassName(activityInfo.packageName, activityInfo.name);
    205                     break;
    206                 }
    207             }
    208         }
    209 
    210         return intent;
    211     }
    212 }
    213