Home | History | Annotate | Download | only in ui
      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.ui;
     19 
     20 import android.app.Dialog;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.res.Configuration;
     24 import android.nfc.NdefMessage;
     25 import android.nfc.NdefRecord;
     26 import android.nfc.NfcAdapter;
     27 import android.nfc.NfcEvent;
     28 import android.os.Bundle;
     29 import android.view.DragEvent;
     30 import android.view.KeyEvent;
     31 import android.view.Menu;
     32 import android.view.MenuItem;
     33 import android.view.MotionEvent;
     34 import android.view.View;
     35 import android.view.accessibility.AccessibilityManager;
     36 
     37 import com.android.mail.compose.ComposeActivity;
     38 import com.android.mail.providers.Folder;
     39 import com.android.mail.utils.StorageLowState;
     40 import com.android.mail.utils.Utils;
     41 
     42 import java.io.UnsupportedEncodingException;
     43 import java.net.URLEncoder;
     44 
     45 /**
     46  * This is the root activity container that holds the left navigation fragment
     47  * (usually a list of folders), and the main content fragment (either a
     48  * conversation list or a conversation view).
     49  */
     50 public class MailActivity extends AbstractMailActivity implements ControllableActivity {
     51     /**
     52      * The activity controller to which we delegate most Activity lifecycle events.
     53      */
     54     private ActivityController mController;
     55 
     56     private ViewMode mViewMode;
     57 
     58     private ToastBarOperation mPendingToastOp;
     59     private boolean mAccessibilityEnabled;
     60     private AccessibilityManager mAccessibilityManager;
     61 
     62     protected ConversationListHelper mConversationListHelper;
     63 
     64     /**
     65      * The account name currently in use. Used to construct the NFC mailto: message. This needs
     66      * to be static since the {@link ComposeActivity} needs to statically change the account name
     67      * and have the NFC message changed accordingly.
     68      */
     69     protected static String sAccountName = null;
     70 
     71     /**
     72      * Create an NFC message (in the NDEF: Nfc Data Exchange Format) to instruct the recepient to
     73      * send an email to the current account.
     74      */
     75     private static class NdefMessageMaker implements NfcAdapter.CreateNdefMessageCallback {
     76         @Override
     77         public NdefMessage createNdefMessage(NfcEvent event) {
     78             if (sAccountName == null) {
     79                 return null;
     80             }
     81             return getMailtoNdef(sAccountName);
     82         }
     83 
     84         /**
     85          * Returns an NDEF message with a single mailto URI record
     86          * for the given email address.
     87          */
     88         private static NdefMessage getMailtoNdef(String account) {
     89             byte[] accountBytes;
     90             try {
     91                 accountBytes = URLEncoder.encode(account, "UTF-8").getBytes("UTF-8");
     92             } catch (UnsupportedEncodingException e) {
     93                 accountBytes = account.getBytes();
     94             }
     95             byte prefix = 0x06; // mailto:
     96             byte[] recordBytes = new byte[accountBytes.length + 1];
     97             recordBytes[0] = prefix;
     98             System.arraycopy(accountBytes, 0, recordBytes, 1, accountBytes.length);
     99             NdefRecord mailto = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI,
    100                     new byte[0], recordBytes);
    101             return new NdefMessage(new NdefRecord[] { mailto });
    102         }
    103     }
    104 
    105     private final NdefMessageMaker mNdefHandler = new NdefMessageMaker();
    106 
    107     public MailActivity() {
    108         super();
    109         mConversationListHelper = new ConversationListHelper();
    110     }
    111 
    112     @Override
    113     public boolean dispatchTouchEvent(MotionEvent ev) {
    114         mController.onTouchEvent(ev);
    115         return super.dispatchTouchEvent(ev);
    116     }
    117 
    118     /**
    119      * Default implementation returns a null view mode.
    120      */
    121     @Override
    122     public ViewMode getViewMode() {
    123         return mViewMode;
    124     }
    125 
    126     @Override
    127     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    128         mController.onActivityResult(requestCode, resultCode, data);
    129     }
    130 
    131     @Override
    132     public void onBackPressed() {
    133         if (!mController.onBackPressed()) {
    134             super.onBackPressed();
    135         }
    136     }
    137 
    138     @Override
    139     public void onCreate(Bundle savedState) {
    140         super.onCreate(savedState);
    141 
    142         mViewMode = new ViewMode();
    143         final boolean tabletUi = Utils.useTabletUI(this.getResources());
    144         mController = ControllerFactory.forActivity(this, mViewMode, tabletUi);
    145         mController.onCreate(savedState);
    146 
    147         mAccessibilityManager =
    148                 (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
    149         mAccessibilityEnabled = mAccessibilityManager.isEnabled();
    150         final NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
    151         if (nfcAdapter != null) {
    152             nfcAdapter.setNdefPushMessageCallback(mNdefHandler, this);
    153         }
    154     }
    155 
    156     @Override
    157     protected void onPostCreate(Bundle savedInstanceState) {
    158         super.onPostCreate(savedInstanceState);
    159 
    160         mController.onPostCreate(savedInstanceState);
    161     }
    162 
    163     @Override
    164     public void onConfigurationChanged(Configuration newConfig) {
    165         super.onConfigurationChanged(newConfig);
    166         mController.onConfigurationChanged(newConfig);
    167     }
    168 
    169     @Override
    170     protected void onRestart() {
    171         super.onRestart();
    172         mController.onRestart();
    173     }
    174 
    175     /**
    176      * Constructs and sets the default NFC message. This message instructs the receiver to send
    177      * email to the account provided as the argument. This message is to be shared with
    178      * "zero-clicks" using NFC. The message will be available as long as the current activity is in
    179      * the foreground.
    180      *
    181      * @param account The email address to send mail to.
    182      */
    183     public static void setNfcMessage(String account) {
    184         sAccountName = account;
    185     }
    186 
    187     @Override
    188     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    189         super.onRestoreInstanceState(savedInstanceState);
    190         mController.onRestoreInstanceState(savedInstanceState);
    191     }
    192 
    193     @Override
    194     public Dialog onCreateDialog(int id, Bundle bundle) {
    195         final Dialog dialog = mController.onCreateDialog(id, bundle);
    196         return dialog == null ? super.onCreateDialog(id, bundle) : dialog;
    197     }
    198 
    199     @Override
    200     public boolean onCreateOptionsMenu(Menu menu) {
    201         return mController.onCreateOptionsMenu(menu) || super.onCreateOptionsMenu(menu);
    202     }
    203 
    204     @Override
    205     public boolean onKeyDown(int keyCode, KeyEvent event) {
    206         return mController.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
    207     }
    208 
    209     @Override
    210     public boolean onOptionsItemSelected(MenuItem item) {
    211         return mController.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
    212     }
    213 
    214     @Override
    215     public void onPause() {
    216         super.onPause();
    217         mController.onPause();
    218     }
    219 
    220     @Override
    221     public void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
    222         super.onPrepareDialog(id, dialog, bundle);
    223         mController.onPrepareDialog(id, dialog, bundle);
    224     }
    225 
    226     @Override
    227     public boolean onPrepareOptionsMenu(Menu menu) {
    228         mController.onPrepareOptionsMenu(menu);
    229         return super.onPrepareOptionsMenu(menu);
    230     }
    231 
    232     @Override
    233     public void onResume() {
    234         super.onResume();
    235         mController.onResume();
    236         final boolean enabled = mAccessibilityManager.isEnabled();
    237         if (enabled != mAccessibilityEnabled) {
    238             onAccessibilityStateChanged(enabled);
    239         }
    240         // App has resumed, re-check the top-level storage situation.
    241         StorageLowState.checkStorageLowMode(this);
    242     }
    243 
    244     @Override
    245     public void onSaveInstanceState(Bundle outState) {
    246         super.onSaveInstanceState(outState);
    247         mController.onSaveInstanceState(outState);
    248     }
    249 
    250     @Override
    251     protected void onStart() {
    252         super.onStart();
    253         mController.onStart();
    254     }
    255 
    256     @Override
    257     public boolean onSearchRequested() {
    258         mController.startSearch();
    259         return true;
    260     }
    261 
    262     @Override
    263     public void onStop() {
    264         super.onStop();
    265         mController.onStop();
    266     }
    267 
    268     @Override
    269     protected void onDestroy() {
    270         super.onDestroy();
    271         mController.onDestroy();
    272     }
    273 
    274     @Override
    275     public void onWindowFocusChanged(boolean hasFocus) {
    276         super.onWindowFocusChanged(hasFocus);
    277         mController.onWindowFocusChanged(hasFocus);
    278     }
    279 
    280     @Override
    281     public String toString() {
    282         final StringBuilder sb = new StringBuilder(super.toString());
    283         sb.append("{ViewMode=");
    284         sb.append(mViewMode);
    285         sb.append(" controller=");
    286         sb.append(mController);
    287         sb.append("}");
    288         return sb.toString();
    289     }
    290 
    291     @Override
    292     public ConversationListCallbacks getListHandler() {
    293         return mController;
    294     }
    295 
    296     @Override
    297     public FolderChangeListener getFolderChangeListener() {
    298         return mController;
    299     }
    300 
    301     @Override
    302     public FolderSelector getFolderSelector() {
    303         return mController;
    304     }
    305 
    306     @Override
    307     public FolderController getFolderController() {
    308         return mController;
    309     }
    310 
    311     @Override
    312     public ConversationSelectionSet getSelectedSet() {
    313         return mController.getSelectedSet();
    314     }
    315 
    316     @Override
    317     public boolean supportsDrag(DragEvent event, Folder folder) {
    318         return mController.supportsDrag(event, folder);
    319     }
    320 
    321     @Override
    322     public void handleDrop(DragEvent event, Folder folder) {
    323         mController.handleDrop(event, folder);
    324     }
    325 
    326     @Override
    327     public void onUndoAvailable(ToastBarOperation undoOp) {
    328         mController.onUndoAvailable(undoOp);
    329     }
    330 
    331     @Override
    332     public Folder getHierarchyFolder() {
    333         return mController.getHierarchyFolder();
    334     }
    335 
    336     @Override
    337     public ConversationUpdater getConversationUpdater() {
    338         return mController;
    339     }
    340 
    341     @Override
    342     public ErrorListener getErrorListener() {
    343         return mController;
    344     }
    345 
    346     @Override
    347     public void setPendingToastOperation(ToastBarOperation op) {
    348         mPendingToastOp = op;
    349     }
    350 
    351     @Override
    352     public ToastBarOperation getPendingToastOperation() {
    353         return mPendingToastOp;
    354     }
    355 
    356     /**
    357      * Sole purpose of this method is to stop clicks where we don't want them in
    358      * the action bar. This can be hooked as a listener to view with
    359      * android:onClick.
    360      *
    361      * @param v
    362      */
    363     public void doNothingClickHandler(View v) {
    364         // Do nothing.
    365     }
    366 
    367     @Override
    368     public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
    369         mController.onAnimationEnd(animatedAdapter);
    370     }
    371 
    372     @Override
    373     public AccountController getAccountController() {
    374         return mController;
    375     }
    376 
    377     @Override
    378     public RecentFolderController getRecentFolderController() {
    379         return mController;
    380     }
    381 
    382     @Override
    383     public UpOrBackController getUpOrBackController() {
    384         return mController;
    385     }
    386 
    387     @Override
    388     public void onFooterViewErrorActionClick(Folder folder, int errorStatus) {
    389         mController.onFooterViewErrorActionClick(folder, errorStatus);
    390     }
    391 
    392     @Override
    393     public void onFooterViewLoadMoreClick(Folder folder) {
    394         mController.onFooterViewLoadMoreClick(folder);
    395     }
    396 
    397     @Override
    398     public void startDragMode() {
    399         mController.startDragMode();
    400     }
    401 
    402     @Override
    403     public void stopDragMode() {
    404         mController.stopDragMode();
    405     }
    406 
    407     @Override
    408     public boolean isAccessibilityEnabled() {
    409         return mAccessibilityEnabled;
    410     }
    411 
    412     public void onAccessibilityStateChanged(boolean enabled) {
    413         mAccessibilityEnabled = enabled;
    414         mController.onAccessibilityStateChanged();
    415     }
    416 
    417     @Override
    418     public final ConversationListHelper getConversationListHelper() {
    419         return mConversationListHelper;
    420     }
    421 
    422     @Override
    423     public FragmentLauncher getFragmentLauncher() {
    424         return mController;
    425     }
    426 }
    427