1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.email.activity.setup;
18 
19 import android.app.ActionBar;
20 import android.app.Fragment;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.support.annotation.NonNull;
25 import android.view.KeyEvent;
26 import android.view.Menu;
27 import android.view.MenuItem;
28 
29 import com.android.email.R;
30 import com.android.email.setup.AuthenticatorSetupIntentHelper;
31 import com.android.emailcommon.utility.IntentUtilities;
32 import com.android.mail.providers.UIProvider.EditSettingsExtras;
33 import com.android.mail.ui.settings.MailPreferenceActivity;
34 import com.android.mail.utils.Utils;
35 
36 import java.util.List;
37 
38 /**
39  * Handles account preferences, using multi-pane arrangement when possible.
40  *
41  * This activity uses the following fragments:
42  *   AccountSettingsFragment
43  *   GeneralPreferences
44  *   DebugFragment
45  *
46  */
47 public class EmailPreferenceActivity extends MailPreferenceActivity {
48     /*
49      * Intent to open account settings for account=1
50         adb shell am start -a android.intent.action.EDIT \
51             -d '"content://ui.email.android.com/settings?ACCOUNT_ID=1"'
52      */
53 
54     // Intent extras for our internal activity launch
55     private static final String EXTRA_ENABLE_DEBUG = "AccountSettings.enable_debug";
56     // STOPSHIP: Do not ship with the debug menu allowed.
57     private static final boolean DEBUG_MENU_ALLOWED = false;
58 
59     // Intent extras for launch directly from system account manager
60     // NOTE: This string must match the one in res/xml/account_preferences.xml
61     private static String INTENT_ACCOUNT_MANAGER_ENTRY;
62 
63     // Key codes used to open a debug settings fragment.
64     private static final int[] SECRET_KEY_CODES = {
65             KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_B, KeyEvent.KEYCODE_U,
66             KeyEvent.KEYCODE_G
67             };
68     private int mSecretKeyCodeIndex = 0;
69 
70     // When the user taps "Email Preferences" 10 times in a row, we'll enable the debug settings.
71     private int mNumGeneralHeaderClicked = 0;
72 
73     private boolean mShowDebugMenu;
74     private Uri mFeedbackUri;
75     private MenuItem mFeedbackMenuItem;
76 
77     @Override
getIntent()78     public Intent getIntent() {
79         final Intent intent = super.getIntent();
80         final long accountId = IntentUtilities.getAccountIdFromIntent(intent);
81         if (accountId < 0) {
82             return intent;
83         }
84         Intent modIntent = new Intent(intent);
85         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, AccountSettingsFragment.class.getCanonicalName());
86         modIntent.putExtra(
87                 EXTRA_SHOW_FRAGMENT_ARGUMENTS,
88                 AccountSettingsFragment.buildArguments(
89                         IntentUtilities.getAccountNameFromIntent(intent)));
90         modIntent.putExtra(EXTRA_NO_HEADERS, true);
91         return modIntent;
92     }
93 
94     @Override
onCreate(Bundle savedInstanceState)95     public void onCreate(Bundle savedInstanceState) {
96         super.onCreate(savedInstanceState);
97 
98         final Intent i = getIntent();
99         if (savedInstanceState == null) {
100             // If we are not restarting from a previous instance, we need to
101             // figure out the initial prefs to show.  (Otherwise, we want to
102             // continue showing whatever the user last selected.)
103             if (INTENT_ACCOUNT_MANAGER_ENTRY == null) {
104                 INTENT_ACCOUNT_MANAGER_ENTRY = getString(R.string.intent_account_manager_entry);
105             }
106             if (INTENT_ACCOUNT_MANAGER_ENTRY.equals(i.getAction())) {
107                 // This case occurs if we're changing account settings from Settings -> Accounts.
108                 // We get an account object in the intent, but it's not actually useful to us since
109                 // it's always just the first account of that type. The user can't specify which
110                 // account they wish to view from within the settings UI, so just dump them at the
111                 // main screen.
112                 // android.accounts.Account acct = i.getParcelableExtra("account");
113             } else if (i.hasExtra(EditSettingsExtras.EXTRA_FOLDER)) {
114                 throw new IllegalArgumentException("EXTRA_FOLDER is no longer supported");
115             } else {
116                 // Otherwise, we're called from within the Email app and look for our extras
117                 final long accountId = IntentUtilities.getAccountIdFromIntent(i);
118                 if (accountId != -1) {
119                     final Bundle args = AccountSettingsFragment.buildArguments(accountId);
120                     startPreferencePanel(AccountSettingsFragment.class.getName(), args,
121                             0, null, null, 0);
122                 }
123             }
124         }
125         mShowDebugMenu = i.getBooleanExtra(EXTRA_ENABLE_DEBUG, false);
126 
127         final ActionBar actionBar = getActionBar();
128         if (actionBar != null) {
129             actionBar.setDisplayOptions(
130                     ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
131         }
132 
133         mFeedbackUri = Utils.getValidUri(getString(R.string.email_feedback_uri));
134     }
135 
136     /**
137      * Listen for secret sequence and, if heard, enable debug menu
138      */
139     @Override
onKeyDown(int keyCode, @NonNull KeyEvent event)140     public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
141         if (event.getKeyCode() == SECRET_KEY_CODES[mSecretKeyCodeIndex]) {
142             mSecretKeyCodeIndex++;
143             if (mSecretKeyCodeIndex == SECRET_KEY_CODES.length) {
144                 mSecretKeyCodeIndex = 0;
145                 enableDebugMenu();
146             }
147         } else {
148             mSecretKeyCodeIndex = 0;
149         }
150         return super.onKeyDown(keyCode, event);
151     }
152 
153     @Override
onCreateOptionsMenu(Menu menu)154     public boolean onCreateOptionsMenu(Menu menu) {
155         super.onCreateOptionsMenu(menu);
156         getMenuInflater().inflate(R.menu.settings_menu, menu);
157 
158         mFeedbackMenuItem = menu.findItem(R.id.feedback_menu_item);
159         return true;
160     }
161 
162     @Override
onPrepareOptionsMenu(Menu menu)163     public boolean onPrepareOptionsMenu(Menu menu) {
164         super.onPrepareOptionsMenu(menu);
165 
166         if (mFeedbackMenuItem != null) {
167             // We only want to enable the feedback menu item, if there is a valid feedback uri
168             mFeedbackMenuItem.setVisible(!Uri.EMPTY.equals(mFeedbackUri));
169         }
170         return true;
171     }
172 
173 
174     @Override
onOptionsItemSelected(MenuItem item)175     public boolean onOptionsItemSelected(MenuItem item) {
176         switch (item.getItemId()) {
177             case android.R.id.home:
178                 // The app icon on the action bar is pressed.  Just emulate a back press.
179                 // TODO: this should navigate to the main screen, even if a sub-setting is open.
180                 // But we shouldn't just finish(), as we want to show "discard changes?" dialog
181                 // when necessary.
182                 onBackPressed();
183                 break;
184             case R.id.feedback_menu_item:
185                 Utils.sendFeedback(this, mFeedbackUri, false /* reportingProblem */);
186                 break;
187             default:
188                 return super.onOptionsItemSelected(item);
189         }
190         return true;
191     }
192 
193     @Override
isValidFragment(String fragmentName)194     public boolean isValidFragment(String fragmentName) {
195         // This activity is not exported, so we can allow any fragment
196         return true;
197     }
198 
enableDebugMenu()199     private void enableDebugMenu() {
200         mShowDebugMenu = true;
201         invalidateHeaders();
202     }
203 
onAddNewAccount()204     private void onAddNewAccount() {
205         final Intent setupIntent = AuthenticatorSetupIntentHelper.actionNewAccountIntent(this);
206         startActivity(setupIntent);
207     }
208 
209     @Override
onBuildExtraHeaders(List<Header> target)210     public void onBuildExtraHeaders(List<Header> target) {
211         super.onBuildExtraHeaders(target);
212 
213         loadHeadersFromResource(R.xml.email_extra_preference_headers, target);
214 
215         // if debug header is enabled, show it
216         if (DEBUG_MENU_ALLOWED) {
217             if (mShowDebugMenu) {
218                 // setup lightweight header for debugging
219                 final Header debugHeader = new Header();
220                 debugHeader.title = getText(R.string.debug_title);
221                 debugHeader.summary = null;
222                 debugHeader.iconRes = 0;
223                 debugHeader.fragment = DebugFragment.class.getCanonicalName();
224                 debugHeader.fragmentArguments = null;
225                 target.add(debugHeader);
226             }
227         }
228     }
229 
230     /**
231      * Called when the user selects an item in the header list.  Handles save-data cases as needed
232      *
233      * @param header The header that was selected.
234      * @param position The header's position in the list.
235      */
236     @Override
onHeaderClick(@onNull Header header, int position)237     public void onHeaderClick(@NonNull Header header, int position) {
238         // Secret keys:  Click 10x to enable debug settings
239         if (position == 0) {
240             mNumGeneralHeaderClicked++;
241             if (mNumGeneralHeaderClicked == 10) {
242                 enableDebugMenu();
243             }
244         } else {
245             mNumGeneralHeaderClicked = 0;
246         }
247         if (header.id == R.id.add_account_header) {
248             onAddNewAccount();
249             return;
250         }
251 
252         // Process header click normally
253         super.onHeaderClick(header, position);
254     }
255 
256     @Override
onAttachFragment(Fragment f)257     public void onAttachFragment(Fragment f) {
258         super.onAttachFragment(f);
259         // When we're changing fragments, enable/disable the add account button
260         invalidateOptionsMenu();
261     }
262 }
263