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 
    104         // query for the attachment using its cid
    105         final ContentResolver cr = getActivity().getContentResolver();
    106         final Cursor c = cr.query(queryUri, UIProvider.ATTACHMENT_PROJECTION, null, null, null);
    107         if (c == null) {
    108             return null;
    109         }
    110 
    111         // create the attachment from the cursor, if one was found
    112         final Attachment target;
    113         try {
    114             if (!c.moveToFirst()) {
    115                 return null;
    116             }
    117             target = new Attachment(c);
    118         } finally {
    119             c.close();
    120         }
    121 
    122         // try to return a response that includes a stream to the attachment data
    123         try {
    124             final ParcelFileDescriptor fd = cr.openFileDescriptor(target.contentUri, "r");
    125             final InputStream stream = new FileInputStream(fd.getFileDescriptor());
    126             return new WebResourceResponse(target.getContentType(), null, stream);
    127         } catch (FileNotFoundException e) {
    128             // if no attachment file was found return null to let webview handle it
    129             return null;
    130         }
    131     }
    132 
    133     @Override
    134     public boolean shouldOverrideUrlLoading(WebView view, String url) {
    135         if (mActivity == null) {
    136             return false;
    137         }
    138 
    139         final Uri uri = Uri.parse(url);
    140         if (Utils.divertMailtoUri(mActivity, uri, mAccount)) {
    141             return true;
    142         }
    143 
    144         final Intent intent;
    145         if (mAccount != null && !Utils.isEmpty(mAccount.viewIntentProxyUri)) {
    146             intent = generateProxyIntent(uri);
    147         } else {
    148             intent = new Intent(Intent.ACTION_VIEW, uri);
    149             intent.putExtra(Browser.EXTRA_APPLICATION_ID, mActivity.getPackageName());
    150             intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
    151         }
    152 
    153         boolean result = false;
    154         try {
    155             intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
    156                     | Intent.FLAG_ACTIVITY_NO_ANIMATION);
    157             mActivity.startActivity(intent);
    158             result = true;
    159         } catch (ActivityNotFoundException ex) {
    160             // If no application can handle the URL, assume that the
    161             // caller can handle it.
    162         }
    163 
    164         return result;
    165     }
    166 
    167     private Intent generateProxyIntent(Uri uri) {
    168         return generateProxyIntent(
    169                 mActivity, mAccount.viewIntentProxyUri, uri, mAccount.getEmailAddress());
    170     }
    171 
    172     public static Intent generateProxyIntent(
    173             Context context, Uri proxyUri, Uri uri, String accountName) {
    174         final Intent intent = new Intent(Intent.ACTION_VIEW, proxyUri);
    175         intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ORIGINAL_URI, uri);
    176         intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ACCOUNT_NAME, accountName);
    177 
    178         PackageManager manager = null;
    179         // We need to catch the exception to make CanvasConversationHeaderView
    180         // test pass.  Bug: http://b/issue?id=3470653.
    181         try {
    182             manager = context.getPackageManager();
    183         } catch (UnsupportedOperationException e) {
    184             LogUtils.e(LOG_TAG, e, "Error getting package manager");
    185         }
    186 
    187         if (manager != null) {
    188             // Try and resolve the intent, to find an activity from this package
    189             final List<ResolveInfo> resolvedActivities = manager.queryIntentActivities(
    190                     intent, PackageManager.MATCH_DEFAULT_ONLY);
    191 
    192             final String packageName = context.getPackageName();
    193 
    194             // Now try and find one that came from this package, if one is not found, the UI
    195             // provider must have specified an intent that is to be handled by a different apk.
    196             // In that case, the class name will not be set on the intent, so the default
    197             // intent resolution will be used.
    198             for (ResolveInfo resolveInfo: resolvedActivities) {
    199                 final ActivityInfo activityInfo = resolveInfo.activityInfo;
    200                 if (packageName.equals(activityInfo.packageName)) {
    201                     intent.setClassName(activityInfo.packageName, activityInfo.name);
    202                     break;
    203                 }
    204             }
    205         }
    206 
    207         return intent;
    208     }
    209 }
    210