diff options
Diffstat (limited to 'src/com')
92 files changed, 2693 insertions, 1803 deletions
diff --git a/src/com/android/settings/AdvancedSecuritySettings.java b/src/com/android/settings/AdvancedSecuritySettings.java deleted file mode 100644 index 3ddbf96..0000000 --- a/src/com/android/settings/AdvancedSecuritySettings.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.settings; - -import com.android.internal.widget.LockPatternUtils; - -import android.app.ListFragment; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Bundle; -import android.service.trust.TrustAgentService; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.CheckBox; -import android.widget.TextView; - -import java.util.List; - -public class AdvancedSecuritySettings extends ListFragment implements View.OnClickListener { - static final String TAG = "AdvancedSecuritySettings"; - - private static final String SERVICE_INTERFACE = TrustAgentService.SERVICE_INTERFACE; - - private final ArraySet<ComponentName> mActiveAgents = new ArraySet<ComponentName>(); - private final ArrayMap<ComponentName, AgentInfo> mAvailableAgents - = new ArrayMap<ComponentName, AgentInfo>(); - - private LockPatternUtils mLockPatternUtils; - - public static final class AgentInfo { - CharSequence label; - ComponentName component; // service that implements ITrustAgent - - @Override - public boolean equals(Object other) { - if (other instanceof AgentInfo) { - return component.equals(((AgentInfo)other).component); - } - return true; - } - - public int compareTo(AgentInfo other) { - return component.compareTo(other.component); - } - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - if (mLockPatternUtils == null) { - mLockPatternUtils = new LockPatternUtils( - container.getContext().getApplicationContext()); - } - setListAdapter(new AgentListAdapter()); - return inflater.inflate(R.layout.advanced_security_settings, container, false); - } - - @Override - public void onResume() { - super.onResume(); - updateList(); - } - - void updateList() { - Context context = getActivity(); - if (context == null) { - return; - } - - loadActiveAgents(); - - PackageManager pm = getActivity().getPackageManager(); - Intent trustAgentIntent = new Intent(SERVICE_INTERFACE); - List<ResolveInfo> resolveInfos = pm.queryIntentServices(trustAgentIntent, - PackageManager.GET_META_DATA); - - mAvailableAgents.clear(); - mAvailableAgents.ensureCapacity(resolveInfos.size()); - - for (ResolveInfo resolveInfo : resolveInfos) { - if (resolveInfo.serviceInfo == null) continue; - if (!TrustAgentUtils.checkProvidePermission(resolveInfo, pm)) continue; - ComponentName name = TrustAgentUtils.getComponentName(resolveInfo); - if (!mAvailableAgents.containsKey(name)) { - AgentInfo agentInfo = new AgentInfo(); - agentInfo.label = resolveInfo.loadLabel(pm); - agentInfo.component = name; - mAvailableAgents.put(name, agentInfo); - } - } - ((BaseAdapter) getListAdapter()).notifyDataSetChanged(); - } - - @Override - public void onClick(View view) { - ViewHolder h = (ViewHolder) view.getTag(); - - if (view.getId() == R.id.clickable) { - boolean wasActive = mActiveAgents.contains(h.agentInfo.component); - loadActiveAgents(); - if (!wasActive) { - mActiveAgents.add(h.agentInfo.component); - } else { - mActiveAgents.remove(h.agentInfo.component); - } - saveActiveAgents(); - ((BaseAdapter) getListAdapter()).notifyDataSetChanged(); - } - } - - private void loadActiveAgents() { - mActiveAgents.clear(); - List<ComponentName> activeTrustAgents = mLockPatternUtils.getEnabledTrustAgents(); - if (activeTrustAgents != null) { - mActiveAgents.addAll(activeTrustAgents); - } - } - - private void saveActiveAgents() { - mLockPatternUtils.setEnabledTrustAgents(mActiveAgents); - } - - static class ViewHolder { - TextView name; - CheckBox checkbox; - TextView description; - AgentInfo agentInfo; - View clickable; - } - - class AgentListAdapter extends BaseAdapter { - final LayoutInflater mInflater; - - AgentListAdapter() { - mInflater = (LayoutInflater) - getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - public boolean hasStableIds() { - return false; - } - - public int getCount() { - return mAvailableAgents.size(); - } - - public Object getItem(int position) { - return mAvailableAgents.valueAt(position); - } - - public long getItemId(int position) { - return position; - } - - public boolean areAllItemsEnabled() { - return false; - } - - public boolean isEnabled(int position) { - return true; - } - - public View getView(int position, View convertView, ViewGroup parent) { - View v; - if (convertView == null) { - v = newView(parent); - } else { - v = convertView; - } - bindView(v, position); - return v; - } - - public View newView(ViewGroup parent) { - View v = mInflater.inflate(R.layout.trust_agent_item, parent, false); - ViewHolder h = new ViewHolder(); - h.name = (TextView)v.findViewById(R.id.name); - h.checkbox = (CheckBox)v.findViewById(R.id.checkbox); - h.clickable = v.findViewById(R.id.clickable); - h.clickable.setOnClickListener(AdvancedSecuritySettings.this); - h.description = (TextView)v.findViewById(R.id.description); - v.setTag(h); - h.clickable.setTag(h); - return v; - } - - public void bindView(View view, int position) { - ViewHolder vh = (ViewHolder) view.getTag(); - AgentInfo item = mAvailableAgents.valueAt(position); - vh.name.setText(item.label); - vh.checkbox.setChecked(mActiveAgents.contains(item.component)); - vh.agentInfo = item; - } - } -} diff --git a/src/com/android/settings/AirplaneModeEnabler.java b/src/com/android/settings/AirplaneModeEnabler.java index d1c591e..4ce5198 100644 --- a/src/com/android/settings/AirplaneModeEnabler.java +++ b/src/com/android/settings/AirplaneModeEnabler.java @@ -25,6 +25,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.preference.CheckBoxPreference; import android.preference.Preference; +import android.preference.SwitchPreference; import android.provider.Settings; import com.android.internal.telephony.PhoneStateIntentReceiver; @@ -36,7 +37,7 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene private PhoneStateIntentReceiver mPhoneStateReceiver; - private final CheckBoxPreference mCheckBoxPref; + private final SwitchPreference mSwitchPref; private static final int EVENT_SERVICE_STATE_CHANGED = 3; @@ -58,10 +59,10 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene } }; - public AirplaneModeEnabler(Context context, CheckBoxPreference airplaneModeCheckBoxPreference) { + public AirplaneModeEnabler(Context context, SwitchPreference airplaneModeCheckBoxPreference) { mContext = context; - mCheckBoxPref = airplaneModeCheckBoxPreference; + mSwitchPref = airplaneModeCheckBoxPreference; airplaneModeCheckBoxPreference.setPersistent(false); @@ -71,10 +72,10 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene public void resume() { - mCheckBoxPref.setChecked(isAirplaneModeOn(mContext)); + mSwitchPref.setChecked(isAirplaneModeOn(mContext)); mPhoneStateReceiver.registerIntent(); - mCheckBoxPref.setOnPreferenceChangeListener(this); + mSwitchPref.setOnPreferenceChangeListener(this); mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, mAirplaneModeObserver); @@ -82,7 +83,7 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene public void pause() { mPhoneStateReceiver.unregisterIntent(); - mCheckBoxPref.setOnPreferenceChangeListener(null); + mSwitchPref.setOnPreferenceChangeListener(null); mContext.getContentResolver().unregisterContentObserver(mAirplaneModeObserver); } @@ -96,7 +97,7 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, enabling ? 1 : 0); // Update the UI to reflect system setting - mCheckBoxPref.setChecked(enabling); + mSwitchPref.setChecked(enabling); // Post the intent Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); @@ -113,7 +114,7 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene * - mobile does not send failure notification, fail on timeout. */ private void onAirplaneModeChanged() { - mCheckBoxPref.setChecked(isAirplaneModeOn(mContext)); + mSwitchPref.setChecked(isAirplaneModeOn(mContext)); } /** diff --git a/src/com/android/settings/AirplaneModeVoiceActivity.java b/src/com/android/settings/AirplaneModeVoiceActivity.java new file mode 100644 index 0000000..3ab0c37 --- /dev/null +++ b/src/com/android/settings/AirplaneModeVoiceActivity.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings; + +import android.content.Intent; +import android.provider.Settings; +import android.util.Log; + +/** + * Activity for modifying the {@link Settings.Global#AIRPLANE_MODE_ON AIRPLANE_MODE_ON} + * setting using the Voice Interaction API. + */ +public class AirplaneModeVoiceActivity extends VoiceSettingsActivity { + private static final String TAG = "AirplaneModeVoiceActivity"; + + protected void onVoiceSettingInteraction(Intent intent) { + if (intent.hasExtra(Settings.EXTRA_AIRPLANE_MODE_ENABLED)) { + boolean enabled = + intent.getBooleanExtra(Settings.EXTRA_AIRPLANE_MODE_ENABLED, false); + Settings.Global.putInt(getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, enabled ? 1 : 0); + } else { + Log.v(TAG, "Missing airplane mode extra"); + } + } +} diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java index 738d433..8cfee92 100644 --- a/src/com/android/settings/ApnEditor.java +++ b/src/com/android/settings/ApnEditor.java @@ -327,6 +327,13 @@ public class ApnEditor extends PreferenceActivity mMvnoType.setSummary( checkNull(mvnoDescription(mMvnoType.getValue()))); mMvnoMatchData.setSummary(checkNull(mMvnoMatchData.getText())); + // allow user to edit carrier_enabled for some APN + boolean ceEditable = getResources().getBoolean(R.bool.config_allow_edit_carrier_enabled); + if (ceEditable) { + mCarrierEnabled.setEnabled(true); + } else { + mCarrierEnabled.setEnabled(false); + } } /** @@ -571,6 +578,7 @@ public class ApnEditor extends PreferenceActivity values.put(Telephony.Carriers.MVNO_TYPE, checkNotSet(mMvnoType.getValue())); values.put(Telephony.Carriers.MVNO_MATCH_DATA, checkNotSet(mMvnoMatchData.getText())); + values.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled.isChecked() ? 1 : 0); getContentResolver().update(mUri, values, null, null); return true; @@ -664,6 +672,8 @@ public class ApnEditor extends PreferenceActivity if (pref != null) { if (pref.equals(mPassword)){ pref.setSummary(starify(sharedPreferences.getString(key, ""))); + } else if (pref.equals(mCarrierEnabled)) { + // do nothing } else { pref.setSummary(checkNull(sharedPreferences.getString(key, ""))); } diff --git a/src/com/android/settings/ApnSettings.java b/src/com/android/settings/ApnSettings.java index ef79f2b..d214df2 100644 --- a/src/com/android/settings/ApnSettings.java +++ b/src/com/android/settings/ApnSettings.java @@ -39,8 +39,12 @@ import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Telephony; import android.util.Log; +import android.view.LayoutInflater; import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; import android.widget.Toast; import com.android.internal.telephony.Phone; @@ -50,7 +54,7 @@ import com.android.internal.telephony.TelephonyProperties; import java.util.ArrayList; -public class ApnSettings extends PreferenceActivity implements +public class ApnSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener { static final String TAG = "ApnSettings"; @@ -121,33 +125,49 @@ public class ApnSettings extends PreferenceActivity implements } @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); mUm = (UserManager) getSystemService(Context.USER_SERVICE); + mMobileStateFilter = new IntentFilter( + TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); + + if (!mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { + setHasOptionsMenu(true); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + TextView empty = (TextView) getView().findViewById(android.R.id.empty); + if (empty != null) { + empty.setText(R.string.apn_settings_not_available); + getListView().setEmptyView(empty); + } + if (mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { mUnavailable = true; - setContentView(R.layout.apn_disallowed_preference_screen); + setPreferenceScreen(new PreferenceScreen(getActivity(), null)); return; } addPreferencesFromResource(R.xml.apn_settings); - getListView().setItemsCanFocus(true); - mMobileStateFilter = new IntentFilter( - TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); + getListView().setItemsCanFocus(true); } @Override - protected void onResume() { + public void onResume() { super.onResume(); if (mUnavailable) { return; } - registerReceiver(mMobileStateReceiver, mMobileStateFilter); + getActivity().registerReceiver(mMobileStateReceiver, mMobileStateFilter); if (!mRestoreDefaultApnMode) { fillList(); @@ -157,18 +177,18 @@ public class ApnSettings extends PreferenceActivity implements } @Override - protected void onPause() { + public void onPause() { super.onPause(); if (mUnavailable) { return; } - unregisterReceiver(mMobileStateReceiver); + getActivity().unregisterReceiver(mMobileStateReceiver); } @Override - protected void onDestroy() { + public void onDestroy() { super.onDestroy(); if (mRestoreDefaultApnThread != null) { @@ -199,7 +219,7 @@ public class ApnSettings extends PreferenceActivity implements String key = cursor.getString(ID_INDEX); String type = cursor.getString(TYPES_INDEX); - ApnPreference pref = new ApnPreference(this); + ApnPreference pref = new ApnPreference(getActivity()); pref.setKey(key); pref.setTitle(name); @@ -228,16 +248,18 @@ public class ApnSettings extends PreferenceActivity implements } @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(0, MENU_NEW, 0, - getResources().getString(R.string.menu_new)) - .setIcon(android.R.drawable.ic_menu_add) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - menu.add(0, MENU_RESTORE, 0, - getResources().getString(R.string.menu_restore)) - .setIcon(android.R.drawable.ic_menu_upload); - return true; + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (!mUnavailable) { + menu.add(0, MENU_NEW, 0, + getResources().getString(R.string.menu_new)) + .setIcon(android.R.drawable.ic_menu_add) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + menu.add(0, MENU_RESTORE, 0, + getResources().getString(R.string.menu_restore)) + .setIcon(android.R.drawable.ic_menu_upload); + } + + super.onCreateOptionsMenu(menu, inflater); } @Override @@ -329,9 +351,9 @@ public class ApnSettings extends PreferenceActivity implements fillList(); getPreferenceScreen().setEnabled(true); mRestoreDefaultApnMode = false; - dismissDialog(DIALOG_RESTORE_DEFAULTAPN); + removeDialog(DIALOG_RESTORE_DEFAULTAPN); Toast.makeText( - ApnSettings.this, + getActivity(), getResources().getString( R.string.restore_default_apn_completed), Toast.LENGTH_LONG).show(); @@ -362,20 +384,13 @@ public class ApnSettings extends PreferenceActivity implements } @Override - protected Dialog onCreateDialog(int id) { + public Dialog onCreateDialog(int id) { if (id == DIALOG_RESTORE_DEFAULTAPN) { - ProgressDialog dialog = new ProgressDialog(this); + ProgressDialog dialog = new ProgressDialog(getActivity()); dialog.setMessage(getResources().getString(R.string.restore_default_apn)); dialog.setCancelable(false); return dialog; } return null; } - - @Override - protected void onPrepareDialog(int id, Dialog dialog) { - if (id == DIALOG_RESTORE_DEFAULTAPN) { - getPreferenceScreen().setEnabled(false); - } - } } diff --git a/src/com/android/settings/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/ConfirmDeviceCredentialActivity.java index 6a5c486..6b2bfd2 100644 --- a/src/com/android/settings/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/ConfirmDeviceCredentialActivity.java @@ -18,6 +18,7 @@ package com.android.settings; import android.app.Activity; +import android.app.KeyguardManager; import android.content.Intent; import android.os.Bundle; import android.util.Log; @@ -34,8 +35,8 @@ public class ConfirmDeviceCredentialActivity extends Activity { super.onCreate(savedInstanceState); Intent intent = getIntent(); - String title = intent.getStringExtra(Intent.EXTRA_TITLE); - String details = intent.getStringExtra(Intent.EXTRA_DETAILS); + String title = intent.getStringExtra(KeyguardManager.EXTRA_TITLE); + String details = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION); ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this); if (!helper.launchConfirmationActivity(0 /* request code */, title, details)) { diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java index 129b201..7e92cc6 100644 --- a/src/com/android/settings/CryptKeeper.java +++ b/src/com/android/settings/CryptKeeper.java @@ -37,9 +37,8 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.IMountService; import android.os.storage.StorageManager; - -import android.phone.PhoneManager; import android.provider.Settings; +import android.telecomm.TelecommManager; import android.telephony.TelephonyManager; import android.text.Editable; import android.text.TextUtils; @@ -118,6 +117,8 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList private boolean mValidationRequested; /** A flag to indicate that the volume is in a bad state (e.g. partially encrypted). */ private boolean mEncryptionGoneBad; + /** If gone bad, should we show encryption failed (false) or corrupt (true)*/ + private boolean mCorrupt; /** A flag to indicate when the back event should be ignored */ private boolean mIgnoreBack = false; private int mCooldown; @@ -133,6 +134,9 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList // how long we wait to clear a wrong pattern private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 1500; + // how long we wait to clear a right pattern + private static final int RIGHT_PATTERN_CLEAR_TIMEOUT_MS = 500; + private Runnable mClearPatternRunnable = new Runnable() { public void run() { mLockPatternView.clearPattern(); @@ -167,9 +171,18 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList if (failedAttempts == 0) { // The password was entered successfully. Simply do nothing // and wait for the service restart to switch to surfacefligner + if (mLockPatternView != null) { + mLockPatternView.removeCallbacks(mClearPatternRunnable); + mLockPatternView.postDelayed(mClearPatternRunnable, RIGHT_PATTERN_CLEAR_TIMEOUT_MS); + } } else if (failedAttempts == MAX_FAILED_ATTEMPTS) { // Factory reset the device. sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR")); + } else if (failedAttempts == -1) { + // Right password, but decryption failed. Tell user bad news ... + setContentView(R.layout.crypt_keeper_progress); + showFactoryReset(true); + return; } else { // Wrong entry. Handle pattern case. if (mLockPatternView != null) { @@ -213,12 +226,14 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList } private class ValidationTask extends AsyncTask<Void, Void, Boolean> { + int state; + @Override protected Boolean doInBackground(Void... params) { final IMountService service = getMountService(); try { Log.d(TAG, "Validating encryption state."); - int state = service.getEncryptionState(); + state = service.getEncryptionState(); if (state == IMountService.ENCRYPTION_STATE_NONE) { Log.w(TAG, "Unexpectedly in CryptKeeper even though there is no encryption."); return true; // Unexpected, but fine, I guess... @@ -236,6 +251,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList if (Boolean.FALSE.equals(result)) { Log.w(TAG, "Incomplete, or corrupted encryption detected. Prompting user to wipe."); mEncryptionGoneBad = true; + mCorrupt = state == IMountService.ENCRYPTION_STATE_ERROR_CORRUPT; } else { Log.d(TAG, "Encryption state validated. Proceeding to configure UI"); } @@ -392,7 +408,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList private void setupUi() { if (mEncryptionGoneBad || isDebugView(FORCE_VIEW_ERROR)) { setContentView(R.layout.crypt_keeper_progress); - showFactoryReset(); + showFactoryReset(mCorrupt); return; } @@ -404,6 +420,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList new AsyncTask<Void, Void, Void>() { int type = StorageManager.CRYPT_TYPE_PASSWORD; String owner_info; + boolean pattern_visible; @Override public Void doInBackground(Void... v) { @@ -411,6 +428,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList final IMountService service = getMountService(); type = service.getPasswordType(); owner_info = service.getField("OwnerInfo"); + pattern_visible = !("0".equals(service.getField("PatternVisible"))); } catch (Exception e) { Log.e(TAG, "Error calling mount service " + e); } @@ -440,6 +458,10 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList passwordEntryInit(); + if (mLockPatternView != null) { + mLockPatternView.setInStealthMode(!pattern_visible); + } + if (mCooldown > 0) { setBackFunctionality(false); cooldown(); // in case we are cooling down and coming back from emergency dialler @@ -508,7 +530,13 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList updateProgress(); } - private void showFactoryReset() { + /** + * Show factory reset screen allowing the user to reset their phone when + * there is nothing else we can do + * @param corrupt true if userdata is corrupt, false if encryption failed + * partway through + */ + private void showFactoryReset(boolean corrupt) { // Hide the encryption-bot to make room for the "factory reset" button findViewById(R.id.encroid).setVisibility(View.GONE); @@ -524,8 +552,13 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList }); // Alert the user of the failure. - ((TextView) findViewById(R.id.title)).setText(R.string.crypt_keeper_failed_title); - ((TextView) findViewById(R.id.status)).setText(R.string.crypt_keeper_failed_summary); + if (corrupt) { + ((TextView) findViewById(R.id.title)).setText(R.string.crypt_keeper_data_corrupt_title); + ((TextView) findViewById(R.id.status)).setText(R.string.crypt_keeper_data_corrupt_summary); + } else { + ((TextView) findViewById(R.id.title)).setText(R.string.crypt_keeper_failed_title); + ((TextView) findViewById(R.id.status)).setText(R.string.crypt_keeper_failed_summary); + } final View view = findViewById(R.id.bottom_divider); // TODO(viki): Why would the bottom divider be missing in certain layouts? Investigate. @@ -538,7 +571,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList final String state = SystemProperties.get("vold.encrypt_progress"); if ("error_partially_encrypted".equals(state)) { - showFactoryReset(); + showFactoryReset(false); return; } @@ -859,7 +892,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList } int textId; - if (getPhoneManager().isInAPhoneCall()) { + if (getTelecommManager().isInCall()) { // Show "return to call" textId = R.string.cryptkeeper_return_to_call; } else { @@ -873,9 +906,9 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList } private void takeEmergencyCallAction() { - PhoneManager phoneManager = getPhoneManager(); - if (phoneManager.isInAPhoneCall()) { - phoneManager.showCallScreen(false /* showDialpad */); + TelecommManager telecommManager = getTelecommManager(); + if (telecommManager.isInCall()) { + telecommManager.showInCallScreen(false /* showDialpad */); } else { launchEmergencyDialer(); } @@ -894,8 +927,8 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList return (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); } - private PhoneManager getPhoneManager() { - return (PhoneManager) getSystemService(Context.PHONE_SERVICE); + private TelecommManager getTelecommManager() { + return (TelecommManager) getSystemService(Context.TELECOMM_SERVICE); } /** diff --git a/src/com/android/settings/CryptKeeperConfirm.java b/src/com/android/settings/CryptKeeperConfirm.java index 71d5e96..7641525 100644 --- a/src/com/android/settings/CryptKeeperConfirm.java +++ b/src/com/android/settings/CryptKeeperConfirm.java @@ -25,6 +25,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.ServiceManager; +import android.os.UserHandle; import android.os.storage.IMountService; import android.util.Log; import android.view.LayoutInflater; @@ -32,6 +33,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; +import com.android.internal.widget.LockPatternUtils; + public class CryptKeeperConfirm extends Fragment { public static class Blank extends Activity { @@ -90,6 +93,27 @@ public class CryptKeeperConfirm extends Fragment { return; } + /* WARNING - nasty hack! + Settings for the lock screen are not available to the crypto + screen (CryptKeeper) at boot. We duplicate the ones that + CryptKeeper needs to the crypto key/value store when they are + modified (see LockPatternUtils). + However, prior to encryption, the crypto key/value store is not + persisted across reboots, thus modified settings are lost to + CryptKeeper. + In order to make sure CryptKeeper had the correct settings after + first encrypting, we thus need to rewrite them, which ensures the + crypto key/value store is up to date. On encryption, this store + is then persisted, and the settings will be there on future + reboots. + */ + LockPatternUtils utils = new LockPatternUtils(getActivity()); + utils.setVisiblePatternEnabled(utils.isVisiblePatternEnabled()); + if (utils.isOwnerInfoEnabled()) { + utils.setOwnerInfo(utils.getOwnerInfo(UserHandle.USER_OWNER), + UserHandle.USER_OWNER); + } + Intent intent = new Intent(getActivity(), Blank.class); intent.putExtras(getArguments()); diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index f7a0618..17c6d88 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -818,14 +818,21 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable View title = null; if (detail.detailLabels != null) { - for (CharSequence label : detail.detailLabels) { + final int n = detail.detailLabels.length; + for (int i = 0; i < n; ++i) { + CharSequence label = detail.detailLabels[i]; + CharSequence contentDescription = detail.detailContentDescriptions[i]; title = inflater.inflate(R.layout.data_usage_app_title, mAppTitles, false); - ((TextView) title.findViewById(R.id.app_title)).setText(label); + TextView appTitle = (TextView) title.findViewById(R.id.app_title); + appTitle.setText(label); + appTitle.setContentDescription(contentDescription); mAppTitles.addView(title); } } else { title = inflater.inflate(R.layout.data_usage_app_title, mAppTitles, false); - ((TextView) title.findViewById(R.id.app_title)).setText(detail.label); + TextView appTitle = (TextView) title.findViewById(R.id.app_title); + appTitle.setText(detail.label); + appTitle.setContentDescription(detail.contentDescription); mAppTitles.addView(title); } @@ -1786,10 +1793,12 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable public static void show(DataUsageSummary parent) { if (!parent.isAdded()) return; + final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate); + if (policy == null) return; + final Resources res = parent.getResources(); final CharSequence message; - final long minLimitBytes = (long) ( - parent.mPolicyEditor.getPolicy(parent.mTemplate).warningBytes * 1.2f); + final long minLimitBytes = (long) (policy.warningBytes * 1.2f); final long limitBytes; // TODO: customize default limits based on network template @@ -2224,6 +2233,7 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable if (detail != null) { icon.setImageDrawable(detail.icon); title.setText(detail.label); + title.setContentDescription(detail.contentDescription); } else { icon.setImageDrawable(null); title.setText(null); diff --git a/src/com/android/settings/DateTimeSettings.java b/src/com/android/settings/DateTimeSettings.java index 77561bd..8eb9c52 100644 --- a/src/com/android/settings/DateTimeSettings.java +++ b/src/com/android/settings/DateTimeSettings.java @@ -16,6 +16,7 @@ package com.android.settings; +import android.app.admin.DevicePolicyManager; import android.app.Activity; import android.app.AlarmManager; import android.app.DatePickerDialog; @@ -89,12 +90,23 @@ public class DateTimeSettings extends SettingsPreferenceFragment boolean autoTimeEnabled = getAutoState(Settings.Global.AUTO_TIME); boolean autoTimeZoneEnabled = getAutoState(Settings.Global.AUTO_TIME_ZONE); + mAutoTimePref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME); + + DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context + .DEVICE_POLICY_SERVICE); + if (dpm.getAutoTimeRequired()) { + mAutoTimePref.setEnabled(false); + + // If Settings.Global.AUTO_TIME is false it will be set to true + // by the device policy manager very soon. + // Note that this app listens to that change. + } + Intent intent = getActivity().getIntent(); boolean isFirstRun = intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); mDummyDate = Calendar.getInstance(); - mAutoTimePref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME); mAutoTimePref.setChecked(autoTimeEnabled); mAutoTimeZonePref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME_ZONE); // Override auto-timezone if it's a wifi-only device or if we're still in setup wizard. diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java index ba8e2f1..d801ae0 100644 --- a/src/com/android/settings/DevelopmentSettings.java +++ b/src/com/android/settings/DevelopmentSettings.java @@ -31,7 +31,9 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.database.Cursor; import android.hardware.usb.IUsbManager; +import android.net.Uri; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.BatteryManager; @@ -110,6 +112,7 @@ public class DevelopmentSettings extends SettingsPreferenceFragment private static final String DEBUG_APP_KEY = "debug_app"; private static final String WAIT_FOR_DEBUGGER_KEY = "wait_for_debugger"; private static final String VERIFY_APPS_OVER_USB_KEY = "verify_apps_over_usb"; + private static final String DEBUG_VIEW_ATTRIBUTES = "debug_view_attributes"; private static final String STRICT_MODE_KEY = "strict_mode"; private static final String POINTER_LOCATION_KEY = "pointer_location"; private static final String SHOW_TOUCHES_KEY = "show_touches"; @@ -117,6 +120,7 @@ public class DevelopmentSettings extends SettingsPreferenceFragment private static final String DISABLE_OVERLAYS_KEY = "disable_overlays"; private static final String SIMULATE_COLOR_SPACE = "simulate_color_space"; private static final String USE_NUPLAYER_KEY = "use_nuplayer"; + private static final String USB_AUDIO_KEY = "usb_audio"; private static final String USE_AWESOMEPLAYER_PROPERTY = "persist.sys.media.use-awesome"; private static final String SHOW_CPU_USAGE_KEY = "show_cpu_usage"; private static final String FORCE_HARDWARE_UI_KEY = "force_hw_ui"; @@ -151,6 +155,9 @@ public class DevelopmentSettings extends SettingsPreferenceFragment private static final String SHOW_ALL_ANRS_KEY = "show_all_anrs"; private static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY = "webview_data_reduction_proxy"; + // GoogleSetting name for the data reduction proxy setting. + // Setting type: int ( 0 = disallow, 1 = allow ) + private static final String WEBVIEW_DATA_REDUCTION_PROXY = "use_webview_data_reduction_proxy"; private static final String PROCESS_STATS = "proc_stats"; @@ -164,6 +171,11 @@ public class DevelopmentSettings extends SettingsPreferenceFragment private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; + // The setting Uri. Used when querying GoogleSettings. + private static final Uri GOOGLE_SETTINGS_CONTENT_URI = Uri.parse("content://com.google.settings/partner"); + private static final String GOOGLE_SETTINGS_COMPONENT = "com.google.android.gms"; + private static final String GOOGLE_SETTINGS_ACTIVITY = ".app.settings.GoogleSettingsActivity"; + private static String DEFAULT_LOG_RING_BUFFER_SIZE_IN_BYTES = "262144"; // 256K private IWindowManager mWindowManager; @@ -186,6 +198,7 @@ public class DevelopmentSettings extends SettingsPreferenceFragment private CheckBoxPreference mBtHciSnoopLog; private CheckBoxPreference mEnableOemUnlock; private CheckBoxPreference mAllowMockLocation; + private CheckBoxPreference mDebugViewAttributes; private PreferenceScreen mPassword; private String mDebugApp; @@ -222,6 +235,7 @@ public class DevelopmentSettings extends SettingsPreferenceFragment private ListPreference mSimulateColorSpace; private CheckBoxPreference mUseNuplayer; + private CheckBoxPreference mUSBAudio; private CheckBoxPreference mImmediatelyDestroyActivities; private ListPreference mAppProcessLimit; @@ -293,6 +307,7 @@ public class DevelopmentSettings extends SettingsPreferenceFragment mEnableOemUnlock = null; } mAllowMockLocation = findAndInitCheckboxPref(ALLOW_MOCK_LOCATION); + mDebugViewAttributes = findAndInitCheckboxPref(DEBUG_VIEW_ATTRIBUTES); mPassword = (PreferenceScreen) findPreference(LOCAL_BACKUP_PASSWORD); mAllPrefs.add(mPassword); @@ -343,6 +358,7 @@ public class DevelopmentSettings extends SettingsPreferenceFragment mOpenGLTraces = addListPreference(OPENGL_TRACES_KEY); mSimulateColorSpace = addListPreference(SIMULATE_COLOR_SPACE); mUseNuplayer = findAndInitCheckboxPref(USE_NUPLAYER_KEY); + mUSBAudio = findAndInitCheckboxPref(USB_AUDIO_KEY); mImmediatelyDestroyActivities = (CheckBoxPreference) findPreference( IMMEDIATELY_DESTROY_ACTIVITIES_KEY); @@ -366,12 +382,13 @@ public class DevelopmentSettings extends SettingsPreferenceFragment mAllPrefs.add(mProcessStats); mWebViewDataReductionProxy = findAndInitCheckboxPref(WEBVIEW_DATA_REDUCTION_PROXY_KEY); - String key = Settings.Global.getString(getActivity().getContentResolver(), - Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY); - // Disable the selection if the key is not available for some reason. - if (key == null || key.isEmpty()) { - disableForUser(mWebViewDataReductionProxy); - } + mWebViewDataReductionProxy.setOnPreferenceChangeListener( + new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + return handleDataReductionProxyPreferenceChange(); + } + }); } private ListPreference addListPreference(String prefKey) { @@ -516,6 +533,8 @@ public class DevelopmentSettings extends SettingsPreferenceFragment } updateCheckBox(mAllowMockLocation, Settings.Secure.getInt(cr, Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0); + updateCheckBox(mDebugViewAttributes, Settings.Global.getInt(cr, + Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0); updateHdcpValues(); updatePasswordSummary(); updateDebuggerOptions(); @@ -549,6 +568,7 @@ public class DevelopmentSettings extends SettingsPreferenceFragment updateWifiAllowScansWithTrafficOptions(); updateSimulateColorSpace(); updateUseNuplayerOptions(); + updateUSBAudioOptions(); } private void resetDangerousOptions() { @@ -1013,6 +1033,17 @@ public class DevelopmentSettings extends SettingsPreferenceFragment pokeSystemProperties(); } + private void updateUSBAudioOptions() { + updateCheckBox(mUSBAudio, Settings.Secure.getInt(getContentResolver(), + Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED, 0) != 0); + } + + private void writeUSBAudioOptions() { + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED, + mUSBAudio.isChecked() ? 1 : 0); + } + private void updateForceRtlOptions() { updateCheckBox(mForceRtlLayout, Settings.Global.getInt(getActivity().getContentResolver(), Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0); @@ -1263,6 +1294,50 @@ public class DevelopmentSettings extends SettingsPreferenceFragment getActivity().getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0); } + // Reads the googlesetting and converts to an int. Throws an exception if GoogleSettings + // provider does not exist or if the setting name cannot be found. + private int getGoogleSettingValue(String name) throws Exception { + String value = null; + Cursor c = null; + try { + ContentResolver resolver = getActivity().getContentResolver(); + c = resolver.query(GOOGLE_SETTINGS_CONTENT_URI, new String[] { "value" }, + "name=?", new String[]{ name }, null); + if (c != null && c.moveToNext()) value = c.getString(0); + } finally { + if (c != null) c.close(); + } + // Throw an exception if value is null. This will indicate that setting is not found. + return Integer.parseInt(value); + } + + private boolean handleDataReductionProxyPreferenceChange() { + int val; + try { + val = getGoogleSettingValue(WEBVIEW_DATA_REDUCTION_PROXY); + } catch (Exception e) { + // Accessing GoogleSettings failed. Use the developer setting. + return true; + } + + Intent i = new Intent(); + i.setClassName(GOOGLE_SETTINGS_COMPONENT, + GOOGLE_SETTINGS_COMPONENT + GOOGLE_SETTINGS_ACTIVITY); + try { + startActivity(i); + } catch (android.content.ActivityNotFoundException ex) { + // We found the GoogleSetting but for some reason activity not found. + // Do our best and put an alert dialog + new AlertDialog.Builder(getActivity()).setMessage( + getActivity().getResources().getString( + R.string.dev_settings_use_google_settings)) + .setPositiveButton(android.R.string.ok, this) + .show(); + } + // Use GoogleSettings to set. + return false; + } + private void writeWebViewDataReductionProxyOptions() { Settings.Secure.putInt(getActivity().getContentResolver(), Settings.Secure.WEBVIEW_DATA_REDUCTION_PROXY, @@ -1273,9 +1348,17 @@ public class DevelopmentSettings extends SettingsPreferenceFragment } private void updateWebViewDataReductionProxyOptions() { - updateCheckBox(mWebViewDataReductionProxy, Settings.Secure.getInt( - getActivity().getContentResolver(), - Settings.Secure.WEBVIEW_DATA_REDUCTION_PROXY, 0) != 0); + int val = -1; + try { + val = getGoogleSettingValue(WEBVIEW_DATA_REDUCTION_PROXY); + } catch (Exception e) { + // Accessing GoogleSettings failed. Use the developer setting + } + if (val == -1) { + val = Settings.Secure.getInt(getActivity().getContentResolver(), + Settings.Secure.WEBVIEW_DATA_REDUCTION_PROXY, 0); + } + updateCheckBox(mWebViewDataReductionProxy, val != 0); } @Override @@ -1371,6 +1454,10 @@ public class DevelopmentSettings extends SettingsPreferenceFragment Settings.Secure.putInt(getActivity().getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, mAllowMockLocation.isChecked() ? 1 : 0); + } else if (preference == mDebugViewAttributes) { + Settings.Global.putInt(getActivity().getContentResolver(), + Settings.Global.DEBUG_VIEW_ATTRIBUTES, + mDebugViewAttributes.isChecked() ? 1 : 0); } else if (preference == mDebugAppPref) { startActivityForResult(new Intent(getActivity(), AppPicker.class), RESULT_DEBUG_APP); } else if (preference == mWaitForDebugger) { @@ -1417,6 +1504,8 @@ public class DevelopmentSettings extends SettingsPreferenceFragment writeWifiAllowScansWithTrafficOptions(); } else if (preference == mUseNuplayer) { writeUseNuplayerOptions(); + } else if (preference == mUSBAudio) { + writeUSBAudioOptions(); } else { return super.onPreferenceTreeClick(preferenceScreen, preference); } diff --git a/src/com/android/settings/DeviceAdminAdd.java b/src/com/android/settings/DeviceAdminAdd.java index 0bd548f..ed95500 100644 --- a/src/com/android/settings/DeviceAdminAdd.java +++ b/src/com/android/settings/DeviceAdminAdd.java @@ -32,7 +32,10 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.os.Bundle; @@ -103,6 +106,7 @@ public class DeviceAdminAdd extends Activity { mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE); mAppOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); + PackageManager packageManager = getPackageManager(); if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { Log.w(TAG, "Cannot start ADD_DEVICE_ADMIN as a new task"); @@ -120,6 +124,8 @@ public class DeviceAdminAdd extends Activity { } if (action != null && action.equals(DevicePolicyManager.ACTION_SET_PROFILE_OWNER)) { + setResult(RESULT_CANCELED); + setFinishOnTouchOutside(true); mAddingProfileOwner = true; mProfileOwnerName = getIntent().getStringExtra(DevicePolicyManager.EXTRA_PROFILE_OWNER_NAME); @@ -129,11 +135,23 @@ public class DeviceAdminAdd extends Activity { finish(); return; } + try { + PackageInfo packageInfo = packageManager.getPackageInfo(callingPackage, 0); + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + Log.e(TAG, "Cannot set a non-system app as a profile owner"); + finish(); + return; + } + } catch (NameNotFoundException nnfe) { + Log.e(TAG, "Cannot find the package " + callingPackage); + finish(); + return; + } } ActivityInfo ai; try { - ai = getPackageManager().getReceiverInfo(who, PackageManager.GET_META_DATA); + ai = packageManager.getReceiverInfo(who, PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Unable to retrieve device policy " + who, e); finish(); @@ -144,7 +162,7 @@ public class DeviceAdminAdd extends Activity { // No need to check this when deactivating, because it is safe to deactivate an active // invalid device admin. if (!mDPM.isAdminActive(who)) { - List<ResolveInfo> avail = getPackageManager().queryBroadcastReceivers( + List<ResolveInfo> avail = packageManager.queryBroadcastReceivers( new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED), PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); int count = avail == null ? 0 : avail.size(); diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java index f85b74e..ddd6728 100644 --- a/src/com/android/settings/DisplaySettings.java +++ b/src/com/android/settings/DisplaySettings.java @@ -16,6 +16,9 @@ package com.android.settings; +import com.android.internal.view.RotationPolicy; +import com.android.settings.notification.DropDownPreference; +import com.android.settings.notification.DropDownPreference.Callback; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; @@ -26,6 +29,7 @@ import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; +import android.app.Activity; import android.app.ActivityManagerNative; import android.app.Dialog; import android.app.admin.DevicePolicyManager; @@ -65,6 +69,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements private static final String KEY_LIFT_TO_WAKE = "lift_to_wake"; private static final String KEY_DOZE = "doze"; private static final String KEY_AUTO_BRIGHTNESS = "auto_brightness"; + private static final String KEY_AUTO_ROTATE = "auto_rotate"; private static final int DLG_GLOBAL_CHANGE_WARNING = 1; @@ -81,7 +86,8 @@ public class DisplaySettings extends SettingsPreferenceFragment implements @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final ContentResolver resolver = getActivity().getContentResolver(); + final Activity activity = getActivity(); + final ContentResolver resolver = activity.getContentResolver(); addPreferencesFromResource(R.xml.display_settings); @@ -111,19 +117,59 @@ public class DisplaySettings extends SettingsPreferenceFragment implements removePreference(KEY_AUTO_BRIGHTNESS); } - if (isLiftToWakeAvailable(getActivity())) { + if (isLiftToWakeAvailable(activity)) { mLiftToWakePreference = (SwitchPreference) findPreference(KEY_LIFT_TO_WAKE); mLiftToWakePreference.setOnPreferenceChangeListener(this); } else { removePreference(KEY_LIFT_TO_WAKE); } - if (isDozeAvailable(getActivity())) { + if (isDozeAvailable(activity)) { mDozePreference = (SwitchPreference) findPreference(KEY_DOZE); mDozePreference.setOnPreferenceChangeListener(this); } else { removePreference(KEY_DOZE); } + + if (RotationPolicy.isRotationLockToggleVisible(activity)) { + DropDownPreference rotatePreference = + (DropDownPreference) findPreference(KEY_AUTO_ROTATE); + rotatePreference.addItem(activity.getString(R.string.display_auto_rotate_rotate), + false); + int rotateLockedResourceId; + // The following block sets the string used when rotation is locked. + // If the device locks specifically to portrait or landscape (rather than current + // rotation), then we use a different string to include this information. + if (allowAllRotations(activity)) { + rotateLockedResourceId = R.string.display_auto_rotate_stay_in_current; + } else { + if (RotationPolicy.getRotationLockOrientation(activity) + == Configuration.ORIENTATION_PORTRAIT) { + rotateLockedResourceId = + R.string.display_auto_rotate_stay_in_portrait; + } else { + rotateLockedResourceId = + R.string.display_auto_rotate_stay_in_landscape; + } + } + rotatePreference.addItem(activity.getString(rotateLockedResourceId), true); + rotatePreference.setSelectedItem(RotationPolicy.isRotationLocked(activity) ? + 1 : 0); + rotatePreference.setCallback(new Callback() { + @Override + public boolean onItemSelected(int pos, Object value) { + RotationPolicy.setRotationLock(activity, (Boolean) value); + return true; + } + }); + } else { + removePreference(KEY_AUTO_ROTATE); + } + } + + private static boolean allowAllRotations(Context context) { + return Resources.getSystem().getBoolean( + com.android.internal.R.bool.config_allowAllRotations); } private static boolean isLiftToWakeAvailable(Context context) { @@ -382,6 +428,9 @@ public class DisplaySettings extends SettingsPreferenceFragment implements if (!isDozeAvailable(context)) { result.add(KEY_DOZE); } + if (!RotationPolicy.isRotationLockToggleVisible(context)) { + result.add(KEY_AUTO_ROTATE); + } return result; } }; diff --git a/src/com/android/settings/HomeSettings.java b/src/com/android/settings/HomeSettings.java index 845fe1e..2ac93c6 100644 --- a/src/com/android/settings/HomeSettings.java +++ b/src/com/android/settings/HomeSettings.java @@ -19,6 +19,7 @@ package com.android.settings; import java.util.ArrayList; import java.util.List; +import android.app.Activity; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -32,18 +33,22 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.content.pm.UserInfo; import android.graphics.ColorFilter; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.UserManager; import android.preference.Preference; import android.preference.PreferenceGroup; import android.text.TextUtils; import android.util.Log; import android.view.View; +import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.RadioButton; @@ -55,6 +60,10 @@ import com.android.settings.search.SearchIndexableRaw; public class HomeSettings extends SettingsPreferenceFragment implements Indexable { static final String TAG = "HomeSettings"; + // Boolean extra, indicates only launchers that support managed profiles should be shown. + // Note: must match the constant defined in ManagedProvisioning + private static final String EXTRA_SUPPORT_MANAGED_PROFILES = "support_managed_profiles"; + static final int REQUESTING_UNINSTALL = 10; public static final String HOME_PREFS = "home_prefs"; @@ -114,6 +123,8 @@ public class HomeSettings extends SettingsPreferenceFragment implements Indexabl mPm.replacePreferredActivity(mHomeFilter, IntentFilter.MATCH_CATEGORY_EMPTY, mHomeComponentSet, newHome.activityName); + + getActivity().setResult(Activity.RESULT_OK); } void uninstallApp(HomeAppPreference pref) { @@ -171,6 +182,10 @@ public class HomeSettings extends SettingsPreferenceFragment implements Indexabl mPrefs = new ArrayList<HomeAppPreference>(); mHomeComponentSet = new ComponentName[homeActivities.size()]; int prefIndex = 0; + boolean supportManagedProfilesExtra = + getActivity().getIntent().getBooleanExtra(EXTRA_SUPPORT_MANAGED_PROFILES, false); + boolean mustSupportManagedProfile = hasManagedProfile() + || supportManagedProfilesExtra; for (int i = 0; i < homeActivities.size(); i++) { final ResolveInfo candidate = homeActivities.get(i); final ActivityInfo info = candidate.activityInfo; @@ -179,11 +194,19 @@ public class HomeSettings extends SettingsPreferenceFragment implements Indexabl try { Drawable icon = info.loadIcon(mPm); CharSequence name = info.loadLabel(mPm); - HomeAppPreference pref = new HomeAppPreference(context, activityName, prefIndex, - icon, name, this, info); + HomeAppPreference pref; + + if (mustSupportManagedProfile && !launcherHasManagedProfilesFeature(candidate)) { + pref = new HomeAppPreference(context, activityName, prefIndex, + icon, name, this, info, false /* not enabled */, + getResources().getString(R.string.home_work_profile_not_supported)); + } else { + pref = new HomeAppPreference(context, activityName, prefIndex, + icon, name, this, info, true /* enabled */, null); + } + mPrefs.add(pref); mPrefGroup.addPreference(pref); - pref.setEnabled(true); if (activityName.equals(currentDefaultHome)) { mCurrentHome = pref; } @@ -194,6 +217,10 @@ public class HomeSettings extends SettingsPreferenceFragment implements Indexabl } if (mCurrentHome != null) { + if (mCurrentHome.isEnabled()) { + getActivity().setResult(Activity.RESULT_OK); + } + new Handler().post(new Runnable() { public void run() { mCurrentHome.setChecked(true); @@ -202,6 +229,31 @@ public class HomeSettings extends SettingsPreferenceFragment implements Indexabl } } + private boolean hasManagedProfile() { + Context context = getActivity(); + UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); + List<UserInfo> profiles = userManager.getProfiles(context.getUserId()); + for (UserInfo userInfo : profiles) { + if (userInfo.isManagedProfile()) return true; + } + return false; + } + + private boolean launcherHasManagedProfilesFeature(ResolveInfo resolveInfo) { + try { + ApplicationInfo appInfo = getPackageManager().getApplicationInfo( + resolveInfo.activityInfo.packageName, 0 /* default flags */); + return versionNumberAtLeastL(appInfo.targetSdkVersion); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + private boolean versionNumberAtLeastL(int versionNumber) { + // TODO: remove "|| true" once the build code for L is fixed. + return versionNumber >= Build.VERSION_CODES.L || true; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -245,12 +297,14 @@ public class HomeSettings extends SettingsPreferenceFragment implements Indexabl String uninstallTarget; public HomeAppPreference(Context context, ComponentName activity, - int i, Drawable icon, CharSequence title, - HomeSettings parent, ActivityInfo info) { + int i, Drawable icon, CharSequence title, HomeSettings parent, ActivityInfo info, + boolean enabled, CharSequence summary) { super(context); setLayoutResource(R.layout.preference_home_app); setIcon(icon); setTitle(title); + setEnabled(enabled); + setSummary(summary); activityName = activity; fragment = parent; index = i; @@ -305,13 +359,15 @@ public class HomeSettings extends SettingsPreferenceFragment implements Indexabl icon.setEnabled(false); icon.setColorFilter(grayscaleFilter); } else { + icon.setEnabled(true); icon.setOnClickListener(mDeleteClickListener); icon.setTag(indexObj); } View v = view.findViewById(R.id.home_app_pref); - v.setOnClickListener(mHomeClickListener); v.setTag(indexObj); + + v.setOnClickListener(mHomeClickListener); } void setChecked(boolean state) { diff --git a/src/com/android/settings/LocalePicker.java b/src/com/android/settings/LocalePicker.java index 6600703..c6158b1 100644 --- a/src/com/android/settings/LocalePicker.java +++ b/src/com/android/settings/LocalePicker.java @@ -48,15 +48,6 @@ public class LocalePicker extends com.android.internal.app.LocalePicker } @Override - protected boolean isInDeveloperMode() { - final boolean showDev = getActivity().getSharedPreferences(DevelopmentSettings.PREF_FILE, - Context.MODE_PRIVATE).getBoolean( - DevelopmentSettings.PREF_SHOW, - android.os.Build.TYPE.equals("eng")); - return showDev; - } - - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_TARGET_LOCALE)) { diff --git a/src/com/android/settings/ManagedProfileSetup.java b/src/com/android/settings/ManagedProfileSetup.java index 1b3c838..198abe0 100644 --- a/src/com/android/settings/ManagedProfileSetup.java +++ b/src/com/android/settings/ManagedProfileSetup.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.util.Log; import android.os.UserHandle; import android.os.UserManager; @@ -37,6 +38,7 @@ import static android.content.pm.PackageManager.GET_RESOLVED_FILTER; * adds cross-profile intent filters for the appropriate Settings activities). */ public class ManagedProfileSetup extends BroadcastReceiver { + private static final String TAG = "Settings"; private static final String PRIMARY_PROFILE_SETTING = "com.android.settings.PRIMARY_PROFILE_CONTROLLED"; @@ -46,7 +48,8 @@ public class ManagedProfileSetup extends BroadcastReceiver { if (!Utils.isManagedProfile(um)) { return; } - + Log.i(TAG, "Received broadcast: " + broadcast.getAction() + + ". Setting up intent forwarding for managed profile."); final PackageManager pm = context.getPackageManager(); // Clear any previous intent forwarding we set up pm.clearCrossProfileIntentFilters(UserHandle.myUserId()); diff --git a/src/com/android/settings/MasterClearConfirm.java b/src/com/android/settings/MasterClearConfirm.java index 2ba1e7f..0455d74 100644 --- a/src/com/android/settings/MasterClearConfirm.java +++ b/src/com/android/settings/MasterClearConfirm.java @@ -16,22 +16,21 @@ package com.android.settings; +import android.app.ProgressDialog; import android.content.Context; +import android.content.pm.ActivityInfo; +import android.os.AsyncTask; import android.service.persistentdata.PersistentDataBlockManager; import com.android.internal.os.storage.ExternalStorageFormatter; -import com.android.internal.widget.LockPatternUtils; -import android.app.Activity; import android.app.Fragment; import android.content.Intent; -import android.content.res.Resources; import android.os.Bundle; import android.os.UserManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import android.widget.CheckBox; /** * Confirm and execute a reset of the device to a clean "just out of the box" @@ -47,7 +46,6 @@ public class MasterClearConfirm extends Fragment { private View mContentView; private boolean mEraseSdCard; - private Button mFinalButton; /** * The user has gone through the multiple confirmation, so now we go ahead @@ -61,30 +59,68 @@ public class MasterClearConfirm extends Fragment { return; } - PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) + final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); if (pdbManager != null) { - pdbManager.wipe(); - } + // if OEM unlock is enabled, this will be wiped during FR process. + if (!pdbManager.getOemUnlockEnabled()) { + final ProgressDialog progressDialog = getProgressDialog(); + progressDialog.show(); + + // need to prevent orientation changes as we're about to go into + // a long IO request, so we won't be able to access inflate resources on flash + final int oldOrientation = getActivity().getRequestedOrientation(); + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + pdbManager.wipe(); + return null; + } - if (mEraseSdCard) { - Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET); - intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME); - getActivity().startService(intent); + @Override + protected void onPostExecute(Void aVoid) { + progressDialog.hide(); + getActivity().setRequestedOrientation(oldOrientation); + doMasterClear(); + } + }.execute(); + } } else { - getActivity().sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR")); - // Intent handling is asynchronous -- assume it will happen soon. + doMasterClear(); } } + + private ProgressDialog getProgressDialog() { + final ProgressDialog progressDialog = new ProgressDialog(getActivity()); + progressDialog.setIndeterminate(true); + progressDialog.setCancelable(false); + progressDialog.setTitle( + getActivity().getString(R.string.master_clear_progress_title)); + progressDialog.setMessage( + getActivity().getString(R.string.master_clear_progress_text)); + return progressDialog; + } }; + private void doMasterClear() { + if (mEraseSdCard) { + Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET); + intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME); + getActivity().startService(intent); + } else { + getActivity().sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR")); + // Intent handling is asynchronous -- assume it will happen soon. + } + } + /** * Configure the UI for the final confirmation interaction */ private void establishFinalConfirmationState() { - mFinalButton = (Button) mContentView.findViewById(R.id.execute_master_clear); - mFinalButton.setOnClickListener(mFinalClickListener); + mContentView.findViewById(R.id.execute_master_clear) + .setOnClickListener(mFinalClickListener); } @Override @@ -104,6 +140,6 @@ public class MasterClearConfirm extends Fragment { super.onCreate(savedInstanceState); Bundle args = getArguments(); - mEraseSdCard = args != null ? args.getBoolean(MasterClear.ERASE_EXTERNAL_EXTRA) : false; + mEraseSdCard = args != null && args.getBoolean(MasterClear.ERASE_EXTERNAL_EXTRA); } } diff --git a/src/com/android/settings/PrivacySettings.java b/src/com/android/settings/PrivacySettings.java index aac7638..1236c48 100644 --- a/src/com/android/settings/PrivacySettings.java +++ b/src/com/android/settings/PrivacySettings.java @@ -27,9 +27,10 @@ import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserManager; -import android.preference.CheckBoxPreference; import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; import android.provider.Settings; /** @@ -46,8 +47,8 @@ public class PrivacySettings extends SettingsPreferenceFragment implements private static final String CONFIGURE_ACCOUNT = "configure_account"; private static final String PERSONAL_DATA_CATEGORY = "personal_data_category"; private IBackupManager mBackupManager; - private CheckBoxPreference mBackup; - private CheckBoxPreference mAutoRestore; + private SwitchPreference mBackup; + private SwitchPreference mAutoRestore; private Dialog mConfirmDialog; private PreferenceScreen mConfigure; @@ -63,8 +64,12 @@ public class PrivacySettings extends SettingsPreferenceFragment implements mBackupManager = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); - mBackup = (CheckBoxPreference) screen.findPreference(BACKUP_DATA); - mAutoRestore = (CheckBoxPreference) screen.findPreference(AUTO_RESTORE); + mBackup = (SwitchPreference) screen.findPreference(BACKUP_DATA); + mBackup.setOnPreferenceChangeListener(preferenceChangeListener); + + mAutoRestore = (SwitchPreference) screen.findPreference(AUTO_RESTORE); + mAutoRestore.setOnPreferenceChangeListener(preferenceChangeListener); + mConfigure = (PreferenceScreen) screen.findPreference(CONFIGURE_ACCOUNT); if (UserManager.get(getActivity()).hasUserRestriction( @@ -98,29 +103,36 @@ public class PrivacySettings extends SettingsPreferenceFragment implements super.onStop(); } - @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, - Preference preference) { - if (preference == mBackup) { - if (!mBackup.isChecked()) { - showEraseBackupDialog(); - } else { - setBackupEnabled(true); + private OnPreferenceChangeListener preferenceChangeListener = new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (!(preference instanceof SwitchPreference)) { + return true; } - } else if (preference == mAutoRestore) { - boolean curState = mAutoRestore.isChecked(); - try { - mBackupManager.setAutoRestore(curState); - } catch (RemoteException e) { - mAutoRestore.setChecked(!curState); + boolean nextValue = (Boolean) newValue; + boolean result = false; + if (preference == mBackup) { + if (nextValue == false) { + // Don't change Switch status until user makes choice in dialog + // so return false here. + showEraseBackupDialog(); + } else { + setBackupEnabled(true); + result = true; + } + } else if (preference == mAutoRestore) { + try { + mBackupManager.setAutoRestore(nextValue); + result = true; + } catch (RemoteException e) { + mAutoRestore.setChecked(!nextValue); + } } + return result; } - return super.onPreferenceTreeClick(preferenceScreen, preference); - } + }; private void showEraseBackupDialog() { - mBackup.setChecked(true); - mDialogType = DIALOG_ERASE_BACKUP; CharSequence msg = getResources().getText(R.string.backup_erase_dialog_message); // TODO: DialogFragment? @@ -159,7 +171,7 @@ public class PrivacySettings extends SettingsPreferenceFragment implements mConfigure.setEnabled(configureEnabled); mConfigure.setIntent(configIntent); setConfigureSummary(configSummary); -} + } private void setConfigureSummary(String summary) { if (summary != null) { @@ -179,13 +191,20 @@ public class PrivacySettings extends SettingsPreferenceFragment implements } } + @Override public void onClick(DialogInterface dialog, int which) { - if (which == DialogInterface.BUTTON_POSITIVE) { - //updateProviders(); - if (mDialogType == DIALOG_ERASE_BACKUP) { + // Dialog is triggered before Switch status change, that means marking the Switch to + // true in showEraseBackupDialog() method will be override by following status change. + // So we do manual switching here due to users' response. + if (mDialogType == DIALOG_ERASE_BACKUP) { + // Accept turning off backup + if (which == DialogInterface.BUTTON_POSITIVE) { setBackupEnabled(false); - updateConfigureSummary(); + } else if (which == DialogInterface.BUTTON_NEGATIVE) { + // Reject turning off backup + setBackupEnabled(true); } + updateConfigureSummary(); } mDialogType = 0; } @@ -214,4 +233,4 @@ public class PrivacySettings extends SettingsPreferenceFragment implements protected int getHelpResource() { return R.string.help_url_backup_reset; } -} +}
\ No newline at end of file diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java index 75de222..b0a4a53 100644 --- a/src/com/android/settings/RadioInfo.java +++ b/src/com/android/settings/RadioInfo.java @@ -303,9 +303,6 @@ public class RadioInfo extends Activity { imsRegRequiredButton = (Button) findViewById(R.id.ims_reg_required); imsRegRequiredButton.setOnClickListener(mImsRegRequiredHandler); - moOverVolteButton = (Button) findViewById(R.id.mo_over_volte); - moOverVolteButton.setOnClickListener(mMoOverVolteHandler); - smsOverImsButton = (Button) findViewById(R.id.sms_over_ims); smsOverImsButton.setOnClickListener(mSmsOverImsHandler); @@ -363,7 +360,6 @@ public class RadioInfo extends Activity { updatePowerState(); updateCellInfoListRate(); updateImsRegRequiredState(); - updateMoOverImsState(); updateSmsOverImsState(); updateLteRamDumpState(); updateProperties(); @@ -989,35 +985,6 @@ public class RadioInfo extends Activity { imsRegRequiredButton.setText(buttonText); } - private Button moOverVolteButton; - OnClickListener mMoOverVolteHandler = new OnClickListener() { - @Override - public void onClick(View v) { - boolean moOverVolteEnabled = isMoOverVolteEnabled(); - log(String.format("toggle %s: currently %s", - TelephonyProperties.PROPERTY_DBG_IMS_VOLTE_ENABLE, - (moOverVolteEnabled ? "on" : "off"))); - boolean newValue = !moOverVolteEnabled; - SystemProperties.set(TelephonyProperties.PROPERTY_DBG_IMS_VOLTE_ENABLE, - newValue ? "1" : "0"); - updateMoOverImsState(); - } - }; - - private boolean isMoOverVolteEnabled() { - return SystemProperties.getInt(TelephonyProperties.PROPERTY_DBG_IMS_VOLTE_ENABLE, - TelephonyProperties.PROPERTY_DBG_IMS_VOLTE_ENABLE_DEAFULT) == 1; - } - - private void updateMoOverImsState() { - boolean moOverVolteEnabled = isMoOverVolteEnabled(); - log("updateMoOverImsState isMoOverVolteEnabled()=" + moOverVolteEnabled); - String buttonText = moOverVolteEnabled ? - getString(R.string.mo_over_volte_off) : - getString(R.string.mo_over_volte_on); - moOverVolteButton.setText(buttonText); - } - private Button smsOverImsButton; static final String PROPERTY_SMS_OVER_IMS = "persist.radio.imsallowmtsms"; OnClickListener mSmsOverImsHandler = new OnClickListener() { diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index e5f7736..7798937 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -31,7 +31,7 @@ import android.content.res.Resources; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; -import android.preference.CheckBoxPreference; +import android.preference.SwitchPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; @@ -47,7 +47,9 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.widget.LockPatternUtils; +import com.android.settings.TrustAgentUtils.TrustAgentComponentInfo; import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Index; import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; @@ -76,6 +78,8 @@ public class SecuritySettings extends SettingsPreferenceFragment private static final String KEY_DEVICE_ADMIN_CATEGORY = "device_admin_category"; private static final String KEY_LOCK_AFTER_TIMEOUT = "lock_after_timeout"; private static final String KEY_OWNER_INFO_SETTINGS = "owner_info_settings"; + private static final String KEY_ADVANCED_SECURITY = "advanced_security"; + private static final String KEY_MANAGE_TRUST_AGENTS = "manage_trust_agents"; private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123; private static final int CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST = 124; @@ -95,23 +99,31 @@ public class SecuritySettings extends SettingsPreferenceFragment private static final String KEY_TRUST_AGENT = "trust_agent"; private static final String KEY_SCREEN_PINNING = "screen_pinning_settings"; + // These switch preferences need special handling since they're not all stored in Settings. + private static final String SWITCH_PREFERENCE_KEYS[] = { KEY_LOCK_AFTER_TIMEOUT, + KEY_LOCK_ENABLED, KEY_VISIBLE_PATTERN, KEY_BIOMETRIC_WEAK_LIVELINESS, + KEY_POWER_INSTANTLY_LOCKS, KEY_SHOW_PASSWORD, KEY_TOGGLE_INSTALL_APPLICATIONS }; + + // Only allow one trust agent on the platform. + private static final boolean ONLY_ONE_TRUST_AGENT = true; + private DevicePolicyManager mDPM; private ChooseLockSettingsHelper mChooseLockSettingsHelper; private LockPatternUtils mLockPatternUtils; private ListPreference mLockAfter; - private CheckBoxPreference mBiometricWeakLiveliness; - private CheckBoxPreference mVisiblePattern; + private SwitchPreference mBiometricWeakLiveliness; + private SwitchPreference mVisiblePattern; - private CheckBoxPreference mShowPassword; + private SwitchPreference mShowPassword; private KeyStore mKeyStore; private Preference mResetCredentials; - private CheckBoxPreference mToggleAppInstallation; + private SwitchPreference mToggleAppInstallation; private DialogInterface mWarnInstallApps; - private CheckBoxPreference mPowerButtonInstantlyLocks; + private SwitchPreference mPowerButtonInstantlyLocks; private boolean mIsPrimary; @@ -214,33 +226,26 @@ public class SecuritySettings extends SettingsPreferenceFragment PreferenceGroup securityCategory = (PreferenceGroup) root.findPreference(KEY_SECURITY_CATEGORY); if (securityCategory != null) { - PackageManager pm = getPackageManager(); - List<ResolveInfo> resolveInfos = pm.queryIntentServices(TRUST_AGENT_INTENT, - PackageManager.GET_META_DATA); - List<ComponentName> enabledTrustAgents = mLockPatternUtils.getEnabledTrustAgents(); - if (enabledTrustAgents != null && !enabledTrustAgents.isEmpty()) { - for (ResolveInfo resolveInfo : resolveInfos) { - if (resolveInfo.serviceInfo == null) continue; - if (!TrustAgentUtils.checkProvidePermission(resolveInfo, pm)) continue; - TrustAgentUtils.TrustAgentComponentInfo trustAgentComponentInfo = - TrustAgentUtils.getSettingsComponent(pm, resolveInfo); - if (trustAgentComponentInfo.componentName == null || - !enabledTrustAgents.contains( - TrustAgentUtils.getComponentName(resolveInfo)) || - TextUtils.isEmpty(trustAgentComponentInfo.title)) continue; - Preference trustAgentPreference = - new Preference(securityCategory.getContext()); - trustAgentPreference.setKey(KEY_TRUST_AGENT); - trustAgentPreference.setTitle(trustAgentComponentInfo.title); - trustAgentPreference.setSummary(trustAgentComponentInfo.summary); - // Create intent for this preference. - Intent intent = new Intent(); - intent.setComponent(trustAgentComponentInfo.componentName); - intent.setAction(Intent.ACTION_MAIN); - trustAgentPreference.setIntent(intent); - // Add preference to the settings menu. - securityCategory.addPreference(trustAgentPreference); - break; // Only render the first one. + final boolean hasSecurity = mLockPatternUtils.isSecure(); + ArrayList<TrustAgentComponentInfo> agents = + getActiveTrustAgents(getPackageManager(), mLockPatternUtils); + for (int i = 0; i < agents.size(); i++) { + final TrustAgentComponentInfo agent = agents.get(i); + Preference trustAgentPreference = + new Preference(securityCategory.getContext()); + trustAgentPreference.setKey(KEY_TRUST_AGENT); + trustAgentPreference.setTitle(agent.title); + trustAgentPreference.setSummary(agent.summary); + // Create intent for this preference. + Intent intent = new Intent(); + intent.setComponent(agent.componentName); + intent.setAction(Intent.ACTION_MAIN); + trustAgentPreference.setIntent(intent); + // Add preference to the settings menu. + securityCategory.addPreference(trustAgentPreference); + if (!hasSecurity) { + trustAgentPreference.setEnabled(false); + trustAgentPreference.setSummary(R.string.disabled_because_no_backup_security); } } } @@ -254,13 +259,13 @@ public class SecuritySettings extends SettingsPreferenceFragment // biometric weak liveliness mBiometricWeakLiveliness = - (CheckBoxPreference) root.findPreference(KEY_BIOMETRIC_WEAK_LIVELINESS); + (SwitchPreference) root.findPreference(KEY_BIOMETRIC_WEAK_LIVELINESS); // visible pattern - mVisiblePattern = (CheckBoxPreference) root.findPreference(KEY_VISIBLE_PATTERN); + mVisiblePattern = (SwitchPreference) root.findPreference(KEY_VISIBLE_PATTERN); // lock instantly on power key press - mPowerButtonInstantlyLocks = (CheckBoxPreference) root.findPreference( + mPowerButtonInstantlyLocks = (SwitchPreference) root.findPreference( KEY_POWER_INSTANTLY_LOCKS); Preference trustAgentPreference = root.findPreference(KEY_TRUST_AGENT); if (mPowerButtonInstantlyLocks != null && @@ -296,18 +301,14 @@ public class SecuritySettings extends SettingsPreferenceFragment root.findPreference(KEY_SIM_LOCK).setEnabled(false); } } - try { - if (Settings.System.getInt(getContentResolver(), Settings.System.LOCK_TO_APP_ENABLED) - != 0) { - root.findPreference(KEY_SCREEN_PINNING).setSummary( - getResources().getString(R.string.switch_on_text)); - } - } catch (SettingNotFoundException e) { - Log.w(TAG, "No Lock-to-app enabled setting", e); + if (Settings.System.getInt(getContentResolver(), + Settings.System.LOCK_TO_APP_ENABLED, 0) != 0) { + root.findPreference(KEY_SCREEN_PINNING).setSummary( + getResources().getString(R.string.switch_on_text)); } // Show password - mShowPassword = (CheckBoxPreference) root.findPreference(KEY_SHOW_PASSWORD); + mShowPassword = (SwitchPreference) root.findPreference(KEY_SHOW_PASSWORD); mResetCredentials = root.findPreference(KEY_RESET_CREDENTIALS); // Credential storage @@ -331,7 +332,7 @@ public class SecuritySettings extends SettingsPreferenceFragment // Application install PreferenceGroup deviceAdminCategory = (PreferenceGroup) root.findPreference(KEY_DEVICE_ADMIN_CATEGORY); - mToggleAppInstallation = (CheckBoxPreference) findPreference( + mToggleAppInstallation = (SwitchPreference) findPreference( KEY_TOGGLE_INSTALL_APPLICATIONS); mToggleAppInstallation.setChecked(isNonMarketAppsAllowed()); // Side loading of apps. @@ -341,9 +342,54 @@ public class SecuritySettings extends SettingsPreferenceFragment mToggleAppInstallation.setEnabled(false); } + // Advanced Security features + PreferenceGroup advancedCategory = + (PreferenceGroup)root.findPreference(KEY_ADVANCED_SECURITY); + if (advancedCategory != null) { + Preference manageAgents = advancedCategory.findPreference(KEY_MANAGE_TRUST_AGENTS); + if (manageAgents != null && !mLockPatternUtils.isSecure()) { + manageAgents.setEnabled(false); + manageAgents.setSummary(R.string.disabled_because_no_backup_security); + } + } + + // The above preferences come and go based on security state, so we need to update + // the index. This call is expected to be fairly cheap, but we may want to do something + // smarter in the future. + Index.getInstance(getActivity()) + .updateFromClassNameResource(SecuritySettings.class.getName(), true, true); + + for (int i = 0; i < SWITCH_PREFERENCE_KEYS.length; i++) { + final Preference pref = findPreference(SWITCH_PREFERENCE_KEYS[i]); + if (pref != null) pref.setOnPreferenceChangeListener(this); + } return root; } + private static ArrayList<TrustAgentComponentInfo> getActiveTrustAgents( + PackageManager pm, LockPatternUtils utils) { + ArrayList<TrustAgentComponentInfo> result = new ArrayList<TrustAgentComponentInfo>(); + List<ResolveInfo> resolveInfos = pm.queryIntentServices(TRUST_AGENT_INTENT, + PackageManager.GET_META_DATA); + List<ComponentName> enabledTrustAgents = utils.getEnabledTrustAgents(); + if (enabledTrustAgents != null && !enabledTrustAgents.isEmpty()) { + for (int i = 0; i < resolveInfos.size(); i++) { + ResolveInfo resolveInfo = resolveInfos.get(i); + if (resolveInfo.serviceInfo == null) continue; + if (!TrustAgentUtils.checkProvidePermission(resolveInfo, pm)) continue; + TrustAgentComponentInfo trustAgentComponentInfo = + TrustAgentUtils.getSettingsComponent(pm, resolveInfo); + if (trustAgentComponentInfo.componentName == null || + !enabledTrustAgents.contains( + TrustAgentUtils.getComponentName(resolveInfo)) || + TextUtils.isEmpty(trustAgentComponentInfo.title)) continue; + result.add(trustAgentComponentInfo); + if (ONLY_ONE_TRUST_AGENT) break; + } + } + return result; + } + private boolean isNonMarketAppsAllowed() { return Settings.Global.getInt(getContentResolver(), Settings.Global.INSTALL_NON_MARKET_APPS, 0) > 0; @@ -366,16 +412,17 @@ public class SecuritySettings extends SettingsPreferenceFragment .setIcon(com.android.internal.R.drawable.ic_dialog_alert) .setMessage(getResources().getString(R.string.install_all_warning)) .setPositiveButton(android.R.string.yes, this) - .setNegativeButton(android.R.string.no, null) + .setNegativeButton(android.R.string.no, this) .show(); } @Override public void onClick(DialogInterface dialog, int which) { - if (dialog == mWarnInstallApps && which == DialogInterface.BUTTON_POSITIVE) { - setNonMarketAppsAllowed(true); + if (dialog == mWarnInstallApps) { + boolean turnOn = which == DialogInterface.BUTTON_POSITIVE; + setNonMarketAppsAllowed(turnOn); if (mToggleAppInstallation != null) { - mToggleAppInstallation.setChecked(true); + mToggleAppInstallation.setChecked(turnOn); } } } @@ -490,8 +537,6 @@ public class SecuritySettings extends SettingsPreferenceFragment @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { final String key = preference.getKey(); - - final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) { startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment", R.string.lock_settings_picker_title, SET_OR_CHANGE_LOCK_METHOD_REQUEST, null); @@ -506,42 +551,6 @@ public class SecuritySettings extends SettingsPreferenceFragment // can't be reached, but is here in case things change in the future startBiometricWeakImprove(); } - } else if (KEY_BIOMETRIC_WEAK_LIVELINESS.equals(key)) { - if (isToggled(preference)) { - lockPatternUtils.setBiometricWeakLivelinessEnabled(true); - } else { - // In this case the user has just unchecked the checkbox, but this action requires - // them to confirm their password. We need to re-check the checkbox until - // they've confirmed their password - mBiometricWeakLiveliness.setChecked(true); - ChooseLockSettingsHelper helper = - new ChooseLockSettingsHelper(this.getActivity(), this); - if (!helper.launchConfirmationActivity( - CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF, null, null)) { - // If this returns false, it means no password confirmation is required, so - // go ahead and uncheck it here. - // Note: currently a backup is required for biometric_weak so this code path - // can't be reached, but is here in case things change in the future - lockPatternUtils.setBiometricWeakLivelinessEnabled(false); - mBiometricWeakLiveliness.setChecked(false); - } - } - } else if (KEY_LOCK_ENABLED.equals(key)) { - lockPatternUtils.setLockPatternEnabled(isToggled(preference)); - } else if (KEY_VISIBLE_PATTERN.equals(key)) { - lockPatternUtils.setVisiblePatternEnabled(isToggled(preference)); - } else if (KEY_POWER_INSTANTLY_LOCKS.equals(key)) { - lockPatternUtils.setPowerButtonInstantlyLocks(isToggled(preference)); - } else if (preference == mShowPassword) { - Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, - mShowPassword.isChecked() ? 1 : 0); - } else if (preference == mToggleAppInstallation) { - if (mToggleAppInstallation.isChecked()) { - mToggleAppInstallation.setChecked(false); - warnAppInstallation(); - } else { - setNonMarketAppsAllowed(false); - } } else if (KEY_TRUST_AGENT.equals(key)) { ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this.getActivity(), this); @@ -556,14 +565,9 @@ public class SecuritySettings extends SettingsPreferenceFragment // If we didn't handle it, let preferences handle it. return super.onPreferenceTreeClick(preferenceScreen, preference); } - return true; } - private boolean isToggled(Preference pref) { - return ((CheckBoxPreference) pref).isChecked(); - } - /** * see confirmPatternThenDisableAndClear */ @@ -596,7 +600,10 @@ public class SecuritySettings extends SettingsPreferenceFragment @Override public boolean onPreferenceChange(Preference preference, Object value) { - if (preference == mLockAfter) { + boolean result = true; + final String key = preference.getKey(); + final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); + if (KEY_LOCK_AFTER_TIMEOUT.equals(key)) { int timeout = Integer.parseInt((String) value); try { Settings.Secure.putInt(getContentResolver(), @@ -605,8 +612,46 @@ public class SecuritySettings extends SettingsPreferenceFragment Log.e("SecuritySettings", "could not persist lockAfter timeout setting", e); } updateLockAfterPreferenceSummary(); + } else if (KEY_LOCK_ENABLED.equals(key)) { + lockPatternUtils.setLockPatternEnabled((Boolean) value); + } else if (KEY_VISIBLE_PATTERN.equals(key)) { + lockPatternUtils.setVisiblePatternEnabled((Boolean) value); + } else if (KEY_BIOMETRIC_WEAK_LIVELINESS.equals(key)) { + if ((Boolean) value) { + lockPatternUtils.setBiometricWeakLivelinessEnabled(true); + } else { + // In this case the user has just unchecked the checkbox, but this action requires + // them to confirm their password. We need to re-check the checkbox until + // they've confirmed their password + mBiometricWeakLiveliness.setChecked(true); + ChooseLockSettingsHelper helper = + new ChooseLockSettingsHelper(this.getActivity(), this); + if (!helper.launchConfirmationActivity( + CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF, null, null)) { + // If this returns false, it means no password confirmation is required, so + // go ahead and uncheck it here. + // Note: currently a backup is required for biometric_weak so this code path + // can't be reached, but is here in case things change in the future + lockPatternUtils.setBiometricWeakLivelinessEnabled(false); + mBiometricWeakLiveliness.setChecked(false); + } + } + } else if (KEY_POWER_INSTANTLY_LOCKS.equals(key)) { + mLockPatternUtils.setPowerButtonInstantlyLocks((Boolean) value); + } else if (KEY_SHOW_PASSWORD.equals(key)) { + Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, + ((Boolean) value) ? 1 : 0); + } else if (KEY_TOGGLE_INSTALL_APPLICATIONS.equals(key)) { + if ((Boolean) value) { + mToggleAppInstallation.setChecked(false); + warnAppInstallation(); + // Don't change Switch status until user makes choice in dialog, so return false. + result = false; + } else { + setNonMarketAppsAllowed(false); + } } - return true; + return result; } @Override @@ -626,7 +671,7 @@ public class SecuritySettings extends SettingsPreferenceFragment public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new SecuritySearchIndexProvider(); - static private class SecuritySearchIndexProvider extends BaseSearchIndexProvider { + private static class SecuritySearchIndexProvider extends BaseSearchIndexProvider { boolean mIsPrimary; @@ -716,6 +761,19 @@ public class SecuritySettings extends SettingsPreferenceFragment result.add(data); } + // Advanced + final LockPatternUtils lockPatternUtils = new LockPatternUtils(context); + if (lockPatternUtils.isSecure()) { + ArrayList<TrustAgentComponentInfo> agents = + getActiveTrustAgents(context.getPackageManager(), lockPatternUtils); + for (int i = 0; i < agents.size(); i++) { + final TrustAgentComponentInfo agent = agents.get(i); + data = new SearchIndexableRaw(context); + data.title = agent.title; + data.screenTitle = screenTitle; + result.add(data); + } + } return result; } @@ -745,6 +803,12 @@ public class SecuritySettings extends SettingsPreferenceFragment keys.add(KEY_CREDENTIALS_MANAGER); } + // TrustAgent settings disappear when the user has no primary security. + if (!lockPatternUtils.isSecure()) { + keys.add(KEY_TRUST_AGENT); + keys.add(KEY_MANAGE_TRUST_AGENTS); + } + return keys; } } diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 123d4fe..84bf615 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -38,6 +38,7 @@ public class Settings extends SettingsActivity { public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ } public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ } public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ } + public static class VoiceInputSettingsActivity extends SettingsActivity { /* empty */ } public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ } public static class LocalePickerActivity extends SettingsActivity { /* empty */ } public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ } @@ -63,6 +64,7 @@ public class Settings extends SettingsActivity { public static class AccessibilityContrastSettingsActivity extends SettingsActivity { /* empty */ } public static class AccessibilityDaltonizerSettingsActivity extends SettingsActivity { /* empty */ } public static class SecuritySettingsActivity extends SettingsActivity { /* empty */ } + public static class UsageAccessSettingsActivity extends SettingsActivity { /* empty */ } public static class LocationSettingsActivity extends SettingsActivity { /* empty */ } public static class PrivacySettingsActivity extends SettingsActivity { /* empty */ } public static class RunningServicesActivity extends SettingsActivity { /* empty */ } @@ -98,5 +100,6 @@ public class Settings extends SettingsActivity { public static class QuickLaunchSettingsActivity extends SettingsActivity { /* empty */ } public static class TopLevelSettings extends SettingsActivity { /* empty */ } + public static class ApnSettingsActivity extends SettingsActivity { /* empty */ } } diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index bb5ac00..c566f6c 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -72,7 +72,6 @@ import com.android.settings.applications.InstalledAppDetails; import com.android.settings.applications.ManageApplications; import com.android.settings.applications.ProcessStatsUi; import com.android.settings.bluetooth.BluetoothSettings; -import com.android.settings.bluetooth.MessageAccessSettings; import com.android.settings.dashboard.DashboardCategory; import com.android.settings.dashboard.DashboardSummary; import com.android.settings.dashboard.DashboardTile; @@ -105,6 +104,7 @@ import com.android.settings.print.PrintSettingsFragment; import com.android.settings.sim.SimSettings; import com.android.settings.tts.TextToSpeechSettings; import com.android.settings.users.UserSettings; +import com.android.settings.voice.VoiceInputSettings; import com.android.settings.vpn2.VpnSettings; import com.android.settings.wfd.WifiDisplaySettings; import com.android.settings.widget.SwitchBar; @@ -184,8 +184,13 @@ public class SettingsActivity extends Activity * that fragment. */ public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title"; - public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID = ":settings:show_fragment_title_resid"; - public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT = ":settings:show_fragment_as_shortcut"; + public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID = + ":settings:show_fragment_title_resid"; + public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT = + ":settings:show_fragment_as_shortcut"; + + public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING = + ":settings:show_fragment_as_subsetting"; private static final String META_DATA_KEY_FRAGMENT_CLASS = "com.android.settings.FRAGMENT_CLASS"; @@ -237,7 +242,6 @@ public class SettingsActivity extends Activity AdvancedWifiSettings.class.getName(), SavedAccessPointsWifiSettings.class.getName(), BluetoothSettings.class.getName(), - MessageAccessSettings.class.getName(), SimSettings.class.getName(), TetherSettings.class.getName(), WifiP2pSettings.class.getName(), @@ -245,6 +249,7 @@ public class SettingsActivity extends Activity DateTimeSettings.class.getName(), LocalePicker.class.getName(), InputMethodAndLanguageSettings.class.getName(), + VoiceInputSettings.class.getName(), SpellCheckersSettings.class.getName(), UserDictionaryList.class.getName(), UserDictionarySettings.class.getName(), @@ -256,6 +261,7 @@ public class SettingsActivity extends Activity NotificationStation.class.getName(), LocationSettings.class.getName(), SecuritySettings.class.getName(), + UsageAccessSettings.class.getName(), PrivacySettings.class.getName(), DeviceAdminSettings.class.getName(), AccessibilitySettings.class.getName(), @@ -290,7 +296,8 @@ public class SettingsActivity extends Activity NotificationAppList.class.getName(), AppNotificationSettings.class.getName(), OtherSoundSettings.class.getName(), - QuickLaunchSettings.class.getName() + QuickLaunchSettings.class.getName(), + ApnSettings.class.getName() }; @@ -495,12 +502,15 @@ public class SettingsActivity extends Activity final String className = cn.getClassName(); mIsShowingDashboard = className.equals(Settings.class.getName()); - final boolean isSubSettings = className.equals(SubSettings.class.getName()); - // If this is a sub settings or not the main Dashboard and not a Shortcut and an initial - // Fragment then apply the SubSettings theme for the ActionBar content insets - if (isSubSettings || - (!mIsShowingDashboard && !mIsShortcut && (initialFragmentName != null))) { + // This is a "Sub Settings" when: + // - this is a real SubSettings + // - or :settings:show_fragment_as_subsetting is passed to the Intent + final boolean isSubSettings = className.equals(SubSettings.class.getName()) || + intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false); + + // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets + if (isSubSettings) { // Check also that we are not a Theme Dialog as we don't want to override them final int themeResId = getThemeResId(); if (themeResId != R.style.Theme_DialogWhenLarge && @@ -550,6 +560,9 @@ public class SettingsActivity extends Activity } else if (isSubSettings) { mDisplayHomeAsUpEnabled = true; mDisplaySearch = true; + } else { + mDisplayHomeAsUpEnabled = false; + mDisplaySearch = false; } setTitleFromIntent(intent); @@ -1042,7 +1055,7 @@ public class SettingsActivity extends Activity } // Show the SIM Cards setting if there are more than 2 SIMs installed. - if(tile.id != R.id.sim_settings || SimSettings.showSimCardScreen(this)){ + if(tile.id != R.id.sim_settings || Utils.showSimCardTile(this)){ category.addTile(tile); } diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java index f89b72e..83b3a68 100644 --- a/src/com/android/settings/SettingsPreferenceFragment.java +++ b/src/com/android/settings/SettingsPreferenceFragment.java @@ -66,7 +66,7 @@ public class SettingsPreferenceFragment extends PreferenceFragment implements Di private boolean mPreferenceHighlighted = false; private Drawable mHighlightDrawable; - private Object mRegisterLock = new Object(); + private ListAdapter mCurrentRootAdapter; private boolean mIsDataSetObserverRegistered = false; private DataSetObserver mDataSetObserver = new DataSetObserver() { @Override @@ -147,6 +147,11 @@ public class SettingsPreferenceFragment extends PreferenceFragment implements Di } @Override + protected void onUnbindPreferences() { + unregisterObserverIfNeeded(); + } + + @Override public void onStop() { super.onStop(); @@ -154,20 +159,23 @@ public class SettingsPreferenceFragment extends PreferenceFragment implements Di } public void registerObserverIfNeeded() { - synchronized (mRegisterLock) { - if (!mIsDataSetObserverRegistered) { - getPreferenceScreen().getRootAdapter().registerDataSetObserver(mDataSetObserver); - mIsDataSetObserverRegistered = true; + if (!mIsDataSetObserverRegistered) { + if (mCurrentRootAdapter != null) { + mCurrentRootAdapter.unregisterDataSetObserver(mDataSetObserver); } + mCurrentRootAdapter = getPreferenceScreen().getRootAdapter(); + mCurrentRootAdapter.registerDataSetObserver(mDataSetObserver); + mIsDataSetObserverRegistered = true; } } public void unregisterObserverIfNeeded() { - synchronized (mRegisterLock) { - if (mIsDataSetObserverRegistered) { - getPreferenceScreen().getRootAdapter().unregisterDataSetObserver(mDataSetObserver); - mIsDataSetObserverRegistered = false; + if (mIsDataSetObserverRegistered) { + if (mCurrentRootAdapter != null) { + mCurrentRootAdapter.unregisterDataSetObserver(mDataSetObserver); + mCurrentRootAdapter = null; } + mIsDataSetObserverRegistered = false; } } diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index d960ce6..8069d52 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -40,9 +40,9 @@ import android.os.Environment; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; import android.text.TextUtils; import android.view.ViewGroup; import android.view.ViewParent; @@ -68,12 +68,12 @@ public class TetherSettings extends SettingsPreferenceFragment private static final int DIALOG_AP_SETTINGS = 1; private WebView mView; - private CheckBoxPreference mUsbTether; + private SwitchPreference mUsbTether; private WifiApEnabler mWifiApEnabler; - private CheckBoxPreference mEnableWifiAp; + private SwitchPreference mEnableWifiAp; - private CheckBoxPreference mBluetoothTether; + private SwitchPreference mBluetoothTether; private BroadcastReceiver mTetherChangeReceiver; @@ -135,10 +135,10 @@ public class TetherSettings extends SettingsPreferenceFragment } mEnableWifiAp = - (CheckBoxPreference) findPreference(ENABLE_WIFI_AP); + (SwitchPreference) findPreference(ENABLE_WIFI_AP); Preference wifiApSettings = findPreference(WIFI_AP_SSID_AND_SECURITY); - mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS); - mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING); + mUsbTether = (SwitchPreference) findPreference(USB_TETHER_SETTINGS); + mBluetoothTether = (SwitchPreference) findPreference(ENABLE_BLUETOOTH_TETHERING); ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); @@ -484,7 +484,7 @@ public class TetherSettings extends SettingsPreferenceFragment if (resultCode == Activity.RESULT_OK) { startTethering(); } else { - //BT and USB need checkbox turned off on failure + //BT and USB need switch turned off on failure //Wifi tethering is never turned on until afterwards switch (mTetherChoice) { case BLUETOOTH_TETHERING: diff --git a/src/com/android/settings/TrustAgentSettings.java b/src/com/android/settings/TrustAgentSettings.java new file mode 100644 index 0000000..54fef36 --- /dev/null +++ b/src/com/android/settings/TrustAgentSettings.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings; + +import java.util.List; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceGroup; +import android.preference.SwitchPreference; +import android.service.trust.TrustAgentService; +import android.util.ArrayMap; +import android.util.ArraySet; + +import com.android.internal.widget.LockPatternUtils; + +public class TrustAgentSettings extends SettingsPreferenceFragment implements + Preference.OnPreferenceChangeListener { + private static final String SERVICE_INTERFACE = TrustAgentService.SERVICE_INTERFACE; + private ArrayMap<ComponentName, AgentInfo> mAvailableAgents; + private final ArraySet<ComponentName> mActiveAgents = new ArraySet<ComponentName>(); + private LockPatternUtils mLockPatternUtils; + + public static final class AgentInfo { + CharSequence label; + ComponentName component; // service that implements ITrustAgent + SwitchPreference preference; + public Drawable icon; + + @Override + public boolean equals(Object other) { + if (other instanceof AgentInfo) { + return component.equals(((AgentInfo)other).component); + } + return true; + } + + public int compareTo(AgentInfo other) { + return component.compareTo(other.component); + } + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.trust_agent_settings); + } + + public void onResume() { + super.onResume(); + updateAgents(); + }; + + private void updateAgents() { + final Context context = getActivity(); + if (mAvailableAgents == null) { + mAvailableAgents = findAvailableTrustAgents(); + } + if (mLockPatternUtils == null) { + mLockPatternUtils = new LockPatternUtils(getActivity()); + } + loadActiveAgents(); + PreferenceGroup category = + (PreferenceGroup) getPreferenceScreen().findPreference("trust_agents"); + category.removeAll(); + final int count = mAvailableAgents.size(); + for (int i = 0; i < count; i++) { + AgentInfo agent = mAvailableAgents.valueAt(i); + final SwitchPreference preference = new SwitchPreference(context); + agent.preference = preference; + preference.setPersistent(false); + preference.setTitle(agent.label); + preference.setIcon(agent.icon); + preference.setPersistent(false); + preference.setOnPreferenceChangeListener(this); + preference.setChecked(mActiveAgents.contains(agent.component)); + category.addPreference(agent.preference); + } + } + + private void loadActiveAgents() { + List<ComponentName> activeTrustAgents = mLockPatternUtils.getEnabledTrustAgents(); + if (activeTrustAgents != null) { + mActiveAgents.addAll(activeTrustAgents); + } + } + + private void saveActiveAgents() { + mLockPatternUtils.setEnabledTrustAgents(mActiveAgents); + } + + ArrayMap<ComponentName, AgentInfo> findAvailableTrustAgents() { + PackageManager pm = getActivity().getPackageManager(); + Intent trustAgentIntent = new Intent(SERVICE_INTERFACE); + List<ResolveInfo> resolveInfos = pm.queryIntentServices(trustAgentIntent, + PackageManager.GET_META_DATA); + + ArrayMap<ComponentName, AgentInfo> agents = new ArrayMap<ComponentName, AgentInfo>(); + final int count = resolveInfos.size(); + agents.ensureCapacity(count); + for (int i = 0; i < count; i++ ) { + ResolveInfo resolveInfo = resolveInfos.get(i); + if (resolveInfo.serviceInfo == null) continue; + if (!TrustAgentUtils.checkProvidePermission(resolveInfo, pm)) continue; + ComponentName name = TrustAgentUtils.getComponentName(resolveInfo); + AgentInfo agentInfo = new AgentInfo(); + agentInfo.label = resolveInfo.loadLabel(pm); + agentInfo.icon = resolveInfo.loadIcon(pm); + agentInfo.component = name; + agents.put(name, agentInfo); + } + return agents; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference instanceof SwitchPreference) { + final int count = mAvailableAgents.size(); + for (int i = 0; i < count; i++) { + AgentInfo agent = mAvailableAgents.valueAt(i); + if (agent.preference == preference) { + if ((Boolean) newValue) { + if (!mActiveAgents.contains(agent.component)) { + mActiveAgents.add(agent.component); + } + } else { + mActiveAgents.remove(agent.component); + } + saveActiveAgents(); + return true; + } + } + } + return false; + } + +} diff --git a/src/com/android/settings/UsageAccessSettings.java b/src/com/android/settings/UsageAccessSettings.java index 8ae277d..8d0650b 100644 --- a/src/com/android/settings/UsageAccessSettings.java +++ b/src/com/android/settings/UsageAccessSettings.java @@ -20,8 +20,12 @@ import com.android.internal.content.PackageMonitor; import android.Manifest; import android.app.ActivityThread; +import android.app.AlertDialog; import android.app.AppOpsManager; +import android.app.Dialog; +import android.app.DialogFragment; import android.content.Context; +import android.content.DialogInterface; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -30,6 +34,7 @@ import android.os.Bundle; import android.os.Looper; import android.os.RemoteException; import android.preference.Preference; +import android.preference.PreferenceScreen; import android.preference.SwitchPreference; import android.util.ArrayMap; import android.util.Log; @@ -176,7 +181,7 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements if (newEntries == null) { mPackageEntryMap.clear(); - getPreferenceScreen().removeAll(); + mPreferenceScreen.removeAll(); return; } @@ -187,7 +192,7 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements final PackageEntry newPackageEntry = newEntries.get(oldPackageEntry.packageName); if (newPackageEntry == null) { // This package has been removed. - getPreferenceScreen().removePreference(oldPackageEntry.preference); + mPreferenceScreen.removePreference(oldPackageEntry.preference); } else { // This package already exists in the preference hierarchy, so reuse that // Preference. @@ -203,7 +208,7 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements packageEntry.preference = new SwitchPreference(mContext); packageEntry.preference.setPersistent(false); packageEntry.preference.setOnPreferenceChangeListener(UsageAccessSettings.this); - getPreferenceScreen().addPreference(packageEntry.preference); + mPreferenceScreen.addPreference(packageEntry.preference); } updatePreference(packageEntry); } @@ -232,20 +237,22 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements } } - private static boolean shouldIgnorePackage(String packageName) { + static boolean shouldIgnorePackage(String packageName) { return packageName.equals("android") || packageName.equals("com.android.settings"); } - private ArrayMap<String, PackageEntry> mPackageEntryMap = new ArrayMap<>(); - private AppOpsManager mAppOpsManager; private AppsRequestingAccessFetcher mLastFetcherTask; + ArrayMap<String, PackageEntry> mPackageEntryMap = new ArrayMap<>(); + AppOpsManager mAppOpsManager; + PreferenceScreen mPreferenceScreen; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.usage_access_settings); - getPreferenceScreen().setOrderingAsAdded(false); + mPreferenceScreen = getPreferenceScreen(); + mPreferenceScreen.setOrderingAsAdded(false); mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); } @@ -302,13 +309,27 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements // Check if we need to do any work. if (pe.appOpMode != newMode) { - mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS, - pe.packageInfo.applicationInfo.uid, packageName, newMode); - pe.appOpMode = newMode; + if (newMode != AppOpsManager.MODE_ALLOWED) { + // Turning off the setting has no warning. + setNewMode(pe, newMode); + return true; + } + + // Turning on the setting has a Warning. + getFragmentManager().beginTransaction() + .add(new WarningDialog(pe), "warning") + .commit(); + return false; } return true; } + void setNewMode(PackageEntry pe, int newMode) { + mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS, + pe.packageInfo.applicationInfo.uid, pe.packageName, newMode); + pe.appOpMode = newMode; + } + private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onPackageAdded(String packageName, int uid) { @@ -320,4 +341,34 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements updateInterestedApps(); } }; + + private class WarningDialog extends DialogFragment + implements DialogInterface.OnClickListener { + private final PackageEntry mEntry; + + public WarningDialog(PackageEntry pe) { + mEntry = pe; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.allow_usage_access_title) + .setMessage(R.string.allow_usage_access_message) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setNegativeButton(R.string.cancel, this) + .setPositiveButton(android.R.string.ok, this) + .create(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + setNewMode(mEntry, AppOpsManager.MODE_ALLOWED); + mEntry.preference.setChecked(true); + } else { + dialog.cancel(); + } + } + } } diff --git a/src/com/android/settings/UsageStats.java b/src/com/android/settings/UsageStatsActivity.java index 08c272e..90aec5b 100755 --- a/src/com/android/settings/UsageStats.java +++ b/src/com/android/settings/UsageStatsActivity.java @@ -17,7 +17,7 @@ package com.android.settings; import android.app.Activity; -import android.app.usage.PackageUsageStats; +import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.Map; import android.text.format.DateUtils; @@ -48,15 +49,15 @@ import android.widget.AdapterView.OnItemSelectedListener; /** * Activity to display package usage statistics. */ -public class UsageStats extends Activity implements OnItemSelectedListener { +public class UsageStatsActivity extends Activity implements OnItemSelectedListener { private static final String TAG = "UsageStatsActivity"; private static final boolean localLOGV = false; private UsageStatsManager mUsageStatsManager; private LayoutInflater mInflater; private UsageStatsAdapter mAdapter; private PackageManager mPm; - - public static class AppNameComparator implements Comparator<PackageUsageStats> { + + public static class AppNameComparator implements Comparator<UsageStats> { private Map<String, String> mAppLabelList; AppNameComparator(Map<String, String> appList) { @@ -64,76 +65,87 @@ public class UsageStats extends Activity implements OnItemSelectedListener { } @Override - public final int compare(PackageUsageStats a, PackageUsageStats b) { + public final int compare(UsageStats a, UsageStats b) { String alabel = mAppLabelList.get(a.getPackageName()); String blabel = mAppLabelList.get(b.getPackageName()); return alabel.compareTo(blabel); } } - public static class LastTimeUsedComparator implements Comparator<PackageUsageStats> { + public static class LastTimeUsedComparator implements Comparator<UsageStats> { @Override - public final int compare(PackageUsageStats a, PackageUsageStats b) { + public final int compare(UsageStats a, UsageStats b) { // return by descending order return (int)(b.getLastTimeUsed() - a.getLastTimeUsed()); } } - - public static class UsageTimeComparator implements Comparator<PackageUsageStats> { + + public static class UsageTimeComparator implements Comparator<UsageStats> { @Override - public final int compare(PackageUsageStats a, PackageUsageStats b) { - return (int)(b.getTotalTimeSpent() - a.getTotalTimeSpent()); + public final int compare(UsageStats a, UsageStats b) { + return (int)(b.getTotalTimeInForeground() - a.getTotalTimeInForeground()); } } - + // View Holder used when displaying views static class AppViewHolder { TextView pkgName; TextView lastTimeUsed; TextView usageTime; } - + class UsageStatsAdapter extends BaseAdapter { // Constants defining order for display order private static final int _DISPLAY_ORDER_USAGE_TIME = 0; private static final int _DISPLAY_ORDER_LAST_TIME_USED = 1; private static final int _DISPLAY_ORDER_APP_NAME = 2; - + private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME; private LastTimeUsedComparator mLastTimeUsedComparator = new LastTimeUsedComparator(); private UsageTimeComparator mUsageTimeComparator = new UsageTimeComparator(); private AppNameComparator mAppLabelComparator; private final ArrayMap<String, String> mAppLabelMap = new ArrayMap<>(); - private final ArrayList<PackageUsageStats> mPackageStats = new ArrayList<>(); + private final ArrayList<UsageStats> mPackageStats = new ArrayList<>(); UsageStatsAdapter() { Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_YEAR, -5); - final android.app.usage.UsageStats stats = - mUsageStatsManager.getRecentStatsSince(cal.getTimeInMillis()); + final List<UsageStats> stats = + mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, + cal.getTimeInMillis(), System.currentTimeMillis()); if (stats == null) { return; } - final int pkgCount = stats.getPackageCount(); - for (int i = 0; i < pkgCount; i++) { - final PackageUsageStats pkgStats = stats.getPackage(i); + ArrayMap<String, UsageStats> map = new ArrayMap<>(); + final int statCount = stats.size(); + for (int i = 0; i < statCount; i++) { + final android.app.usage.UsageStats pkgStats = stats.get(i); // load application labels for each application try { ApplicationInfo appInfo = mPm.getApplicationInfo(pkgStats.getPackageName(), 0); String label = appInfo.loadLabel(mPm).toString(); mAppLabelMap.put(pkgStats.getPackageName(), label); - mPackageStats.add(pkgStats); + + UsageStats existingStats = + map.get(pkgStats.getPackageName()); + if (existingStats == null) { + map.put(pkgStats.getPackageName(), pkgStats); + } else { + existingStats.add(pkgStats); + } + } catch (NameNotFoundException e) { // This package may be gone. } - } + } + mPackageStats.addAll(map.values()); - // Sort list - mAppLabelComparator = new AppNameComparator(mAppLabelMap); - sortList(); + // Sort list + mAppLabelComparator = new AppNameComparator(mAppLabelMap); + sortList(); } @Override @@ -177,20 +189,20 @@ public class UsageStats extends Activity implements OnItemSelectedListener { } // Bind the data efficiently with the holder - PackageUsageStats pkgStats = mPackageStats.get(position); + UsageStats pkgStats = mPackageStats.get(position); if (pkgStats != null) { String label = mAppLabelMap.get(pkgStats.getPackageName()); holder.pkgName.setText(label); holder.lastTimeUsed.setText(DateUtils.formatSameDayTime(pkgStats.getLastTimeUsed(), System.currentTimeMillis(), DateFormat.MEDIUM, DateFormat.MEDIUM)); holder.usageTime.setText( - DateUtils.formatElapsedTime(pkgStats.getTotalTimeSpent() / 1000)); + DateUtils.formatElapsedTime(pkgStats.getTotalTimeInForeground() / 1000)); } else { Log.w(TAG, "No usage stats info for package:" + position); } return convertView; } - + void sortList(int sortOrder) { if (mDisplayOrder == sortOrder) { // do nothing @@ -226,7 +238,7 @@ public class UsageStats extends Activity implements OnItemSelectedListener { Spinner typeSpinner = (Spinner) findViewById(R.id.typeSpinner); typeSpinner.setOnItemSelectedListener(this); - + ListView listView = (ListView) findViewById(R.id.pkg_list); mAdapter = new UsageStatsAdapter(); listView.setAdapter(mAdapter); @@ -242,4 +254,3 @@ public class UsageStats extends Activity implements OnItemSelectedListener { // do nothing } } - diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 255ab58..47ff6af 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -30,9 +30,11 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.pm.Signature; import android.content.pm.UserInfo; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -774,17 +776,24 @@ public final class Utils { DialogInterface.OnClickListener onConfirmListener) { UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); UserInfo userInfo = um.getUserInfo(removingUserId); + int titleResId; + int messageResId; + if (UserHandle.myUserId() == removingUserId) { + titleResId = R.string.user_confirm_remove_self_title; + messageResId = R.string.user_confirm_remove_self_message; + } else if (userInfo.isRestricted()) { + titleResId = R.string.user_profile_confirm_remove_title; + messageResId = R.string.user_profile_confirm_remove_message; + } else if (userInfo.isManagedProfile()) { + titleResId = R.string.work_profile_confirm_remove_title; + messageResId = R.string.work_profile_confirm_remove_message; + } else { + titleResId = R.string.user_confirm_remove_title; + messageResId = R.string.user_confirm_remove_message; + } Dialog dlg = new AlertDialog.Builder(context) - .setTitle(UserHandle.myUserId() == removingUserId - ? R.string.user_confirm_remove_self_title - : (userInfo.isRestricted() - ? R.string.user_profile_confirm_remove_title - : R.string.user_confirm_remove_title)) - .setMessage(UserHandle.myUserId() == removingUserId - ? R.string.user_confirm_remove_self_message - : (userInfo.isRestricted() - ? R.string.user_profile_confirm_remove_message - : R.string.user_confirm_remove_message)) + .setTitle(titleResId) + .setMessage(messageResId) .setPositiveButton(R.string.user_delete_button, onConfirmListener) .setNegativeButton(android.R.string.cancel, null) @@ -820,4 +829,46 @@ public final class Utils { if (icon == null) return null; return CircleFramedDrawable.getInstance(context, icon); } + + /** + * Return whether or not the user should have a SIM Cards option in Settings. + * TODO: Change back to returning true if count is greater than one after testing. + * TODO: See bug 16533525. + */ + public static boolean showSimCardTile(Context context) { + final TelephonyManager tm = + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + + return tm.getSimCount() > 0; + } + + /** + * Determine whether a package is a "system package", in which case certain things (like + * disabling notifications or disabling the package altogether) should be disallowed. + */ + public static boolean isSystemPackage(PackageManager pm, PackageInfo pkg) { + if (sSystemSignature == null) { + sSystemSignature = new Signature[]{ getSystemSignature(pm) }; + } + return sSystemSignature[0] != null && sSystemSignature[0].equals(getFirstSignature(pkg)); + } + + private static Signature[] sSystemSignature; + + private static Signature getFirstSignature(PackageInfo pkg) { + if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) { + return pkg.signatures[0]; + } + return null; + } + + private static Signature getSystemSignature(PackageManager pm) { + try { + final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); + return getFirstSignature(sys); + } catch (NameNotFoundException e) { + } + return null; + } + } diff --git a/src/com/android/settings/VoiceSettingsActivity.java b/src/com/android/settings/VoiceSettingsActivity.java new file mode 100644 index 0000000..b5e8ede --- /dev/null +++ b/src/com/android/settings/VoiceSettingsActivity.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +/** + * Activity for modifying a setting using the Voice Interaction API. This activity + * MUST only modify the setting if the intent was sent using + * {@link android.service.voice.VoiceInteractionSession#startVoiceActivity startVoiceActivity}. + */ +abstract public class VoiceSettingsActivity extends Activity { + + private static final String TAG = "VoiceSettingsActivity"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (isVoiceInteraction()) { + // Only permit if this is a voice interaction. + onVoiceSettingInteraction(getIntent()); + } else { + Log.v(TAG, "Cannot modify settings without voice interaction"); + } + finish(); + } + + /** + * Modify the setting as a voice interaction. The activity will finish + * after this method is called. + */ + abstract protected void onVoiceSettingInteraction(Intent intent); +} diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java index 39d0f4c..ab7e28b 100644 --- a/src/com/android/settings/WirelessSettings.java +++ b/src/com/android/settings/WirelessSettings.java @@ -39,6 +39,7 @@ import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; import android.provider.SearchIndexableResource; import android.provider.Settings; import android.telephony.TelephonyManager; @@ -79,7 +80,7 @@ public class WirelessSettings extends SettingsPreferenceFragment public static final int REQUEST_CODE_EXIT_ECM = 1; private AirplaneModeEnabler mAirplaneModeEnabler; - private CheckBoxPreference mAirplaneModePreference; + private SwitchPreference mAirplaneModePreference; private NfcEnabler mNfcEnabler; private NfcAdapter mNfcAdapter; private NsdEnabler mNsdEnabler; @@ -128,9 +129,13 @@ public class WirelessSettings extends SettingsPreferenceFragment if (mTm.hasIccCard() && (ni != null)) { // Check for carrier apps that can handle provisioning first Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP); - provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX - + mTm.getSimOperator()); - if (mPm.resolveActivity(provisioningIntent, 0 /* flags */) != null) { + List<String> carrierPackages = + mTm.getCarrierPackageNamesForIntent(provisioningIntent); + if (carrierPackages != null && !carrierPackages.isEmpty()) { + if (carrierPackages.size() != 1) { + Log.w(TAG, "Multiple matching carrier apps found, launching the first."); + } + provisioningIntent.setPackage(carrierPackages.get(0)); startActivity(provisioningIntent); return; } @@ -254,8 +259,8 @@ public class WirelessSettings extends SettingsPreferenceFragment final boolean isSecondaryUser = UserHandle.myUserId() != UserHandle.USER_OWNER; final Activity activity = getActivity(); - mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE); - CheckBoxPreference nfc = (CheckBoxPreference) findPreference(KEY_TOGGLE_NFC); + mAirplaneModePreference = (SwitchPreference) findPreference(KEY_TOGGLE_AIRPLANE); + SwitchPreference nfc = (SwitchPreference) findPreference(KEY_TOGGLE_NFC); PreferenceScreen androidBeam = (PreferenceScreen) findPreference(KEY_ANDROID_BEAM_SETTINGS); CheckBoxPreference nsd = (CheckBoxPreference) findPreference(KEY_TOGGLE_NSD); diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 36025a6..92c478e 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -18,6 +18,7 @@ package com.android.settings.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; import android.app.ActivityManagerNative; +import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; @@ -28,6 +29,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.os.UserHandle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; @@ -197,11 +199,15 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements private int mLongPressTimeoutDefault; + private DevicePolicyManager mDpm; + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.accessibility_settings); initializeAllPreferences(); + mDpm = (DevicePolicyManager) (getActivity() + .getSystemService(Context.DEVICE_POLICY_SERVICE)); } @Override @@ -434,7 +440,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements accessibilityManager.getInstalledAccessibilityServiceList(); Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings( getActivity()); - + List<String> permittedServices = mDpm.getPermittedAccessibilityServices( + UserHandle.myUserId()); final boolean accessibilityEnabled = Settings.Secure.getInt(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; @@ -454,11 +461,26 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements preference.setTitle(title); final boolean serviceEnabled = accessibilityEnabled && enabledServices.contains(componentName); + String serviceEnabledString; if (serviceEnabled) { - preference.setSummary(getString(R.string.accessibility_feature_state_on)); + serviceEnabledString = getString(R.string.accessibility_feature_state_on); } else { - preference.setSummary(getString(R.string.accessibility_feature_state_off)); + serviceEnabledString = getString(R.string.accessibility_feature_state_off); + } + + // Disable all accessibility services that are not permitted. + String packageName = serviceInfo.packageName; + boolean serviceAllowed = + permittedServices == null || permittedServices.contains(packageName); + preference.setEnabled(serviceAllowed || serviceEnabled); + + String summaryString; + if (serviceAllowed) { + summaryString = serviceEnabledString; + } else { + summaryString = getString(R.string.accessibility_feature_or_input_method_not_allowed); } + preference.setSummary(summaryString); preference.setOrder(i); preference.setFragment(ToggleAccessibilityServicePreferenceFragment.class.getName()); @@ -491,19 +513,13 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements if (mServicesCategory.getPreferenceCount() == 0) { if (mNoServicesMessagePreference == null) { - mNoServicesMessagePreference = new Preference(getActivity()) { - @Override - protected void onBindView(View view) { - super.onBindView(view); - TextView summaryView = (TextView) view.findViewById(R.id.summary); - String title = getString(R.string.accessibility_no_services_installed); - summaryView.setText(title); - } - }; + mNoServicesMessagePreference = new Preference(getActivity()); mNoServicesMessagePreference.setPersistent(false); mNoServicesMessagePreference.setLayoutResource( R.layout.text_description_preference); mNoServicesMessagePreference.setSelectable(false); + mNoServicesMessagePreference.setSummary( + getString(R.string.accessibility_no_services_installed)); } mServicesCategory.addPreference(mNoServicesMessagePreference); } diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 9fe70df..b23035b 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -55,10 +55,10 @@ public abstract class ToggleFeaturePreferenceFragment getActivity()); setPreferenceScreen(preferenceScreen); mSummaryPreference = new Preference(getActivity()) { - @Override + @Override protected void onBindView(View view) { super.onBindView(view); - TextView summaryView = (TextView) view.findViewById(R.id.summary); + final TextView summaryView = (TextView) view.findViewById(android.R.id.summary); summaryView.setText(getSummary()); sendAccessibilityEvent(summaryView); } diff --git a/src/com/android/settings/accounts/AccountSettings.java b/src/com/android/settings/accounts/AccountSettings.java index ffd6037..9bcef13 100644 --- a/src/com/android/settings/accounts/AccountSettings.java +++ b/src/com/android/settings/accounts/AccountSettings.java @@ -29,10 +29,12 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; +import android.os.Process; import android.util.Log; import android.util.SparseArray; import android.view.Menu; @@ -41,6 +43,7 @@ import android.view.MenuItem; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceGroup; +import android.preference.PreferenceCategory; import android.preference.PreferenceScreen; import com.android.settings.R; @@ -50,8 +53,10 @@ import com.android.settings.Utils; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.List; import static android.content.Intent.EXTRA_USER; +import static android.os.UserManager.DISALLOW_MODIFY_ACCOUNTS; /** * Settings screen for the account types on the device. @@ -64,26 +69,20 @@ public class AccountSettings extends SettingsPreferenceFragment implements AuthenticatorHelper.OnAccountsUpdateListener, OnPreferenceClickListener { public static final String TAG = "AccountSettings"; - private static final String KEY_ACCOUNT = "account"; - private static final String KEY_ADD_ACCOUNT = "add_account"; - private static final String KEY_CATEGORY_PERSONAL = "account_personal"; - private static final String KEY_ADD_ACCOUNT_PERSONAL = "add_account_personal"; - private static final String KEY_CATEGORY_WORK = "account_work"; - private static final String KEY_ADD_ACCOUNT_WORK = "add_account_work"; + private static final String KEY_ACCOUNT = "account"; private static final String ADD_ACCOUNT_ACTION = "android.settings.ADD_ACCOUNT_SETTINGS"; - - private static final ArrayList<String> EMPTY_LIST = new ArrayList<String>(); - private static final String TAG_CONFIRM_AUTO_SYNC_CHANGE = "confirmAutoSyncChange"; + private static final int ORDER_LAST = 1001; + private static final int ORDER_NEXT_TO_LAST = 1000; private UserManager mUm; - private SparseArray<ProfileData> mProfiles; + private SparseArray<ProfileData> mProfiles = new SparseArray<ProfileData>(); private ManagedProfileBroadcastReceiver mManagedProfileBroadcastReceiver = new ManagedProfileBroadcastReceiver(); - private boolean mIsSingleProfileUi = true; + private Preference mProfileNotAvailablePreference; /** * Holds data related to the accounts belonging to one profile. @@ -98,64 +97,62 @@ public class AccountSettings extends SettingsPreferenceFragment */ public Preference addAccountPreference; /** - * The user handle of the user that these accounts belong to. + * The preference that displays the button to remove the managed profile */ - public UserHandle userHandle; + public Preference removeWorkProfilePreference; /** * The {@link AuthenticatorHelper} that holds accounts data for this profile. */ public AuthenticatorHelper authenticatorHelper; + /** + * The {@link UserInfo} of the profile. + */ + public UserInfo userInfo; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mUm = (UserManager) getSystemService(Context.USER_SERVICE); - mProfiles = new SparseArray<ProfileData>(2); + mProfileNotAvailablePreference = new Preference(getActivity()); setHasOptionsMenu(true); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.account_settings, menu); - final UserHandle currentProfile = UserHandle.getCallingUserHandle(); - if (mIsSingleProfileUi) { - menu.findItem(R.id.account_settings_menu_auto_sync) - .setVisible(true) - .setOnMenuItemClickListener(new MasterSyncStateClickListener(currentProfile)); - menu.removeItem(R.id.account_settings_menu_auto_sync_personal); - menu.removeItem(R.id.account_settings_menu_auto_sync_work); - } else { - final UserHandle managedProfile = Utils.getManagedProfile(mUm); - - menu.findItem(R.id.account_settings_menu_auto_sync_personal) - .setVisible(true) - .setOnMenuItemClickListener(new MasterSyncStateClickListener(currentProfile)); - menu.findItem(R.id.account_settings_menu_auto_sync_work) - .setVisible(true) - .setOnMenuItemClickListener(new MasterSyncStateClickListener(managedProfile)); - menu.removeItem(R.id.account_settings_menu_auto_sync); - } super.onCreateOptionsMenu(menu, inflater); } @Override public void onPrepareOptionsMenu(Menu menu) { - final UserHandle currentProfile = UserHandle.getCallingUserHandle(); - if (mIsSingleProfileUi) { + final UserHandle currentProfile = Process.myUserHandle(); + if (mProfiles.size() == 1) { menu.findItem(R.id.account_settings_menu_auto_sync) + .setVisible(true) + .setOnMenuItemClickListener(new MasterSyncStateClickListener(currentProfile)) .setChecked(ContentResolver.getMasterSyncAutomaticallyAsUser( currentProfile.getIdentifier())); - } else { - final UserHandle managedProfile = Utils.getManagedProfile(mUm); + menu.findItem(R.id.account_settings_menu_auto_sync_personal).setVisible(false); + menu.findItem(R.id.account_settings_menu_auto_sync_work).setVisible(false); + } else if (mProfiles.size() > 1) { + // We assume there's only one managed profile, otherwise UI needs to change + final UserHandle managedProfile = mProfiles.valueAt(1).userInfo.getUserHandle(); menu.findItem(R.id.account_settings_menu_auto_sync_personal) + .setVisible(true) + .setOnMenuItemClickListener(new MasterSyncStateClickListener(currentProfile)) .setChecked(ContentResolver.getMasterSyncAutomaticallyAsUser( currentProfile.getIdentifier())); menu.findItem(R.id.account_settings_menu_auto_sync_work) + .setVisible(true) + .setOnMenuItemClickListener(new MasterSyncStateClickListener(managedProfile)) .setChecked(ContentResolver.getMasterSyncAutomaticallyAsUser( managedProfile.getIdentifier())); - } + menu.findItem(R.id.account_settings_menu_auto_sync).setVisible(false); + } else { + Log.w(TAG, "Method onPrepareOptionsMenu called before mProfiles was initialized"); + } } @Override @@ -192,10 +189,22 @@ public class AccountSettings extends SettingsPreferenceFragment ProfileData profileData = mProfiles.valueAt(i); if (preference == profileData.addAccountPreference) { Intent intent = new Intent(ADD_ACCOUNT_ACTION); - intent.putExtra(EXTRA_USER, profileData.userHandle); + intent.putExtra(EXTRA_USER, profileData.userInfo.getUserHandle()); startActivity(intent); return true; } + if (preference == profileData.removeWorkProfilePreference) { + final int userId = profileData.userInfo.id; + Utils.createRemoveConfirmationDialog(getActivity(), userId, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mUm.removeUser(userId); + } + } + ).show(); + return true; + } } return false; } @@ -204,61 +213,82 @@ public class AccountSettings extends SettingsPreferenceFragment // Load the preferences from an XML resource addPreferencesFromResource(R.xml.account_settings); + if (Utils.isManagedProfile(mUm)) { + // This should not happen + Log.e(TAG, "We should not be showing settings for a managed profile"); + finish(); + return; + } + + final PreferenceScreen preferenceScreen = (PreferenceScreen) findPreference(KEY_ACCOUNT); if(mUm.isLinkedUser()) { // Restricted user or similar - updateSingleProfileUi(); + UserInfo userInfo = mUm.getUserInfo(UserHandle.myUserId()); + updateProfileUi(userInfo, false /* no category needed */, preferenceScreen); } else { - if (Utils.isManagedProfile(mUm)) { - // This should not happen - Log.w(TAG, "We should not be showing settings for a managed profile"); - updateSingleProfileUi(); + List<UserInfo> profiles = mUm.getProfiles(UserHandle.myUserId()); + final int profilesCount = profiles.size(); + final boolean addCategory = profilesCount > 1; + for (int i = 0; i < profilesCount; i++) { + updateProfileUi(profiles.get(i), addCategory, preferenceScreen); } - final UserHandle currentProfile = UserHandle.getCallingUserHandle(); - final UserHandle managedProfile = Utils.getManagedProfile(mUm); - if (managedProfile == null) { - updateSingleProfileUi(); - } else { - mIsSingleProfileUi = false; - updateProfileUi(currentProfile, KEY_CATEGORY_PERSONAL, KEY_ADD_ACCOUNT_PERSONAL, - EMPTY_LIST); - final ArrayList<String> unusedPreferences = new ArrayList<String>(2); - unusedPreferences.add(KEY_ADD_ACCOUNT); - updateProfileUi(managedProfile, KEY_CATEGORY_WORK, KEY_ADD_ACCOUNT_WORK, - unusedPreferences); - } - } - final int count = mProfiles.size(); - for (int i = 0; i < count; i++) { - updateAccountTypes(mProfiles.valueAt(i)); } - } - private void updateSingleProfileUi() { - final ArrayList<String> unusedPreferences = new ArrayList<String>(2); - unusedPreferences.add(KEY_CATEGORY_PERSONAL); - unusedPreferences.add(KEY_CATEGORY_WORK); - updateProfileUi(UserHandle.getCallingUserHandle(), KEY_ACCOUNT, KEY_ADD_ACCOUNT, - unusedPreferences); + // Add all preferences, starting with one for the primary profile. + // Note that we're relying on the ordering given by the SparseArray keys, and on the + // value of UserHandle.USER_OWNER being smaller than all the rest. + final int profilesCount = mProfiles.size(); + for (int i = 0; i < profilesCount; i++) { + ProfileData profileData = mProfiles.valueAt(i); + if (!profileData.preferenceGroup.equals(preferenceScreen)) { + preferenceScreen.addPreference(profileData.preferenceGroup); + } + updateAccountTypes(profileData); + } } - private void updateProfileUi(final UserHandle userHandle, String categoryKey, - String addAccountKey, ArrayList<String> unusedPreferences) { - final int count = unusedPreferences.size(); - for (int i = 0; i < count; i++) { - removePreference(unusedPreferences.get(i)); - } + private void updateProfileUi(final UserInfo userInfo, boolean addCategory, + PreferenceScreen parent) { + final Context context = getActivity(); final ProfileData profileData = new ProfileData(); - profileData.preferenceGroup = (PreferenceGroup) findPreference(categoryKey); - if (mUm.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, userHandle)) { - removePreference(addAccountKey); + profileData.userInfo = userInfo; + if (addCategory) { + profileData.preferenceGroup = new PreferenceCategory(context); + profileData.preferenceGroup.setTitle(userInfo.isManagedProfile() + ? R.string.category_work : R.string.category_personal); + parent.addPreference(profileData.preferenceGroup); } else { - profileData.addAccountPreference = findPreference(addAccountKey); - profileData.addAccountPreference.setOnPreferenceClickListener(this); + profileData.preferenceGroup = parent; + } + if (userInfo.isEnabled()) { + profileData.authenticatorHelper = new AuthenticatorHelper(context, + userInfo.getUserHandle(), mUm, this); + if (!mUm.hasUserRestriction(DISALLOW_MODIFY_ACCOUNTS, userInfo.getUserHandle())) { + profileData.addAccountPreference = newAddAccountPreference(context); + } + } + if (userInfo.isManagedProfile()) { + profileData.removeWorkProfilePreference = newRemoveWorkProfilePreference(context); } - profileData.userHandle = userHandle; - profileData.authenticatorHelper = new AuthenticatorHelper( - getActivity(), userHandle, mUm, this); - mProfiles.put(userHandle.getIdentifier(), profileData); + mProfiles.put(userInfo.id, profileData); + } + + private Preference newAddAccountPreference(Context context) { + Preference preference = new Preference(context); + preference.setTitle(R.string.add_account_label); + preference.setIcon(R.drawable.ic_menu_add_dark); + preference.setOnPreferenceClickListener(this); + preference.setOrder(ORDER_NEXT_TO_LAST); + return preference; + } + + private Preference newRemoveWorkProfilePreference(Context context) { + Preference preference = new Preference(context); + preference.setTitle(R.string.remove_managed_profile_label); + preference.setIcon(R.drawable.ic_menu_delete); + preference.setOnPreferenceClickListener(this); + preference.setOrder(ORDER_LAST); + return preference; } private void cleanUpPreferences() { @@ -266,32 +296,52 @@ public class AccountSettings extends SettingsPreferenceFragment if (preferenceScreen != null) { preferenceScreen.removeAll(); } + mProfiles.clear(); } private void listenToAccountUpdates() { final int count = mProfiles.size(); for (int i = 0; i < count; i++) { - mProfiles.valueAt(i).authenticatorHelper.listenToAccountUpdates(); + AuthenticatorHelper authenticatorHelper = mProfiles.valueAt(i).authenticatorHelper; + if (authenticatorHelper != null) { + authenticatorHelper.listenToAccountUpdates(); + } } } private void stopListeningToAccountUpdates() { final int count = mProfiles.size(); for (int i = 0; i < count; i++) { - mProfiles.valueAt(i).authenticatorHelper.stopListeningToAccountUpdates(); + AuthenticatorHelper authenticatorHelper = mProfiles.valueAt(i).authenticatorHelper; + if (authenticatorHelper != null) { + authenticatorHelper.stopListeningToAccountUpdates(); + } } } private void updateAccountTypes(ProfileData profileData) { profileData.preferenceGroup.removeAll(); - final ArrayList<AccountPreference> preferences = getAccountTypePreferences( - profileData.authenticatorHelper, profileData.userHandle); - final int count = preferences.size(); - for (int i = 0; i < count; i++) { - profileData.preferenceGroup.addPreference(preferences.get(i)); + if (profileData.userInfo.isEnabled()) { + final ArrayList<AccountPreference> preferences = getAccountTypePreferences( + profileData.authenticatorHelper, profileData.userInfo.getUserHandle()); + final int count = preferences.size(); + for (int i = 0; i < count; i++) { + profileData.preferenceGroup.addPreference(preferences.get(i)); + } + if (profileData.addAccountPreference != null) { + profileData.preferenceGroup.addPreference(profileData.addAccountPreference); + } + } else { + // Put a label instead of the accounts list + mProfileNotAvailablePreference.setEnabled(false); + mProfileNotAvailablePreference.setIcon(R.drawable.empty_icon); + mProfileNotAvailablePreference.setTitle(null); + mProfileNotAvailablePreference.setSummary( + R.string.managed_profile_not_available_label); + profileData.preferenceGroup.addPreference(mProfileNotAvailablePreference); } - if (profileData.addAccountPreference != null) { - profileData.preferenceGroup.addPreference(profileData.addAccountPreference); + if (profileData.removeWorkProfilePreference != null) { + profileData.preferenceGroup.addPreference(profileData.removeWorkProfilePreference); } } @@ -404,6 +454,9 @@ public class AccountSettings extends SettingsPreferenceFragment // Build new state updateUi(); listenToAccountUpdates(); + // Force the menu to update. Note that #onPrepareOptionsMenu uses data built by + // #updateUi so we must call this later + getActivity().invalidateOptionsMenu(); return; } Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction()); diff --git a/src/com/android/settings/accounts/ManageAccountsSettings.java b/src/com/android/settings/accounts/ManageAccountsSettings.java index b69bf3f..8787bce 100644 --- a/src/com/android/settings/accounts/ManageAccountsSettings.java +++ b/src/com/android/settings/accounts/ManageAccountsSettings.java @@ -97,6 +97,8 @@ public class ManageAccountsSettings extends AccountPreferenceBase public void onStart() { super.onStart(); mAuthenticatorHelper.listenToAccountUpdates(); + updateAuthDescriptions(); + showAccountsIfNeeded(); } @Override @@ -124,8 +126,6 @@ public class ManageAccountsSettings extends AccountPreferenceBase if (args != null && args.containsKey(KEY_ACCOUNT_LABEL)) { getActivity().setTitle(args.getString(KEY_ACCOUNT_LABEL)); } - updateAuthDescriptions(); - showAccountsIfNeeded(); } @Override diff --git a/src/com/android/settings/applications/AppOpsState.java b/src/com/android/settings/applications/AppOpsState.java index 75a8372..580c44e 100644 --- a/src/com/android/settings/applications/AppOpsState.java +++ b/src/com/android/settings/applications/AppOpsState.java @@ -190,13 +190,15 @@ public class AppOpsState { AppOpsManager.OP_WRITE_SETTINGS, AppOpsManager.OP_SYSTEM_ALERT_WINDOW, AppOpsManager.OP_WAKE_LOCK, - AppOpsManager.OP_PROJECT_MEDIA }, + AppOpsManager.OP_PROJECT_MEDIA, + AppOpsManager.OP_ACTIVATE_VPN, }, new boolean[] { false, true, true, true, true, true, + false, false, } ); diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 463520d..4b1bc10 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -316,22 +316,13 @@ public class InstalledAppDetails extends Fragment } } - private boolean isThisASystemPackage() { - try { - PackageInfo sys = mPm.getPackageInfo("android", PackageManager.GET_SIGNATURES); - return (mPackageInfo != null && mPackageInfo.signatures != null && - sys.signatures[0].equals(mPackageInfo.signatures[0])); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } - private boolean handleDisableable(Button button) { boolean disableable = false; // Try to prevent the user from bricking their phone // by not allowing disabling of apps signed with the // system cert and any launcher app in the system. - if (mHomePackages.contains(mAppEntry.info.packageName) || isThisASystemPackage()) { + if (mHomePackages.contains(mAppEntry.info.packageName) + || Utils.isSystemPackage(mPm, mPackageInfo)) { // Disable button for core system applications. button.setText(R.string.disable_text); } else if (mAppEntry.info.enabled) { @@ -347,18 +338,22 @@ public class InstalledAppDetails extends Fragment private void initUninstallButtons() { mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; + final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; boolean enabled = true; if (mUpdatedSysApp) { mUninstallButton.setText(R.string.app_factory_reset); - boolean specialDisable = false; - if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - specialDisable = handleDisableable(mSpecialDisableButton); + boolean showSpecialDisable = false; + if (isBundled) { + showSpecialDisable = handleDisableable(mSpecialDisableButton); mSpecialDisableButton.setOnClickListener(this); } - mMoreControlButtons.setVisibility(specialDisable ? View.VISIBLE : View.GONE); + if (mAppControlRestricted) { + showSpecialDisable = false; + } + mMoreControlButtons.setVisibility(showSpecialDisable ? View.VISIBLE : View.GONE); } else { mMoreControlButtons.setVisibility(View.GONE); - if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + if (isBundled) { enabled = handleDisableable(mUninstallButton); } else if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0 @@ -377,19 +372,27 @@ public class InstalledAppDetails extends Fragment enabled = false; } - // If this is the default (or only) home app, suppress uninstall (even if - // we still think it should be allowed for other reasons) + // Home apps need special handling. Bundled ones we don't risk downgrading + // because that can interfere with home-key resolution. Furthermore, we + // can't allow uninstallation of the only home app, and we don't want to + // allow uninstallation of an explicitly preferred one -- the user can go + // to Home settings and pick a different one, after which we'll permit + // uninstallation of the now-not-default one. if (enabled && mHomePackages.contains(mPackageInfo.packageName)) { - ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); - ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); - if (currentDefaultHome == null) { - // No preferred default, so permit uninstall only when - // there is more than one candidate - enabled = (mHomePackages.size() > 1); + if (isBundled) { + enabled = false; } else { - // There is an explicit default home app -- forbid uninstall of - // that one, but permit it for installed-but-inactive ones. - enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName()); + ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); + ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); + if (currentDefaultHome == null) { + // No preferred default, so permit uninstall only when + // there is more than one candidate + enabled = (mHomePackages.size() > 1); + } else { + // There is an explicit default home app -- forbid uninstall of + // that one, but permit it for installed-but-inactive ones. + enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName()); + } } } @@ -415,7 +418,7 @@ public class InstalledAppDetails extends Fragment // this does not bode well } mNotificationSwitch.setChecked(enabled); - if (isThisASystemPackage()) { + if (Utils.isSystemPackage(mPm, mPackageInfo)) { mNotificationSwitch.setEnabled(false); } else { mNotificationSwitch.setEnabled(true); diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index 1889634..ac5a78a 100644 --- a/src/com/android/settings/applications/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -74,7 +74,6 @@ import com.android.settings.SettingsActivity; import com.android.settings.UserSpinnerAdapter; import com.android.settings.Settings.RunningServicesActivity; import com.android.settings.Settings.StorageUseActivity; -import com.android.settings.UserSpinnerAdapter.UserDetails; import com.android.settings.applications.ApplicationsState.AppEntry; import com.android.settings.deviceinfo.StorageMeasurement; import com.android.settings.Utils; @@ -142,6 +141,7 @@ public class ManageApplications extends Fragment implements static final String TAG = "ManageApplications"; static final boolean DEBUG = false; + private static final String EXTRA_LIST_TYPE = "currentListType"; private static final String EXTRA_SORT_ORDER = "sortOrder"; private static final String EXTRA_SHOW_BACKGROUND = "showBackground"; private static final String EXTRA_DEFAULT_LIST_TYPE = "defaultListType"; @@ -467,7 +467,8 @@ public class ManageApplications extends Fragment implements // These are for keeping track of activity and spinner switch state. private boolean mActivityResumed; - + + private static final int LIST_TYPE_MISSING = -1; static final int LIST_TYPE_DOWNLOADED = 0; static final int LIST_TYPE_RUNNING = 1; static final int LIST_TYPE_SDCARD = 2; @@ -954,9 +955,13 @@ public class ManageApplications extends Fragment implements if (savedInstanceState == null) { // First time init: make sure view pager is showing the correct tab. - for (int i = 0; i < mTabs.size(); i++) { + int extraCurrentListType = getActivity().getIntent().getIntExtra(EXTRA_LIST_TYPE, + LIST_TYPE_MISSING); + int currentListType = (extraCurrentListType != LIST_TYPE_MISSING) + ? extraCurrentListType : mDefaultListType; + for (int i = 0; i < mNumTabs; i++) { TabInfo tab = mTabs.get(i); - if (tab.mListType == mDefaultListType) { + if (tab.mListType == currentListType) { mViewPager.setCurrentItem(i); break; } @@ -1036,6 +1041,8 @@ public class ManageApplications extends Fragment implements if (selectedUser.getIdentifier() != UserHandle.myUserId()) { Intent intent = new Intent(Settings.ACTION_APPLICATION_SETTINGS); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + int currentTab = mViewPager.getCurrentItem(); + intent.putExtra(EXTRA_LIST_TYPE, mTabs.get(currentTab).mListType); mContext.startActivityAsUser(intent, selectedUser); getActivity().finish(); } diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index e8bad0c..eca0cca 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -318,6 +318,6 @@ public final class BluetoothDevicePreference extends Preference implements return R.drawable.ic_bt_headset_hfp; } } - return 0; + return R.drawable.ic_settings_bluetooth2; } } diff --git a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java index bf0356c..4466aea 100644 --- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java @@ -92,7 +92,6 @@ public final class BluetoothNameDialogFragment extends DialogFragment implements mDeviceNameEdited = savedInstanceState.getBoolean(KEY_NAME_EDITED, false); } mAlertDialog = new AlertDialog.Builder(getActivity()) - .setIcon(android.R.drawable.ic_dialog_info) .setTitle(R.string.bluetooth_rename_device) .setView(createDialogView(deviceName)) .setPositiveButton(R.string.bluetooth_rename_button, @@ -178,7 +177,6 @@ public final class BluetoothNameDialogFragment extends DialogFragment implements mDeviceNameUpdated = true; mDeviceNameEdited = false; mDeviceNameView.setText(mLocalAdapter.getName()); - getActivity().setTitle(mLocalAdapter.getName()); } } diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java index 9922042..02eed99 100755 --- a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java @@ -175,7 +175,8 @@ public final class BluetoothPairingDialog extends AlertActivity implements private View createPinEntryView(String deviceName) { View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null); - TextView messageView = (TextView) view.findViewById(R.id.message); + TextView messageViewCaption = (TextView) view.findViewById(R.id.message_caption); + TextView messageViewContent = (TextView) view.findViewById(R.id.message_subhead); TextView messageView2 = (TextView) view.findViewById(R.id.message_below_pin); CheckBox alphanumericPin = (CheckBox) view.findViewById(R.id.alphanumeric_pin); mPairingView = (EditText) view.findViewById(R.id.text); @@ -194,7 +195,7 @@ public final class BluetoothPairingDialog extends AlertActivity implements break; case BluetoothDevice.PAIRING_VARIANT_PASSKEY: - messageId1 = R.string.bluetooth_enter_passkey_msg; + messageId1 = R.string.bluetooth_enter_pin_msg; messageId2 = R.string.bluetooth_enter_passkey_other_device; // Maximum of 6 digits for passkey maxLength = BLUETOOTH_PASSKEY_MAX_LENGTH; @@ -206,9 +207,8 @@ public final class BluetoothPairingDialog extends AlertActivity implements return null; } - // HTML escape deviceName, Format the message string, then parse HTML style tags - String messageText = getString(messageId1, Html.escapeHtml(deviceName)); - messageView.setText(Html.fromHtml(messageText)); + messageViewCaption.setText(messageId1); + messageViewContent.setText(deviceName); messageView2.setText(messageId2); mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER); mPairingView.setFilters(new InputFilter[] { @@ -219,33 +219,46 @@ public final class BluetoothPairingDialog extends AlertActivity implements private View createView(CachedBluetoothDeviceManager deviceManager) { View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_confirm, null); - // Escape device name to avoid HTML injection. + // Escape device name to avoid HTML injection. String name = Html.escapeHtml(deviceManager.getName(mDevice)); - TextView messageView = (TextView) view.findViewById(R.id.message); - - String messageText; // formatted string containing HTML style tags + TextView messageViewCaption = (TextView) view.findViewById(R.id.message_caption); + TextView messageViewContent = (TextView) view.findViewById(R.id.message_subhead); + TextView pairingViewCaption = (TextView) view.findViewById(R.id.pairing_caption); + TextView pairingViewContent = (TextView) view.findViewById(R.id.pairing_subhead); + TextView messagePairing = (TextView) view.findViewById(R.id.pairing_code_message); + + String messageCaption = null; + String pairingContent = null; switch (mType) { + case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY: + case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN: case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION: - messageText = getString(R.string.bluetooth_confirm_passkey_msg, - name, mPairingKey); + messageCaption = getString(R.string.bluetooth_enter_pin_msg); + pairingContent = mPairingKey; break; case BluetoothDevice.PAIRING_VARIANT_CONSENT: case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT: - messageText = getString(R.string.bluetooth_incoming_pairing_msg, name); - break; - - case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY: - case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN: - messageText = getString(R.string.bluetooth_display_passkey_pin_msg, name, - mPairingKey); + messageCaption = getString(R.string.bluetooth_enter_pin_msg); break; default: Log.e(TAG, "Incorrect pairing type received, not creating view"); return null; } - messageView.setText(Html.fromHtml(messageText)); + + if (messageViewCaption != null) { + messageViewCaption.setText(messageCaption); + messageViewContent.setText(name); + } + + if (pairingContent != null) { + pairingViewCaption.setVisibility(View.VISIBLE); + pairingViewContent.setVisibility(View.VISIBLE); + pairingViewContent.setText(pairingContent); + messagePairing.setVisibility(View.VISIBLE); + } + return view; } diff --git a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java index ea36fee..44198d3 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java @@ -90,7 +90,9 @@ public final class BluetoothPairingRequest extends BroadcastReceiver { .setContentText(res.getString(R.string.bluetooth_notif_message, name)) .setContentIntent(pending) .setAutoCancel(true) - .setDefaults(Notification.DEFAULT_SOUND); + .setDefaults(Notification.DEFAULT_SOUND) + .setColor(res.getColor( + com.android.internal.R.color.system_notification_accent_color)); NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java index c3b93be..1ede05b 100644 --- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java +++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java @@ -122,17 +122,20 @@ public final class BluetoothPermissionRequest extends BroadcastReceiver { break; } Notification notification = new Notification.Builder(context) - .setContentTitle(title) - .setTicker(message) - .setContentText(message) - .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) - .setAutoCancel(true) - .setPriority(Notification.PRIORITY_MAX) - .setOnlyAlertOnce(false) - .setDefaults(Notification.DEFAULT_ALL) - .setContentIntent(PendingIntent.getActivity(context, 0, connectionAccessIntent, 0)) - .setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent, 0)) - .build(); + .setContentTitle(title) + .setTicker(message) + .setContentText(message) + .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) + .setAutoCancel(true) + .setPriority(Notification.PRIORITY_MAX) + .setOnlyAlertOnce(false) + .setDefaults(Notification.DEFAULT_ALL) + .setContentIntent(PendingIntent.getActivity(context, 0, + connectionAccessIntent, 0)) + .setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent, 0)) + .setColor(context.getResources().getColor( + com.android.internal.R.color.system_notification_accent_color)) + .build(); notification.flags |= Notification.FLAG_NO_CLEAR; /* cannot be set with the builder */ diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index f1125bc..ec288f3 100755 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -18,28 +18,39 @@ package com.android.settings.bluetooth; import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; +import android.app.Activity; +import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.Log; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.EditText; import android.widget.TextView; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Index; import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; import com.android.settings.widget.SwitchBar; @@ -58,19 +69,20 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem private static final int MENU_ID_SCAN = Menu.FIRST; private static final int MENU_ID_RENAME_DEVICE = Menu.FIRST + 1; private static final int MENU_ID_SHOW_RECEIVED = Menu.FIRST + 2; - private static final int MENU_ID_MESSAGE_ACCESS = Menu.FIRST + 3; /* Private intent to show the list of received files */ private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES = "android.btopp.intent.action.OPEN_RECEIVED_FILES"; + private static View mSettingsDialogView = null; + private BluetoothEnabler mBluetoothEnabler; private PreferenceGroup mPairedDevicesCategory; private PreferenceGroup mAvailableDevicesCategory; private boolean mAvailableDevicesCategoryIsPresent; - private boolean mActivityStarted; + private boolean mInitialScanStarted; private TextView mEmptyView; private SwitchBar mSwitchBar; @@ -86,13 +98,14 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)) { - updateDeviceName(); + updateDeviceName(context); } } - private void updateDeviceName() { + private void updateDeviceName(Context context) { if (mLocalAdapter.isEnabled() && mMyDevicePreference != null) { - mMyDevicePreference.setTitle(mLocalAdapter.getName()); + mMyDevicePreference.setSummary(context.getResources().getString( + R.string.bluetooth_is_visible_message, mLocalAdapter.getName())); } } }; @@ -105,7 +118,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mActivityStarted = (savedInstanceState == null); // don't auto start scan after rotation + mInitialScanStarted = (savedInstanceState != null); // don't auto start scan after rotation mEmptyView = (TextView) getView().findViewById(android.R.id.empty); getListView().setEmptyView(mEmptyView); @@ -140,6 +153,9 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem } super.onResume(); + // Make the device visible to other devices. + mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + if (isUiRestricted()) { setDeviceListGroup(getPreferenceScreen()); removeAllDevices(); @@ -149,11 +165,8 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem getActivity().registerReceiver(mReceiver, mIntentFilter); if (mLocalAdapter != null) { - updateContent(mLocalAdapter.getBluetoothState(), mActivityStarted); + updateContent(mLocalAdapter.getBluetoothState()); } - - // Make the device visible to other devices. - mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); } @Override @@ -163,14 +176,14 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem mBluetoothEnabler.pause(); } + // Make the device only visible to connected devices. + mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE); + if (isUiRestricted()) { return; } getActivity().unregisterReceiver(mReceiver); - - // Make the device only visible to connected devices. - mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE); } @Override @@ -191,12 +204,6 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(Menu.NONE, MENU_ID_SHOW_RECEIVED, 0, R.string.bluetooth_show_received_files) .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - // Message Access API is still not finished, once completed we undo this check. - // Bug 16232864 - if (android.os.SystemProperties.get("show_bluetooth_message_access").equals("true")){ - menu.add(Menu.NONE, MENU_ID_MESSAGE_ACCESS, 0, R.string.bluetooth_show_message_access) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - } super.onCreateOptionsMenu(menu, inflater); } @@ -218,23 +225,28 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES); getActivity().sendBroadcast(intent); return true; - - case MENU_ID_MESSAGE_ACCESS: - if (getActivity() instanceof SettingsActivity) { - ((SettingsActivity) getActivity()).startPreferencePanel( - MessageAccessSettings.class.getCanonicalName(), null, - R.string.bluetooth_show_message_access, null, this, 0); - } - return true; } return super.onOptionsItemSelected(item); } private void startScanning() { - if (isUiRestricted()) return; + if (isUiRestricted()) { + return; + } + if (!mAvailableDevicesCategoryIsPresent) { getPreferenceScreen().addPreference(mAvailableDevicesCategory); + mAvailableDevicesCategoryIsPresent = true; } + + if (mAvailableDevicesCategory != null) { + setDeviceListGroup(mAvailableDevicesCategory); + removeAllDevices(); + } + + mLocalManager.getCachedDeviceManager().clearCachedDevices(); + mAvailableDevicesCategory.removeAll(); + mInitialScanStarted = true; mLocalAdapter.startScanning(true); } @@ -245,16 +257,18 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem } private void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId, - BluetoothDeviceFilter.Filter filter) { + BluetoothDeviceFilter.Filter filter, boolean addCachedDevices) { preferenceGroup.setTitle(titleId); getPreferenceScreen().addPreference(preferenceGroup); setFilter(filter); setDeviceListGroup(preferenceGroup); - addCachedDevices(); + if (addCachedDevices) { + addCachedDevices(); + } preferenceGroup.setEnabled(true); } - private void updateContent(int bluetoothState, boolean scanState) { + private void updateContent(int bluetoothState) { final PreferenceScreen preferenceScreen = getPreferenceScreen(); int messageId = 0; @@ -277,9 +291,13 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem } addDeviceCategory(mPairedDevicesCategory, R.string.bluetooth_preference_paired_devices, - BluetoothDeviceFilter.BONDED_DEVICE_FILTER); + BluetoothDeviceFilter.BONDED_DEVICE_FILTER, true); int numberOfPairedDevices = mPairedDevicesCategory.getPreferenceCount(); + if (isUiRestricted() || numberOfPairedDevices <= 0) { + preferenceScreen.removePreference(mPairedDevicesCategory); + } + // Available devices category if (mAvailableDevicesCategory == null) { mAvailableDevicesCategory = new BluetoothProgressCategory(getActivity()); @@ -289,30 +307,17 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem } addDeviceCategory(mAvailableDevicesCategory, R.string.bluetooth_preference_found_devices, - BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER); + BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER, mInitialScanStarted); int numberOfAvailableDevices = mAvailableDevicesCategory.getPreferenceCount(); - mAvailableDevicesCategoryIsPresent = true; - - if (numberOfAvailableDevices == 0) { - preferenceScreen.removePreference(mAvailableDevicesCategory); - mAvailableDevicesCategoryIsPresent = false; - } - if (numberOfPairedDevices == 0) { - preferenceScreen.removePreference(mPairedDevicesCategory); - if (scanState == true) { - mActivityStarted = false; - startScanning(); - } else { - if (!mAvailableDevicesCategoryIsPresent) { - getPreferenceScreen().addPreference(mAvailableDevicesCategory); - } - } + if (!mInitialScanStarted) { + startScanning(); } if (mMyDevicePreference == null) { mMyDevicePreference = new Preference(getActivity()); } + mMyDevicePreference.setSummary(getResources().getString( R.string.bluetooth_is_visible_message, mLocalAdapter.getName())); mMyDevicePreference.setSelectable(false); @@ -349,7 +354,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem @Override public void onBluetoothStateChanged(int bluetoothState) { super.onBluetoothStateChanged(bluetoothState); - updateContent(bluetoothState, true); + updateContent(bluetoothState); } @Override @@ -361,30 +366,69 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem } } - @Override public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { setDeviceListGroup(getPreferenceScreen()); removeAllDevices(); - updateContent(mLocalAdapter.getBluetoothState(), false); + updateContent(mLocalAdapter.getBluetoothState()); } private final View.OnClickListener mDeviceProfilesListener = new View.OnClickListener() { public void onClick(View v) { // User clicked on advanced options icon for a device in the list - if (v.getTag() instanceof CachedBluetoothDevice) { - if (isUiRestricted()) return; - - CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag(); + if (!(v.getTag() instanceof CachedBluetoothDevice)) { + Log.w(TAG, "onClick() called for other View: " + v); + return; + } - Bundle args = new Bundle(1); - args.putParcelable(DeviceProfilesSettings.EXTRA_DEVICE, device.getDevice()); + final CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag(); + final Activity activity = getActivity(); + DeviceProfilesSettings profileFrag = (DeviceProfilesSettings)activity. + getFragmentManager().findFragmentById(R.id.bluetooth_fragment_settings); - ((SettingsActivity) getActivity()).startPreferencePanel( - DeviceProfilesSettings.class.getName(), args, - R.string.bluetooth_device_advanced_title, null, null, 0); - } else { - Log.w(TAG, "onClick() called for other View: " + v); // TODO remove + if (mSettingsDialogView != null){ + ViewGroup parent = (ViewGroup) mSettingsDialogView.getParent(); + if (parent != null) { + parent.removeView(mSettingsDialogView); + } } + if (profileFrag == null) { + LayoutInflater inflater = getActivity().getLayoutInflater(); + mSettingsDialogView = inflater.inflate(R.layout.bluetooth_device_settings, null); + profileFrag = (DeviceProfilesSettings)activity.getFragmentManager() + .findFragmentById(R.id.bluetooth_fragment_settings); + } + + final View dialogLayout = mSettingsDialogView; + AlertDialog.Builder settingsDialog = new AlertDialog.Builder(activity); + profileFrag.setDevice(device); + final EditText deviceName = (EditText)dialogLayout.findViewById(R.id.name); + deviceName.setText(device.getName(), TextView.BufferType.EDITABLE); + settingsDialog.setView(dialogLayout); + settingsDialog.setTitle(R.string.bluetooth_preference_paired_devices); + settingsDialog.setPositiveButton(R.string.okay, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + EditText deviceName = (EditText)dialogLayout.findViewById(R.id.name); + device.setName(deviceName.getText().toString()); + } + }); + final Context context = v.getContext(); + settingsDialog.setNegativeButton(R.string.forget, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + device.unpair(); + com.android.settings.bluetooth.Utils.updateSearchIndex(activity, + BluetoothSettings.class.getName(), device.getName(), + context.getResources().getString(R.string.bluetooth_settings), + R.drawable.ic_settings_bluetooth2, false); + } + }); + + AlertDialog dialog = settingsDialog.create(); + dialog.create(); + dialog.show(); } }; diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java index a7104df..3b64ade 100755 --- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java +++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java @@ -539,7 +539,7 @@ final class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> { if (bondState == BluetoothDevice.BOND_NONE) { mProfiles.clear(); mConnectAfterPairing = false; // cancel auto-connect - setPhonebookPermissionChoice(ACCESS_ALLOWED); + setPhonebookPermissionChoice(ACCESS_UNKNOWN); setMessagePermissionChoice(ACCESS_UNKNOWN); mPhonebookRejectedTimes = 0; savePhonebookRejectTimes(); diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java b/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java index ff282cc..0b53b1a 100755 --- a/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java +++ b/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java @@ -86,7 +86,9 @@ final class CachedBluetoothDeviceManager { BluetoothDevice device) { CachedBluetoothDevice newDevice = new CachedBluetoothDevice(mContext, adapter, profileManager, device); - mCachedDevices.add(newDevice); + synchronized (mCachedDevices) { + mCachedDevices.add(newDevice); + } return newDevice; } @@ -110,6 +112,10 @@ final class CachedBluetoothDeviceManager { return device.getAddress(); } + public synchronized void clearCachedDevices() { + mCachedDevices.clear(); + } + public synchronized void onScanningStateChanged(boolean started) { if (!started) return; diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java index f482ecd..e7208b5 100644 --- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java +++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java @@ -167,6 +167,12 @@ public abstract class DeviceListPreferenceFragment extends } void createDevicePreference(CachedBluetoothDevice cachedDevice) { + if (mDeviceListGroup == null) { + Log.w(TAG, "Trying to create a device preference before the list group/category " + + "exists!"); + return; + } + BluetoothDevicePreference preference = new BluetoothDevicePreference( getActivity(), cachedDevice); diff --git a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java index fb7668f..64c807f 100755 --- a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java +++ b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java @@ -17,6 +17,7 @@ package com.android.settings.bluetooth; import android.app.AlertDialog; +import android.app.Fragment; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; @@ -36,6 +37,7 @@ import android.text.TextWatcher; import android.app.Dialog; import android.widget.Button; import android.text.Editable; + import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.search.Index; @@ -52,15 +54,12 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener { private static final String TAG = "DeviceProfilesSettings"; - private static final String KEY_RENAME_DEVICE = "rename_device"; private static final String KEY_PROFILE_CONTAINER = "profile_container"; private static final String KEY_UNPAIR = "unpair"; private static final String KEY_PBAP_SERVER = "PBAP Server"; - public static final String EXTRA_DEVICE = "device"; - private RenameEditTextPreference mRenameDeviceNamePref; - private LocalBluetoothManager mManager; private CachedBluetoothDevice mCachedDevice; + private LocalBluetoothManager mManager; private LocalBluetoothProfileManager mProfileManager; private PreferenceGroup mProfileContainer; @@ -72,66 +71,18 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment private AlertDialog mDisconnectDialog; private boolean mProfileGroupIsRemoved; - private class RenameEditTextPreference implements TextWatcher{ - public void afterTextChanged(Editable s) { - Dialog d = mDeviceNamePref.getDialog(); - if (d instanceof AlertDialog) { - ((AlertDialog) d).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(s.length() > 0); - } - } - - // TextWatcher interface - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // not used - } - - // TextWatcher interface - public void onTextChanged(CharSequence s, int start, int before, int count) { - // not used - } - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - BluetoothDevice device; - if (savedInstanceState != null) { - device = savedInstanceState.getParcelable(EXTRA_DEVICE); - } else { - Bundle args = getArguments(); - device = args.getParcelable(EXTRA_DEVICE); - } - addPreferencesFromResource(R.xml.bluetooth_device_advanced); getPreferenceScreen().setOrderingAsAdded(false); mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER); - mDeviceNamePref = (EditTextPreference) findPreference(KEY_RENAME_DEVICE); - if (device == null) { - Log.w(TAG, "Activity started without a remote Bluetooth device"); - finish(); - return; // TODO: test this failure path - } - mRenameDeviceNamePref = new RenameEditTextPreference(); mManager = LocalBluetoothManager.getInstance(getActivity()); CachedBluetoothDeviceManager deviceManager = mManager.getCachedDeviceManager(); mProfileManager = mManager.getProfileManager(); - mCachedDevice = deviceManager.findDevice(device); - if (mCachedDevice == null) { - Log.w(TAG, "Device not found, cannot connect to it"); - finish(); - return; // TODO: test this failure path - } - - String deviceName = mCachedDevice.getName(); - mDeviceNamePref.setSummary(deviceName); - mDeviceNamePref.setText(deviceName); - mDeviceNamePref.setOnPreferenceChangeListener(this); - - // Add a preference for each profile - addPreferencesForProfiles(); } @Override @@ -141,12 +92,14 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment mDisconnectDialog.dismiss(); mDisconnectDialog = null; } + if (mCachedDevice != null) { + mCachedDevice.unregisterCallback(this); + } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putParcelable(EXTRA_DEVICE, mCachedDevice.getDevice()); } @Override @@ -154,18 +107,13 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment super.onResume(); mManager.setForegroundActivity(getActivity()); - mCachedDevice.registerCallback(this); - if(mCachedDevice.getBondState() == BluetoothDevice.BOND_NONE) - finish(); - refresh(); - EditText et = mDeviceNamePref.getEditText(); - if (et != null) { - et.addTextChangedListener(mRenameDeviceNamePref); - Dialog d = mDeviceNamePref.getDialog(); - if (d instanceof AlertDialog) { - Button b = ((AlertDialog) d).getButton(AlertDialog.BUTTON_POSITIVE); - b.setEnabled(et.getText().length() > 0); + if (mCachedDevice != null) { + mCachedDevice.registerCallback(this); + if (mCachedDevice.getBondState() == BluetoothDevice.BOND_NONE) { + finish(); + return; } + refresh(); } } @@ -173,11 +121,25 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment public void onPause() { super.onPause(); - mCachedDevice.unregisterCallback(this); + if (mCachedDevice != null) { + mCachedDevice.unregisterCallback(this); + } + mManager.setForegroundActivity(null); } + public void setDevice(CachedBluetoothDevice cachedDevice) { + mCachedDevice = cachedDevice; + + mCachedDevice.registerCallback(this); + if (isResumed()) { + addPreferencesForProfiles(); + refresh(); + } + } + private void addPreferencesForProfiles() { + mProfileContainer.removeAll(); for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) { Preference pref = createProfilePreference(profile); mProfileContainer.addPreference(pref); @@ -238,28 +200,6 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment return pref; } - @Override - public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { - String key = preference.getKey(); - if (key.equals(KEY_UNPAIR)) { - unpairDevice(); - finish(); - final Context context = preference.getContext(); - - SearchIndexableRaw data = new SearchIndexableRaw(context); - data.className = BluetoothSettings.class.getName(); - data.title = mCachedDevice.getName(); - data.screenTitle = context.getResources().getString(R.string.bluetooth_settings); - data.iconResId = R.drawable.ic_settings_bluetooth2; - data.enabled = false; - - Index.getInstance(context).updateFromSearchIndexableData(data); - return true; - } - - return super.onPreferenceTreeClick(screen, preference); - } - public boolean onPreferenceChange(Preference preference, Object newValue) { if (preference == mDeviceNamePref) { mCachedDevice.setName((String) newValue); @@ -331,14 +271,16 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment mDisconnectDialog, disconnectListener, title, Html.fromHtml(message)); } + @Override public void onDeviceAttributesChanged() { refresh(); } private void refresh() { - String deviceName = mCachedDevice.getName(); - mDeviceNamePref.setSummary(deviceName); - mDeviceNamePref.setText(deviceName); + final EditText deviceNameField = (EditText) getView().findViewById(R.id.name); + if (deviceNameField != null) { + deviceNameField.setText(mCachedDevice.getName()); + } refreshProfiles(); } @@ -391,8 +333,4 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment private int getProfilePreferenceIndex(int profIndex) { return mProfileContainer.getOrder() + profIndex * 10; } - - private void unpairDevice() { - mCachedDevice.unpair(); - } } diff --git a/src/com/android/settings/bluetooth/MessageAccessSettings.java b/src/com/android/settings/bluetooth/MessageAccessSettings.java deleted file mode 100644 index 913357c..0000000 --- a/src/com/android/settings/bluetooth/MessageAccessSettings.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.bluetooth; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.ActivityManagerNative; -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.UserHandle; -import android.preference.SwitchPreference; -import android.preference.Preference; -import android.preference.PreferenceGroup; -import android.preference.PreferenceScreen; -import android.provider.SearchIndexableResource; -import android.util.Log; - -import com.android.settings.accounts.AuthenticatorHelper; -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.Utils; -import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settings.search.Indexable; - -import java.util.ArrayList; -import java.util.List; - -public class MessageAccessSettings extends SettingsPreferenceFragment - implements AuthenticatorHelper.OnAccountsUpdateListener, Indexable { - private static final String TAG = "MessageAccessSettings"; - private static final String GMAIL_PACKAGE_NAME = "com.google.android.gm"; - private static final String EMAIL_PACKAGE_NAME = "com.google.android.email"; - - private Account[] mAccounts; - private UserHandle mUserHandle; - private PreferenceGroup mAvailableAccounts; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // TODO: Define behavior for managed profile. See: http://b/16287773 - mUserHandle = new UserHandle(UserHandle.myUserId()); - - addPreferencesFromResource(R.xml.bluetooth_message_access); - } - - @Override - public void onResume() { - super.onResume(); - initPreferences(); - } - - @Override - public void onAccountsUpdate(final UserHandle userHandle) { - mAccounts = AccountManager.get(getActivity()).getAccountsAsUser( - mUserHandle.getIdentifier()); - - final int mAccountsSize = mAccounts.length; - for (int i = 0; i < mAccountsSize; ++i){ - Log.d(TAG, String.format("account.type = %s\n", mAccounts[i].type)); - } - } - - /** - * Retrieves the email icon for a given account's email preference - * - * @param accountPref The user's account to retrieve the icon from. - * - * @return The drawable representing the icon of the user's email preference - **/ - private Drawable getIcon(AccountPreference accountPref){ - Drawable icon = null; - - // Currently only two types of icons are allowed. - final String packageName = accountPref.account.type.equals("com.google") - ? GMAIL_PACKAGE_NAME : EMAIL_PACKAGE_NAME; - - try{ - icon = getPackageManager().getApplicationIcon(packageName); - }catch(NameNotFoundException nnfe){ - icon = null; - } - - return icon; - } - - private void initPreferences() { - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - mAvailableAccounts = (PreferenceGroup)preferenceScreen.findPreference("accounts"); - mAccounts = AccountManager.get(getActivity()).getAccountsAsUser( - mUserHandle.getIdentifier()); - - final int mAccountsSize = mAccounts.length; - for (int i = 0; i < mAccountsSize; ++i){ - AccountPreference accountPref = new AccountPreference(getActivity(), mAccounts[i]); - Drawable icon = getIcon(accountPref); - if (icon != null){ - accountPref.setIcon(icon); - } - mAvailableAccounts.addPreference(accountPref); - } - } - - private class AccountPreference extends SwitchPreference - implements Preference.OnPreferenceChangeListener{ - private Account account; - - AccountPreference(Context context, Account account){ - super(context); - this.account = account; - setTitle(account.type); - setSummary(account.name); - - setOnPreferenceChangeListener(this); - } - - @Override - public boolean onPreferenceChange(Preference preference, Object val) { - if (preference instanceof AccountPreference){ - final AccountPreference accountPref = (AccountPreference) preference; - - if (((Boolean)val).booleanValue()){ - // Enable paired deviced to connect, fill in once API is available - Log.w(TAG, String.format( - "User has turned on '%s' for Bluetooth message access.", - accountPref.account.name)); - } else { - // Disable paired deviced to connect, fill in once API is available - Log.w(TAG, String.format( - "User has turned off '%s' for Bluetooth message access.", - accountPref.account.name)); - } - } - return true; - } - } - - public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider() { - @Override - public List<SearchIndexableResource> getXmlResourcesToIndex( - Context context, boolean enabled) { - List<SearchIndexableResource> indexables = new ArrayList<SearchIndexableResource>(); - SearchIndexableResource indexable = new SearchIndexableResource(context); - indexable.xmlResId = R.xml.bluetooth_message_access; - indexables.add(indexable); - return indexables; - } - }; -} diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java index 1970400..e9230de 100755 --- a/src/com/android/settings/bluetooth/Utils.java +++ b/src/com/android/settings/bluetooth/Utils.java @@ -24,6 +24,8 @@ import android.content.DialogInterface; import android.widget.Toast; import com.android.settings.R; +import com.android.settings.search.Index; +import com.android.settings.search.SearchIndexableRaw; /** * Utils is a helper class that contains constants for various @@ -101,4 +103,19 @@ final class Utils { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } } + + /** + * Update the search Index for a specific class name and resources. + */ + public static void updateSearchIndex(Context context, String className, String title, + String screenTitle, int iconResId, boolean enabled) { + SearchIndexableRaw data = new SearchIndexableRaw(context); + data.className = className; + data.title = title; + data.screenTitle = screenTitle; + data.iconResId = iconResId; + data.enabled = enabled; + + Index.getInstance(context).updateFromSearchIndexableData(data); + } } diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java index 92eaade..4ff4dfd 100644 --- a/src/com/android/settings/fuelgauge/BatteryEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryEntry.java @@ -16,15 +16,20 @@ package com.android.settings.fuelgauge; +import android.app.AppGlobals; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.os.BatteryStats; import android.os.Handler; +import android.os.RemoteException; +import android.os.UserHandle; import android.os.UserManager; +import android.util.Log; import com.android.internal.os.BatterySipper; import com.android.settings.R; @@ -256,9 +261,17 @@ public class BatteryEntry { System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length); // Convert package names to user-facing labels where possible + IPackageManager ipm = AppGlobals.getPackageManager(); + final int userId = UserHandle.getUserId(uid); for (int i = 0; i < packageLabels.length; i++) { try { - ApplicationInfo ai = pm.getApplicationInfo(packageLabels[i], 0); + final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i], + 0 /* no flags */, userId); + if (ai == null) { + Log.d(PowerUsageSummary.TAG, "Retrieving null app info for package " + + packageLabels[i] + ", user " + userId); + continue; + } CharSequence label = ai.loadLabel(pm); if (label != null) { packageLabels[i] = label.toString(); @@ -268,10 +281,14 @@ public class BatteryEntry { icon = ai.loadIcon(pm); break; } - } catch (PackageManager.NameNotFoundException e) { + } catch (RemoteException e) { + Log.d(PowerUsageSummary.TAG, "Error while retrieving app info for package " + + packageLabels[i] + ", user " + userId, e); } } - if (icon == null) icon = defaultActivityIcon; + if (icon == null) { + icon = defaultActivityIcon; + } if (packageLabels.length == 1) { name = packageLabels[0]; @@ -279,7 +296,12 @@ public class BatteryEntry { // Look for an official name for this UID. for (String pkgName : sipper.mPackages) { try { - final PackageInfo pi = pm.getPackageInfo(pkgName, 0); + final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId); + if (pi == null) { + Log.d(PowerUsageSummary.TAG, "Retrieving null package info for package " + + pkgName + ", user " + userId); + continue; + } if (pi.sharedUserLabel != 0) { final CharSequence nm = pm.getText(pkgName, pi.sharedUserLabel, pi.applicationInfo); @@ -292,7 +314,9 @@ public class BatteryEntry { break; } } - } catch (PackageManager.NameNotFoundException e) { + } catch (RemoteException e) { + Log.d(PowerUsageSummary.TAG, "Error while retrieving package info for package " + + pkgName + ", user " + userId, e); } } } diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java index d88c516..9d3edd5 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java @@ -86,7 +86,7 @@ public class BatteryHistoryChart extends View { void addTick(int x, int bin) { if (bin != mLastBin && mNumTicks < mTicks.length) { - mTicks[mNumTicks] = x | bin << CHART_DATA_BIN_SHIFT; + mTicks[mNumTicks] = (x&CHART_DATA_X_MASK) | (bin<<CHART_DATA_BIN_SHIFT); mNumTicks++; mLastBin = bin; } @@ -540,6 +540,7 @@ public class BatteryHistoryChart extends View { } mDrainString = ""; mChargeDurationString = ""; + setContentDescription(mChargeLabelString); int pos = 0; int lastInteresting = 0; @@ -804,6 +805,9 @@ public class BatteryHistoryChart extends View { } if (curWalltime != 0 && rec.isDeltaData()) { x = mLevelLeft + (int)(((curWalltime-walltimeStart)*levelWidth)/walltimeChange); + if (x < 0) { + x = 0; + } if (false) { StringBuilder sb = new StringBuilder(128); sb.append("walloff="); @@ -984,6 +988,9 @@ public class BatteryHistoryChart extends View { } else { // Figure out where the actual data ends on the screen. x = mLevelLeft + (int)(((mEndDataWallTime-walltimeStart)*levelWidth)/walltimeChange); + if (x < 0) { + x = 0; + } } finishPaths(x, h, levelh, startX, lastY, curLevelPath, lastX, diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java b/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java index 248d471..63e4e13 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java @@ -18,31 +18,27 @@ package com.android.settings.fuelgauge; import android.app.Fragment; import android.content.Intent; +import android.os.BatteryStats; import android.os.Bundle; -import android.os.Parcel; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; public class BatteryHistoryDetail extends Fragment { public static final String EXTRA_STATS = "stats"; public static final String EXTRA_BROADCAST = "broadcast"; - private BatteryStatsImpl mStats; + private BatteryStats mStats; private Intent mBatteryBroadcast; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - byte[] data = getArguments().getByteArray(EXTRA_STATS); - Parcel parcel = Parcel.obtain(); - parcel.unmarshall(data, 0, data.length); - parcel.setDataPosition(0); - mStats = com.android.internal.os.BatteryStatsImpl.CREATOR - .createFromParcel(parcel); + String histFile = getArguments().getString(EXTRA_STATS); + mStats = BatteryStatsHelper.statsFromFile(getActivity(), histFile); mBatteryBroadcast = getArguments().getParcelable(EXTRA_BROADCAST); } diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java index 300b98f..e7326b1 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java @@ -47,8 +47,6 @@ public class BatteryHistoryPreference extends Preference { setLayoutResource(R.layout.preference_batteryhistory); mStats = stats; mBatteryBroadcast = batteryBroadcast; - // Make it non selectable - setSelectable(false); } BatteryStats getStats() { diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java index 990b654..a558533 100644 --- a/src/com/android/settings/fuelgauge/PowerGaugePreference.java +++ b/src/com/android/settings/fuelgauge/PowerGaugePreference.java @@ -34,12 +34,15 @@ public class PowerGaugePreference extends Preference { private BatteryEntry mInfo; private int mProgress; private CharSequence mProgressText; + private final CharSequence mContentDescription; - public PowerGaugePreference(Context context, Drawable icon, BatteryEntry info) { + public PowerGaugePreference(Context context, Drawable icon, CharSequence contentDescription, + BatteryEntry info) { super(context); setLayoutResource(R.layout.preference_app_percentage); setIcon(icon != null ? icon : new ColorDrawable(0)); mInfo = info; + mContentDescription = contentDescription; } public void setPercent(double percentOfMax, double percentOfTotal) { @@ -62,5 +65,10 @@ public class PowerGaugePreference extends Preference { final TextView text1 = (TextView) view.findViewById(android.R.id.text1); text1.setText(mProgressText); + + if (mContentDescription != null) { + final TextView titleView = (TextView) view.findViewById(android.R.id.title); + titleView.setContentDescription(mContentDescription); + } } } diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 3ce2ac0..5325ed3 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -21,11 +21,11 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.drawable.Drawable; import android.os.BatteryStats; import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.os.Parcel; import android.os.UserHandle; import android.os.UserManager; import android.preference.Preference; @@ -45,8 +45,6 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import java.util.List; -import java.util.ArrayList; -import java.util.Collections; /** * Displays a list of apps and subsystems that consume power, ordered by how much power was @@ -56,10 +54,12 @@ public class PowerUsageSummary extends PreferenceFragment { private static final boolean DEBUG = false; - private static final String TAG = "PowerUsageSummary"; + static final String TAG = "PowerUsageSummary"; private static final String KEY_APP_LIST = "app_list"; + private static final String BATTERY_HISTORY_FILE = "tmp_bat_history.bin"; + private static final int MENU_STATS_TYPE = Menu.FIRST; private static final int MENU_STATS_REFRESH = Menu.FIRST + 1; private static final int MENU_BATTERY_SAVER = Menu.FIRST + 2; @@ -113,8 +113,15 @@ public class PowerUsageSummary extends PreferenceFragment { } @Override + public void onStart() { + super.onStart(); + mStatsHelper.clearStats(); + } + + @Override public void onResume() { super.onResume(); + BatteryStatsHelper.dropFile(getActivity(), BATTERY_HISTORY_FILE); updateBatteryStatus(getActivity().registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED))); if (mHandler.hasMessages(MSG_REFRESH_STATS)) { @@ -150,11 +157,9 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { if (preference instanceof BatteryHistoryPreference) { - Parcel hist = Parcel.obtain(); - mStatsHelper.getStats().writeToParcelWithoutUids(hist, 0); - byte[] histData = hist.marshall(); + mStatsHelper.storeStatsHistoryInFile(BATTERY_HISTORY_FILE); Bundle args = new Bundle(); - args.putByteArray(BatteryHistoryDetail.EXTRA_STATS, histData); + args.putString(BatteryHistoryDetail.EXTRA_STATS, BATTERY_HISTORY_FILE); args.putParcelable(BatteryHistoryDetail.EXTRA_BROADCAST, mStatsHelper.getBatteryBroadcast()); SettingsActivity sa = (SettingsActivity) getActivity(); @@ -251,7 +256,8 @@ public class PowerUsageSummary extends PreferenceFragment { mAppListGroup.addPreference(mHistPref); boolean addedSome = false; - PowerProfile powerProfile = mStatsHelper.getPowerProfile(); + final PowerProfile powerProfile = mStatsHelper.getPowerProfile(); + final BatteryStats stats = mStatsHelper.getStats(); final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP) { final List<UserHandle> profiles = mUm.getUserProfiles(); @@ -260,7 +266,7 @@ public class PowerUsageSummary extends PreferenceFragment { final List<BatterySipper> usageList = mStatsHelper.getUsageList(); - final int dischargeAmount = mStatsHelper.getStats().getDischargeAmount(mStatsType); + final int dischargeAmount = stats != null ? stats.getDischargeAmount(mStatsType) : 0; final int numSippers = usageList.size(); for (int i = 0; i < numSippers; i++) { final BatterySipper sipper = usageList.get(i); @@ -294,8 +300,12 @@ public class PowerUsageSummary extends PreferenceFragment { } final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid())); final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper); + final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(), + userHandle); + final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(), + userHandle); final PowerGaugePreference pref = new PowerGaugePreference(getActivity(), - mUm.getBadgedDrawableForUser(entry.getIcon(), userHandle), entry); + badgedIcon, contentDescription, entry); final double percentOfMax = (sipper.value * 100) / mStatsHelper.getMaxPower(); sipper.percent = percentOfTotal; @@ -334,7 +344,7 @@ public class PowerUsageSummary extends PreferenceFragment { if (pgp != null) { final int userId = UserHandle.getUserId(entry.sipper.getUid()); final UserHandle userHandle = new UserHandle(userId); - pgp.setIcon(mUm.getBadgedDrawableForUser(entry.getIcon(), userHandle)); + pgp.setIcon(mUm.getBadgedIconForUser(entry.getIcon(), userHandle)); pgp.setTitle(entry.name); } break; diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java index 1601cd0..e1bcac5 100644 --- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java +++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java @@ -18,6 +18,7 @@ package com.android.settings.inputmethod; import android.app.Activity; import android.app.Fragment; +import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -34,6 +35,7 @@ import android.hardware.input.InputManager; import android.hardware.input.KeyboardLayout; import android.os.Bundle; import android.os.Handler; +import android.os.UserHandle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; @@ -80,7 +82,6 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment InputMethodPreference.OnSavePreferenceListener { private static final String KEY_SPELL_CHECKERS = "spellcheckers_settings"; private static final String KEY_PHONE_LANGUAGE = "phone_language"; - private static final String KEY_CHOOSE_INPUT_METHODS = "choose_input_methods"; private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method"; private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector"; private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings"; @@ -88,14 +89,6 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment // false: on ICS or later private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false; - private static final String[] sSystemSettingNames = { - System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE, - }; - - private static final String[] sHardKeyboardKeys = { - "auto_replace", "auto_caps", "auto_punctuate", - }; - private int mDefaultInputMethodSelectorVisibility = 0; private ListPreference mShowInputMethodSelectorPref; private PreferenceCategory mKeyboardSettingsCategory; @@ -111,6 +104,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment private SettingsObserver mSettingsObserver; private Intent mIntentWaitingForResult; private InputMethodSettingValuesWrapper mInputMethodSettingValues; + private DevicePolicyManager mDpm; @Override public void onCreate(Bundle icicle) { @@ -162,14 +156,6 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment } mKeyboardSettingsCategory.removeAll(); getPreferenceScreen().addPreference(mKeyboardSettingsCategory); - } else { - final Preference pref = findPreference(KEY_CHOOSE_INPUT_METHODS); - final Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS); - intent.setClass(activity, SubSettings.class); - intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, getClass().getName()); - intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, - R.string.choose_input_methods); - pref.setIntent(intent); } // Build hard keyboard and game controller preference categories. @@ -192,6 +178,8 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment mHandler = new Handler(); mSettingsObserver = new SettingsObserver(mHandler, activity); + mDpm = (DevicePolicyManager) (getActivity(). + getSystemService(Context.DEVICE_POLICY_SERVICE)); } private void updateInputMethodSelectorSummary(int value) { @@ -274,16 +262,6 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment } } - // Hard keyboard - if (!mHardKeyboardPreferenceList.isEmpty()) { - for (int i = 0; i < sHardKeyboardKeys.length; ++i) { - CheckBoxPreference chkPref = (CheckBoxPreference) - mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]); - chkPref.setChecked( - System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0); - } - } - updateInputDevices(); // Refresh internal states in mInputMethodSettingValues to keep the latest @@ -339,15 +317,6 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment } } else if (preference instanceof CheckBoxPreference) { final CheckBoxPreference chkPref = (CheckBoxPreference) preference; - if (!mHardKeyboardPreferenceList.isEmpty()) { - for (int i = 0; i < sHardKeyboardKeys.length; ++i) { - if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) { - System.putInt(getContentResolver(), sSystemSettingNames[i], - chkPref.isChecked() ? 1 : 0); - return true; - } - } - } if (chkPref == mGameControllerCategory.findPreference("vibrate_input_devices")) { System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES, chkPref.isChecked() ? 1 : 0); @@ -366,16 +335,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment // and want to pretend that the language is valid for all locales. // We need a way to support languages that aren't tied to a particular // locale instead of hiding the locale qualifier. - if (language.equals("zz")) { - String country = conf.locale.getCountry(); - if (country.equals("ZZ")) { - localeName = "[Developer] Accented English (zz_ZZ)"; - } else if (country.equals("ZY")) { - localeName = "[Developer] Fake Bi-Directional (zz_ZY)"; - } else { - localeName = ""; - } - } else if (hasOnlyOneLanguageInstance(language, + if (hasOnlyOneLanguageInstance(language, Resources.getSystem().getAssets().getLocales())) { localeName = conf.locale.getDisplayLanguage(conf.locale); } else { @@ -439,6 +399,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment mKeyboardSettingsCategory.removePreference(pref); } mInputMethodPreferenceList.clear(); + List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser(); final Context context = getActivity(); final List<InputMethodInfo> imis = mShowsOnlyFullImeAndKeyboardList ? mInputMethodSettingValues.getInputMethodList() @@ -446,8 +407,11 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment final int N = (imis == null ? 0 : imis.size()); for (int i = 0; i < N; ++i) { final InputMethodInfo imi = imis.get(i); + final boolean isAllowedByOrganization = permittedList == null + || permittedList.contains(imi.getPackageName()); final InputMethodPreference pref = new InputMethodPreference( - context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */, this); + context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */, + isAllowedByOrganization, this); mInputMethodPreferenceList.add(pref); } final Collator collator = Collator.getInstance(); @@ -564,35 +528,33 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment private void updateHardKeyboards() { mHardKeyboardPreferenceList.clear(); - if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) { - final int[] devices = InputDevice.getDeviceIds(); - for (int i = 0; i < devices.length; i++) { - InputDevice device = InputDevice.getDevice(devices[i]); - if (device != null - && !device.isVirtual() - && device.isFullKeyboard()) { - final InputDeviceIdentifier identifier = device.getIdentifier(); - final String keyboardLayoutDescriptor = - mIm.getCurrentKeyboardLayoutForInputDevice(identifier); - final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? - mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null; - - final PreferenceScreen pref = new PreferenceScreen(getActivity(), null); - pref.setTitle(device.getName()); - if (keyboardLayout != null) { - pref.setSummary(keyboardLayout.toString()); - } else { - pref.setSummary(R.string.keyboard_layout_default_label); - } - pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - showKeyboardLayoutDialog(identifier); - return true; - } - }); - mHardKeyboardPreferenceList.add(pref); + final int[] devices = InputDevice.getDeviceIds(); + for (int i = 0; i < devices.length; i++) { + InputDevice device = InputDevice.getDevice(devices[i]); + if (device != null + && !device.isVirtual() + && device.isFullKeyboard()) { + final InputDeviceIdentifier identifier = device.getIdentifier(); + final String keyboardLayoutDescriptor = + mIm.getCurrentKeyboardLayoutForInputDevice(identifier); + final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? + mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null; + + final PreferenceScreen pref = new PreferenceScreen(getActivity(), null); + pref.setTitle(device.getName()); + if (keyboardLayout != null) { + pref.setSummary(keyboardLayout.toString()); + } else { + pref.setSummary(R.string.keyboard_layout_default_label); } + pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + showKeyboardLayoutDialog(identifier); + return true; + } + }); + mHardKeyboardPreferenceList.add(pref); } } @@ -702,8 +664,8 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { List<SearchIndexableRaw> indexables = new ArrayList<>(); - Resources resources = context.getResources(); - String screenTitle = context.getString(R.string.language_keyboard_settings_title); + final Resources resources = context.getResources(); + final String screenTitle = context.getString(R.string.language_keyboard_settings_title); // Locale picker. if (context.getAssets().getLocales().length > 1) { @@ -795,116 +757,55 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment // Hard keyboards InputManager inputManager = (InputManager) context.getSystemService( Context.INPUT_SERVICE); - if (resources.getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) { - boolean hasHardKeyboards = false; - - final int[] devices = InputDevice.getDeviceIds(); - for (int i = 0; i < devices.length; i++) { - InputDevice device = InputDevice.getDevice(devices[i]); - if (device == null || device.isVirtual() || !device.isFullKeyboard()) { - continue; - } - - hasHardKeyboards = true; - - InputDeviceIdentifier identifier = device.getIdentifier(); - String keyboardLayoutDescriptor = - inputManager.getCurrentKeyboardLayoutForInputDevice(identifier); - KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? - inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null; - - String summary; - if (keyboardLayout != null) { - summary = keyboardLayout.toString(); - } else { - summary = context.getString(R.string.keyboard_layout_default_label); - } + boolean hasHardKeyboards = false; - indexable = new SearchIndexableRaw(context); - indexable.key = device.getName(); - indexable.title = device.getName(); - indexable.summaryOn = summary; - indexable.summaryOff = summary; - indexable.screenTitle = screenTitle; - indexables.add(indexable); + final int[] devices = InputDevice.getDeviceIds(); + for (int i = 0; i < devices.length; i++) { + InputDevice device = InputDevice.getDevice(devices[i]); + if (device == null || device.isVirtual() || !device.isFullKeyboard()) { + continue; } - if (hasHardKeyboards) { - // Hard keyboard category. - indexable = new SearchIndexableRaw(context); - indexable.key = "builtin_keyboard_settings"; - indexable.title = context.getString( - R.string.builtin_keyboard_settings_title); - indexable.screenTitle = screenTitle; - indexables.add(indexable); - - // Auto replace. - indexable = new SearchIndexableRaw(context); - indexable.key = "auto_replace"; - indexable.title = context.getString(R.string.auto_replace); - indexable.summaryOn = context.getString(R.string.auto_replace_summary); - indexable.summaryOff = context.getString(R.string.auto_replace_summary); - indexable.screenTitle = screenTitle; - indexables.add(indexable); - - // Auto caps. - indexable = new SearchIndexableRaw(context); - indexable.key = "auto_caps"; - indexable.title = context.getString(R.string.auto_caps); - indexable.summaryOn = context.getString(R.string.auto_caps_summary); - indexable.summaryOff = context.getString(R.string.auto_caps_summary); - indexable.screenTitle = screenTitle; - indexables.add(indexable); - - // Auto punctuate. - indexable = new SearchIndexableRaw(context); - indexable.key = "auto_punctuate"; - indexable.title = context.getString(R.string.auto_punctuate); - indexable.summaryOn = context.getString(R.string.auto_punctuate_summary); - indexable.summaryOff = context.getString(R.string.auto_punctuate_summary); - indexable.screenTitle = screenTitle; - indexables.add(indexable); - } - } + hasHardKeyboards = true; - // Voice recognizers. - List<ResolveInfo> recognizers = context.getPackageManager() - .queryIntentServices(new Intent(RecognitionService.SERVICE_INTERFACE), - PackageManager.GET_META_DATA); + InputDeviceIdentifier identifier = device.getIdentifier(); + String keyboardLayoutDescriptor = + inputManager.getCurrentKeyboardLayoutForInputDevice(identifier); + KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? + inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null; - final int recognizerCount = recognizers.size(); + String summary; + if (keyboardLayout != null) { + summary = keyboardLayout.toString(); + } else { + summary = context.getString(R.string.keyboard_layout_default_label); + } - // Recognizer settings. - if (recognizerCount > 0) { indexable = new SearchIndexableRaw(context); - indexable.key = "recognizer_settings"; - indexable.title = context.getString(R.string.recognizer_settings_title); + indexable.key = device.getName(); + indexable.title = device.getName(); + indexable.summaryOn = summary; + indexable.summaryOff = summary; indexable.screenTitle = screenTitle; indexables.add(indexable); } - if (recognizerCount > 1) { - // Recognizer chooser. + if (hasHardKeyboards) { + // Hard keyboard category. indexable = new SearchIndexableRaw(context); - indexable.key = "recognizer_title"; - indexable.title = context.getString(R.string.recognizer_title); + indexable.key = "builtin_keyboard_settings"; + indexable.title = context.getString( + R.string.builtin_keyboard_settings_title); indexable.screenTitle = screenTitle; indexables.add(indexable); } - for (int i = 0; i < recognizerCount; i++) { - ResolveInfo recognizer = recognizers.get(i); - - ServiceInfo serviceInfo = recognizer.serviceInfo; - ComponentName componentName = new ComponentName(serviceInfo.packageName, - serviceInfo.name); - - indexable = new SearchIndexableRaw(context); - indexable.key = componentName.flattenToString(); - indexable.title = recognizer.loadLabel(context.getPackageManager()).toString(); - indexable.screenTitle = screenTitle; - indexables.add(indexable); - } + // Voice input + indexable = new SearchIndexableRaw(context); + indexable.key = "voice_input_settings"; + indexable.title = context.getString(R.string.voice_input_settings); + indexable.screenTitle = screenTitle; + indexables.add(indexable); // Text-to-speech. TtsEngines ttsEngines = new TtsEngines(context); diff --git a/src/com/android/settings/inputmethod/InputMethodPreference.java b/src/com/android/settings/inputmethod/InputMethodPreference.java index 111f79b..5cf5d6a 100755 --- a/src/com/android/settings/inputmethod/InputMethodPreference.java +++ b/src/com/android/settings/inputmethod/InputMethodPreference.java @@ -68,6 +68,7 @@ class InputMethodPreference extends SwitchPreference implements OnPreferenceClic private final boolean mHasPriorityInSorting; private final OnSavePreferenceListener mOnSaveListener; private final InputMethodSettingValuesWrapper mInputMethodSettingValues; + private final boolean mIsAllowedByOrganization; private AlertDialog mDialog = null; @@ -78,14 +79,18 @@ class InputMethodPreference extends SwitchPreference implements OnPreferenceClic * @param imi The {@link InputMethodInfo} of this preference. * @param isImeEnabler true if this preference is the IME enabler that has enable/disable * switches for all available IMEs, not the list of enabled IMEs. + * @param isAllowedByOrganization false if the IME has been disabled by a device or profile + owner. * @param onSaveListener The listener called when this preference has been changed and needs * to save the state to shared preference. */ InputMethodPreference(final Context context, final InputMethodInfo imi, - final boolean isImeEnabler, final OnSavePreferenceListener onSaveListener) { + final boolean isImeEnabler, final boolean isAllowedByOrganization, + final OnSavePreferenceListener onSaveListener) { super(context); setPersistent(false); mImi = imi; + mIsAllowedByOrganization = isAllowedByOrganization; mOnSaveListener = onSaveListener; if (!isImeEnabler) { // Hide switch widget. @@ -178,7 +183,7 @@ class InputMethodPreference extends SwitchPreference implements OnPreferenceClic mImi, getContext()); // Only when this preference has a switch and an input method should be always enabled, // this preference should be disabled to prevent accidentally disabling an input method. - setEnabled(!(isAlwaysChecked && isImeEnabler())); + setEnabled(!((isAlwaysChecked && isImeEnabler()) || (!mIsAllowedByOrganization))); setChecked(mInputMethodSettingValues.isEnabledImi(mImi)); setSummary(getSummaryString()); } @@ -189,6 +194,9 @@ class InputMethodPreference extends SwitchPreference implements OnPreferenceClic private String getSummaryString() { final Context context = getContext(); + if (!mIsAllowedByOrganization) { + return context.getString(R.string.accessibility_feature_or_input_method_not_allowed); + } final InputMethodManager imm = getInputMethodManager(); final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(mImi, true); final ArrayList<CharSequence> subtypeLabels = new ArrayList<>(); diff --git a/src/com/android/settings/location/DimmableIconPreference.java b/src/com/android/settings/location/DimmableIconPreference.java new file mode 100644 index 0000000..acde1c1 --- /dev/null +++ b/src/com/android/settings/location/DimmableIconPreference.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.location; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.preference.Preference; +import android.util.AttributeSet; + +/** + * A preference item that can dim the icon when it's disabled, either directly or because its parent + * is disabled. + */ +public class DimmableIconPreference extends Preference { + private static final int ICON_ALPHA_ENABLED = 255; + private static final int ICON_ALPHA_DISABLED = 102; + + public DimmableIconPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public DimmableIconPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DimmableIconPreference(Context context) { + super(context); + } + + private void dimIcon(boolean dimmed) { + Drawable icon = getIcon(); + if (icon != null) { + icon.mutate().setAlpha(dimmed ? ICON_ALPHA_DISABLED : ICON_ALPHA_ENABLED); + setIcon(icon); + } + } + + @Override + public void onParentChanged(Preference parent, boolean disableChild) { + dimIcon(disableChild); + super.onParentChanged(parent, disableChild); + } + + @Override + public void setEnabled(boolean enabled) { + dimIcon(!enabled); + super.setEnabled(enabled); + } +} diff --git a/src/com/android/settings/location/InjectedSetting.java b/src/com/android/settings/location/InjectedSetting.java index d8a3f49..5f4440a 100644 --- a/src/com/android/settings/location/InjectedSetting.java +++ b/src/com/android/settings/location/InjectedSetting.java @@ -23,7 +23,7 @@ import com.android.internal.annotations.Immutable; import com.android.internal.util.Preconditions; /** - * Specifies a setting that is being injected into Settings > Location > Location services. + * Specifies a setting that is being injected into Settings > Location > Location services. * * @see android.location.SettingInjectorService */ diff --git a/src/com/android/settings/location/RecentLocationApps.java b/src/com/android/settings/location/RecentLocationApps.java index 8e1ec05..e28f96b 100644 --- a/src/com/android/settings/location/RecentLocationApps.java +++ b/src/com/android/settings/location/RecentLocationApps.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.IPackageManager; +import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Process; @@ -30,6 +31,8 @@ import android.os.UserHandle; import android.os.UserManager; import android.preference.Preference; import android.util.Log; +import android.view.View; +import android.widget.TextView; import com.android.settings.R; import com.android.settings.SettingsActivity; @@ -74,12 +77,35 @@ public class RecentLocationApps { } } - private Preference createRecentLocationEntry( + /** + * Subclass of {@link Preference} to intercept views and allow content description to be set on + * them for accessibility purposes. + */ + private static class AccessiblePreference extends DimmableIconPreference { + public CharSequence mContentDescription; + + public AccessiblePreference(Context context, CharSequence contentDescription) { + super(context); + mContentDescription = contentDescription; + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + if (mContentDescription != null) { + final TextView titleView = (TextView) view.findViewById(android.R.id.title); + titleView.setContentDescription(mContentDescription); + } + } + } + + private AccessiblePreference createRecentLocationEntry( Drawable icon, CharSequence label, boolean isHighBattery, + CharSequence contentDescription, Preference.OnPreferenceClickListener listener) { - Preference pref = new Preference(mActivity); + AccessiblePreference pref = new AccessiblePreference(mActivity, contentDescription); pref.setIcon(icon); pref.setTitle(label); if (isHighBattery) { @@ -172,19 +198,29 @@ public class RecentLocationApps { int uid = ops.getUid(); int userId = UserHandle.getUserId(uid); - Preference preference = null; + AccessiblePreference preference = null; try { IPackageManager ipm = AppGlobals.getPackageManager(); ApplicationInfo appInfo = ipm.getApplicationInfo(packageName, PackageManager.GET_META_DATA, userId); + if (appInfo == null) { + Log.w(TAG, "Null application info retrieved for package " + packageName + + ", userId " + userId); + return null; + } + Resources res = mActivity.getResources(); - Drawable icon = um.getBadgedDrawableForUser( - mPackageManager.getApplicationIcon(appInfo), new UserHandle(userId)); + final UserHandle userHandle = new UserHandle(userId); + Drawable appIcon = mPackageManager.getApplicationIcon(appInfo); + Drawable icon = um.getBadgedDrawableForUser(appIcon, userHandle); + CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo); + CharSequence badgedAppLabel = um.getBadgedLabelForUser(appLabel.toString(), userHandle); preference = createRecentLocationEntry(icon, - mPackageManager.getApplicationLabel(appInfo), highBattery, + appLabel, highBattery, badgedAppLabel, new PackageEntryClickedListener(packageName)); } catch (RemoteException e) { - Log.w(TAG, "Error while retrieving application info", e); + Log.w(TAG, "Error while retrieving application info for package " + packageName + + ", userId " + userId, e); } return preference; diff --git a/src/com/android/settings/location/SettingsInjector.java b/src/com/android/settings/location/SettingsInjector.java index 5f8d030..edf67b8 100644 --- a/src/com/android/settings/location/SettingsInjector.java +++ b/src/com/android/settings/location/SettingsInjector.java @@ -247,7 +247,7 @@ class SettingsInjector { * Adds an injected setting to the root. */ private Preference addServiceSetting(List<Preference> prefs, InjectedSetting info) { - Preference pref = new Preference(mContext); + Preference pref = new DimmableIconPreference(mContext); pref.setTitle(info.title); pref.setSummary(null); PackageManager pm = mContext.getPackageManager(); diff --git a/src/com/android/settings/net/UidDetail.java b/src/com/android/settings/net/UidDetail.java index fd44d47..0b14254 100644 --- a/src/com/android/settings/net/UidDetail.java +++ b/src/com/android/settings/net/UidDetail.java @@ -20,6 +20,8 @@ import android.graphics.drawable.Drawable; public class UidDetail { public CharSequence label; + public CharSequence contentDescription; public CharSequence[] detailLabels; + public CharSequence[] detailContentDescriptions; public Drawable icon; } diff --git a/src/com/android/settings/net/UidDetailProvider.java b/src/com/android/settings/net/UidDetailProvider.java index cd101c9..4b54137 100644 --- a/src/com/android/settings/net/UidDetailProvider.java +++ b/src/com/android/settings/net/UidDetailProvider.java @@ -16,8 +16,10 @@ package com.android.settings.net; +import android.app.AppGlobals; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -28,7 +30,9 @@ import android.net.ConnectivityManager; import android.net.TrafficStats; import android.os.UserManager; import android.os.UserHandle; +import android.os.RemoteException; import android.text.TextUtils; +import android.util.Log; import android.util.SparseArray; import com.android.settings.R; @@ -39,6 +43,7 @@ import com.android.settings.Utils; * {@link TrafficStats#UID_TETHERING} and {@link UserInfo}. */ public class UidDetailProvider { + private static final String TAG = "DataUsage"; private final Context mContext; private final SparseArray<UidDetail> mUidDetailCache; @@ -148,28 +153,43 @@ public class UidDetailProvider { final String[] packageNames = pm.getPackagesForUid(uid); final int length = packageNames != null ? packageNames.length : 0; try { + final int userId = UserHandle.getUserId(uid); + UserHandle userHandle = new UserHandle(userId); + IPackageManager ipm = AppGlobals.getPackageManager(); if (length == 1) { - final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0); - detail.label = info.loadLabel(pm).toString(); - detail.icon = um.getBadgedDrawableForUser(info.loadIcon(pm), - new UserHandle(UserHandle.getUserId(uid))); + final ApplicationInfo info = ipm.getApplicationInfo(packageNames[0], + 0 /* no flags */, userId); + if (info != null) { + detail.label = info.loadLabel(pm).toString(); + detail.icon = um.getBadgedIconForUser(info.loadIcon(pm), + new UserHandle(userId)); + } } else if (length > 1) { detail.detailLabels = new CharSequence[length]; + detail.detailContentDescriptions = new CharSequence[length]; for (int i = 0; i < length; i++) { final String packageName = packageNames[i]; final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); - final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0); - - detail.detailLabels[i] = appInfo.loadLabel(pm).toString(); - if (packageInfo.sharedUserLabel != 0) { - detail.label = pm.getText(packageName, packageInfo.sharedUserLabel, - packageInfo.applicationInfo).toString(); - detail.icon = um.getBadgedDrawableForUser(appInfo.loadIcon(pm), - new UserHandle(UserHandle.getUserId(uid))); + final ApplicationInfo appInfo = ipm.getApplicationInfo(packageName, + 0 /* no flags */, userId); + + if (appInfo != null) { + detail.detailLabels[i] = appInfo.loadLabel(pm).toString(); + detail.detailContentDescriptions[i] = um.getBadgedLabelForUser( + detail.detailLabels[i], userHandle); + if (packageInfo.sharedUserLabel != 0) { + detail.label = pm.getText(packageName, packageInfo.sharedUserLabel, + packageInfo.applicationInfo).toString(); + detail.icon = um.getBadgedIconForUser(appInfo.loadIcon(pm), userHandle); + } } } } + detail.contentDescription = um.getBadgedLabelForUser(detail.label, userHandle); } catch (NameNotFoundException e) { + Log.w(TAG, "Error while building UI detail for uid "+uid, e); + } catch (RemoteException e) { + Log.w(TAG, "Error while building UI detail for uid "+uid, e); } if (TextUtils.isEmpty(detail.label)) { diff --git a/src/com/android/settings/nfc/NfcEnabler.java b/src/com/android/settings/nfc/NfcEnabler.java index 018b8ae..2fb95e4 100644 --- a/src/com/android/settings/nfc/NfcEnabler.java +++ b/src/com/android/settings/nfc/NfcEnabler.java @@ -21,10 +21,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.nfc.NfcAdapter; -import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; import com.android.settings.R; /** @@ -34,7 +34,7 @@ import com.android.settings.R; */ public class NfcEnabler implements Preference.OnPreferenceChangeListener { private final Context mContext; - private final CheckBoxPreference mCheckbox; + private final SwitchPreference mSwitch; private final PreferenceScreen mAndroidBeam; private final NfcAdapter mNfcAdapter; private final IntentFilter mIntentFilter; @@ -50,16 +50,16 @@ public class NfcEnabler implements Preference.OnPreferenceChangeListener { } }; - public NfcEnabler(Context context, CheckBoxPreference checkBoxPreference, + public NfcEnabler(Context context, SwitchPreference switchPreference, PreferenceScreen androidBeam) { mContext = context; - mCheckbox = checkBoxPreference; + mSwitch = switchPreference; mAndroidBeam = androidBeam; mNfcAdapter = NfcAdapter.getDefaultAdapter(context); if (mNfcAdapter == null) { // NFC is not supported - mCheckbox.setEnabled(false); + mSwitch.setEnabled(false); mAndroidBeam.setEnabled(false); mIntentFilter = null; return; @@ -73,7 +73,7 @@ public class NfcEnabler implements Preference.OnPreferenceChangeListener { } handleNfcStateChanged(mNfcAdapter.getAdapterState()); mContext.registerReceiver(mReceiver, mIntentFilter); - mCheckbox.setOnPreferenceChangeListener(this); + mSwitch.setOnPreferenceChangeListener(this); } public void pause() { @@ -81,14 +81,14 @@ public class NfcEnabler implements Preference.OnPreferenceChangeListener { return; } mContext.unregisterReceiver(mReceiver); - mCheckbox.setOnPreferenceChangeListener(null); + mSwitch.setOnPreferenceChangeListener(null); } public boolean onPreferenceChange(Preference preference, Object value) { // Turn NFC on/off final boolean desiredState = (Boolean) value; - mCheckbox.setEnabled(false); + mSwitch.setEnabled(false); if (desiredState) { mNfcAdapter.enable(); @@ -102,14 +102,14 @@ public class NfcEnabler implements Preference.OnPreferenceChangeListener { private void handleNfcStateChanged(int newState) { switch (newState) { case NfcAdapter.STATE_OFF: - mCheckbox.setChecked(false); - mCheckbox.setEnabled(true); + mSwitch.setChecked(false); + mSwitch.setEnabled(true); mAndroidBeam.setEnabled(false); mAndroidBeam.setSummary(R.string.android_beam_disabled_summary); break; case NfcAdapter.STATE_ON: - mCheckbox.setChecked(true); - mCheckbox.setEnabled(true); + mSwitch.setChecked(true); + mSwitch.setEnabled(true); mAndroidBeam.setEnabled(true); if (mNfcAdapter.isNdefPushEnabled()) { mAndroidBeam.setSummary(R.string.android_beam_on_summary); @@ -118,13 +118,13 @@ public class NfcEnabler implements Preference.OnPreferenceChangeListener { } break; case NfcAdapter.STATE_TURNING_ON: - mCheckbox.setChecked(true); - mCheckbox.setEnabled(false); + mSwitch.setChecked(true); + mSwitch.setEnabled(false); mAndroidBeam.setEnabled(false); break; case NfcAdapter.STATE_TURNING_OFF: - mCheckbox.setChecked(false); - mCheckbox.setEnabled(false); + mSwitch.setChecked(false); + mSwitch.setEnabled(false); mAndroidBeam.setEnabled(false); break; } diff --git a/src/com/android/settings/nfc/PaymentDefaultDialog.java b/src/com/android/settings/nfc/PaymentDefaultDialog.java index 6bc29e1..33ac947 100644 --- a/src/com/android/settings/nfc/PaymentDefaultDialog.java +++ b/src/com/android/settings/nfc/PaymentDefaultDialog.java @@ -34,6 +34,7 @@ public final class PaymentDefaultDialog extends AlertActivity implements DialogInterface.OnClickListener { public static final String TAG = "PaymentDefaultDialog"; + private static final int PAYMENT_APP_MAX_CAPTION_LENGTH = 40; private PaymentBackend mBackend; private ComponentName mNewDefault; @@ -109,12 +110,14 @@ public final class PaymentDefaultDialog extends AlertActivity implements p.mTitle = getString(R.string.nfc_payment_set_default_label); if (defaultPaymentApp == null) { String formatString = getString(R.string.nfc_payment_set_default); - String msg = String.format(formatString, requestedPaymentApp.caption); + String msg = String.format(formatString, + sanitizePaymentAppCaption(requestedPaymentApp.caption.toString())); p.mMessage = msg; } else { String formatString = getString(R.string.nfc_payment_set_default_instead_of); - String msg = String.format(formatString, requestedPaymentApp.caption, - defaultPaymentApp.caption); + String msg = String.format(formatString, + sanitizePaymentAppCaption(requestedPaymentApp.caption.toString()), + sanitizePaymentAppCaption(defaultPaymentApp.caption.toString())); p.mMessage = msg; } p.mPositiveButtonText = getString(R.string.yes); @@ -126,4 +129,15 @@ public final class PaymentDefaultDialog extends AlertActivity implements return true; } + private String sanitizePaymentAppCaption(String input) { + String sanitizedString = input.replace('\n', ' ').replace('\r', ' ').trim(); + + + if (sanitizedString.length() > PAYMENT_APP_MAX_CAPTION_LENGTH) { + return sanitizedString.substring(0, PAYMENT_APP_MAX_CAPTION_LENGTH); + } + + return sanitizedString; + } + } diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java index 6075748..efa43cd 100644 --- a/src/com/android/settings/notification/AppNotificationSettings.java +++ b/src/com/android/settings/notification/AppNotificationSettings.java @@ -36,6 +36,7 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import com.android.internal.widget.LockPatternUtils; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.notification.NotificationAppList.AppRow; @@ -134,6 +135,11 @@ public class AppNotificationSettings extends SettingsPreferenceFragment { mPriority = (SwitchPreference) findPreference(KEY_PRIORITY); mSensitive = (SwitchPreference) findPreference(KEY_SENSITIVE); + final boolean secure = new LockPatternUtils(getActivity()).isSecure(); + if (!secure) { + getPreferenceScreen().removePreference(mSensitive); + } + mAppRow = NotificationAppList.loadAppRow(pm, info, mBackend); if (intent.hasExtra(EXTRA_HAS_SETTINGS_INTENT)) { // use settings intent from extra @@ -149,7 +155,9 @@ public class AppNotificationSettings extends SettingsPreferenceFragment { mBlock.setChecked(mAppRow.banned); mPriority.setChecked(mAppRow.priority); - mSensitive.setChecked(mAppRow.sensitive); + if (mSensitive != null) { + mSensitive.setChecked(mAppRow.sensitive); + } mBlock.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override @@ -167,13 +175,15 @@ public class AppNotificationSettings extends SettingsPreferenceFragment { } }); - mSensitive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final boolean sensitive = (Boolean) newValue; - return mBackend.setSensitive(pkg, uid, sensitive); - } - }); + if (mSensitive != null) { + mSensitive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean sensitive = (Boolean) newValue; + return mBackend.setSensitive(pkg, uid, sensitive); + } + }); + } } private void toastAndFinish() { diff --git a/src/com/android/settings/notification/NotificationAppList.java b/src/com/android/settings/notification/NotificationAppList.java index 3879bef..7ca4b18 100644 --- a/src/com/android/settings/notification/NotificationAppList.java +++ b/src/com/android/settings/notification/NotificationAppList.java @@ -193,30 +193,6 @@ public class NotificationAppList extends PinnedHeaderListFragment parent.getPaddingEnd() - eat, parent.getPaddingBottom()); } - private boolean isSystemApp(PackageInfo pkg) { - if (mSystemSignature == null) { - mSystemSignature = new Signature[]{ getSystemSignature() }; - } - return mSystemSignature[0] != null && mSystemSignature[0].equals(getFirstSignature(pkg)); - } - - private static Signature getFirstSignature(PackageInfo pkg) { - if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) { - return pkg.signatures[0]; - } - return null; - } - - private Signature getSystemSignature() { - final PackageManager pm = mContext.getPackageManager(); - try { - final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); - return getFirstSignature(sys); - } catch (NameNotFoundException e) { - } - return null; - } - private static class ViewHolder { ViewGroup row; ImageView icon; @@ -375,6 +351,7 @@ public class NotificationAppList extends PinnedHeaderListFragment public boolean priority; public boolean sensitive; public boolean first; // first app in section + public boolean isSystem; } private static final Comparator<AppRow> mRowComparator = new Comparator<AppRow>() { @@ -385,6 +362,7 @@ public class NotificationAppList extends PinnedHeaderListFragment } }; + public static AppRow loadAppRow(PackageManager pm, PackageInfo pkg, Backend backend) { final AppRow row = new AppRow(); row.pkg = pkg.packageName; @@ -399,16 +377,28 @@ public class NotificationAppList extends PinnedHeaderListFragment row.banned = backend.getNotificationsBanned(row.pkg, row.uid); row.priority = backend.getHighPriority(row.pkg, row.uid); row.sensitive = backend.getSensitive(row.pkg, row.uid); + row.isSystem = Utils.isSystemPackage(pm, pkg); return row; } - public static void collectConfigActivities(PackageManager pm, ArrayMap<String, AppRow> rows) { + public static List<ResolveInfo> queryNotificationConfigActivities(PackageManager pm) { if (DEBUG) Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is " + APP_NOTIFICATION_PREFS_CATEGORY_INTENT); final List<ResolveInfo> resolveInfos = pm.queryIntentActivities( APP_NOTIFICATION_PREFS_CATEGORY_INTENT, - PackageManager.MATCH_DEFAULT_ONLY); - if (DEBUG) Log.d(TAG, "Found " + resolveInfos.size() + " preference activities"); + 0 //PackageManager.MATCH_DEFAULT_ONLY + ); + return resolveInfos; + } + public static void collectConfigActivities(PackageManager pm, ArrayMap<String, AppRow> rows) { + final List<ResolveInfo> resolveInfos = queryNotificationConfigActivities(pm); + applyConfigActivities(pm, rows, resolveInfos); + } + + public static void applyConfigActivities(PackageManager pm, ArrayMap<String, AppRow> rows, + List<ResolveInfo> resolveInfos) { + if (DEBUG) Log.d(TAG, "Found " + resolveInfos.size() + " preference activities" + + (resolveInfos.size() == 0 ? " ;_;" : "")); for (ResolveInfo ri : resolveInfos) { final ActivityInfo activityInfo = ri.activityInfo; final ApplicationInfo appInfo = activityInfo.applicationInfo; @@ -425,7 +415,7 @@ public class NotificationAppList extends PinnedHeaderListFragment + activityInfo.packageName); continue; } - row.settingsIntent = new Intent(Intent.ACTION_MAIN) + row.settingsIntent = new Intent(APP_NOTIFICATION_PREFS_CATEGORY_INTENT) .setClassName(activityInfo.packageName, activityInfo.name); } } @@ -439,18 +429,41 @@ public class NotificationAppList extends PinnedHeaderListFragment mRows.clear(); mSortedRows.clear(); - // collect all non-system apps + // collect all launchable apps, plus any packages that have notification settings final PackageManager pm = mContext.getPackageManager(); - for (PackageInfo pkg : pm.getInstalledPackages(PackageManager.GET_SIGNATURES)) { - if (pkg.applicationInfo == null || isSystemApp(pkg)) { - if (DEBUG) Log.d(TAG, "Skipping " + pkg.packageName); + final List<ResolveInfo> resolvedApps = pm.queryIntentActivities( + new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER), + PackageManager.MATCH_DEFAULT_ONLY + ); + final List<ResolveInfo> resolvedConfigActivities + = queryNotificationConfigActivities(pm); + resolvedApps.addAll(resolvedConfigActivities); + + for (ResolveInfo info : resolvedApps) { + String pkgName = info.activityInfo.packageName; + if (mRows.containsKey(pkgName)) { + // we already have this app, thanks + continue; + } + + PackageInfo pkg = null; + try { + pkg = pm.getPackageInfo(pkgName, + PackageManager.GET_SIGNATURES); + } catch (NameNotFoundException e) { + if (DEBUG) Log.d(TAG, "Skipping (NNFE): " + pkg.packageName); + continue; + } + if (info.activityInfo.applicationInfo == null) { + if (DEBUG) Log.d(TAG, "Skipping (no applicationInfo): " + pkg.packageName); continue; } final AppRow row = loadAppRow(pm, pkg, mBackend); - mRows.put(row.pkg, row); + mRows.put(pkgName, row); } - // collect config activities - collectConfigActivities(pm, mRows); + + // add config activities to the list + applyConfigActivities(pm, mRows, resolvedConfigActivities); // sort rows mSortedRows.addAll(mRows.values()); Collections.sort(mSortedRows, mRowComparator); diff --git a/src/com/android/settings/notification/NotificationSettings.java b/src/com/android/settings/notification/NotificationSettings.java index 2b0fd5c..3094032 100644 --- a/src/com/android/settings/notification/NotificationSettings.java +++ b/src/com/android/settings/notification/NotificationSettings.java @@ -30,6 +30,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.Vibrator; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceCategory; @@ -40,6 +41,8 @@ import android.provider.SearchIndexableResource; import android.provider.Settings; import android.util.Log; +import android.widget.SeekBar; +import com.android.internal.widget.LockPatternUtils; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; @@ -75,6 +78,8 @@ public class NotificationSettings extends SettingsPreferenceFragment implements private Context mContext; private PackageManager mPM; private boolean mVoiceCapable; + private Vibrator mVibrator; + private VolumeSeekBarPreference mRingOrNotificationPreference; private Preference mPhoneRingtonePreference; private Preference mNotificationRingtonePreference; @@ -82,6 +87,8 @@ public class NotificationSettings extends SettingsPreferenceFragment implements private TwoStatePreference mNotificationPulse; private DropDownPreference mLockscreen; private Preference mNotificationAccess; + private boolean mSecure; + private int mLockscreenSelectedValue; @Override public void onCreate(Bundle savedInstanceState) { @@ -89,16 +96,25 @@ public class NotificationSettings extends SettingsPreferenceFragment implements mContext = getActivity(); mPM = mContext.getPackageManager(); mVoiceCapable = Utils.isVoiceCapable(mContext); + mSecure = new LockPatternUtils(getActivity()).isSecure(); + + mVibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE); + if (mVibrator != null && !mVibrator.hasVibrator()) { + mVibrator = null; + } + addPreferencesFromResource(R.xml.notification_settings); final PreferenceCategory sound = (PreferenceCategory) findPreference(KEY_SOUND); initVolumePreference(KEY_MEDIA_VOLUME, AudioManager.STREAM_MUSIC); initVolumePreference(KEY_ALARM_VOLUME, AudioManager.STREAM_ALARM); if (mVoiceCapable) { - initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING); + mRingOrNotificationPreference = + initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING); sound.removePreference(sound.findPreference(KEY_NOTIFICATION_VOLUME)); } else { - initVolumePreference(KEY_NOTIFICATION_VOLUME, AudioManager.STREAM_NOTIFICATION); + mRingOrNotificationPreference = + initVolumePreference(KEY_NOTIFICATION_VOLUME, AudioManager.STREAM_NOTIFICATION); sound.removePreference(sound.findPreference(KEY_RING_VOLUME)); } initRingtones(sound); @@ -130,10 +146,19 @@ public class NotificationSettings extends SettingsPreferenceFragment implements // === Volumes === - private void initVolumePreference(String key, int stream) { + private VolumeSeekBarPreference initVolumePreference(String key, int stream) { final VolumeSeekBarPreference volumePref = (VolumeSeekBarPreference) findPreference(key); - volumePref.setStream(stream); volumePref.setCallback(mVolumeCallback); + volumePref.setStream(stream); + return volumePref; + } + + private void updateRingOrNotificationIcon(int progress) { + mRingOrNotificationPreference.showIcon(progress > 0 + ? R.drawable.ring_notif + : (mVibrator == null + ? R.drawable.ring_notif_mute + : R.drawable.ring_notif_vibrate)); } private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback { @@ -151,6 +176,14 @@ public class NotificationSettings extends SettingsPreferenceFragment implements } } + @Override + public void onStreamValueChanged(int stream, int progress) { + if (stream == AudioManager.STREAM_RING) { + mHandler.removeMessages(H.UPDATE_RINGER_ICON); + mHandler.obtainMessage(H.UPDATE_RINGER_ICON, progress, 0).sendToTarget(); + } + } + public void stopSample() { if (mCurrent != null) { mCurrent.stopSample(); @@ -303,10 +336,13 @@ public class NotificationSettings extends SettingsPreferenceFragment implements Log.i(TAG, "Preference not found: " + KEY_LOCK_SCREEN_NOTIFICATIONS); return; } + mLockscreen.addItem(R.string.lock_screen_notifications_summary_show, R.string.lock_screen_notifications_summary_show); - mLockscreen.addItem(R.string.lock_screen_notifications_summary_hide, - R.string.lock_screen_notifications_summary_hide); + if (mSecure) { + mLockscreen.addItem(R.string.lock_screen_notifications_summary_hide, + R.string.lock_screen_notifications_summary_hide); + } mLockscreen.addItem(R.string.lock_screen_notifications_summary_disable, R.string.lock_screen_notifications_summary_disable); updateLockscreenNotifications(); @@ -314,12 +350,16 @@ public class NotificationSettings extends SettingsPreferenceFragment implements @Override public boolean onItemSelected(int pos, Object value) { final int val = (Integer) value; + if (val == mLockscreenSelectedValue) { + return true; + } final boolean enabled = val != R.string.lock_screen_notifications_summary_disable; final boolean show = val == R.string.lock_screen_notifications_summary_show; Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, show ? 1 : 0); Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, enabled ? 1 : 0); + mLockscreenSelectedValue = val; return true; } }); @@ -329,12 +369,12 @@ public class NotificationSettings extends SettingsPreferenceFragment implements if (mLockscreen == null) { return; } - final boolean allowPrivate = getLockscreenAllowPrivateNotifications(); final boolean enabled = getLockscreenNotificationsEnabled(); - final int selectedVal = !enabled ? R.string.lock_screen_notifications_summary_disable : + final boolean allowPrivate = !mSecure || getLockscreenAllowPrivateNotifications(); + mLockscreenSelectedValue = !enabled ? R.string.lock_screen_notifications_summary_disable : allowPrivate ? R.string.lock_screen_notifications_summary_show : R.string.lock_screen_notifications_summary_hide; - mLockscreen.setSelectedValue(selectedVal); + mLockscreen.setSelectedValue(mLockscreenSelectedValue); } private boolean getLockscreenNotificationsEnabled() { @@ -415,6 +455,7 @@ public class NotificationSettings extends SettingsPreferenceFragment implements private static final int UPDATE_PHONE_RINGTONE = 1; private static final int UPDATE_NOTIFICATION_RINGTONE = 2; private static final int STOP_SAMPLE = 3; + private static final int UPDATE_RINGER_ICON = 4; private H() { super(Looper.getMainLooper()); @@ -432,6 +473,9 @@ public class NotificationSettings extends SettingsPreferenceFragment implements case STOP_SAMPLE: mVolumeCallback.stopSample(); break; + case UPDATE_RINGER_ICON: + updateRingOrNotificationIcon(msg.arg1); + break; } } } diff --git a/src/com/android/settings/notification/RedactionInterstitial.java b/src/com/android/settings/notification/RedactionInterstitial.java index 773f3af..2bfad1a 100644 --- a/src/com/android/settings/notification/RedactionInterstitial.java +++ b/src/com/android/settings/notification/RedactionInterstitial.java @@ -82,7 +82,7 @@ public class RedactionInterstitial extends SettingsActivity { final boolean enabled = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0; final boolean show = Settings.Secure.getInt(getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0; + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1) != 0; mShowAllButton.setChecked(enabled && show); mRedactSensitiveButton.setChecked(enabled && !show); mHideAllButton.setChecked(!enabled); diff --git a/src/com/android/settings/notification/RedactionSettingsStandalone.java b/src/com/android/settings/notification/RedactionSettingsStandalone.java new file mode 100644 index 0000000..26c05c1 --- /dev/null +++ b/src/com/android/settings/notification/RedactionSettingsStandalone.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settings.notification; + +import com.android.settings.R; + +import android.content.Intent; +import com.android.settings.SettingsActivity; +import com.android.settings.notification.RedactionInterstitial.RedactionInterstitialFragment; + +/** Wrapper to allow external activites to jump directly to the {@link RedactionInterstitial} */ +public class RedactionSettingsStandalone extends SettingsActivity { + + @Override + public Intent getIntent() { + Intent modIntent = new Intent(super.getIntent()); + modIntent.putExtra(EXTRA_SHOW_FRAGMENT, RedactionInterstitialFragment.class.getName()) + .putExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, true) + .putExtra(EXTRA_PREFS_SET_BACK_TEXT, (String) null) + .putExtra(EXTRA_PREFS_SET_NEXT_TEXT, getString( + R.string.app_notifications_dialog_done)); + return modIntent; + } + + @Override + protected boolean isValidFragment(String fragmentName) { + return RedactionInterstitialFragment.class.getName().equals(fragmentName); + } +} diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java index 11a83a7..f94e6a1 100644 --- a/src/com/android/settings/notification/VolumeSeekBarPreference.java +++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java @@ -26,6 +26,7 @@ import android.preference.SeekBarVolumizer; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.widget.ImageView; import android.widget.SeekBar; import com.android.settings.R; @@ -39,6 +40,7 @@ public class VolumeSeekBarPreference extends SeekBarPreference private SeekBar mSeekBar; private SeekBarVolumizer mVolumizer; private Callback mCallback; + private ImageView mIconView; public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { @@ -93,9 +95,35 @@ public class VolumeSeekBarPreference extends SeekBarPreference }; final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null; if (mVolumizer == null) { - mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc); + mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc) { + // we need to piggyback on SBV's SeekBar listener to update our icon + @Override + public void onProgressChanged(SeekBar seekBar, int progress, + boolean fromTouch) { + super.onProgressChanged(seekBar, progress, fromTouch); + mCallback.onStreamValueChanged(mStream, progress); + } + }; } mVolumizer.setSeekBar(mSeekBar); + mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon); + mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress()); + } + + // during initialization, this preference is the SeekBar listener + @Override + public void onProgressChanged(SeekBar seekBar, int progress, + boolean fromTouch) { + super.onProgressChanged(seekBar, progress, fromTouch); + mCallback.onStreamValueChanged(mStream, progress); + } + + public void showIcon(int resId) { + // Instead of using setIcon, which will trigger listeners, this just decorates the + // preference temporarily with a new icon. + if (mIconView != null) { + mIconView.setImageResource(resId); + } } private Uri getMediaVolumeUri() { @@ -106,5 +134,6 @@ public class VolumeSeekBarPreference extends SeekBarPreference public interface Callback { void onSampleStarting(SeekBarVolumizer sbv); + void onStreamValueChanged(int stream, int progress); } } diff --git a/src/com/android/settings/notification/ZenModeConditionSelection.java b/src/com/android/settings/notification/ZenModeConditionSelection.java index 4b1c229..610baba 100644 --- a/src/com/android/settings/notification/ZenModeConditionSelection.java +++ b/src/com/android/settings/notification/ZenModeConditionSelection.java @@ -54,14 +54,14 @@ public class ZenModeConditionSelection extends RadioGroup { b.setChecked(true); } - private RadioButton newRadioButton(Object tag) { + private RadioButton newRadioButton(Condition condition) { final RadioButton button = new RadioButton(mContext); - button.setTag(tag); + button.setTag(condition); button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { - handleSubscribe((Uri)button.getTag()); + handleSubscribe((Condition) button.getTag()); } } }); @@ -95,7 +95,7 @@ public class ZenModeConditionSelection extends RadioGroup { RadioButton v = (RadioButton) findViewWithTag(c.id); if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_UNKNOWN) { if (v == null) { - v = newRadioButton(c.id); + v = newRadioButton(c); } } if (v != null) { @@ -105,10 +105,10 @@ public class ZenModeConditionSelection extends RadioGroup { } } - protected void handleSubscribe(Uri id) { - if (DEBUG) Log.d(TAG, "handleSubscribe " + id); + protected void handleSubscribe(Condition c) { + if (DEBUG) Log.d(TAG, "handleSubscribe " + c); try { - mNoMan.setZenModeCondition(id); + mNoMan.setZenModeCondition(c); } catch (RemoteException e) { // noop } diff --git a/src/com/android/settings/search/Index.java b/src/com/android/settings/search/Index.java index 65d825b..db60dfe 100644 --- a/src/com/android/settings/search/Index.java +++ b/src/com/android/settings/search/Index.java @@ -181,6 +181,16 @@ public class Index { nonIndexableKeys = new HashMap<String, List<String>>(); } + public UpdateData(UpdateData other) { + dataToUpdate = new ArrayList<SearchIndexableData>(other.dataToUpdate); + dataToDelete = new ArrayList<SearchIndexableData>(other.dataToDelete); + nonIndexableKeys = new HashMap<String, List<String>>(other.nonIndexableKeys); + } + + public UpdateData copy() { + return new UpdateData(this); + } + public void clear() { dataToUpdate.clear(); dataToDelete.clear(); @@ -286,7 +296,7 @@ public class Index { } } - public boolean update() { + public void update() { final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE); List<ResolveInfo> list = mContext.getPackageManager().queryIntentContentProviders(intent, 0); @@ -304,7 +314,7 @@ public class Index { addNonIndexablesKeysFromRemoteProvider(packageName, authority); } - return updateInternal(); + updateInternal(); } private boolean addIndexablesFromRemoteProvider(String packageName, String authority) { @@ -443,11 +453,10 @@ public class Index { } } - private boolean updateFromRemoteProvider(String packageName, String authority) { - if (!addIndexablesFromRemoteProvider(packageName, authority)) { - return false; + private void updateFromRemoteProvider(String packageName, String authority) { + if (addIndexablesFromRemoteProvider(packageName, authority)) { + updateInternal(); } - return updateInternal(); } /** @@ -457,9 +466,8 @@ public class Index { * @param rebuild true means that you want to delete the data from the Index first. * @param includeInSearchResults true means that you want the bit "enabled" set so that the * data will be seen included into the search results - * @return true of the Index update has been successful. */ - public boolean updateFromClassNameResource(String className, boolean rebuild, + public void updateFromClassNameResource(String className, boolean rebuild, boolean includeInSearchResults) { if (className == null) { throw new IllegalArgumentException("class name cannot be null!"); @@ -467,7 +475,7 @@ public class Index { final SearchIndexableResource res = SearchIndexableResources.getResourceByName(className); if (res == null ) { Log.e(LOG_TAG, "Cannot find SearchIndexableResources for class name: " + className); - return false; + return; } res.context = mContext; res.enabled = includeInSearchResults; @@ -476,15 +484,14 @@ public class Index { } addIndexableData(res); mDataToProcess.forceUpdate = true; - boolean result = updateInternal(); + updateInternal(); res.enabled = false; - return result; } - public boolean updateFromSearchIndexableData(SearchIndexableData data) { + public void updateFromSearchIndexableData(SearchIndexableData data) { addIndexableData(data); mDataToProcess.forceUpdate = true; - return updateInternal(); + updateInternal(); } private SQLiteDatabase getReadableDatabase() { @@ -510,21 +517,12 @@ public class Index { SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH); } - private boolean updateInternal() { + private void updateInternal() { synchronized (mDataToProcess) { final UpdateIndexTask task = new UpdateIndexTask(); - task.execute(mDataToProcess); - try { - final boolean result = task.get(); - mDataToProcess.clear(); - return result; - } catch (InterruptedException e) { - Log.e(LOG_TAG, "Cannot update index", e); - return false; - } catch (ExecutionException e) { - Log.e(LOG_TAG, "Cannot update index", e); - return false; - } + UpdateData copy = mDataToProcess.copy(); + task.execute(copy); + mDataToProcess.clear(); } } @@ -1027,8 +1025,14 @@ public class Index { return; } + // The DocID should contains more than the title string itself (you may have two settings + // with the same title). So we need to use a combination of the title and the screenTitle. + StringBuilder sb = new StringBuilder(updatedTitle); + sb.append(screenTitle); + int docId = sb.toString().hashCode(); + ContentValues values = new ContentValues(); - values.put(IndexColumns.DOCID, updatedTitle.hashCode()); + values.put(IndexColumns.DOCID, docId); values.put(IndexColumns.LOCALE, locale); values.put(IndexColumns.DATA_RANK, rank); values.put(IndexColumns.DATA_TITLE, updatedTitle); @@ -1143,7 +1147,7 @@ public class Index { /** * A private class for updating the Index database */ - private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Boolean> { + private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Void> { @Override protected void onPreExecute() { @@ -1152,15 +1156,13 @@ public class Index { } @Override - protected void onPostExecute(Boolean aBoolean) { - super.onPostExecute(aBoolean); + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); mIsAvailable.set(true); } @Override - protected Boolean doInBackground(UpdateData... params) { - boolean result = false; - + protected Void doInBackground(UpdateData... params) { final List<SearchIndexableData> dataToUpdate = params[0].dataToUpdate; final List<SearchIndexableData> dataToDelete = params[0].dataToDelete; final Map<String, List<String>> nonIndexableKeys = params[0].nonIndexableKeys; @@ -1180,11 +1182,11 @@ public class Index { forceUpdate); } database.setTransactionSuccessful(); - result = true; } finally { database.endTransaction(); } - return result; + + return null; } private boolean processDataToUpdate(SQLiteDatabase database, String localeStr, diff --git a/src/com/android/settings/search/Ranking.java b/src/com/android/settings/search/Ranking.java index 44717c1..2c76002 100644 --- a/src/com/android/settings/search/Ranking.java +++ b/src/com/android/settings/search/Ranking.java @@ -30,7 +30,6 @@ import com.android.settings.WallpaperTypeSettings; import com.android.settings.WirelessSettings; import com.android.settings.accessibility.AccessibilitySettings; import com.android.settings.bluetooth.BluetoothSettings; -import com.android.settings.bluetooth.MessageAccessSettings; import com.android.settings.deviceinfo.Memory; import com.android.settings.fuelgauge.BatterySaverSettings; import com.android.settings.fuelgauge.PowerUsageSummary; @@ -43,6 +42,7 @@ import com.android.settings.notification.ZenModeSettings; import com.android.settings.print.PrintSettingsFragment; import com.android.settings.sim.SimSettings; import com.android.settings.users.UserSettings; +import com.android.settings.voice.VoiceInputSettings; import com.android.settings.wifi.AdvancedWifiSettings; import com.android.settings.wifi.SavedAccessPointsWifiSettings; import com.android.settings.wifi.WifiSettings; @@ -93,7 +93,6 @@ public final class Ranking { // BT sRankMap.put(BluetoothSettings.class.getName(), RANK_BT); - sRankMap.put(MessageAccessSettings.class.getName(), RANK_BT); // SIM Cards sRankMap.put(SimSettings.class.getName(), RANK_SIM); @@ -139,6 +138,7 @@ public final class Ranking { // IMEs sRankMap.put(InputMethodAndLanguageSettings.class.getName(), RANK_IME); + sRankMap.put(VoiceInputSettings.class.getName(), RANK_IME); // Privacy sRankMap.put(PrivacySettings.class.getName(), RANK_PRIVACY); diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java index a3d2b8d..105ce7e 100644 --- a/src/com/android/settings/search/SearchIndexableResources.java +++ b/src/com/android/settings/search/SearchIndexableResources.java @@ -18,7 +18,6 @@ package com.android.settings.search; import android.provider.SearchIndexableResource; -import com.android.settings.ChooseLockGeneric; import com.android.settings.DataUsageSummary; import com.android.settings.DateTimeSettings; import com.android.settings.DevelopmentSettings; @@ -33,7 +32,6 @@ import com.android.settings.WallpaperTypeSettings; import com.android.settings.WirelessSettings; import com.android.settings.accessibility.AccessibilitySettings; import com.android.settings.bluetooth.BluetoothSettings; -import com.android.settings.bluetooth.MessageAccessSettings; import com.android.settings.deviceinfo.Memory; import com.android.settings.fuelgauge.BatterySaverSettings; import com.android.settings.fuelgauge.PowerUsageSummary; @@ -46,6 +44,7 @@ import com.android.settings.notification.ZenModeSettings; import com.android.settings.print.PrintSettingsFragment; import com.android.settings.sim.SimSettings; import com.android.settings.users.UserSettings; +import com.android.settings.voice.VoiceInputSettings; import com.android.settings.wifi.AdvancedWifiSettings; import com.android.settings.wifi.SavedAccessPointsWifiSettings; import com.android.settings.wifi.WifiSettings; @@ -92,17 +91,10 @@ public final class SearchIndexableResources { sResMap.put(SimSettings.class.getName(), new SearchIndexableResource( Ranking.getRankForClassName(SimSettings.class.getName()), - R.xml.sim_settings, + NO_DATA_RES_ID, SimSettings.class.getName(), R.drawable.ic_sim_sd)); - sResMap.put(MessageAccessSettings.class.getName(), - new SearchIndexableResource( - Ranking.getRankForClassName(MessageAccessSettings.class.getName()), - NO_DATA_RES_ID, - MessageAccessSettings.class.getName(), - R.drawable.ic_settings_bluetooth2)); - sResMap.put(DataUsageSummary.class.getName(), new SearchIndexableResource( Ranking.getRankForClassName(DataUsageSummary.class.getName()), @@ -208,14 +200,6 @@ public final class SearchIndexableResources { SecuritySettings.class.getName(), R.drawable.ic_settings_security)); - sResMap.put(ChooseLockGeneric.ChooseLockGenericFragment.class.getName(), - new SearchIndexableResource( - Ranking.getRankForClassName( - ChooseLockGeneric.ChooseLockGenericFragment.class.getName()), - R.xml.security_settings_picker, - ChooseLockGeneric.ChooseLockGenericFragment.class.getName(), - R.drawable.ic_settings_security)); - sResMap.put(ScreenPinningSettings.class.getName(), new SearchIndexableResource( Ranking.getRankForClassName(ScreenPinningSettings.class.getName()), @@ -230,6 +214,13 @@ public final class SearchIndexableResources { InputMethodAndLanguageSettings.class.getName(), R.drawable.ic_settings_language)); + sResMap.put(VoiceInputSettings.class.getName(), + new SearchIndexableResource( + Ranking.getRankForClassName(VoiceInputSettings.class.getName()), + NO_DATA_RES_ID, + VoiceInputSettings.class.getName(), + R.drawable.ic_settings_language)); + sResMap.put(PrivacySettings.class.getName(), new SearchIndexableResource( Ranking.getRankForClassName(PrivacySettings.class.getName()), diff --git a/src/com/android/settings/sim/SimSettings.java b/src/com/android/settings/sim/SimSettings.java index 2e1c0f5..abfeccb 100644 --- a/src/com/android/settings/sim/SimSettings.java +++ b/src/com/android/settings/sim/SimSettings.java @@ -16,6 +16,7 @@ package com.android.settings.sim; +import android.provider.SearchIndexableResource; import com.android.settings.R; import android.app.AlertDialog; @@ -55,6 +56,7 @@ import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.settings.RestrictedSettingsFragment; import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.Utils; import com.android.settings.notification.DropDownPreference; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; @@ -86,17 +88,7 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable private SubInfoRecord mCalls = null; private SubInfoRecord mSMS = null; - /** - * Return whether or not the user should have a SIM Cards option in Settings. - * TODO: Change back to returning true if count is greater than one after testing. - * TODO: See bug 16533525. - */ - public static boolean showSimCardScreen(Context context) { - final TelephonyManager tm = - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - - return tm.getSimCount() > 0; - } + private int mNumSims; public SimSettings() { super(DISALLOW_CONFIG_SIM); @@ -107,7 +99,7 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable super.onCreate(bundle); if (mSubInfoList == null) { - mSubInfoList = SubscriptionManager.getActivatedSubInfoList(getActivity()); + mSubInfoList = SubscriptionManager.getActiveSubInfoList(); } createPreferences(); @@ -124,10 +116,14 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable final int numSlots = tm.getSimCount(); mAvailableSubInfos = new ArrayList<SubInfoRecord>(numSlots); + mNumSims = 0; for (int i = 0; i < numSlots; ++i) { final SubInfoRecord sir = findRecordBySlotId(i); simCards.addPreference(new SimPreference(getActivity(), sir, i)); mAvailableSubInfos.add(sir); + if (sir != null) { + mNumSims++; + } } updateActivitesCategory(); @@ -139,7 +135,7 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable } private void updateSimSlotValues() { - SubscriptionManager.getAllSubInfoList(getActivity()); + SubscriptionManager.getAllSubInfoList(); final PreferenceCategory simCards = (PreferenceCategory)findPreference(SIM_CARD_CATEGORY); final PreferenceScreen prefScreen = getPreferenceScreen(); @@ -201,10 +197,11 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable private void updateSmsValues() { final DropDownPreference simPref = (DropDownPreference) findPreference(KEY_SMS); - final SubInfoRecord sir = findRecordBySubId(SubscriptionManager.getPreferredSmsSubId()); + final SubInfoRecord sir = findRecordBySubId(SubscriptionManager.getDefaultSmsSubId()); if (sir != null) { simPref.setSelectedItem(sir.mSlotId + 1); } + simPref.setEnabled(mNumSims > 1); } private void updateCellularDataValues() { @@ -213,6 +210,7 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable if (sir != null) { simPref.setSelectedItem(sir.mSlotId); } + simPref.setEnabled(mNumSims > 1); } private void updateCallValues() { @@ -221,6 +219,7 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable if (sir != null) { simPref.setSelectedItem(sir.mSlotId + 1); } + simPref.setEnabled(mNumSims > 1); } @Override @@ -273,8 +272,6 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable // SubscriptionManager.setDefaultSMSSubId(subId); } - updateAllOptions(); - return true; } }); @@ -347,16 +344,17 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable final Spinner displayNumbers = (Spinner)dialogLayout.findViewById(R.id.display_numbers); - SubscriptionManager.setDisplayNumberFormat(getActivity(), + SubscriptionManager.setDisplayNumberFormat( displayNumbers.getSelectedItemPosition() == 0 ? SubscriptionManager.DISPLAY_NUMBER_LAST : SubscriptionManager.DISPLAY_NUMBER_FIRST, mSubInfoRecord.mSubId); mSubInfoRecord.mDisplayName = nameText.getText().toString(); - SubscriptionManager.setDisplayName(getActivity(), mSubInfoRecord.mDisplayName, + SubscriptionManager.setDisplayName(mSubInfoRecord.mDisplayName, mSubInfoRecord.mSubId); updateAllOptions(); + update(); } }); @@ -370,4 +368,26 @@ public class SimSettings extends RestrictedSettingsFragment implements Indexable builder.create().show(); } } + + /** + * For search + */ + public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, + boolean enabled) { + ArrayList<SearchIndexableResource> result = + new ArrayList<SearchIndexableResource>(); + + if (Utils.showSimCardTile(context)) { + SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.sim_settings; + result.add(sir); + } + + return result; + } + }; + } diff --git a/src/com/android/settings/tts/TtsEngineSettingsFragment.java b/src/com/android/settings/tts/TtsEngineSettingsFragment.java index c8531fa..2449353 100644 --- a/src/com/android/settings/tts/TtsEngineSettingsFragment.java +++ b/src/com/android/settings/tts/TtsEngineSettingsFragment.java @@ -140,7 +140,7 @@ public class TtsEngineSettingsFragment extends SettingsPreferenceFragment implem mLocalePreference.setEntries(entries); mLocalePreference.setEntryValues(entryValues); - mLocalePreference.setValue(value.toString()); + mLocalePreference.setValue(value != null ? value.toString() : null); mLocalePreference.setEnabled(entries.length > 0); } diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index 0518f56..9eee4ac 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -159,34 +159,17 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen private boolean panelOpen; private boolean immutable; private List<Preference> mChildren = new ArrayList<Preference>(); - private final ColorFilter grayscaleFilter; AppRestrictionsPreference(Context context, OnClickListener listener) { super(context); setLayoutResource(R.layout.preference_app_restrictions); this.listener = listener; - - ColorMatrix colorMatrix = new ColorMatrix(); - colorMatrix.setSaturation(0f); - float[] matrix = colorMatrix.getArray(); - matrix[18] = 0.5f; - grayscaleFilter = new ColorMatrixColorFilter(colorMatrix); } private void setSettingsEnabled(boolean enable) { hasSettings = enable; } - @Override - public void setChecked(boolean checked) { - if (checked) { - getIcon().setColorFilter(null); - } else { - getIcon().setColorFilter(grayscaleFilter); - } - super.setChecked(checked); - } - void setRestrictions(ArrayList<RestrictionEntry> restrictions) { this.restrictions = restrictions; } @@ -246,6 +229,8 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen final Switch toggle = (Switch) widget.getChildAt(0); toggle.setEnabled(!isImmutable()); toggle.setTag(this); + toggle.setClickable(true); + toggle.setFocusable(true); toggle.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 7dc83ef..f5ea6aa 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -418,9 +418,11 @@ public class UserSettings extends SettingsPreferenceFragment int userId = newUserInfo.id; UserHandle user = new UserHandle(userId); mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user); - mUserManager.setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user); + // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise + // the putIntForUser() will fail. Secure.putIntForUser(getContentResolver(), Secure.LOCATION_MODE, Secure.LOCATION_MODE_OFF, userId); + mUserManager.setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user); Bitmap bitmap = createBitmapFromDrawable( USER_DRAWABLES[userId % UserSettings.USER_DRAWABLES.length]); mUserManager.setUserIcon(userId, bitmap); @@ -630,14 +632,14 @@ public class UserSettings extends SettingsPreferenceFragment Dialog dlg = new AlertDialog.Builder(context) .setTitle(R.string.user_exit_guest_confirm_title) .setMessage(R.string.user_exit_guest_confirm_message) - .setPositiveButton(android.R.string.yes, + .setPositiveButton(R.string.user_exit_guest_dialog_remove, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { exitGuest(); } }) - .setNegativeButton(android.R.string.no, null) + .setNegativeButton(android.R.string.cancel, null) .create(); return dlg; } @@ -741,6 +743,10 @@ public class UserSettings extends SettingsPreferenceFragment final boolean voiceCapable = Utils.isVoiceCapable(context); final ArrayList<Integer> missingIcons = new ArrayList<Integer>(); for (UserInfo user : users) { + if (user.isManagedProfile()) { + // Managed profiles appear under Accounts Settings instead + continue; + } Preference pref; if (user.id == UserHandle.myUserId()) { pref = mMePreference; @@ -773,19 +779,11 @@ public class UserSettings extends SettingsPreferenceFragment if (!isInitialized(user)) { if (user.isRestricted()) { pref.setSummary(R.string.user_summary_restricted_not_set_up); - } else if (user.isManagedProfile()) { - pref.setSummary(R.string.user_summary_managed_profile_not_set_up); } else { pref.setSummary(R.string.user_summary_not_set_up); } } else if (user.isRestricted()) { pref.setSummary(R.string.user_summary_restricted_profile); - } else if (user.isManagedProfile()) { - if (user.isEnabled()) { - pref.setSummary(R.string.user_summary_managed_profile); - } else { - pref.setSummary(R.string.user_summary_managed_profile_not_enabled); - } } if (user.iconPath != null) { if (mUserIcons.get(user.id) == null) { @@ -794,6 +792,8 @@ public class UserSettings extends SettingsPreferenceFragment } else { setPhotoId(pref, user); } + } else { + pref.setIcon(getEncircledDefaultAvatar()); } } @@ -807,7 +807,19 @@ public class UserSettings extends SettingsPreferenceFragment mUserListCategory.addPreference(pref); } - if (!mIsGuest) { + boolean showGuestPreference = !mIsGuest; + // If user has DISALLOW_ADD_USER don't allow creating a guest either. + if (showGuestPreference && mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) { + showGuestPreference = false; + // If guest already exists, no user creation needed. + for (UserInfo user : users) { + if (user.isGuest()) { + showGuestPreference = true; + break; + } + } + } + if (showGuestPreference) { // Add a virtual Guest user for guest defaults Preference pref = new UserPreference(getActivity(), null, UserPreference.USERID_GUEST_DEFAULTS, @@ -825,7 +837,7 @@ public class UserSettings extends SettingsPreferenceFragment if (missingIcons.size() > 0) { loadIconsAsync(missingIcons); } - boolean moreUsers = mUserManager.getMaxSupportedUsers() > users.size(); + boolean moreUsers = mUserManager.canAddMoreUsers(); mAddUser.setEnabled(moreUsers); } @@ -841,6 +853,9 @@ public class UserSettings extends SettingsPreferenceFragment protected Void doInBackground(List<Integer>... values) { for (int userId : values[0]) { Bitmap bitmap = mUserManager.getUserIcon(userId); + if (bitmap == null) { + bitmap = createBitmapFromDrawable(R.drawable.ic_avatar_default_1); + } mUserIcons.append(userId, bitmap); } return null; @@ -911,7 +926,7 @@ public class UserSettings extends SettingsPreferenceFragment if (!isInitialized(user)) { mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_SETUP_USER, user.id, user.serialNumber)); - } else if (!user.isManagedProfile()) { + } else { switchUserNow(userId); } } @@ -936,6 +951,10 @@ public class UserSettings extends SettingsPreferenceFragment } } // No guest user. Create one. + if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) { + Log.i(TAG, "Blocking guest creation because it is restricted"); + return; + } UserInfo guestUser = mUserManager.createGuest(getActivity(), getResources().getString(R.string.user_guest)); if (guestUser != null) { diff --git a/src/com/android/settings/voice/VoiceInputSettings.java b/src/com/android/settings/voice/VoiceInputSettings.java index 309c6e9..262f145 100644 --- a/src/com/android/settings/voice/VoiceInputSettings.java +++ b/src/com/android/settings/voice/VoiceInputSettings.java @@ -16,18 +16,33 @@ package com.android.settings.voice; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.preference.Preference; import android.provider.Settings; +import android.service.voice.VoiceInteractionService; +import android.service.voice.VoiceInteractionServiceInfo; +import android.speech.RecognitionService; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; +import com.android.settings.search.SearchIndexableRaw; import com.android.settings.voice.VoiceInputPreference.RadioButtonGroupState; import android.os.Bundle; import android.preference.PreferenceCategory; import android.widget.Checkable; +import java.util.ArrayList; +import java.util.List; + public class VoiceInputSettings extends SettingsPreferenceFragment implements - Preference.OnPreferenceClickListener, RadioButtonGroupState { + Preference.OnPreferenceClickListener, RadioButtonGroupState, Indexable { private static final String TAG = "VoiceInputSettings"; private static final boolean DBG = false; @@ -145,7 +160,7 @@ public class VoiceInputSettings extends SettingsPreferenceFragment implements VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i); if (info.key.equals(key)) { Settings.Secure.putString(getActivity().getContentResolver(), - Settings.Secure.VOICE_INTERACTION_SERVICE, null); + Settings.Secure.VOICE_INTERACTION_SERVICE, ""); Settings.Secure.putString(getActivity().getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE, key); return; @@ -160,4 +175,68 @@ public class VoiceInputSettings extends SettingsPreferenceFragment implements } return true; } + + // For Search + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + + @Override + public List<SearchIndexableRaw> getRawDataToIndex(Context context, + boolean enabled) { + + List<SearchIndexableRaw> indexables = new ArrayList<>(); + + final String screenTitle = context.getString(R.string.voice_input_settings_title); + + SearchIndexableRaw indexable = new SearchIndexableRaw(context); + indexable.key = "voice_service_preference_section_title"; + indexable.title = context.getString(R.string.voice_service_preference_section_title); + indexable.screenTitle = screenTitle; + indexables.add(indexable); + + final List<ResolveInfo> voiceInteractions = + context.getPackageManager().queryIntentServices( + new Intent(VoiceInteractionService.SERVICE_INTERFACE), + PackageManager.GET_META_DATA); + + final int countInteractions = voiceInteractions.size(); + for (int i = 0; i < countInteractions; i++) { + ResolveInfo info = voiceInteractions.get(i); + VoiceInteractionServiceInfo visInfo = new VoiceInteractionServiceInfo( + context.getPackageManager(), info.serviceInfo); + if (visInfo.getParseError() != null) { + continue; + } + indexables.add(getSearchIndexableRaw(context, info, screenTitle)); + } + + final List<ResolveInfo> recognitions = + context.getPackageManager().queryIntentServices( + new Intent(RecognitionService.SERVICE_INTERFACE), + PackageManager.GET_META_DATA); + + final int countRecognitions = recognitions.size(); + for (int i = 0; i < countRecognitions; i++) { + ResolveInfo info = recognitions.get(i); + indexables.add(getSearchIndexableRaw(context, info, screenTitle)); + } + + return indexables; + } + + private SearchIndexableRaw getSearchIndexableRaw(Context context, + ResolveInfo info, String screenTitle) { + + ServiceInfo serviceInfo = info.serviceInfo; + ComponentName componentName = new ComponentName(serviceInfo.packageName, + serviceInfo.name); + + SearchIndexableRaw indexable = new SearchIndexableRaw(context); + indexable.key = componentName.flattenToString(); + indexable.title = info.loadLabel(context.getPackageManager()).toString(); + indexable.screenTitle = screenTitle; + + return indexable; + } + }; } diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java index 5be9046..7516392 100644 --- a/src/com/android/settings/vpn2/VpnSettings.java +++ b/src/com/android/settings/vpn2/VpnSettings.java @@ -16,13 +16,11 @@ package com.android.settings.vpn2; -import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.IConnectivityManager; @@ -31,13 +29,10 @@ import android.os.Handler; import android.os.Message; import android.os.ServiceManager; import android.os.SystemProperties; -import android.os.UserHandle; import android.os.UserManager; -import android.os.Process; import android.preference.Preference; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; -import android.provider.Settings; import android.security.Credentials; import android.security.KeyStore; import android.text.TextUtils; @@ -49,14 +44,13 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.ListView; +import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Spinner; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; @@ -64,10 +58,6 @@ import com.android.internal.net.VpnProfile; import com.android.internal.util.ArrayUtils; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.UserSpinnerAdapter; -import com.android.settings.UserSpinnerAdapter.UserDetails; -import com.android.settings.Utils; - import com.google.android.collect.Lists; import java.util.ArrayList; @@ -76,12 +66,11 @@ import java.util.List; public class VpnSettings extends SettingsPreferenceFragment implements Handler.Callback, Preference.OnPreferenceClickListener, - DialogInterface.OnClickListener, DialogInterface.OnDismissListener, OnItemSelectedListener { + DialogInterface.OnClickListener, DialogInterface.OnDismissListener { private static final String TAG = "VpnSettings"; private static final String TAG_LOCKDOWN = "lockdown"; - private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS"; private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN"; // TODO: migrate to using DialogFragment when editing @@ -102,7 +91,6 @@ public class VpnSettings extends SettingsPreferenceFragment implements private String mSelectedKey; private boolean mUnavailable; - private UserSpinnerAdapter mProfileSpinnerAdapter; @Override public void onCreate(Bundle savedState) { @@ -130,39 +118,6 @@ public class VpnSettings extends SettingsPreferenceFragment implements } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - Context context = getActivity(); - mProfileSpinnerAdapter = Utils.createUserSpinnerAdapter(mUm, getActivity()); - if (mProfileSpinnerAdapter != null) { - Spinner spinner = (Spinner) getActivity().getLayoutInflater().inflate( - R.layout.spinner_view, null); - - spinner.setAdapter(mProfileSpinnerAdapter); - spinner.setOnItemSelectedListener(this); - setPinnedHeaderView(spinner); - } - } - - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - UserHandle selectedUser = mProfileSpinnerAdapter.getUserHandle(position); - if (selectedUser.getIdentifier() != UserHandle.myUserId()) { - Intent intent = new Intent(ACTION_VPN_SETTINGS); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Activity activity = getActivity(); - activity.startActivityAsUser(intent, selectedUser); - activity.finish(); - } - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - // Nothing to do - } - - @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.vpn, menu); @@ -537,7 +492,7 @@ public class VpnSettings extends SettingsPreferenceFragment implements private static class TitleAdapter extends ArrayAdapter<CharSequence> { public TitleAdapter(Context context, List<CharSequence> objects) { - super(context, com.android.internal.R.layout.select_dialog_singlechoice_holo, + super(context, com.android.internal.R.layout.select_dialog_singlechoice_material, android.R.id.text1, objects); } } diff --git a/src/com/android/settings/widget/SetupWizardIllustration.java b/src/com/android/settings/widget/SetupWizardIllustration.java index dcc4c65..717ec35 100644 --- a/src/com/android/settings/widget/SetupWizardIllustration.java +++ b/src/com/android/settings/widget/SetupWizardIllustration.java @@ -20,9 +20,11 @@ package com.android.settings.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.util.Log; +import android.view.Gravity; +import android.view.ViewOutlineProvider; import android.widget.FrameLayout; import com.android.settings.R; @@ -42,7 +44,8 @@ public class SetupWizardIllustration extends FrameLayout { private float mBaselineGridSize; private Drawable mBackground; private Drawable mForeground; - private int mForegroundHeight = 0; + private final Rect mViewBounds = new Rect(); + private final Rect mForegroundBounds = new Rect(); private float mScale = 1.0f; private float mAspectRatio = 0.0f; @@ -91,6 +94,12 @@ public class SetupWizardIllustration extends FrameLayout { } @Override + public void onResolveDrawables(int layoutDirection) { + mBackground.setLayoutDirection(layoutDirection); + mForeground.setLayoutDirection(layoutDirection); + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mAspectRatio != 0.0f) { int parentWidth = MeasureSpec.getSize(widthMeasureSpec); @@ -98,6 +107,7 @@ public class SetupWizardIllustration extends FrameLayout { illustrationHeight -= illustrationHeight % mBaselineGridSize; setPaddingRelative(0, illustrationHeight, 0, 0); } + setOutlineProvider(ViewOutlineProvider.BOUNDS); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @@ -106,25 +116,25 @@ public class SetupWizardIllustration extends FrameLayout { final int layoutWidth = right - left; final int layoutHeight = bottom - top; if (mForeground != null) { - final float intrinsicWidth = mForeground.getIntrinsicWidth(); - final float intrinsicHeight = mForeground.getIntrinsicHeight(); - if (intrinsicWidth <= 0 || intrinsicHeight <= 0) { - Log.e(TAG, "Foreground drawable intrinsic size must be defined and positive"); - mForeground = null; - mForegroundHeight = 0; - mScale = 1.0f; - } else { - // Scale the foreground to fill the width of the view - mScale = layoutWidth / intrinsicWidth; - mForegroundHeight = (int) (intrinsicHeight * mScale); - mForeground.setBounds(0, 0, layoutWidth, mForegroundHeight); + int intrinsicWidth = mForeground.getIntrinsicWidth(); + int intrinsicHeight = mForeground.getIntrinsicHeight(); + final int layoutDirection = getLayoutDirection(); + + mViewBounds.set(0, 0, layoutWidth, layoutHeight); + if (mAspectRatio != 0f) { + mScale = layoutWidth / (float) intrinsicWidth; + intrinsicWidth = layoutWidth; + intrinsicHeight = (int) (intrinsicHeight * mScale); } + Gravity.apply(Gravity.FILL_HORIZONTAL | Gravity.TOP, intrinsicWidth, intrinsicHeight, + mViewBounds, mForegroundBounds, layoutDirection); + mForeground.setBounds(mForegroundBounds); } if (mBackground != null) { // Scale the bounds by mScale to compensate for the scale done to the canvas before // drawing. mBackground.setBounds(0, 0, (int) Math.ceil(layoutWidth / mScale), - (int) Math.ceil((layoutHeight - mForegroundHeight) / mScale)); + (int) Math.ceil((layoutHeight - mForegroundBounds.height()) / mScale)); } super.onLayout(changed, left, top, right, bottom); } @@ -134,7 +144,7 @@ public class SetupWizardIllustration extends FrameLayout { if (mBackground != null) { canvas.save(); // Draw the background filling parts not covered by the illustration - canvas.translate(0, mForegroundHeight); + canvas.translate(0, mForegroundBounds.height()); // Scale the background so its size matches the foreground canvas.scale(mScale, mScale, 0, 0); mBackground.draw(canvas); diff --git a/src/com/android/settings/widget/SwitchBar.java b/src/com/android/settings/widget/SwitchBar.java index e24d83f..c15ac41 100644 --- a/src/com/android/settings/widget/SwitchBar.java +++ b/src/com/android/settings/widget/SwitchBar.java @@ -124,7 +124,7 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC public void setEnabled(boolean enabled) { super.setEnabled(enabled); mTextView.setEnabled(enabled); - mSwitch.setEnabled(false); + mSwitch.setEnabled(enabled); } public final ToggleSwitch getSwitch() { diff --git a/src/com/android/settings/wifi/AccessPoint.java b/src/com/android/settings/wifi/AccessPoint.java index b3fafa4..ac818a7 100644 --- a/src/com/android/settings/wifi/AccessPoint.java +++ b/src/com/android/settings/wifi/AccessPoint.java @@ -20,6 +20,7 @@ import com.android.settings.R; import android.content.Context; import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; import android.net.NetworkInfo.DetailedState; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; @@ -266,14 +267,21 @@ class AccessPoint extends Preference { Drawable drawable = getIcon(); if (drawable == null) { - drawable = context.getTheme().obtainStyledAttributes( - wifi_signal_attributes).getDrawable(0); - setIcon(drawable); + // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then + // set the icon (drawable) to that state's drawable. + StateListDrawable sld = (StateListDrawable) context.getTheme() + .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0); + // If sld is null then we are indexing and therefore do not have access to + // (nor need to display) the drawable. + if (sld != null) { + sld.setState((security != SECURITY_NONE) ? STATE_SECURED : STATE_NONE); + drawable = sld.getCurrent(); + setIcon(drawable); + } } if (drawable != null) { drawable.setLevel(level); - drawable.setState((security != SECURITY_NONE) ? STATE_SECURED : STATE_NONE); } } } @@ -452,7 +460,7 @@ class AccessPoint extends Preference { if (result.seen == 0) continue; - if (result.status != ScanResult.ENABLED) + if (result.autoJoinStatus != ScanResult.ENABLED) numBlackListed++; if (result.frequency > LOWER_FREQ_5GHZ @@ -520,6 +528,10 @@ class AccessPoint extends Preference { final Context context = getContext(); updateIcon(getLevel(), context); + // Force new summary + setSummary(null); + + // Update to new summary StringBuilder summary = new StringBuilder(); if (mState != null) { // This is the active connection @@ -550,21 +562,6 @@ class AccessPoint extends Preference { if (mConfig != null) { // Is saved network summary.append(context.getString(R.string.wifi_remembered)); } - - if (security != SECURITY_NONE) { - String securityStrFormat; - if (summary.length() == 0) { - securityStrFormat = context.getString(R.string.wifi_secured_first_item); - } else { - securityStrFormat = context.getString(R.string.wifi_secured_second_item); - } - } - - } - - // This is a workaround, see bug report... - if (summary.length() < 1) { - summary.append(" "); } if (WifiSettings.mVerboseLogging > 0) { @@ -591,7 +588,11 @@ class AccessPoint extends Preference { } } - setSummary(summary.toString()); + if (summary.length() > 0) { + setSummary(summary.toString()); + } else { + showSummary = false; + } } /** diff --git a/src/com/android/settings/wifi/AdvancedWifiSettings.java b/src/com/android/settings/wifi/AdvancedWifiSettings.java index b7316d0..dfb86cc 100644 --- a/src/com/android/settings/wifi/AdvancedWifiSettings.java +++ b/src/com/android/settings/wifi/AdvancedWifiSettings.java @@ -20,6 +20,9 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.NetworkScoreManager; +import android.net.NetworkScorerAppManager; +import android.net.NetworkScorerAppManager.NetworkScorerAppData; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WpsInfo; @@ -29,6 +32,7 @@ import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; import android.provider.Settings; import android.provider.Settings.Global; import android.security.Credentials; @@ -40,6 +44,8 @@ import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; +import java.util.Collection; + public class AdvancedWifiSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener { @@ -49,15 +55,15 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment private static final String KEY_FREQUENCY_BAND = "frequency_band"; private static final String KEY_NOTIFY_OPEN_NETWORKS = "notify_open_networks"; private static final String KEY_SLEEP_POLICY = "sleep_policy"; - private static final String KEY_POOR_NETWORK_DETECTION = "wifi_poor_network_detection"; private static final String KEY_SCAN_ALWAYS_AVAILABLE = "wifi_scan_always_available"; private static final String KEY_INSTALL_CREDENTIALS = "install_credentials"; + private static final String KEY_WIFI_ASSISTANT = "wifi_assistant"; private static final String KEY_WIFI_DIRECT = "wifi_direct"; private static final String KEY_WPS_PUSH = "wps_push_button"; private static final String KEY_WPS_PIN = "wps_pin_entry"; - private static final String KEY_SUSPEND_OPTIMIZATIONS = "suspend_optimizations"; private WifiManager mWifiManager; + private NetworkScoreManager mNetworkScoreManager; private IntentFilter mFilter; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -84,6 +90,8 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment mFilter = new IntentFilter(); mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + mNetworkScoreManager = + (NetworkScoreManager) getSystemService(Context.NETWORK_SCORE_SERVICE); } @Override @@ -102,27 +110,14 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment } private void initPreferences() { - CheckBoxPreference notifyOpenNetworks = - (CheckBoxPreference) findPreference(KEY_NOTIFY_OPEN_NETWORKS); + SwitchPreference notifyOpenNetworks = + (SwitchPreference) findPreference(KEY_NOTIFY_OPEN_NETWORKS); notifyOpenNetworks.setChecked(Settings.Global.getInt(getContentResolver(), Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1); notifyOpenNetworks.setEnabled(mWifiManager.isWifiEnabled()); - CheckBoxPreference poorNetworkDetection = - (CheckBoxPreference) findPreference(KEY_POOR_NETWORK_DETECTION); - if (poorNetworkDetection != null) { - if (Utils.isWifiOnly(getActivity())) { - getPreferenceScreen().removePreference(poorNetworkDetection); - } else { - poorNetworkDetection.setChecked(Global.getInt(getContentResolver(), - Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, - WifiManager.DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED ? - 1 : 0) == 1); - } - } - - CheckBoxPreference scanAlwaysAvailable = - (CheckBoxPreference) findPreference(KEY_SCAN_ALWAYS_AVAILABLE); + SwitchPreference scanAlwaysAvailable = + (SwitchPreference) findPreference(KEY_SCAN_ALWAYS_AVAILABLE); scanAlwaysAvailable.setChecked(Global.getInt(getContentResolver(), Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1); @@ -133,7 +128,22 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment Preference pref = findPreference(KEY_INSTALL_CREDENTIALS); pref.setIntent(intent); - Intent wifiDirectIntent = new Intent(getActivity(), + final Context context = getActivity(); + NetworkScorerAppData scorer = WifiSettings.getWifiAssistantApp(context); + SwitchPreference wifiAssistant = (SwitchPreference)findPreference(KEY_WIFI_ASSISTANT); + if (scorer != null) { + final boolean checked = NetworkScorerAppManager.getActiveScorer(context) != null; + wifiAssistant.setSummary(getResources().getString( + R.string.wifi_automatically_manage_summary, scorer.mScorerName)); + wifiAssistant.setOnPreferenceChangeListener(this); + wifiAssistant.setChecked(checked); + } else { + if (wifiAssistant != null) { + getPreferenceScreen().removePreference(wifiAssistant); + } + } + + Intent wifiDirectIntent = new Intent(context, com.android.settings.Settings.WifiP2pSettingsActivity.class); Preference wifiDirectPref = findPreference(KEY_WIFI_DIRECT); wifiDirectPref.setIntent(wifiDirectIntent); @@ -142,7 +152,7 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment Preference wpsPushPref = findPreference(KEY_WPS_PUSH); wpsPushPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference arg0) { - WpsDialog wpsDialog = new WpsDialog(getActivity(), WpsInfo.PBC); + WpsDialog wpsDialog = new WpsDialog(context, WpsInfo.PBC); wpsDialog.show(); return true; } @@ -152,17 +162,12 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment Preference wpsPinPref = findPreference(KEY_WPS_PIN); wpsPinPref.setOnPreferenceClickListener(new OnPreferenceClickListener(){ public boolean onPreferenceClick(Preference arg0) { - WpsDialog wpsDialog = new WpsDialog(getActivity(), WpsInfo.DISPLAY); + WpsDialog wpsDialog = new WpsDialog(context, WpsInfo.DISPLAY); wpsDialog.show(); return true; } }); - CheckBoxPreference suspendOptimizations = - (CheckBoxPreference) findPreference(KEY_SUSPEND_OPTIMIZATIONS); - suspendOptimizations.setChecked(Global.getInt(getContentResolver(), - Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1); - ListPreference frequencyPref = (ListPreference) findPreference(KEY_FREQUENCY_BAND); if (mWifiManager.isDualBandSupported()) { @@ -183,7 +188,7 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment ListPreference sleepPolicyPref = (ListPreference) findPreference(KEY_SLEEP_POLICY); if (sleepPolicyPref != null) { - if (Utils.isWifiOnly(getActivity())) { + if (Utils.isWifiOnly(context)) { sleepPolicyPref.setEntries(R.array.wifi_sleep_policy_entries_wifi_only); } sleepPolicyPref.setOnPreferenceChangeListener(this); @@ -228,19 +233,11 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment if (KEY_NOTIFY_OPEN_NETWORKS.equals(key)) { Global.putInt(getContentResolver(), Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, - ((CheckBoxPreference) preference).isChecked() ? 1 : 0); - } else if (KEY_POOR_NETWORK_DETECTION.equals(key)) { - Global.putInt(getContentResolver(), - Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, - ((CheckBoxPreference) preference).isChecked() ? 1 : 0); - } else if (KEY_SUSPEND_OPTIMIZATIONS.equals(key)) { - Global.putInt(getContentResolver(), - Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, - ((CheckBoxPreference) preference).isChecked() ? 1 : 0); + ((SwitchPreference) preference).isChecked() ? 1 : 0); } else if (KEY_SCAN_ALWAYS_AVAILABLE.equals(key)) { Global.putInt(getContentResolver(), Global.WIFI_SCAN_ALWAYS_AVAILABLE, - ((CheckBoxPreference) preference).isChecked() ? 1 : 0); + ((SwitchPreference) preference).isChecked() ? 1 : 0); } else { return super.onPreferenceTreeClick(screen, preference); } @@ -249,6 +246,7 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment @Override public boolean onPreferenceChange(Preference preference, Object newValue) { + final Context context = getActivity(); String key = preference.getKey(); if (KEY_FREQUENCY_BAND.equals(key)) { @@ -257,10 +255,32 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment mWifiManager.setFrequencyBand(value, true); updateFrequencyBandSummary(preference, value); } catch (NumberFormatException e) { - Toast.makeText(getActivity(), R.string.wifi_setting_frequency_band_error, + Toast.makeText(context, R.string.wifi_setting_frequency_band_error, Toast.LENGTH_SHORT).show(); return false; } + } else if (KEY_WIFI_ASSISTANT.equals(key)) { + if (((Boolean)newValue).booleanValue() == false) { + mNetworkScoreManager.setActiveScorer(null); + return true; + } + + NetworkScorerAppData wifiAssistant = WifiSettings.getWifiAssistantApp(context); + Intent intent = new Intent(); + if (wifiAssistant.mConfigurationActivityClassName != null) { + // App has a custom configuration activity; launch that. + // This custom activity will be responsible for launching the system + // dialog. + intent.setClassName(wifiAssistant.mPackageName, + wifiAssistant.mConfigurationActivityClassName); + } else { + // Fall back on the system dialog. + intent.setAction(NetworkScoreManager.ACTION_CHANGE_ACTIVE); + intent.putExtra(NetworkScoreManager.EXTRA_PACKAGE_NAME, + wifiAssistant.mPackageName); + } + + startActivity(intent); } if (KEY_SLEEP_POLICY.equals(key)) { @@ -270,7 +290,7 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment Integer.parseInt(stringValue)); updateSleepPolicySummary(preference, stringValue); } catch (NumberFormatException e) { - Toast.makeText(getActivity(), R.string.wifi_setting_sleep_policy_error, + Toast.makeText(context, R.string.wifi_setting_sleep_policy_error, Toast.LENGTH_SHORT).show(); return false; } @@ -280,18 +300,19 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment } private void refreshWifiInfo() { + final Context context = getActivity(); WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); Preference wifiMacAddressPref = findPreference(KEY_MAC_ADDRESS); String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress(); wifiMacAddressPref.setSummary(!TextUtils.isEmpty(macAddress) ? macAddress - : getActivity().getString(R.string.status_unavailable)); + : context.getString(R.string.status_unavailable)); wifiMacAddressPref.setSelectable(false); Preference wifiIpAddressPref = findPreference(KEY_CURRENT_IP_ADDRESS); - String ipAddress = Utils.getWifiIpAddresses(getActivity()); + String ipAddress = Utils.getWifiIpAddresses(context); wifiIpAddressPref.setSummary(ipAddress == null ? - getActivity().getString(R.string.status_unavailable) : ipAddress); + context.getString(R.string.status_unavailable) : ipAddress); wifiIpAddressPref.setSelectable(false); } diff --git a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java index a91d153..10c86dc 100644 --- a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java +++ b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java @@ -53,6 +53,9 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment private Bundle mAccessPointSavedState; private AccessPoint mSelectedAccessPoint; + // Instance state key + private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state"; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -69,6 +72,13 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); + + if (savedInstanceState != null) { + if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { + mAccessPointSavedState = + savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); + } + } } private void initPreferences() { @@ -150,6 +160,11 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment public Dialog onCreateDialog(int dialogId) { switch (dialogId) { case WifiSettings.WIFI_DIALOG_ID: + if (mDlgAccessPoint == null) { // For re-launch from saved state + mDlgAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState); + // Reset the saved access point data + mAccessPointSavedState = null; + } mSelectedAccessPoint = mDlgAccessPoint; mDialog = new WifiDialog(getActivity(), this, mDlgAccessPoint, false); return mDialog; @@ -159,6 +174,20 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment } @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + // If the dialog is showing, save its state. + if (mDialog != null && mDialog.isShowing()) { + if (mDlgAccessPoint != null) { + mAccessPointSavedState = new Bundle(); + mDlgAccessPoint.saveWifiState(mAccessPointSavedState); + outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState); + } + } + } + + @Override public void onClick(DialogInterface dialogInterface, int button) { if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { mWifiManager.forget(mSelectedAccessPoint.networkId, null); diff --git a/src/com/android/settings/wifi/WifiApEnabler.java b/src/com/android/settings/wifi/WifiApEnabler.java index 9a3b49d..fc34f3b 100644 --- a/src/com/android/settings/wifi/WifiApEnabler.java +++ b/src/com/android/settings/wifi/WifiApEnabler.java @@ -33,7 +33,7 @@ import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.preference.CheckBoxPreference; +import android.preference.SwitchPreference; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -41,7 +41,7 @@ import android.widget.Toast; public class WifiApEnabler { private final Context mContext; - private final CheckBoxPreference mCheckBox; + private final SwitchPreference mSwitch; private final CharSequence mOriginalSummary; private WifiManager mWifiManager; @@ -66,17 +66,17 @@ public class WifiApEnabler { ConnectivityManager.EXTRA_ERRORED_TETHER); updateTetherState(available.toArray(), active.toArray(), errored.toArray()); } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { - enableWifiCheckBox(); + enableWifiSwitch(); } } }; - public WifiApEnabler(Context context, CheckBoxPreference checkBox) { + public WifiApEnabler(Context context, SwitchPreference switchPreference) { mContext = context; - mCheckBox = checkBox; - mOriginalSummary = checkBox.getSummary(); - checkBox.setPersistent(false); + mSwitch = switchPreference; + mOriginalSummary = switchPreference.getSummary(); + switchPreference.setPersistent(false); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -90,21 +90,21 @@ public class WifiApEnabler { public void resume() { mContext.registerReceiver(mReceiver, mIntentFilter); - enableWifiCheckBox(); + enableWifiSwitch(); } public void pause() { mContext.unregisterReceiver(mReceiver); } - private void enableWifiCheckBox() { + private void enableWifiSwitch() { boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0; if(!isAirplaneMode) { - mCheckBox.setEnabled(true); + mSwitch.setEnabled(true); } else { - mCheckBox.setSummary(mOriginalSummary); - mCheckBox.setEnabled(false); + mSwitch.setSummary(mOriginalSummary); + mSwitch.setEnabled(false); } } @@ -122,9 +122,9 @@ public class WifiApEnabler { if (mWifiManager.setWifiApEnabled(null, enable)) { /* Disable here, enabled on receiving success broadcast */ - mCheckBox.setEnabled(false); + mSwitch.setEnabled(false); } else { - mCheckBox.setSummary(R.string.wifi_error); + mSwitch.setSummary(R.string.wifi_error); } /** @@ -147,7 +147,7 @@ public class WifiApEnabler { public void updateConfigSummary(WifiConfiguration wifiConfig) { String s = mContext.getString( com.android.internal.R.string.wifi_tether_configure_ssid_default); - mCheckBox.setSummary(String.format( + mSwitch.setSummary(String.format( mContext.getString(R.string.wifi_tether_enabled_subtext), (wifiConfig == null) ? s : wifiConfig.SSID)); } @@ -173,38 +173,38 @@ public class WifiApEnabler { WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration(); updateConfigSummary(wifiConfig); } else if (wifiErrored) { - mCheckBox.setSummary(R.string.wifi_error); + mSwitch.setSummary(R.string.wifi_error); } } private void handleWifiApStateChanged(int state) { switch (state) { case WifiManager.WIFI_AP_STATE_ENABLING: - mCheckBox.setSummary(R.string.wifi_tether_starting); - mCheckBox.setEnabled(false); + mSwitch.setSummary(R.string.wifi_tether_starting); + mSwitch.setEnabled(false); break; case WifiManager.WIFI_AP_STATE_ENABLED: /** * Summary on enable is handled by tether * broadcast notice */ - mCheckBox.setChecked(true); + mSwitch.setChecked(true); /* Doesnt need the airplane check */ - mCheckBox.setEnabled(true); + mSwitch.setEnabled(true); break; case WifiManager.WIFI_AP_STATE_DISABLING: - mCheckBox.setSummary(R.string.wifi_tether_stopping); - mCheckBox.setEnabled(false); + mSwitch.setSummary(R.string.wifi_tether_stopping); + mSwitch.setEnabled(false); break; case WifiManager.WIFI_AP_STATE_DISABLED: - mCheckBox.setChecked(false); - mCheckBox.setSummary(mOriginalSummary); - enableWifiCheckBox(); + mSwitch.setChecked(false); + mSwitch.setSummary(mOriginalSummary); + enableWifiSwitch(); break; default: - mCheckBox.setChecked(false); - mCheckBox.setSummary(R.string.wifi_error); - enableWifiCheckBox(); + mSwitch.setChecked(false); + mSwitch.setSummary(R.string.wifi_error); + enableWifiSwitch(); } } } diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java index fc2276a..3b07b9f 100644 --- a/src/com/android/settings/wifi/WifiConfigController.java +++ b/src/com/android/settings/wifi/WifiConfigController.java @@ -24,11 +24,11 @@ import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; import android.net.LinkAddress; -import android.net.LinkProperties; import android.net.NetworkInfo.DetailedState; import android.net.NetworkUtils; import android.net.ProxyInfo; import android.net.RouteInfo; +import android.net.StaticIpConfiguration; import android.net.Uri; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.AuthAlgorithm; @@ -61,6 +61,7 @@ import com.android.settings.ProxySelector; import com.android.settings.R; import java.net.InetAddress; +import java.net.Inet4Address; import java.util.Iterator; /** @@ -137,7 +138,8 @@ public class WifiConfigController implements TextWatcher, private IpAssignment mIpAssignment = IpAssignment.UNASSIGNED; private ProxySettings mProxySettings = ProxySettings.UNASSIGNED; - private LinkProperties mLinkProperties = new LinkProperties(); + private ProxyInfo mHttpProxy = null; + private StaticIpConfiguration mStaticIpConfiguration = null; private String[] mLevels; private boolean mEdit; @@ -216,13 +218,15 @@ public class WifiConfigController implements TextWatcher, if (config.getIpAssignment() == IpAssignment.STATIC) { mIpSettingsSpinner.setSelection(STATIC_IP); showAdvancedFields = true; + // Display IP address. + StaticIpConfiguration staticConfig = config.getStaticIpConfiguration(); + if (staticConfig != null && staticConfig.ipAddress != null) { + addRow(group, R.string.wifi_ip_address, + staticConfig.ipAddress.getAddress().getHostAddress()); + } } else { mIpSettingsSpinner.setSelection(DHCP); } - //Display IP addresses - for(InetAddress a : config.getLinkProperties().getAddresses()) { - addRow(group, R.string.wifi_ip_address, a.getHostAddress()); - } if (config.getProxySettings() == ProxySettings.STATIC) { @@ -462,19 +466,20 @@ public class WifiConfigController implements TextWatcher, } config.setIpConfiguration( - new IpConfiguration(mIpAssignment, mProxySettings, mLinkProperties)); + new IpConfiguration(mIpAssignment, mProxySettings, + mStaticIpConfiguration, mHttpProxy)); return config; } private boolean ipAndProxyFieldsAreValid() { - mLinkProperties.clear(); mIpAssignment = (mIpSettingsSpinner != null && mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) ? IpAssignment.STATIC : IpAssignment.DHCP; if (mIpAssignment == IpAssignment.STATIC) { - int result = validateIpConfigFields(mLinkProperties); + mStaticIpConfiguration = new StaticIpConfiguration(); + int result = validateIpConfigFields(mStaticIpConfiguration); if (result != 0) { return false; } @@ -482,6 +487,7 @@ public class WifiConfigController implements TextWatcher, final int selectedPosition = mProxySettingsSpinner.getSelectedItemPosition(); mProxySettings = ProxySettings.NONE; + mHttpProxy = null; if (selectedPosition == PROXY_STATIC && mProxyHostView != null) { mProxySettings = ProxySettings.STATIC; String host = mProxyHostView.getText().toString(); @@ -496,8 +502,7 @@ public class WifiConfigController implements TextWatcher, result = R.string.proxy_error_invalid_port; } if (result == 0) { - ProxyInfo proxyProperties= new ProxyInfo(host, port, exclusionList); - mLinkProperties.setHttpProxy(proxyProperties); + mHttpProxy = new ProxyInfo(host, port, exclusionList); } else { return false; } @@ -511,22 +516,27 @@ public class WifiConfigController implements TextWatcher, if (uri == null) { return false; } - ProxyInfo proxyInfo = new ProxyInfo(uri); - mLinkProperties.setHttpProxy(proxyInfo); + mHttpProxy = new ProxyInfo(uri); } return true; } - private int validateIpConfigFields(LinkProperties linkProperties) { + private Inet4Address getIPv4Address(String text) { + try { + return (Inet4Address) NetworkUtils.numericToInetAddress(text); + } catch (IllegalArgumentException|ClassCastException e) { + return null; + } + } + + private int validateIpConfigFields(StaticIpConfiguration staticIpConfiguration) { if (mIpAddressView == null) return 0; String ipAddr = mIpAddressView.getText().toString(); if (TextUtils.isEmpty(ipAddr)) return R.string.wifi_ip_settings_invalid_ip_address; - InetAddress inetAddr = null; - try { - inetAddr = NetworkUtils.numericToInetAddress(ipAddr); - } catch (IllegalArgumentException e) { + Inet4Address inetAddr = getIPv4Address(ipAddr); + if (inetAddr == null) { return R.string.wifi_ip_settings_invalid_ip_address; } @@ -536,7 +546,7 @@ public class WifiConfigController implements TextWatcher, if (networkPrefixLength < 0 || networkPrefixLength > 32) { return R.string.wifi_ip_settings_invalid_network_prefix_length; } - linkProperties.addLinkAddress(new LinkAddress(inetAddr, networkPrefixLength)); + staticIpConfiguration.ipAddress = new LinkAddress(inetAddr, networkPrefixLength); } catch (NumberFormatException e) { // Set the hint as default after user types in ip address mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString( @@ -555,13 +565,11 @@ public class WifiConfigController implements TextWatcher, } catch (java.net.UnknownHostException u) { } } else { - InetAddress gatewayAddr = null; - try { - gatewayAddr = NetworkUtils.numericToInetAddress(gateway); - } catch (IllegalArgumentException e) { + InetAddress gatewayAddr = getIPv4Address(gateway); + if (gatewayAddr == null) { return R.string.wifi_ip_settings_invalid_gateway; } - linkProperties.addRoute(new RouteInfo(gatewayAddr)); + staticIpConfiguration.gateway = gatewayAddr; } String dns = mDns1View.getText().toString(); @@ -571,22 +579,20 @@ public class WifiConfigController implements TextWatcher, //If everything else is valid, provide hint as a default option mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint)); } else { - try { - dnsAddr = NetworkUtils.numericToInetAddress(dns); - } catch (IllegalArgumentException e) { + dnsAddr = getIPv4Address(dns); + if (dnsAddr == null) { return R.string.wifi_ip_settings_invalid_dns; } - linkProperties.addDnsServer(dnsAddr); + staticIpConfiguration.dnsServers.add(dnsAddr); } if (mDns2View.length() > 0) { dns = mDns2View.getText().toString(); - try { - dnsAddr = NetworkUtils.numericToInetAddress(dns); - } catch (IllegalArgumentException e) { + dnsAddr = getIPv4Address(dns); + if (dnsAddr == null) { return R.string.wifi_ip_settings_invalid_dns; } - linkProperties.addDnsServer(dnsAddr); + staticIpConfiguration.dnsServers.add(dnsAddr); } return 0; } @@ -797,28 +803,26 @@ public class WifiConfigController implements TextWatcher, mDns2View.addTextChangedListener(this); } if (config != null) { - LinkProperties linkProperties = config.getLinkProperties(); - Iterator<LinkAddress> iterator = linkProperties.getLinkAddresses().iterator(); - if (iterator.hasNext()) { - LinkAddress linkAddress = iterator.next(); - mIpAddressView.setText(linkAddress.getAddress().getHostAddress()); - mNetworkPrefixLengthView.setText(Integer.toString(linkAddress - .getNetworkPrefixLength())); - } + StaticIpConfiguration staticConfig = config.getStaticIpConfiguration(); + if (staticConfig != null) { + if (staticConfig.ipAddress != null) { + mIpAddressView.setText( + staticConfig.ipAddress.getAddress().getHostAddress()); + mNetworkPrefixLengthView.setText(Integer.toString(staticConfig.ipAddress + .getNetworkPrefixLength())); + } - for (RouteInfo route : linkProperties.getRoutes()) { - if (route.isDefaultRoute()) { - mGatewayView.setText(route.getGateway().getHostAddress()); - break; + if (staticConfig.gateway != null) { + mGatewayView.setText(staticConfig.gateway.getHostAddress()); } - } - Iterator<InetAddress> dnsIterator = linkProperties.getDnsServers().iterator(); - if (dnsIterator.hasNext()) { - mDns1View.setText(dnsIterator.next().getHostAddress()); - } - if (dnsIterator.hasNext()) { - mDns2View.setText(dnsIterator.next().getHostAddress()); + Iterator<InetAddress> dnsIterator = staticConfig.dnsServers.iterator(); + if (dnsIterator.hasNext()) { + mDns1View.setText(dnsIterator.next().getHostAddress()); + } + if (dnsIterator.hasNext()) { + mDns2View.setText(dnsIterator.next().getHostAddress()); + } } } } else { @@ -848,7 +852,7 @@ public class WifiConfigController implements TextWatcher, mProxyExclusionListView.addTextChangedListener(this); } if (config != null) { - ProxyInfo proxyProperties = config.getLinkProperties().getHttpProxy(); + ProxyInfo proxyProperties = config.getHttpProxy(); if (proxyProperties != null) { mProxyHostView.setText(proxyProperties.getHost()); mProxyPortView.setText(Integer.toString(proxyProperties.getPort())); @@ -865,7 +869,7 @@ public class WifiConfigController implements TextWatcher, mProxyPacView.addTextChangedListener(this); } if (config != null) { - ProxyInfo proxyInfo = config.getLinkProperties().getHttpProxy(); + ProxyInfo proxyInfo = config.getHttpProxy(); if (proxyInfo != null) { mProxyPacView.setText(proxyInfo.getPacFileUrl().toString()); } diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index e83cb06..50789b8 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -27,7 +27,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.location.LocationManager; @@ -42,6 +41,7 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WpsInfo; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -50,6 +50,7 @@ import android.preference.PreferenceScreen; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -100,12 +101,7 @@ public class WifiSettings extends RestrictedSettingsFragment private static final int MENU_ID_MODIFY = Menu.FIRST + 8; private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9; - private static final String KEY_ASSISTANT_DISMISS_TIME = "wifi_assistant_dismiss_time"; - private static final String KEY_ASSISTANT_START_TIME = "wifi_assistant_start_time"; - - private static final long MILI_SECONDS_30_DAYS = 30L * 24L * 60L * 60L * 1000L; - private static final long MILI_SECONDS_90_DAYS = MILI_SECONDS_30_DAYS * 3L; - private static final long MILI_SECONDS_180_DAYS = MILI_SECONDS_90_DAYS * 2L; + private static final String KEY_ASSISTANT_DISMISS_PLATFORM = "assistant_dismiss_platform"; public static final int WIFI_DIALOG_ID = 1; /* package */ static final int WPS_PBC_DIALOG_ID = 2; @@ -129,7 +125,6 @@ public class WifiSettings extends RestrictedSettingsFragment private WifiManager.ActionListener mConnectListener; private WifiManager.ActionListener mSaveListener; private WifiManager.ActionListener mForgetListener; - private boolean mP2pSupported; private WifiEnabler mWifiEnabler; // An access point being editted is stored here. @@ -156,7 +151,7 @@ public class WifiSettings extends RestrictedSettingsFragment private boolean mDlgEdit; private AccessPoint mDlgAccessPoint; private Bundle mAccessPointSavedState; - private Preference mWifiAssistantPreference; + private View mWifiAssistantCard; private NetworkScorerAppData mWifiAssistantApp; /** verbose logging flag. this flag is set thru developer debugging options @@ -165,52 +160,6 @@ public class WifiSettings extends RestrictedSettingsFragment /* End of "used in Wifi Setup context" */ - /** Holds the Wifi Assistant Card. */ - private class WifiAssistantPreference extends Preference { - public WifiAssistantPreference() { - super(getActivity()); - setLayoutResource(R.layout.wifi_assistant_card); - } - - @Override - public void onBindView(View view) { - super.onBindView(view); - Button setup = (Button)view.findViewById(R.id.setup); - Button noThanks = (Button)view.findViewById(R.id.no_thanks_button); - - if (setup != null && noThanks != null) { - setup.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(); - if (mWifiAssistantApp.mConfigurationActivityClassName != null) { - // App has a custom configuration activity; launch that. - // This custom activity will be responsible for launching the system - // dialog. - intent.setClassName(mWifiAssistantApp.mPackageName, - mWifiAssistantApp.mConfigurationActivityClassName); - } else { - // Fall back on the system dialog. - intent.setAction(NetworkScoreManager.ACTION_CHANGE_ACTIVE); - intent.putExtra(NetworkScoreManager.EXTRA_PACKAGE_NAME, - mWifiAssistantApp.mPackageName); - } - startActivityForResult(intent, REQUEST_ENABLE_WIFI_ASSISTANT); - } - }); - - noThanks.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - setWifiAssistantTimeout(); - getPreferenceScreen().removePreference(WifiAssistantPreference.this); - mWifiAssistantApp = null; - } - }); - } - } - } - /** A restricted multimap for use in constructAccessPoints */ private static class Multimap<K,V> { private final HashMap<K,List<V>> store = new HashMap<K,List<V>>(); @@ -285,7 +234,7 @@ public class WifiSettings extends RestrictedSettingsFragment mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - handleEvent(context, intent); + handleEvent(intent); } }; @@ -296,7 +245,6 @@ public class WifiSettings extends RestrictedSettingsFragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT); mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); mConnectListener = new WifiManager.ActionListener() { @@ -347,7 +295,7 @@ public class WifiSettings extends RestrictedSettingsFragment if (savedInstanceState != null) { mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE); if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) { - mAccessPointSavedState = + mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE); } } @@ -373,8 +321,7 @@ public class WifiSettings extends RestrictedSettingsFragment prepareWifiAssistantCard(); - mEmptyView = (TextView) getView().findViewById(android.R.id.empty); - getListView().setEmptyView(mEmptyView); + mEmptyView = initEmptyView(); registerForContextMenu(getListView()); setHasOptionsMenu(true); } @@ -383,8 +330,8 @@ public class WifiSettings extends RestrictedSettingsFragment public void onActivityResult(int requestCode, int resultCode, Intent resultData) { if (requestCode == REQUEST_ENABLE_WIFI_ASSISTANT) { if (resultCode == Activity.RESULT_OK) { - setWifiAssistantTimeout(); - getPreferenceScreen().removePreference(mWifiAssistantPreference); + disableWifiAssistantCardUntilPlatformUpgrade(); + getListView().removeHeaderView(mWifiAssistantCard); mWifiAssistantApp = null; } } else { @@ -706,8 +653,9 @@ public class WifiSettings extends RestrictedSettingsFragment addMessagePreference(R.string.wifi_empty_list_wifi_on); } + getListView().removeHeaderView(mWifiAssistantCard); if (mWifiAssistantApp != null) { - getPreferenceScreen().addPreference(mWifiAssistantPreference); + getListView().addHeaderView(mWifiAssistantCard); } for (AccessPoint accessPoint : accessPoints) { @@ -732,64 +680,106 @@ public class WifiSettings extends RestrictedSettingsFragment } } - private boolean prepareWifiAssistantCard() { - if (mWifiAssistantPreference == null) { - mWifiAssistantPreference = new WifiAssistantPreference(); + /** + * Returns the Network Scorer for the Wifi Assistant App. + */ + public static NetworkScorerAppData getWifiAssistantApp(Context context) { + Collection<NetworkScorerAppData> scorers = + NetworkScorerAppManager.getAllValidScorers(context); + + if (scorers.isEmpty()) { + return null; } + // TODO: b/13780935 - Implement proper scorer selection. Rather than pick the first + // scorer on the system, we should allow the user to select one. + return scorers.iterator().next(); + } + + private void prepareWifiAssistantCard() { if (getActivity() instanceof WifiPickerActivity) { - return false; + return; } if (NetworkScorerAppManager.getActiveScorer(getActivity()) != null) { // A scorer is already enabled; don't show the card. - return false; + return; } Collection<NetworkScorerAppData> scorers = NetworkScorerAppManager.getAllValidScorers(getActivity()); if (scorers.isEmpty()) { // No scorers are available to enable; don't show the card. - return false; + return; } SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences(); - long lastTimeoutEndTime = sharedPreferences.getLong(KEY_ASSISTANT_START_TIME, 0); - long dismissTime = sharedPreferences.getLong(KEY_ASSISTANT_DISMISS_TIME, 0); - - boolean shouldShow = ((System.currentTimeMillis() - lastTimeoutEndTime) > dismissTime); - if (shouldShow) { - // TODO: b/13780935 - Implement proper scorer selection. Rather than pick the first - // scorer on the system, we should allow the user to select one. - mWifiAssistantApp = scorers.iterator().next(); + int lastDismissPlatform = sharedPreferences.getInt(KEY_ASSISTANT_DISMISS_PLATFORM, 0); + + if (Build.VERSION.SDK_INT <= lastDismissPlatform) { + // User has dismissed the Wi-Fi assistant card on this SDK release. Suppress the card + // until the next major platform upgrade. + return; + } + + // TODO: b/13780935 - Implement proper scorer selection. Rather than pick the first + // scorer on the system, we should allow the user to select one. + mWifiAssistantApp = scorers.iterator().next(); + + if (mWifiAssistantCard == null) { + mWifiAssistantCard = LayoutInflater.from(getActivity()) + .inflate(R.layout.wifi_assistant_card, getListView(), false); + Button setup = (Button) mWifiAssistantCard.findViewById(R.id.setup); + Button noThanks = (Button) mWifiAssistantCard.findViewById(R.id.no_thanks_button); + + if (setup != null && noThanks != null) { + setup.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(); + if (mWifiAssistantApp.mConfigurationActivityClassName != null) { + // App has a custom configuration activity; launch that. + // This custom activity will be responsible for launching the system + // dialog. + intent.setClassName(mWifiAssistantApp.mPackageName, + mWifiAssistantApp.mConfigurationActivityClassName); + } else { + // Fall back on the system dialog. + intent.setAction(NetworkScoreManager.ACTION_CHANGE_ACTIVE); + intent.putExtra(NetworkScoreManager.EXTRA_PACKAGE_NAME, + mWifiAssistantApp.mPackageName); + } + startActivityForResult(intent, REQUEST_ENABLE_WIFI_ASSISTANT); + } + }); + + noThanks.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + disableWifiAssistantCardUntilPlatformUpgrade(); + getListView().removeHeaderView(mWifiAssistantCard); + mWifiAssistantApp = null; + } + }); + } } - return shouldShow; } - private void setWifiAssistantTimeout() { + private void disableWifiAssistantCardUntilPlatformUpgrade() { SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences(); SharedPreferences.Editor editor = sharedPreferences.edit(); - long dismissTime = sharedPreferences.getLong(KEY_ASSISTANT_DISMISS_TIME, 0); - - if (dismissTime == 0) { - dismissTime = MILI_SECONDS_30_DAYS; - } else if (dismissTime == MILI_SECONDS_30_DAYS) { - dismissTime = MILI_SECONDS_90_DAYS; - } else if (dismissTime == MILI_SECONDS_90_DAYS) { - dismissTime = MILI_SECONDS_180_DAYS; - } else if (dismissTime == MILI_SECONDS_180_DAYS) { - dismissTime = java.lang.Long.MAX_VALUE; - } - - editor.putLong(KEY_ASSISTANT_DISMISS_TIME, dismissTime); - editor.putLong(KEY_ASSISTANT_START_TIME, System.currentTimeMillis()); + editor.putInt(KEY_ASSISTANT_DISMISS_PLATFORM, Build.VERSION.SDK_INT); editor.apply(); } + protected TextView initEmptyView() { + TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty); + getListView().setEmptyView(emptyView); + return emptyView; + } + private void setOffMessage() { if (mEmptyView != null) { - mEmptyView.setCompoundDrawablesWithIntrinsicBounds(0, - R.drawable.ic_wifi_emptystate, 0, 0); mEmptyView.setText(R.string.wifi_empty_list_wifi_off); if (android.provider.Settings.Global.getInt(getActivity().getContentResolver(), android.provider.Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) { @@ -823,7 +813,13 @@ public class WifiSettings extends RestrictedSettingsFragment final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks(); if (configs != null) { - savedNetworksExist = (configs.size() > 0); + // Update "Saved Networks" menu option. + if (savedNetworksExist != (configs.size() > 0)) { + savedNetworksExist = !savedNetworksExist; + if (context instanceof Activity) { + ((Activity) context).invalidateOptionsMenu(); + } + } for (WifiConfiguration config : configs) { AccessPoint accessPoint = new AccessPoint(context, config); if (lastInfo != null && lastState != null) { @@ -861,7 +857,7 @@ public class WifiSettings extends RestrictedSettingsFragment return accessPoints; } - private void handleEvent(Context context, Intent intent) { + private void handleEvent(Intent intent) { String action = intent.getAction(); if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java index b52aaa7..c4a5c96 100644 --- a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java +++ b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java @@ -18,18 +18,19 @@ package com.android.settings.wifi; import android.content.Intent; import android.content.res.TypedArray; +import android.database.DataSetObserver; import android.net.wifi.WifiConfiguration; import android.os.Bundle; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.ImageButton; +import android.widget.AbsListView.LayoutParams; +import android.widget.ListAdapter; import android.widget.ListView; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.TextView; import com.android.settings.R; @@ -47,6 +48,11 @@ public class WifiSettingsForSetupWizard extends WifiSettings { // show a text regarding data charges when wifi connection is required during setup wizard protected static final String EXTRA_SHOW_WIFI_REQUIRED_INFO = "wifi_show_wifi_required_info"; + private View mAddOtherNetworkItem; + private ListAdapter mAdapter; + private TextView mEmptyFooter; + private boolean mListLastEmpty = false; + @Override public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -60,9 +66,9 @@ public class WifiSettingsForSetupWizard extends WifiSettings { list.addHeaderView(header, null, false); } - final View other = inflater.inflate(R.layout.setup_wifi_add_network, list, false); - list.addFooterView(other, null, true); - other.setOnClickListener(new OnClickListener() { + mAddOtherNetworkItem = inflater.inflate(R.layout.setup_wifi_add_network, list, false); + list.addFooterView(mAddOtherNetworkItem, null, true); + mAddOtherNetworkItem.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mWifiManager.isWifiEnabled()) { @@ -92,6 +98,15 @@ public class WifiSettingsForSetupWizard extends WifiSettings { if (hasNextButton()) { getNextButton().setVisibility(View.GONE); } + + mAdapter = getPreferenceScreen().getRootAdapter(); + mAdapter.registerDataSetObserver(new DataSetObserver() { + @Override + public void onChanged() { + super.onChanged(); + updateFooter(); + } + }); } @Override @@ -133,4 +148,30 @@ public class WifiSettingsForSetupWizard extends WifiSettings { activity.networkSelected(); super.connect(networkId); } + + @Override + protected TextView initEmptyView() { + mEmptyFooter = new TextView(getActivity()); + mEmptyFooter.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT)); + mEmptyFooter.setGravity(Gravity.CENTER); + mEmptyFooter.setCompoundDrawablesWithIntrinsicBounds(0, + R.drawable.ic_wifi_emptystate, 0,0); + return mEmptyFooter; + } + + protected void updateFooter() { + final boolean isEmpty = mAdapter.isEmpty(); + if (isEmpty != mListLastEmpty) { + final ListView list = getListView(); + if (isEmpty) { + list.removeFooterView(mAddOtherNetworkItem); + list.addFooterView(mEmptyFooter, null, false); + } else { + list.removeFooterView(mEmptyFooter); + list.addFooterView(mAddOtherNetworkItem, null, true); + } + mListLastEmpty = isEmpty; + } + } } diff --git a/src/com/android/settings/wifi/WifiSetupActivity.java b/src/com/android/settings/wifi/WifiSetupActivity.java index 3dd7a03..5452a03 100644 --- a/src/com/android/settings/wifi/WifiSetupActivity.java +++ b/src/com/android/settings/wifi/WifiSetupActivity.java @@ -218,6 +218,7 @@ public class WifiSetupActivity extends WifiPickerActivity final Intent nextIntent = new Intent(ACTION_NEXT); nextIntent.putExtra(EXTRA_SCRIPT_URI, intent.getStringExtra(EXTRA_SCRIPT_URI)); nextIntent.putExtra(EXTRA_ACTION_ID, intent.getStringExtra(EXTRA_ACTION_ID)); + nextIntent.putExtra(EXTRA_THEME, intent.getStringExtra(EXTRA_THEME)); nextIntent.putExtra(EXTRA_RESULT_CODE, resultCode); startActivityForResult(nextIntent, NEXT_REQUEST); } diff --git a/src/com/android/settings/wifi/WpsDialog.java b/src/com/android/settings/wifi/WpsDialog.java index 662d477..d0b116b 100644 --- a/src/com/android/settings/wifi/WpsDialog.java +++ b/src/com/android/settings/wifi/WpsDialog.java @@ -57,7 +57,7 @@ public class WpsDialog extends AlertDialog { private static final int WPS_TIMEOUT_S = 120; private WifiManager mWifiManager; - private WifiManager.WpsListener mWpsListener; + private WifiManager.WpsCallback mWpsListener; private int mWpsSetup; private final IntentFilter mFilter; @@ -81,8 +81,9 @@ public class WpsDialog extends AlertDialog { mContext = context; mWpsSetup = wpsSetup; - class WpsListener implements WifiManager.WpsListener { - public void onStartSuccess(String pin) { + class WpsListener extends WifiManager.WpsCallback { + + public void onStarted(String pin) { if (pin != null) { updateDialog(DialogState.WPS_START, String.format( mContext.getString(R.string.wifi_wps_onstart_pin), pin)); @@ -91,12 +92,13 @@ public class WpsDialog extends AlertDialog { R.string.wifi_wps_onstart_pbc)); } } - public void onCompletion() { + + public void onSucceeded() { updateDialog(DialogState.WPS_COMPLETE, mContext.getString(R.string.wifi_wps_complete)); } - public void onFailure(int reason) { + public void onFailed(int reason) { String msg; switch (reason) { case WifiManager.WPS_OVERLAP_ERROR: |