0) {
+ mBandListAdapter.remove(
+ mBandListAdapter.getItem(0));
+ }
+ }
+
+ private void log(String msg) {
+ Log.d(LOG_TAG, "[BandsList] " + msg);
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_BAND_SCAN_COMPLETED:
+ ar = (AsyncResult) msg.obj;
+
+ bandListLoaded(ar);
+ break;
+
+ case EVENT_BAND_SELECTION_DONE:
+ ar = (AsyncResult) msg.obj;
+
+ getWindow().setFeatureInt(
+ Window.FEATURE_INDETERMINATE_PROGRESS,
+ Window.PROGRESS_VISIBILITY_OFF);
+
+ displayBandSelectionResult(ar.exception);
+ break;
+ }
+ }
+ };
+
+
+}
diff --git a/src/com/android/settings/BatteryInfo.java b/src/com/android/settings/BatteryInfo.java
new file mode 100644
index 00000000000..f9962fa2f40
--- /dev/null
+++ b/src/com/android/settings/BatteryInfo.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2006 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.widget.TextView;
+
+import com.android.internal.app.IBatteryStats;
+
+public class BatteryInfo extends Activity {
+ private TextView mStatus;
+ private TextView mLevel;
+ private TextView mScale;
+ private TextView mHealth;
+ private TextView mVoltage;
+ private TextView mTemperature;
+ private TextView mTechnology;
+ private TextView mUptime;
+ private TextView mAwakeBattery;
+ private TextView mAwakePlugged;
+ private TextView mScreenOn;
+ private IBatteryStats mBatteryStats;
+ private IPowerManager mScreenStats;
+
+ private static final int EVENT_TICK = 1;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_TICK:
+ updateBatteryStats();
+ sendEmptyMessageDelayed(EVENT_TICK, 1000);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Format a number of tenths-units as a decimal string without using a
+ * conversion to float. E.g. 347 -> "34.7"
+ */
+ private final String tenthsToFixedString(int x) {
+ int tens = x / 10;
+ return new String("" + tens + "." + (x - 10*tens));
+ }
+
+ /**
+ *Listens for intent broadcasts
+ */
+ private IntentFilter mIntentFilter;
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ int plugType = intent.getIntExtra("plugged", 0);
+
+ mLevel.setText("" + intent.getIntExtra("level", 0));
+ mScale.setText("" + intent.getIntExtra("scale", 0));
+ mVoltage.setText("" + intent.getIntExtra("voltage", 0) + " "
+ + getString(R.string.battery_info_voltage_units));
+ mTemperature.setText("" + tenthsToFixedString(intent.getIntExtra("temperature", 0))
+ + getString(R.string.battery_info_temperature_units));
+ mTechnology.setText("" + intent.getStringExtra("technology"));
+
+ int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
+ String statusString;
+ if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+ statusString = getString(R.string.battery_info_status_charging);
+ if (plugType > 0) {
+ statusString = statusString + " " + getString(
+ (plugType == BatteryManager.BATTERY_PLUGGED_AC)
+ ? R.string.battery_info_status_charging_ac
+ : R.string.battery_info_status_charging_usb);
+ }
+ } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+ statusString = getString(R.string.battery_info_status_discharging);
+ } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
+ statusString = getString(R.string.battery_info_status_not_charging);
+ } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
+ statusString = getString(R.string.battery_info_status_full);
+ } else {
+ statusString = getString(R.string.battery_info_status_unknown);
+ }
+ mStatus.setText(statusString);
+
+ int health = intent.getIntExtra("health", BatteryManager.BATTERY_HEALTH_UNKNOWN);
+ String healthString;
+ if (health == BatteryManager.BATTERY_HEALTH_GOOD) {
+ healthString = getString(R.string.battery_info_health_good);
+ } else if (health == BatteryManager.BATTERY_HEALTH_OVERHEAT) {
+ healthString = getString(R.string.battery_info_health_overheat);
+ } else if (health == BatteryManager.BATTERY_HEALTH_DEAD) {
+ healthString = getString(R.string.battery_info_health_dead);
+ } else if (health == BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE) {
+ healthString = getString(R.string.battery_info_health_over_voltage);
+ } else if (health == BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE) {
+ healthString = getString(R.string.battery_info_health_unspecified_failure);
+ } else {
+ healthString = getString(R.string.battery_info_health_unknown);
+ }
+ mHealth.setText(healthString);
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.battery_info);
+
+ // create the IntentFilter that will be used to listen
+ // to battery status broadcasts
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ mStatus = (TextView)findViewById(R.id.status);
+ mLevel = (TextView)findViewById(R.id.level);
+ mScale = (TextView)findViewById(R.id.scale);
+ mHealth = (TextView)findViewById(R.id.health);
+ mTechnology = (TextView)findViewById(R.id.technology);
+ mVoltage = (TextView)findViewById(R.id.voltage);
+ mTemperature = (TextView)findViewById(R.id.temperature);
+ mUptime = (TextView) findViewById(R.id.uptime);
+ mAwakeBattery = (TextView) findViewById(R.id.awakeBattery);
+ mAwakePlugged = (TextView) findViewById(R.id.awakePlugged);
+ mScreenOn = (TextView) findViewById(R.id.screenOn);
+
+ // Get awake time plugged in and on battery
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+ mScreenStats = IPowerManager.Stub.asInterface(ServiceManager.getService(POWER_SERVICE));
+ mHandler.sendEmptyMessageDelayed(EVENT_TICK, 1000);
+
+ registerReceiver(mIntentReceiver, mIntentFilter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mHandler.removeMessages(EVENT_TICK);
+
+ // we are no longer on the screen stop the observers
+ unregisterReceiver(mIntentReceiver);
+ }
+
+ private void updateBatteryStats() {
+ long uptime = SystemClock.elapsedRealtime();
+ mUptime.setText(DateUtils.formatElapsedTime(uptime / 1000));
+
+ if (mBatteryStats != null) {
+ try {
+ long awakeTimeBattery = mBatteryStats.getAwakeTimeBattery() / 1000;
+ long awakeTimePluggedIn = mBatteryStats.getAwakeTimePlugged() / 1000;
+ mAwakeBattery.setText(DateUtils.formatElapsedTime(awakeTimeBattery / 1000)
+ + " (" + (100 * awakeTimeBattery / uptime) + "%)");
+ mAwakePlugged.setText(DateUtils.formatElapsedTime(awakeTimePluggedIn / 1000)
+ + " (" + (100 * awakeTimePluggedIn / uptime) + "%)");
+ } catch (RemoteException re) {
+ mAwakeBattery.setText("Unknown");
+ mAwakePlugged.setText("Unknown");
+ }
+ }
+ if (mScreenStats != null) {
+ try {
+ long screenOnTime = mScreenStats.getScreenOnTime();
+ mScreenOn.setText(DateUtils.formatElapsedTime(screenOnTime / 1000));
+ } catch (RemoteException re) {
+ mScreenOn.setText("Unknown");
+ }
+ }
+ }
+
+}
diff --git a/src/com/android/settings/BrightnessPreference.java b/src/com/android/settings/BrightnessPreference.java
new file mode 100644
index 00000000000..a9851cc93a2
--- /dev/null
+++ b/src/com/android/settings/BrightnessPreference.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 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.Context;
+import android.os.RemoteException;
+import android.os.IHardwareService;
+import android.os.ServiceManager;
+import android.preference.SeekBarPreference;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.SeekBar;
+
+import java.util.Map;
+
+public class BrightnessPreference extends SeekBarPreference implements
+ SeekBar.OnSeekBarChangeListener {
+
+ private SeekBar mSeekBar;
+
+ private int mOldBrightness;
+
+ // Backlight range is from 0 - 255. Need to make sure that user
+ // doesn't set the backlight to 0 and get stuck
+ private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
+ private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
+
+ public BrightnessPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ mSeekBar = getSeekBar(view);
+ mSeekBar.setOnSeekBarChangeListener(this);
+ mSeekBar.setMax(MAXIMUM_BACKLIGHT - MINIMUM_BACKLIGHT);
+ try {
+ mOldBrightness = Settings.System.getInt(getContext().getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS);
+ } catch (SettingNotFoundException snfe) {
+ mOldBrightness = MAXIMUM_BACKLIGHT;
+ }
+ mSeekBar.setProgress(mOldBrightness - MINIMUM_BACKLIGHT);
+ }
+
+ public void onProgressChanged(SeekBar seekBar, int progress,
+ boolean fromTouch) {
+ setBrightness(progress + MINIMUM_BACKLIGHT);
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // NA
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // NA
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (positiveResult) {
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS,
+ mSeekBar.getProgress() + MINIMUM_BACKLIGHT);
+ } else {
+ setBrightness(mOldBrightness);
+ }
+ }
+
+ private void setBrightness(int brightness) {
+ try {
+ IHardwareService hardware = IHardwareService.Stub.asInterface(
+ ServiceManager.getService("hardware"));
+ if (hardware != null) {
+ hardware.setScreenBacklight(brightness);
+ }
+ } catch (RemoteException doe) {
+
+ }
+ }
+}
+
diff --git a/src/com/android/settings/ChooseLockPattern.java b/src/com/android/settings/ChooseLockPattern.java
new file mode 100644
index 00000000000..47fc07f6424
--- /dev/null
+++ b/src/com/android/settings/ChooseLockPattern.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2007 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.google.android.collect.Lists;
+
+import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import static com.android.internal.widget.LockPatternView.DisplayMode;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * If the user has a lock pattern set already, makes them confirm the existing one.
+ *
+ * Then, prompts the user to choose a lock pattern:
+ * - prompts for initial pattern
+ * - asks for confirmation / restart
+ * - saves chosen password when confirmed
+ */
+public class ChooseLockPattern extends Activity implements View.OnClickListener{
+
+ /**
+ * Used by the choose lock pattern wizard to indicate the wizard is
+ * finished, and each activity in the wizard should finish.
+ *
+ * Previously, each activity in the wizard would finish itself after
+ * starting the next activity. However, this leads to broken 'Back'
+ * behavior. So, now an activity does not finish itself until it gets this
+ * result.
+ */
+ static final int RESULT_FINISHED = RESULT_FIRST_USER;
+
+ // how long after a confirmation message is shown before moving on
+ static final int INFORMATION_MSG_TIMEOUT_MS = 3000;
+
+ // how long we wait to clear a wrong pattern
+ private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
+
+ private static final int ID_EMPTY_MESSAGE = -1;
+
+
+ protected TextView mHeaderText;
+ protected LockPatternView mLockPatternView;
+ protected TextView mFooterText;
+ private TextView mFooterLeftButton;
+ private TextView mFooterRightButton;
+
+ protected List mChosenPattern = null;
+
+ protected LockPatternUtils mLockPatternUtils;
+
+ /**
+ * The patten used during the help screen to show how to draw a pattern.
+ */
+ private final List mAnimatePattern =
+ Collections.unmodifiableList(
+ Lists.newArrayList(
+ LockPatternView.Cell.of(0, 0),
+ LockPatternView.Cell.of(0, 1),
+ LockPatternView.Cell.of(1, 1),
+ LockPatternView.Cell.of(2, 1)
+ ));
+
+
+ /**
+ * The pattern listener that responds according to a user choosing a new
+ * lock pattern.
+ */
+ protected LockPatternView.OnPatternListener mChooseNewLockPatternListener = new LockPatternView.OnPatternListener() {
+
+ public void onPatternStart() {
+ mLockPatternView.removeCallbacks(mClearPatternRunnable);
+ patternInProgress();
+ }
+
+ public void onPatternCleared() {
+ mLockPatternView.removeCallbacks(mClearPatternRunnable);
+ }
+
+ public void onPatternDetected(List pattern) {
+ if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
+ if (mChosenPattern == null) throw new IllegalStateException("null chosen pattern in stage 'need to confirm");
+ if (mChosenPattern.equals(pattern)) {
+ updateStage(Stage.ChoiceConfirmed);
+ } else {
+ updateStage(Stage.ConfirmWrong);
+ }
+ } else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
+ if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
+ updateStage(Stage.ChoiceTooShort);
+ } else {
+ mChosenPattern = new ArrayList(pattern);
+ updateStage(Stage.FirstChoiceValid);
+ }
+ } else {
+ throw new IllegalStateException("Unexpected stage " + mUiStage + " when "
+ + "entering the pattern.");
+ }
+ }
+
+ private void patternInProgress() {
+ mHeaderText.setText(R.string.lockpattern_recording_inprogress);
+ mFooterText.setText("");
+ mFooterLeftButton.setEnabled(false);
+ mFooterRightButton.setEnabled(false);
+ }
+ };
+
+
+ /**
+ * The states of the left footer button.
+ */
+ enum LeftButtonMode {
+ Cancel(R.string.cancel, true),
+ CancelDisabled(R.string.cancel, false),
+ Retry(R.string.lockpattern_retry_button_text, true),
+ RetryDisabled(R.string.lockpattern_retry_button_text, false),
+ Gone(ID_EMPTY_MESSAGE, false);
+
+
+ /**
+ * @param text The displayed text for this mode.
+ * @param enabled Whether the button should be enabled.
+ */
+ LeftButtonMode(int text, boolean enabled) {
+ this.text = text;
+ this.enabled = enabled;
+ }
+
+ final int text;
+ final boolean enabled;
+ }
+
+ /**
+ * The states of the right button.
+ */
+ enum RightButtonMode {
+ Continue(R.string.lockpattern_continue_button_text, true),
+ ContinueDisabled(R.string.lockpattern_continue_button_text, false),
+ Confirm(R.string.lockpattern_confirm_button_text, true),
+ ConfirmDisabled(R.string.lockpattern_confirm_button_text, false),
+ Ok(android.R.string.ok, true);
+
+ /**
+ * @param text The displayed text for this mode.
+ * @param enabled Whether the button should be enabled.
+ */
+ RightButtonMode(int text, boolean enabled) {
+ this.text = text;
+ this.enabled = enabled;
+ }
+
+ final int text;
+ final boolean enabled;
+ }
+
+ /**
+ * Keep track internally of where the user is in choosing a pattern.
+ */
+ protected enum Stage {
+
+ Introduction(
+ R.string.lockpattern_recording_intro_header,
+ LeftButtonMode.Cancel, RightButtonMode.ContinueDisabled,
+ R.string.lockpattern_recording_intro_footer, true),
+ HelpScreen(
+ R.string.lockpattern_settings_help_how_to_record,
+ LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
+ ChoiceTooShort(
+ R.string.lockpattern_recording_incorrect_too_short,
+ LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
+ ID_EMPTY_MESSAGE, true),
+ FirstChoiceValid(
+ R.string.lockpattern_pattern_entered_header,
+ LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
+ NeedToConfirm(
+ R.string.lockpattern_need_to_confirm,
+ LeftButtonMode.CancelDisabled, RightButtonMode.ConfirmDisabled,
+ ID_EMPTY_MESSAGE, true),
+ ConfirmWrong(
+ R.string.lockpattern_need_to_unlock_wrong,
+ LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled,
+ ID_EMPTY_MESSAGE, true),
+ ChoiceConfirmed(
+ R.string.lockpattern_pattern_confirmed_header,
+ LeftButtonMode.Cancel, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false);
+
+
+ /**
+ * @param headerMessage The message displayed at the top.
+ * @param leftMode The mode of the left button.
+ * @param rightMode The mode of the right button.
+ * @param footerMessage The footer message.
+ * @param patternEnabled Whether the pattern widget is enabled.
+ */
+ Stage(int headerMessage,
+ LeftButtonMode leftMode,
+ RightButtonMode rightMode,
+ int footerMessage, boolean patternEnabled) {
+ this.headerMessage = headerMessage;
+ this.leftMode = leftMode;
+ this.rightMode = rightMode;
+ this.footerMessage = footerMessage;
+ this.patternEnabled = patternEnabled;
+ }
+
+ final int headerMessage;
+ final LeftButtonMode leftMode;
+ final RightButtonMode rightMode;
+ final int footerMessage;
+ final boolean patternEnabled;
+ }
+
+ private Stage mUiStage = Stage.Introduction;
+
+ private Runnable mClearPatternRunnable = new Runnable() {
+ public void run() {
+ mLockPatternView.clearPattern();
+ }
+ };
+
+ private static final String KEY_UI_STAGE = "uiStage";
+ private static final String KEY_PATTERN_CHOICE = "chosenPattern";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mLockPatternUtils = new LockPatternUtils(getContentResolver());
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ setupViews();
+
+ // make it so unhandled touch events within the unlock screen go to the
+ // lock pattern view.
+ final LinearLayoutWithDefaultTouchRecepient topLayout
+ = (LinearLayoutWithDefaultTouchRecepient) findViewById(
+ R.id.topLayout);
+ topLayout.setDefaultTouchRecepient(mLockPatternView);
+
+ if (savedInstanceState == null) {
+ // first launch
+ updateStage(Stage.Introduction);
+ if (mLockPatternUtils.savedPatternExists()) {
+ confirmPattern();
+ }
+ } else {
+ // restore from previous state
+ final String patternString = savedInstanceState.getString(KEY_PATTERN_CHOICE);
+ if (patternString != null) {
+ mChosenPattern = LockPatternUtils.stringToPattern(patternString);
+ }
+ updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
+ }
+ }
+
+ /**
+ * Keep all "find view" related stuff confined to this function since in
+ * case someone needs to subclass and customize.
+ */
+ protected void setupViews() {
+ setContentView(R.layout.choose_lock_pattern);
+
+ mHeaderText = (TextView) findViewById(R.id.headerText);
+
+ mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
+ mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ mFooterText = (TextView) findViewById(R.id.footerText);
+
+ mFooterLeftButton = (TextView) findViewById(R.id.footerLeftButton);
+ mFooterRightButton = (TextView) findViewById(R.id.footerRightButton);
+
+ mFooterLeftButton.setOnClickListener(this);
+ mFooterRightButton.setOnClickListener(this);
+ }
+
+ public void onClick(View v) {
+ if (v == mFooterLeftButton) {
+ if (mUiStage.leftMode == LeftButtonMode.Retry) {
+ mChosenPattern = null;
+ mLockPatternView.clearPattern();
+ updateStage(Stage.Introduction);
+ } else if (mUiStage.leftMode == LeftButtonMode.Cancel) {
+ // They are canceling the entire wizard
+ setResult(RESULT_FINISHED);
+ finish();
+ } else {
+ throw new IllegalStateException("left footer button pressed, but stage of " +
+ mUiStage + " doesn't make sense");
+ }
+ } else if (v == mFooterRightButton) {
+
+ if (mUiStage.rightMode == RightButtonMode.Continue) {
+ if (mUiStage != Stage.FirstChoiceValid) {
+ throw new IllegalStateException("expected ui stage " + Stage.FirstChoiceValid
+ + " when button is " + RightButtonMode.Continue);
+ }
+ updateStage(Stage.NeedToConfirm);
+ } else if (mUiStage.rightMode == RightButtonMode.Confirm) {
+ if (mUiStage != Stage.ChoiceConfirmed) {
+ throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
+ + " when button is " + RightButtonMode.Confirm);
+ }
+ saveChosenPatternAndFinish();
+ } else if (mUiStage.rightMode == RightButtonMode.Ok) {
+ if (mUiStage != Stage.HelpScreen) {
+ throw new IllegalStateException("Help screen is only mode with ok button, but " +
+ "stage is " + mUiStage);
+ }
+ mLockPatternView.clearPattern();
+ mLockPatternView.setDisplayMode(DisplayMode.Correct);
+ updateStage(Stage.Introduction);
+ }
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
+ if (mUiStage == Stage.HelpScreen) {
+ updateStage(Stage.Introduction);
+ return true;
+ }
+ }
+ if (keyCode == KeyEvent.KEYCODE_MENU && mUiStage == Stage.Introduction) {
+ updateStage(Stage.HelpScreen);
+ return true;
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**
+ * Launch screen to confirm the existing lock pattern.
+ * @see #onActivityResult(int, int, android.content.Intent)
+ */
+ protected void confirmPattern() {
+ final Intent intent = new Intent();
+ intent.setClassName("com.android.settings", "com.android.settings.ConfirmLockPattern");
+ startActivityForResult(intent, 55);
+ }
+
+ /**
+ * @see #confirmPattern
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode != 55) {
+ return;
+ }
+
+ if (resultCode != Activity.RESULT_OK) {
+ setResult(RESULT_FINISHED);
+ finish();
+ }
+ updateStage(Stage.Introduction);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putInt(KEY_UI_STAGE, mUiStage.ordinal());
+ if (mChosenPattern != null) {
+ outState.putString(KEY_PATTERN_CHOICE, LockPatternUtils.patternToString(mChosenPattern));
+ }
+ }
+
+
+ /**
+ * Updates the messages and buttons appropriate to what stage the user
+ * is at in choosing a view. This doesn't handle clearing out the pattern;
+ * the pattern is expected to be in the right state.
+ * @param stage
+ */
+ protected void updateStage(Stage stage) {
+
+ mUiStage = stage;
+
+ // header text, footer text, visibility and
+ // enabled state all known from the stage
+ if (stage == Stage.ChoiceTooShort) {
+ mHeaderText.setText(
+ getResources().getString(
+ stage.headerMessage,
+ LockPatternUtils.MIN_LOCK_PATTERN_SIZE));
+ } else {
+ mHeaderText.setText(stage.headerMessage);
+ }
+ if (stage.footerMessage == ID_EMPTY_MESSAGE) {
+ mFooterText.setText("");
+ } else {
+ mFooterText.setText(stage.footerMessage);
+ }
+
+ if (stage.leftMode == LeftButtonMode.Gone) {
+ mFooterLeftButton.setVisibility(View.GONE);
+ } else {
+ mFooterLeftButton.setVisibility(View.VISIBLE);
+ mFooterLeftButton.setText(stage.leftMode.text);
+ mFooterLeftButton.setEnabled(stage.leftMode.enabled);
+ }
+
+ mFooterRightButton.setText(stage.rightMode.text);
+ mFooterRightButton.setEnabled(stage.rightMode.enabled);
+
+ // same for whether the patten is enabled
+ if (stage.patternEnabled) {
+ mLockPatternView.enableInput();
+ } else {
+ mLockPatternView.disableInput();
+ }
+
+ // the rest of the stuff varies enough that it is easier just to handle
+ // on a case by case basis.
+ mLockPatternView.setDisplayMode(DisplayMode.Correct);
+
+ switch (mUiStage) {
+ case Introduction:
+ mLockPatternView.clearPattern();
+ break;
+ case HelpScreen:
+ mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
+ break;
+ case ChoiceTooShort:
+ mLockPatternView.setDisplayMode(DisplayMode.Wrong);
+ postClearPatternRunnable();
+ break;
+ case FirstChoiceValid:
+ break;
+ case NeedToConfirm:
+ mLockPatternView.clearPattern();
+ break;
+ case ConfirmWrong:
+ mLockPatternView.setDisplayMode(DisplayMode.Wrong);
+ postClearPatternRunnable();
+ break;
+ case ChoiceConfirmed:
+ break;
+ }
+ }
+
+
+ // clear the wrong pattern unless they have started a new one
+ // already
+ private void postClearPatternRunnable() {
+ mLockPatternView.removeCallbacks(mClearPatternRunnable);
+ mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
+ }
+
+ private void saveChosenPatternAndFinish() {
+ boolean patternExistedBefore = mLockPatternUtils.savedPatternExists();
+ mLockPatternUtils.saveLockPattern(mChosenPattern);
+
+ // if setting pattern for first time, enable the lock gesture. otherwise,
+ // keep the user's setting.
+ if (!patternExistedBefore) {
+ mLockPatternUtils.setLockPatternEnabled(true);
+ mLockPatternUtils.setVisiblePatternEnabled(true);
+ }
+
+ setResult(RESULT_FINISHED);
+ finish();
+ }
+}
diff --git a/src/com/android/settings/ChooseLockPatternExample.java b/src/com/android/settings/ChooseLockPatternExample.java
new file mode 100644
index 00000000000..77517b9d3d4
--- /dev/null
+++ b/src/com/android/settings/ChooseLockPatternExample.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 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.graphics.drawable.AnimationDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
+import android.widget.ImageView;
+
+public class ChooseLockPatternExample extends Activity implements View.OnClickListener {
+ private static final int REQUESTCODE_CHOOSE = 1;
+ private static final long START_DELAY = 1000;
+ protected static final String TAG = "Settings";
+ private View mNextButton;
+ private View mSkipButton;
+ private View mImageView;
+ private AnimationDrawable mAnimation;
+ private Handler mHandler = new Handler();
+ private Runnable mRunnable = new Runnable() {
+ public void run() {
+ startAnimation(mAnimation);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.choose_lock_pattern_example);
+ initViews();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mHandler.postDelayed(mRunnable, START_DELAY);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ stopAnimation(mAnimation);
+ }
+
+ public void onClick(View v) {
+ if (v == mSkipButton) {
+ // Canceling, so finish all
+ setResult(ChooseLockPattern.RESULT_FINISHED);
+ finish();
+ } else if (v == mNextButton) {
+ stopAnimation(mAnimation);
+ Intent intent = new Intent(this, ChooseLockPattern.class);
+ startActivityForResult(intent, REQUESTCODE_CHOOSE);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUESTCODE_CHOOSE && resultCode == ChooseLockPattern.RESULT_FINISHED) {
+ setResult(resultCode);
+ finish();
+ }
+ }
+
+ private void initViews() {
+ mNextButton = findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(this);
+
+ mSkipButton = findViewById(R.id.skip_button);
+ mSkipButton.setOnClickListener(this);
+
+ mImageView = (ImageView) findViewById(R.id.lock_anim);
+ mImageView.setBackgroundResource(R.drawable.lock_anim);
+ mImageView.setOnClickListener(this);
+ mAnimation = (AnimationDrawable) mImageView.getBackground();
+ }
+
+ protected void startAnimation(final AnimationDrawable animation) {
+ if (animation != null && !animation.isRunning()) {
+ animation.run();
+ }
+ }
+
+ protected void stopAnimation(final AnimationDrawable animation) {
+ if (animation != null && animation.isRunning()) animation.stop();
+ }
+}
+
diff --git a/src/com/android/settings/ChooseLockPatternTutorial.java b/src/com/android/settings/ChooseLockPatternTutorial.java
new file mode 100644
index 00000000000..a0a878a0554
--- /dev/null
+++ b/src/com/android/settings/ChooseLockPatternTutorial.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 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.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+public class ChooseLockPatternTutorial extends Activity implements View.OnClickListener {
+ private static final int REQUESTCODE_EXAMPLE = 1;
+
+ private View mNextButton;
+ private View mSkipButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Don't show the tutorial if the user has seen it before.
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(getContentResolver());
+ if (savedInstanceState == null && lockPatternUtils.savedPatternExists()) {
+ Intent intent = new Intent();
+ intent.setClassName("com.android.settings", "com.android.settings.ChooseLockPattern");
+ startActivity(intent);
+ finish();
+ } else {
+ initViews();
+ }
+ }
+
+ private void initViews() {
+ setContentView(R.layout.choose_lock_pattern_tutorial);
+ mNextButton = findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(this);
+ mSkipButton = findViewById(R.id.skip_button);
+ mSkipButton.setOnClickListener(this);
+ }
+
+ public void onClick(View v) {
+ if (v == mSkipButton) {
+ // Canceling, so finish all
+ setResult(ChooseLockPattern.RESULT_FINISHED);
+ finish();
+ } else if (v == mNextButton) {
+ startActivityForResult(new Intent(this, ChooseLockPatternExample.class),
+ REQUESTCODE_EXAMPLE);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUESTCODE_EXAMPLE && resultCode == ChooseLockPattern.RESULT_FINISHED) {
+ setResult(resultCode);
+ finish();
+ }
+ }
+
+}
+
diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java
new file mode 100644
index 00000000000..44baccc6504
--- /dev/null
+++ b/src/com/android/settings/ConfirmLockPattern.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2008 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 com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.view.Window;
+
+import java.util.List;
+
+/**
+ * Launch this when you want the user to confirm their lock pattern.
+ *
+ * Sets an activity result of {@link Activity#RESULT_OK} when the user
+ * successfully confirmed their pattern.
+ */
+public class ConfirmLockPattern extends Activity {
+
+ /**
+ * Names of {@link CharSequence} fields within the originating {@link Intent}
+ * that are used to configure the keyguard confirmation view's labeling.
+ * The view will use the system-defined resource strings for any labels that
+ * the caller does not supply.
+ */
+ public static final String HEADER_TEXT = "com.android.settings.ConfirmLockPattern.header";
+ public static final String FOOTER_TEXT = "com.android.settings.ConfirmLockPattern.footer";
+ public static final String HEADER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.header_wrong";
+ public static final String FOOTER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.footer_wrong";
+
+ // how long we wait to clear a wrong pattern
+ private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
+
+ private static final String KEY_NUM_WRONG_ATTEMPTS = "num_wrong_attempts";
+
+ private LockPatternView mLockPatternView;
+ private LockPatternUtils mLockPatternUtils;
+ private int mNumWrongConfirmAttempts;
+ private CountDownTimer mCountdownTimer;
+
+ private TextView mHeaderTextView;
+ private TextView mFooterTextView;
+
+ // caller-supplied text for various prompts
+ private CharSequence mHeaderText;
+ private CharSequence mFooterText;
+ private CharSequence mHeaderWrongText;
+ private CharSequence mFooterWrongText;
+
+
+ private enum Stage {
+ NeedToUnlock,
+ NeedToUnlockWrong,
+ LockedOut
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mLockPatternUtils = new LockPatternUtils(getContentResolver());
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.confirm_lock_pattern);
+
+ mHeaderTextView = (TextView) findViewById(R.id.headerText);
+ mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
+ mFooterTextView = (TextView) findViewById(R.id.footerText);
+
+ // make it so unhandled touch events within the unlock screen go to the
+ // lock pattern view.
+ final LinearLayoutWithDefaultTouchRecepient topLayout
+ = (LinearLayoutWithDefaultTouchRecepient) findViewById(
+ R.id.topLayout);
+ topLayout.setDefaultTouchRecepient(mLockPatternView);
+
+ Intent intent = getIntent();
+ if (intent != null) {
+ mHeaderText = intent.getCharSequenceExtra(HEADER_TEXT);
+ mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
+ mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
+ mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
+ }
+
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+ mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
+ updateStage(Stage.NeedToUnlock);
+
+ if (savedInstanceState != null) {
+ mNumWrongConfirmAttempts = savedInstanceState.getInt(KEY_NUM_WRONG_ATTEMPTS);
+ } else {
+ // on first launch, if no lock pattern is set, then finish with
+ // success (don't want user to get stuck confirming something that
+ // doesn't exist).
+ if (!mLockPatternUtils.savedPatternExists()) {
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ // deliberately not calling super since we are managing this in full
+ outState.putInt(KEY_NUM_WRONG_ATTEMPTS, mNumWrongConfirmAttempts);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ }
+ }
+
+ private void updateStage(Stage stage) {
+
+ switch (stage) {
+ case NeedToUnlock:
+ if (mHeaderText != null) {
+ mHeaderTextView.setText(mHeaderText);
+ } else {
+ mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
+ }
+ if (mFooterText != null) {
+ mFooterTextView.setText(mFooterText);
+ } else {
+ mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
+ }
+
+ mLockPatternView.setEnabled(true);
+ mLockPatternView.enableInput();
+ break;
+ case NeedToUnlockWrong:
+ if (mHeaderWrongText != null) {
+ mHeaderTextView.setText(mHeaderWrongText);
+ } else {
+ mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
+ }
+ if (mFooterWrongText != null) {
+ mFooterTextView.setText(mFooterWrongText);
+ } else {
+ mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
+ }
+
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
+ mLockPatternView.setEnabled(true);
+ mLockPatternView.enableInput();
+ break;
+ case LockedOut:
+ mLockPatternView.clearPattern();
+ // enabled = false means: disable input, and have the
+ // appearance of being disabled.
+ mLockPatternView.setEnabled(false); // appearance of being disabled
+ break;
+ }
+ }
+
+ private Runnable mClearPatternRunnable = new Runnable() {
+ public void run() {
+ mLockPatternView.clearPattern();
+ }
+ };
+
+ // clear the wrong pattern unless they have started a new one
+ // already
+ private void postClearPatternRunnable() {
+ mLockPatternView.removeCallbacks(mClearPatternRunnable);
+ mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
+ }
+
+ /**
+ * The pattern listener that responds according to a user confirming
+ * an existing lock pattern.
+ */
+ private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener = new LockPatternView.OnPatternListener() {
+
+ public void onPatternStart() {
+ mLockPatternView.removeCallbacks(mClearPatternRunnable);
+ }
+
+ public void onPatternCleared() {
+ mLockPatternView.removeCallbacks(mClearPatternRunnable);
+ }
+
+ public void onPatternDetected(List pattern) {
+ if (mLockPatternUtils.checkPattern(pattern)) {
+ setResult(RESULT_OK);
+ finish();
+ } else {
+ if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
+ ++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+ handleAttemptLockout(deadline);
+ } else {
+ updateStage(Stage.NeedToUnlockWrong);
+ postClearPatternRunnable();
+ }
+ }
+ }
+ };
+
+
+ private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ updateStage(Stage.LockedOut);
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ mCountdownTimer = new CountDownTimer(
+ elapsedRealtimeDeadline - elapsedRealtime,
+ LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
+ final int secondsCountdown = (int) (millisUntilFinished / 1000);
+ mFooterTextView.setText(getString(
+ R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
+ secondsCountdown));
+ }
+
+ @Override
+ public void onFinish() {
+ mNumWrongConfirmAttempts = 0;
+ updateStage(Stage.NeedToUnlock);
+ }
+ }.start();
+ }
+}
diff --git a/src/com/android/settings/DateTimeSettings.java b/src/com/android/settings/DateTimeSettings.java
new file mode 100644
index 00000000000..e78215af54e
--- /dev/null
+++ b/src/com/android/settings/DateTimeSettings.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2008 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.Dialog;
+import android.app.DatePickerDialog;
+import android.app.TimePickerDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.text.format.DateFormat;
+import android.widget.DatePicker;
+import android.widget.TimePicker;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class DateTimeSettings
+ extends PreferenceActivity
+ implements OnSharedPreferenceChangeListener,
+ TimePickerDialog.OnTimeSetListener , DatePickerDialog.OnDateSetListener {
+
+ private static final String HOURS_12 = "12";
+ private static final String HOURS_24 = "24";
+
+ private Calendar mDummyDate;
+ private static final String KEY_DATE_FORMAT = "date_format";
+ private static final String KEY_AUTO_TIME = "auto_time";
+
+ private static final int DIALOG_DATEPICKER = 0;
+ private static final int DIALOG_TIMEPICKER = 1;
+
+ private CheckBoxPreference mAutoPref;
+ private Preference mTimePref;
+ private Preference mTime24Pref;
+ private Preference mTimeZone;
+ private Preference mDatePref;
+ private ListPreference mDateFormat;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.date_time_prefs);
+
+ initUI();
+ }
+
+ private void initUI() {
+ boolean autoEnabled = getAutoState();
+
+ mDummyDate = Calendar.getInstance();
+ mDummyDate.set(mDummyDate.get(Calendar.YEAR), 11, 31, 13, 0, 0);
+
+ mAutoPref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME);
+ mAutoPref.setChecked(autoEnabled);
+ mTimePref = findPreference("time");
+ mTime24Pref = findPreference("24 hour");
+ mTimeZone = findPreference("timezone");
+ mDatePref = findPreference("date");
+ mDateFormat = (ListPreference) findPreference(KEY_DATE_FORMAT);
+
+ int currentFormatIndex = -1;
+ String [] dateFormats = getResources().getStringArray(R.array.date_format_values);
+ String [] formattedDates = new String[dateFormats.length];
+ String currentFormat = getDateFormat();
+ // Initialize if DATE_FORMAT is not set in the system settings
+ // This can happen after a factory reset (or data wipe)
+ if (currentFormat == null) {
+ currentFormat = getResources().getString(R.string.default_date_format);
+ setDateFormat(currentFormat);
+ }
+ for (int i = 0; i < formattedDates.length; i++) {
+ formattedDates[i] = DateFormat.format(dateFormats[i], mDummyDate).toString();
+ if (currentFormat.equals(dateFormats[i])) currentFormatIndex = i;
+ }
+
+ mDateFormat.setEntries(formattedDates);
+ mDateFormat.setEntryValues(R.array.date_format_values);
+ mDateFormat.setValue(currentFormat);
+
+ mTimePref.setEnabled(!autoEnabled);
+ mDatePref.setEnabled(!autoEnabled);
+ mTimeZone.setEnabled(!autoEnabled);
+
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ ((CheckBoxPreference)mTime24Pref).setChecked(is24Hour());
+
+ // Register for time ticks and other reasons for time change
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ registerReceiver(mIntentReceiver, filter, null, null);
+
+ updateTimeAndDateDisplay();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(mIntentReceiver);
+ }
+
+ private void updateTimeAndDateDisplay() {
+ java.text.DateFormat shortDateFormat = DateFormat.getDateFormat(this);
+ Date now = Calendar.getInstance().getTime();
+ Date dummyDate = mDummyDate.getTime();
+ mTimePref.setSummary(DateFormat.getTimeFormat(this).format(now));
+ mTimeZone.setSummary(getTimeZoneText());
+ mDatePref.setSummary(shortDateFormat.format(now));
+ mDateFormat.setSummary(shortDateFormat.format(dummyDate));
+ }
+
+ public void onDateSet(DatePicker view, int year, int month, int day) {
+ Calendar c = Calendar.getInstance();
+
+ c.set(Calendar.YEAR, year);
+ c.set(Calendar.MONTH, month);
+ c.set(Calendar.DAY_OF_MONTH, day);
+ long when = c.getTimeInMillis();
+
+ if (when / 1000 < Integer.MAX_VALUE) {
+ SystemClock.setCurrentTimeMillis(when);
+ }
+ updateTimeAndDateDisplay();
+ }
+
+ public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+ Calendar c = Calendar.getInstance();
+
+ c.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ c.set(Calendar.MINUTE, minute);
+ long when = c.getTimeInMillis();
+
+ if (when / 1000 < Integer.MAX_VALUE) {
+ SystemClock.setCurrentTimeMillis(when);
+ }
+ updateTimeAndDateDisplay();
+ timeUpdated();
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
+ if (key.equals(KEY_DATE_FORMAT)) {
+ String format = preferences.getString(key,
+ getResources().getString(R.string.default_date_format));
+ Settings.System.putString(getContentResolver(),
+ Settings.System.DATE_FORMAT, format);
+ updateTimeAndDateDisplay();
+ } else if (key.equals(KEY_AUTO_TIME)) {
+ boolean autoEnabled = preferences.getBoolean(key, true);
+ Settings.System.putInt(getContentResolver(),
+ Settings.System.AUTO_TIME,
+ autoEnabled ? 1 : 0);
+ mTimePref.setEnabled(!autoEnabled);
+ mDatePref.setEnabled(!autoEnabled);
+ mTimeZone.setEnabled(!autoEnabled);
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(int id) {
+ Dialog d;
+
+ switch (id) {
+ case DIALOG_DATEPICKER: {
+ final Calendar calendar = Calendar.getInstance();
+ d = new DatePickerDialog(
+ this,
+ this,
+ calendar.get(Calendar.YEAR),
+ calendar.get(Calendar.MONTH),
+ calendar.get(Calendar.DAY_OF_MONTH));
+ d.setTitle(getResources().getString(R.string.date_time_changeDate_text));
+ break;
+ }
+ case DIALOG_TIMEPICKER: {
+ final Calendar calendar = Calendar.getInstance();
+ d = new TimePickerDialog(
+ this,
+ this,
+ calendar.get(Calendar.HOUR_OF_DAY),
+ calendar.get(Calendar.MINUTE),
+ DateFormat.is24HourFormat(this));
+ d.setTitle(getResources().getString(R.string.date_time_changeTime_text));
+ break;
+ }
+ default:
+ d = null;
+ break;
+ }
+
+ return d;
+ }
+
+ @Override
+ public void onPrepareDialog(int id, Dialog d) {
+ switch (id) {
+ case DIALOG_DATEPICKER: {
+ DatePickerDialog datePicker = (DatePickerDialog)d;
+ final Calendar calendar = Calendar.getInstance();
+ datePicker.updateDate(
+ calendar.get(Calendar.YEAR),
+ calendar.get(Calendar.MONTH),
+ calendar.get(Calendar.DAY_OF_MONTH));
+ break;
+ }
+ case DIALOG_TIMEPICKER: {
+ TimePickerDialog timePicker = (TimePickerDialog)d;
+ final Calendar calendar = Calendar.getInstance();
+ timePicker.updateTime(
+ calendar.get(Calendar.HOUR_OF_DAY),
+ calendar.get(Calendar.MINUTE));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ if (preference == mDatePref) {
+ showDialog(DIALOG_DATEPICKER);
+ } else if (preference == mTimePref) {
+ // The 24-hour mode may have changed, so recreate the dialog
+ removeDialog(DIALOG_TIMEPICKER);
+ showDialog(DIALOG_TIMEPICKER);
+ } else if (preference == mTime24Pref) {
+ set24Hour(((CheckBoxPreference)mTime24Pref).isChecked());
+ updateTimeAndDateDisplay();
+ timeUpdated();
+ } else if (preference == mTimeZone) {
+ Intent intent = new Intent();
+ intent.setClass(this, ZoneList.class);
+ startActivityForResult(intent, 0);
+ }
+ return false;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ updateTimeAndDateDisplay();
+ }
+
+ private void timeUpdated() {
+ Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED);
+ sendBroadcast(timeChanged);
+ }
+
+ /* Get & Set values from the system settings */
+
+ private boolean is24Hour() {
+ return DateFormat.is24HourFormat(this);
+ }
+
+ private void set24Hour(boolean is24Hour) {
+ Settings.System.putString(getContentResolver(),
+ Settings.System.TIME_12_24,
+ is24Hour? HOURS_24 : HOURS_12);
+ }
+
+ private String getDateFormat() {
+ return Settings.System.getString(getContentResolver(),
+ Settings.System.DATE_FORMAT);
+ }
+
+ private boolean getAutoState() {
+ try {
+ return Settings.System.getInt(getContentResolver(),
+ Settings.System.AUTO_TIME) > 0;
+ } catch (SettingNotFoundException snfe) {
+ return true;
+ }
+ }
+
+ private void setDateFormat(String format) {
+ Settings.System.putString(getContentResolver(), Settings.System.DATE_FORMAT, format);
+ }
+
+ /* Helper routines to format timezone */
+
+ private String getTimeZoneText() {
+ TimeZone tz = java.util.Calendar.getInstance().getTimeZone();
+ boolean daylight = tz.inDaylightTime(new Date());
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(formatOffset(tz.getRawOffset() +
+ (daylight ? tz.getDSTSavings() : 0))).
+ append(", ").
+ append(tz.getDisplayName(daylight, TimeZone.LONG));
+
+ return sb.toString();
+ }
+
+ private char[] formatOffset(int off) {
+ off = off / 1000 / 60;
+
+ char[] buf = new char[9];
+ buf[0] = 'G';
+ buf[1] = 'M';
+ buf[2] = 'T';
+
+ if (off < 0) {
+ buf[3] = '-';
+ off = -off;
+ } else {
+ buf[3] = '+';
+ }
+
+ int hours = off / 60;
+ int minutes = off % 60;
+
+ buf[4] = (char) ('0' + hours / 10);
+ buf[5] = (char) ('0' + hours % 10);
+
+ buf[6] = ':';
+
+ buf[7] = (char) ('0' + minutes / 10);
+ buf[8] = (char) ('0' + minutes % 10);
+
+ return buf;
+ }
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateTimeAndDateDisplay();
+ }
+ };
+}
diff --git a/src/com/android/settings/DateTimeSettingsSetupWizard.java b/src/com/android/settings/DateTimeSettingsSetupWizard.java
new file mode 100644
index 00000000000..8dd970b0e9b
--- /dev/null
+++ b/src/com/android/settings/DateTimeSettingsSetupWizard.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 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.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.LinearLayout;
+
+public class DateTimeSettingsSetupWizard extends DateTimeSettings implements OnClickListener {
+ private View mNextButton;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ super.onCreate(icicle);
+ setContentView(R.layout.date_time_settings_setupwizard);
+ mNextButton = findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(this);
+ }
+
+ public void onClick(View v) {
+ setResult(RESULT_OK);
+ finish();
+ }
+}
diff --git a/src/com/android/settings/DebugIntentSender.java b/src/com/android/settings/DebugIntentSender.java
new file mode 100644
index 00000000000..9fed94749e5
--- /dev/null
+++ b/src/com/android/settings/DebugIntentSender.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2006 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.widget.EditText;
+import android.widget.Button;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.text.TextUtils;
+import android.text.Spannable;
+import android.text.Selection;
+import android.net.Uri;
+
+/**
+ * A simple activity that provides a UI for sending intents
+ */
+public class DebugIntentSender extends Activity {
+ private EditText mIntentField;
+ private EditText mDataField;
+ private EditText mAccountField;
+ private EditText mResourceField;
+ private Button mSendBroadcastButton;
+ private Button mStartActivityButton;
+ private View.OnClickListener mClicked = new View.OnClickListener() {
+ public void onClick(View v) {
+ if ((v == mSendBroadcastButton) ||
+ (v == mStartActivityButton)) {
+ String intentAction = mIntentField.getText().toString();
+ String intentData = mDataField.getText().toString();
+ String account = mAccountField.getText().toString();
+ String resource = mResourceField.getText().toString();
+
+ Intent intent = new Intent(intentAction);
+ if (!TextUtils.isEmpty(intentData)) {
+ intent.setData(Uri.parse(intentData));
+ }
+ intent.putExtra("account", account);
+ intent.putExtra("resource", resource);
+ if (v == mSendBroadcastButton) {
+ sendBroadcast(intent);
+ } else {
+ startActivity(intent);
+ }
+
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.intent_sender);
+
+ mIntentField = (EditText) findViewById(R.id.intent);
+ mIntentField.setText(Intent.ACTION_SYNC);
+ Selection.selectAll((Spannable) mIntentField.getText());
+
+ mDataField = (EditText) findViewById(R.id.data);
+ mDataField.setBackgroundResource(android.R.drawable.editbox_background);
+
+ mAccountField = (EditText) findViewById(R.id.account);
+ mResourceField = (EditText) findViewById(R.id.resource);
+
+ mSendBroadcastButton = (Button) findViewById(R.id.sendbroadcast);
+ mSendBroadcastButton.setOnClickListener(mClicked);
+
+ mStartActivityButton = (Button) findViewById(R.id.startactivity);
+ mStartActivityButton.setOnClickListener(mClicked);
+ }
+}
diff --git a/src/com/android/settings/DefaultRingtonePreference.java b/src/com/android/settings/DefaultRingtonePreference.java
new file mode 100644
index 00000000000..8eed5635018
--- /dev/null
+++ b/src/com/android/settings/DefaultRingtonePreference.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2007 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.Context;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.preference.RingtonePreference;
+import android.util.AttributeSet;
+import android.util.Config;
+import android.util.Log;
+
+public class DefaultRingtonePreference extends RingtonePreference {
+ private static final String TAG = "DefaultRingtonePreference";
+
+ public DefaultRingtonePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
+ super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
+
+ /*
+ * Since this preference is for choosing the default ringtone, it
+ * doesn't make sense to show a 'Default' item.
+ */
+ ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
+
+ /*
+ * Similarly, 'Silent' shouldn't be shown here.
+ */
+ ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
+ }
+
+ @Override
+ protected void onSaveRingtone(Uri ringtoneUri) {
+ RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri);
+ }
+
+ @Override
+ protected Uri onRestoreRingtone() {
+ return RingtoneManager.getActualDefaultRingtoneUri(getContext(), getRingtoneType());
+ }
+
+}
diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java
new file mode 100644
index 00000000000..155f085f5f5
--- /dev/null
+++ b/src/com/android/settings/DevelopmentSettings.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 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.os.BatteryManager;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.preference.CheckBoxPreference;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+/*
+ * Displays preferences for application developers.
+ */
+public class DevelopmentSettings extends PreferenceActivity {
+
+ private static final String ENABLE_ADB = "enable_adb";
+ private static final String KEEP_SCREEN_ON = "keep_screen_on";
+ private static final String ALLOW_MOCK_LOCATION = "allow_mock_location";
+
+ private CheckBoxPreference mEnableAdb;
+ private CheckBoxPreference mKeepScreenOn;
+ private CheckBoxPreference mAllowMockLocation;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.development_prefs);
+
+ mEnableAdb = (CheckBoxPreference) findPreference(ENABLE_ADB);
+ mKeepScreenOn = (CheckBoxPreference) findPreference(KEEP_SCREEN_ON);
+ mAllowMockLocation = (CheckBoxPreference) findPreference(ALLOW_MOCK_LOCATION);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mEnableAdb.setChecked(Settings.Secure.getInt(getContentResolver(),
+ Settings.Secure.ADB_ENABLED, 0) != 0);
+ mKeepScreenOn.setChecked(Settings.System.getInt(getContentResolver(),
+ Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0) != 0);
+ mAllowMockLocation.setChecked(Settings.Secure.getInt(getContentResolver(),
+ Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+
+ // Those monkeys kept committing suicide, so we add this property
+ // to disable this functionality
+ if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
+ return false;
+ }
+
+ if (preference == mEnableAdb) {
+ Settings.Secure.putInt(getContentResolver(), Settings.Secure.ADB_ENABLED,
+ mEnableAdb.isChecked() ? 1 : 0);
+ } else if (preference == mKeepScreenOn) {
+ Settings.System.putInt(getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN,
+ mKeepScreenOn.isChecked() ?
+ (BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB) : 0);
+ } else if (preference == mAllowMockLocation) {
+ Settings.Secure.putInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION,
+ mAllowMockLocation.isChecked() ? 1 : 0);
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/android/settings/DeviceInfoSettings.java b/src/com/android/settings/DeviceInfoSettings.java
new file mode 100644
index 00000000000..be01f7dc57e
--- /dev/null
+++ b/src/com/android/settings/DeviceInfoSettings.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2008 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.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.util.Config;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DeviceInfoSettings extends PreferenceActivity {
+
+ private static final String TAG = "DeviceInfoSettings";
+ private static final boolean LOGD = false || Config.LOGD;
+
+ private static final String KEY_CONTAINER = "container";
+ private static final String KEY_TEAM = "team";
+ private static final String KEY_CONTRIBUTORS = "contributors";
+ private static final String KEY_TERMS = "terms";
+ private static final String KEY_LICENSE = "license";
+ private static final String KEY_COPYRIGHT = "copyright";
+ private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings";
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.device_info_settings);
+
+ setStringSummary("firmware_version", Build.VERSION.RELEASE);
+ setValueSummary("baseband_version", "gsm.version.baseband");
+ setStringSummary("device_model", Build.MODEL);
+ setStringSummary("build_number", Build.DISPLAY);
+ findPreference("kernel_version").setSummary(getFormattedKernelVersion());
+
+ /*
+ * Settings is a generic app and should not contain any device-specific
+ * info.
+ */
+
+ // These are contained in the "container" preference group
+ PreferenceGroup parentPreference = (PreferenceGroup) findPreference(KEY_CONTAINER);
+ Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_TERMS,
+ Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
+ Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_LICENSE,
+ Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
+ Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_COPYRIGHT,
+ Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
+ Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_TEAM,
+ Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
+
+ // These are contained by the root preference screen
+ parentPreference = getPreferenceScreen();
+ Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference,
+ KEY_SYSTEM_UPDATE_SETTINGS,
+ Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
+ Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_CONTRIBUTORS,
+ Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
+ }
+
+ private void setStringSummary(String preference, String value) {
+ try {
+ findPreference(preference).setSummary(value);
+ } catch (RuntimeException e) {
+ findPreference(preference).setSummary(
+ getResources().getString(R.string.device_info_default));
+ }
+ }
+
+ private void setValueSummary(String preference, String property) {
+ try {
+ findPreference(preference).setSummary(
+ SystemProperties.get(property,
+ getResources().getString(R.string.device_info_default)));
+ } catch (RuntimeException e) {
+
+ }
+ }
+
+ private String getFormattedKernelVersion() {
+ String procVersionStr;
+
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader("/proc/version"), 256);
+ try {
+ procVersionStr = reader.readLine();
+ } finally {
+ reader.close();
+ }
+
+ final String PROC_VERSION_REGEX =
+ "\\w+\\s+" + /* ignore: Linux */
+ "\\w+\\s+" + /* ignore: version */
+ "([^\\s]+)\\s+" + /* group 1: 2.6.22-omap1 */
+ "\\(([^\\s@]+(?:@[^\\s.]+)?)[^)]*\\)\\s+" + /* group 2: (xxxxxx@xxxxx.constant) */
+ "\\([^)]+\\)\\s+" + /* ignore: (gcc ..) */
+ "([^\\s]+)\\s+" + /* group 3: #26 */
+ "(?:PREEMPT\\s+)?" + /* ignore: PREEMPT (optional) */
+ "(.+)"; /* group 4: date */
+
+ Pattern p = Pattern.compile(PROC_VERSION_REGEX);
+ Matcher m = p.matcher(procVersionStr);
+
+ if (!m.matches()) {
+ Log.e(TAG, "Regex did not match on /proc/version: " + procVersionStr);
+ return "Unavailable";
+ } else if (m.groupCount() < 4) {
+ Log.e(TAG, "Regex match on /proc/version only returned " + m.groupCount()
+ + " groups");
+ return "Unavailable";
+ } else {
+ return (new StringBuilder(m.group(1)).append("\n").append(
+ m.group(2)).append(" ").append(m.group(3)).append("\n")
+ .append(m.group(4))).toString();
+ }
+ } catch (IOException e) {
+ Log.e(TAG,
+ "IO Exception when getting kernel version for Device Info screen",
+ e);
+
+ return "Unavailable";
+ }
+ }
+
+}
diff --git a/src/com/android/settings/Display.java b/src/com/android/settings/Display.java
new file mode 100644
index 00000000000..f90e0f05497
--- /dev/null
+++ b/src/com/android/settings/Display.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2006 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.app.ActivityManagerNative;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+
+public class Display extends Activity implements View.OnClickListener {
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.display);
+
+ mFontSize = (Spinner) findViewById(R.id.fontSize);
+ mFontSize.setOnItemSelectedListener(mFontSizeChanged);
+ String[] states = new String[3];
+ Resources r = getResources();
+ states[0] = r.getString(R.string.small_font);
+ states[1] = r.getString(R.string.medium_font);
+ states[2] = r.getString(R.string.large_font);
+ ArrayAdapter adapter = new ArrayAdapter(this,
+ android.R.layout.simple_spinner_item, states);
+ adapter.setDropDownViewResource(
+ android.R.layout.simple_spinner_dropdown_item);
+ mFontSize.setAdapter(adapter);
+
+ mPreview = (TextView) findViewById(R.id.preview);
+ mPreview.setText(r.getText(R.string.font_size_preview_text));
+
+ Button save = (Button) findViewById(R.id.save);
+ save.setText(r.getText(R.string.font_size_save));
+ save.setOnClickListener(this);
+
+ mTextSizeTyped = new TypedValue();
+ TypedArray styledAttributes =
+ obtainStyledAttributes(android.R.styleable.TextView);
+ styledAttributes.getValue(android.R.styleable.TextView_textSize,
+ mTextSizeTyped);
+
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ mDisplayMetrics = new DisplayMetrics();
+ mDisplayMetrics.density = metrics.density;
+ mDisplayMetrics.heightPixels = metrics.heightPixels;
+ mDisplayMetrics.scaledDensity = metrics.scaledDensity;
+ mDisplayMetrics.widthPixels = metrics.widthPixels;
+ mDisplayMetrics.xdpi = metrics.xdpi;
+ mDisplayMetrics.ydpi = metrics.ydpi;
+
+ styledAttributes.recycle();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ try {
+ mCurConfig.updateFrom(
+ ActivityManagerNative.getDefault().getConfiguration());
+ } catch (RemoteException e) {
+ }
+ if (mCurConfig.fontScale < 1) {
+ mFontSize.setSelection(0);
+ } else if (mCurConfig.fontScale > 1) {
+ mFontSize.setSelection(2);
+ } else {
+ mFontSize.setSelection(1);
+ }
+ updateFontScale();
+ }
+
+ private void updateFontScale() {
+ mDisplayMetrics.scaledDensity = mDisplayMetrics.density *
+ mCurConfig.fontScale;
+
+ float size = mTextSizeTyped.getDimension(mDisplayMetrics);
+ mPreview.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+ }
+
+ public void onClick(View v) {
+ try {
+ ActivityManagerNative.getDefault().updateConfiguration(mCurConfig);
+ } catch (RemoteException e) {
+ }
+ finish();
+ }
+
+ private Spinner.OnItemSelectedListener mFontSizeChanged
+ = new Spinner.OnItemSelectedListener() {
+ public void onItemSelected(android.widget.AdapterView av, View v,
+ int position, long id) {
+ if (position == 0) {
+ mCurConfig.fontScale = .75f;
+ } else if (position == 2) {
+ mCurConfig.fontScale = 1.25f;
+ } else {
+ mCurConfig.fontScale = 1.0f;
+ }
+
+ updateFontScale();
+ }
+
+ public void onNothingSelected(android.widget.AdapterView av) {
+ }
+ };
+
+ private Spinner mFontSize;
+ private TextView mPreview;
+ private TypedValue mTextSizeTyped;
+ private DisplayMetrics mDisplayMetrics;
+ private Configuration mCurConfig = new Configuration();
+}
diff --git a/src/com/android/settings/EditPinPreference.java b/src/com/android/settings/EditPinPreference.java
new file mode 100644
index 00000000000..ee3143c3942
--- /dev/null
+++ b/src/com/android/settings/EditPinPreference.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 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.Context;
+import android.preference.EditTextPreference;
+import android.text.method.DigitsKeyListener;
+import android.text.method.PasswordTransformationMethod;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.EditText;
+
+import java.util.Map;
+
+/**
+ * TODO: Add a soft dialpad for PIN entry.
+ */
+class EditPinPreference extends EditTextPreference {
+
+ private boolean mDialogOpen;
+
+ interface OnPinEnteredListener {
+ void onPinEntered(EditPinPreference preference, boolean positiveResult);
+ }
+
+ private OnPinEnteredListener mPinListener;
+
+ public EditPinPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public EditPinPreference(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setOnPinEnteredListener(OnPinEnteredListener listener) {
+ mPinListener = listener;
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ final EditText editText = (EditText) view.findViewById(android.R.id.edit);
+
+ if (editText != null) {
+ editText.setSingleLine(true);
+ editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ editText.setKeyListener(DigitsKeyListener.getInstance());
+ }
+ }
+
+ public boolean isDialogOpen() {
+ return mDialogOpen;
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ mDialogOpen = false;
+ if (mPinListener != null) {
+ mPinListener.onPinEntered(this, positiveResult);
+ }
+ }
+
+ public void showPinDialog() {
+ mDialogOpen = true;
+ showDialog(null);
+ }
+}
diff --git a/src/com/android/settings/GadgetPickActivity.java b/src/com/android/settings/GadgetPickActivity.java
new file mode 100644
index 00000000000..840a6a55fcb
--- /dev/null
+++ b/src/com/android/settings/GadgetPickActivity.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 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.LauncherActivity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.gadget.GadgetProviderInfo;
+import android.gadget.GadgetManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListView;
+import android.util.Log;
+
+import java.text.Collator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+public class GadgetPickActivity extends LauncherActivity
+{
+ private static final String TAG = "GadgetPickActivity";
+
+ GadgetManager mGadgetManager;
+ int mGadgetId;
+
+ public GadgetPickActivity() {
+ mGadgetManager = GadgetManager.getInstance(this);
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ Bundle extras = getIntent().getExtras();
+ mGadgetId = extras.getInt(GadgetManager.EXTRA_GADGET_ID);
+
+ setResultData(RESULT_CANCELED);
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id)
+ {
+ Intent intent = intentForPosition(position);
+ int result;
+ try {
+ mGadgetManager.bindGadgetId(mGadgetId, intent.getComponent());
+ result = RESULT_OK;
+ } catch (IllegalArgumentException e) {
+ // This is thrown if they're already bound, or otherwise somehow
+ // bogus. Set the result to canceled, and exit. The app *should*
+ // clean up at this point. We could pass the error along, but
+ // it's not clear that that's useful -- the gadget will simply not
+ // appear.
+ result = RESULT_CANCELED;
+ }
+ setResultData(result);
+ finish();
+ }
+
+ @Override
+ public List makeListItems() {
+ List installed = mGadgetManager.getInstalledProviders();
+ PackageManager pm = getPackageManager();
+
+ Drawable defaultIcon = null;
+ IconResizer resizer = new IconResizer();
+
+ ArrayList result = new ArrayList();
+ final int N = installed.size();
+ for (int i=0; i() {
+ Collator mCollator = Collator.getInstance();
+ public int compare(ListItem lhs, ListItem rhs) {
+ return mCollator.compare(lhs.label, rhs.label);
+ }
+ });
+ return result;
+ }
+
+ void setResultData(int code) {
+ Intent result = new Intent();
+ result.putExtra(GadgetManager.EXTRA_GADGET_ID, mGadgetId);
+ setResult(code, result);
+ }
+}
+
diff --git a/src/com/android/settings/InputMethodsSettings.java b/src/com/android/settings/InputMethodsSettings.java
new file mode 100644
index 00000000000..51b770d0942
--- /dev/null
+++ b/src/com/android/settings/InputMethodsSettings.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2008 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.HashSet;
+import java.util.List;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.preference.CheckBoxPreference;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+/*
+ * Displays preferences for input methods.
+ */
+public class InputMethodsSettings extends PreferenceActivity {
+ private List mInputMethodProperties;
+
+ final TextUtils.SimpleStringSplitter mStringColonSplitter
+ = new TextUtils.SimpleStringSplitter(':');
+
+ private String mLastInputMethodId;
+ private String mLastTickedInputMethodId;
+
+ static public String getInputMethodIdFromKey(String key) {
+ return key;
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.input_methods_prefs);
+
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ mInputMethodProperties = imm.getInputMethodList();
+
+ mLastInputMethodId = Settings.Secure.getString(getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD);
+
+ int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties
+ .size());
+ for (int i = 0; i < N; ++i) {
+ InputMethodInfo property = mInputMethodProperties.get(i);
+ String prefKey = property.getId();
+
+ CharSequence label = property.loadLabel(getPackageManager());
+
+ // Add a check box.
+ CheckBoxPreference chkbxPref = new CheckBoxPreference(this);
+ chkbxPref.setKey(prefKey);
+ chkbxPref.setTitle(label);
+ getPreferenceScreen().addPreference(chkbxPref);
+
+ // If setting activity is available, add a setting screen entry.
+ if (null != property.getSettingsActivity()) {
+ PreferenceScreen prefScreen = new PreferenceScreen(this, null);
+ prefScreen.setKey(property.getSettingsActivity());
+ prefScreen.setTitle(getResources().getString(
+ R.string.input_methods_settings_label_format, label));
+ getPreferenceScreen().addPreference(prefScreen);
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ final HashSet enabled = new HashSet();
+ String enabledStr = Settings.Secure.getString(getContentResolver(),
+ Settings.Secure.ENABLED_INPUT_METHODS);
+ if (enabledStr != null) {
+ final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+ splitter.setString(enabledStr);
+ while (splitter.hasNext()) {
+ enabled.add(splitter.next());
+ }
+ }
+
+ // Update the statuses of the Check Boxes.
+ int N = mInputMethodProperties.size();
+ for (int i = 0; i < N; ++i) {
+ final String id = mInputMethodProperties.get(i).getId();
+ CheckBoxPreference pref = (CheckBoxPreference) findPreference(mInputMethodProperties
+ .get(i).getId());
+ pref.setChecked(enabled.contains(id));
+ }
+ mLastTickedInputMethodId = null;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ StringBuilder builder = new StringBuilder(256);
+
+ boolean haveLastInputMethod = false;
+
+ int firstEnabled = -1;
+ int N = mInputMethodProperties.size();
+ for (int i = 0; i < N; ++i) {
+ final String id = mInputMethodProperties.get(i).getId();
+ CheckBoxPreference pref = (CheckBoxPreference) findPreference(id);
+ boolean hasIt = id.equals(mLastInputMethodId);
+ if (pref.isChecked()) {
+ if (builder.length() > 0) builder.append(':');
+ builder.append(id);
+ if (firstEnabled < 0) {
+ firstEnabled = i;
+ }
+ if (hasIt) haveLastInputMethod = true;
+ } else if (hasIt) {
+ mLastInputMethodId = mLastTickedInputMethodId;
+ }
+ }
+
+ // If the last input method is unset, set it as the first enabled one.
+ if (null == mLastInputMethodId || "".equals(mLastInputMethodId)) {
+ if (firstEnabled >= 0) {
+ mLastInputMethodId = mInputMethodProperties.get(firstEnabled).getId();
+ } else {
+ mLastInputMethodId = null;
+ }
+ }
+
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD, mLastInputMethodId);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+ Preference preference) {
+
+ // Those monkeys kept committing suicide, so we add this property
+ // to disable this functionality
+ if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
+ return false;
+ }
+
+ if (preference instanceof CheckBoxPreference) {
+ CheckBoxPreference chkPref = (CheckBoxPreference) preference;
+ String id = getInputMethodIdFromKey(chkPref.getKey());
+ if (chkPref.isChecked()) {
+ mLastTickedInputMethodId = id;
+ } else if (id.equals(mLastTickedInputMethodId)) {
+ mLastTickedInputMethodId = null;
+ }
+ } else if (preference instanceof PreferenceScreen) {
+ if (preference.getIntent() == null) {
+ PreferenceScreen pref = (PreferenceScreen) preference;
+ String activityName = pref.getKey();
+ String packageName = activityName.substring(0, activityName
+ .lastIndexOf("."));
+ if (activityName.length() > 0) {
+ Intent i = new Intent(Intent.ACTION_MAIN);
+ i.setClassName(packageName, activityName);
+ startActivity(i);
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/android/settings/InstalledAppDetails.java b/src/com/android/settings/InstalledAppDetails.java
new file mode 100644
index 00000000000..327874b6493
--- /dev/null
+++ b/src/com/android/settings/InstalledAppDetails.java
@@ -0,0 +1,478 @@
+
+
+/**
+ * Copyright (C) 2007 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.settings.R;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.format.Formatter;
+import android.util.Config;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+import android.content.ComponentName;
+import android.view.View;
+import android.widget.AppSecurityPermissions;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Activity to display application information from Settings. This activity presents
+ * extended information associated with a package like code, data, total size, permissions
+ * used by the application and also the set of default launchable activities.
+ * For system applications, an option to clear user data is displayed only if data size is > 0.
+ * System applications that do not want clear user data do not have this option.
+ * For non-system applications, there is no option to clear data. Instead there is an option to
+ * uninstall the application.
+ */
+public class InstalledAppDetails extends Activity implements View.OnClickListener, DialogInterface.OnClickListener {
+ private static final String TAG="InstalledAppDetails";
+ private static final int _UNKNOWN_APP=R.string.unknown;
+ private ApplicationInfo mAppInfo;
+ private Button mAppButton;
+ private Button mActivitiesButton;
+ private boolean mCanUninstall;
+ private boolean localLOGV=Config.LOGV || false;
+ private TextView mAppSnippetSize;
+ private TextView mTotalSize;
+ private TextView mAppSize;
+ private TextView mDataSize;
+ private PkgSizeObserver mSizeObserver;
+ private ClearUserDataObserver mClearDataObserver;
+ // Views related to cache info
+ private View mCachePanel;
+ private TextView mCacheSize;
+ private Button mClearCacheButton;
+ private ClearCacheObserver mClearCacheObserver;
+ private Button mForceStopButton;
+
+ PackageStats mSizeInfo;
+ private Button mManageSpaceButton;
+ private PackageManager mPm;
+
+ //internal constants used in Handler
+ private static final int OP_SUCCESSFUL = 1;
+ private static final int OP_FAILED = 2;
+ private static final int CLEAR_USER_DATA = 1;
+ private static final int GET_PKG_SIZE = 2;
+ private static final int CLEAR_CACHE = 3;
+ private static final String ATTR_PACKAGE_STATS="PackageStats";
+
+ // invalid size value used initially and also when size retrieval through PackageManager
+ // fails for whatever reason
+ private static final int SIZE_INVALID = -1;
+
+ // Resource strings
+ private CharSequence mInvalidSizeStr;
+ private CharSequence mComputingStr;
+ private CharSequence mAppButtonText;
+
+ // Possible btn states
+ private enum AppButtonStates {
+ CLEAR_DATA,
+ UNINSTALL,
+ NONE
+ }
+ private AppButtonStates mAppButtonState;
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case CLEAR_USER_DATA:
+ processClearMsg(msg);
+ break;
+ case GET_PKG_SIZE:
+ refreshSizeInfo(msg);
+ break;
+ case CLEAR_CACHE:
+ // Refresh size info
+ mPm.getPackageSizeInfo(mAppInfo.packageName, mSizeObserver);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ private boolean isUninstallable() {
+ if (((mAppInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) &&
+ ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0)) {
+ return false;
+ }
+ return true;
+ }
+
+ class ClearUserDataObserver extends IPackageDataObserver.Stub {
+ public void onRemoveCompleted(final String packageName, final boolean succeeded) {
+ final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
+ msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ class PkgSizeObserver extends IPackageStatsObserver.Stub {
+ public int idx;
+ public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
+ Message msg = mHandler.obtainMessage(GET_PKG_SIZE);
+ Bundle data = new Bundle();
+ data.putParcelable(ATTR_PACKAGE_STATS, pStats);
+ msg.setData(data);
+ mHandler.sendMessage(msg);
+
+ }
+ }
+
+ class ClearCacheObserver extends IPackageDataObserver.Stub {
+ public void onRemoveCompleted(final String packageName, final boolean succeeded) {
+ final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
+ msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private String getSizeStr(long size) {
+ if (size == SIZE_INVALID) {
+ return mInvalidSizeStr.toString();
+ }
+ return Formatter.formatFileSize(this, size);
+ }
+
+ private void setAppBtnState() {
+ boolean visible = false;
+ if(mCanUninstall) {
+ //app can clear user data
+ if((mAppInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
+ == ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) {
+ mAppButtonText = getText(R.string.clear_user_data_text);
+ mAppButtonState = AppButtonStates.CLEAR_DATA;
+ visible = true;
+ } else {
+ //hide button if diableClearUserData is set
+ visible = false;
+ mAppButtonState = AppButtonStates.NONE;
+ }
+ } else {
+ visible = true;
+ mAppButtonState = AppButtonStates.UNINSTALL;
+ mAppButtonText = getText(R.string.uninstall_text);
+ }
+ if(visible) {
+ mAppButton.setText(mAppButtonText);
+ mAppButton.setVisibility(View.VISIBLE);
+ } else {
+ mAppButton.setVisibility(View.GONE);
+ }
+ }
+
+ /** Called when the activity is first created. */
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ //get package manager
+ mPm = getPackageManager();
+ //get application's name from intent
+ Intent intent = getIntent();
+ final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME);
+ mComputingStr = getText(R.string.computing_size);
+ // Try retrieving package stats again
+ CharSequence totalSizeStr, appSizeStr, dataSizeStr;
+ totalSizeStr = appSizeStr = dataSizeStr = mComputingStr;
+ if(localLOGV) Log.i(TAG, "Have to compute package sizes");
+ mSizeObserver = new PkgSizeObserver();
+ mPm.getPackageSizeInfo(packageName, mSizeObserver);
+
+ try {
+ mAppInfo = mPm.getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Exception when retrieving package:"+packageName, e);
+ displayErrorDialog(R.string.app_not_found_dlg_text, true, true);
+ }
+ setContentView(R.layout.installed_app_details);
+ ((ImageView)findViewById(R.id.app_icon)).setImageDrawable(mAppInfo.loadIcon(mPm));
+ //set application name TODO version
+ CharSequence appName = mAppInfo.loadLabel(mPm);
+ if(appName == null) {
+ appName = getString(_UNKNOWN_APP);
+ }
+ ((TextView)findViewById(R.id.app_name)).setText(appName);
+ mAppSnippetSize = ((TextView)findViewById(R.id.app_size));
+ mAppSnippetSize.setText(totalSizeStr);
+ //TODO download str and download url
+ //set values on views
+ mTotalSize = (TextView)findViewById(R.id.total_size_text);
+ mTotalSize.setText(totalSizeStr);
+ mAppSize = (TextView)findViewById(R.id.application_size_text);
+ mAppSize.setText(appSizeStr);
+ mDataSize = (TextView)findViewById(R.id.data_size_text);
+ mDataSize.setText(dataSizeStr);
+
+ mAppButton = ((Button)findViewById(R.id.uninstall_button));
+ //determine if app is a system app
+ mCanUninstall = !isUninstallable();
+ if(localLOGV) Log.i(TAG, "Is systemPackage "+mCanUninstall);
+ setAppBtnState();
+ mManageSpaceButton = (Button)findViewById(R.id.manage_space_button);
+ if(mAppInfo.manageSpaceActivityName != null) {
+ mManageSpaceButton.setVisibility(View.VISIBLE);
+ mManageSpaceButton.setOnClickListener(this);
+ }
+
+ // Cache section
+ mCachePanel = findViewById(R.id.cache_panel);
+ mCacheSize = (TextView) findViewById(R.id.cache_size_text);
+ mCacheSize.setText(mComputingStr);
+ mClearCacheButton = (Button) findViewById(R.id.clear_cache_button);
+ mForceStopButton = (Button) findViewById(R.id.force_stop_button);
+ mForceStopButton.setOnClickListener(this);
+
+ //clear activities
+ mActivitiesButton = (Button)findViewById(R.id.clear_activities_button);
+ List prefActList = new ArrayList();
+ //intent list cannot be null. so pass empty list
+ List intentList = new ArrayList();
+ mPm.getPreferredActivities(intentList, prefActList, packageName);
+ if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list");
+ TextView autoLaunchView = (TextView)findViewById(R.id.auto_launch);
+ if(prefActList.size() <= 0) {
+ //disable clear activities button
+ autoLaunchView.setText(R.string.auto_launch_disable_text);
+ mActivitiesButton.setEnabled(false);
+ } else {
+ autoLaunchView.setText(R.string.auto_launch_enable_text);
+ mActivitiesButton.setOnClickListener(this);
+ }
+
+ // security permissions section
+ LinearLayout permsView = (LinearLayout) findViewById(R.id.permissions_section);
+ AppSecurityPermissions asp = new AppSecurityPermissions(this, packageName);
+ if(asp.getPermissionCount() > 0) {
+ permsView.setVisibility(View.VISIBLE);
+ // Make the security sections header visible
+ LinearLayout securityList = (LinearLayout) permsView.findViewById(
+ R.id.security_settings_list);
+ securityList.addView(asp.getPermissionsView());
+ } else {
+ permsView.setVisibility(View.GONE);
+ }
+ }
+
+ private void displayErrorDialog(int msgId, final boolean finish, final boolean changed) {
+ //display confirmation dialog
+ new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.app_not_found_dlg_title))
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(getString(msgId))
+ .setNeutralButton(getString(R.string.dlg_ok),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ //force to recompute changed value
+ setIntentAndFinish(finish, changed);
+ }
+ }
+ )
+ .show();
+ }
+
+ private void setIntentAndFinish(boolean finish, boolean appChanged) {
+ if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
+ Intent intent = new Intent();
+ intent.putExtra(ManageApplications.APP_CHG, appChanged);
+ setResult(ManageApplications.RESULT_OK, intent);
+ mAppButton.setEnabled(false);
+ if(finish) {
+ finish();
+ }
+ }
+
+ /*
+ * Private method to handle get size info notification from observer when
+ * the async operation from PackageManager is complete. The current user data
+ * info has to be refreshed in the manage applications screen as well as the current screen.
+ */
+ private void refreshSizeInfo(Message msg) {
+ boolean changed = false;
+ PackageStats newPs = msg.getData().getParcelable(ATTR_PACKAGE_STATS);
+ long newTot = newPs.cacheSize+newPs.codeSize+newPs.dataSize;
+ if(mSizeInfo == null) {
+ mSizeInfo = newPs;
+ String str = getSizeStr(newTot);
+ mTotalSize.setText(str);
+ mAppSnippetSize.setText(str);
+ mAppSize.setText(getSizeStr(newPs.codeSize));
+ mDataSize.setText(getSizeStr(newPs.dataSize));
+ mCacheSize.setText(getSizeStr(newPs.cacheSize));
+ } else {
+ long oldTot = mSizeInfo.cacheSize+mSizeInfo.codeSize+mSizeInfo.dataSize;
+ if(newTot != oldTot) {
+ String str = getSizeStr(newTot);
+ mTotalSize.setText(str);
+ mAppSnippetSize.setText(str);
+ changed = true;
+ }
+ if(newPs.codeSize != mSizeInfo.codeSize) {
+ mAppSize.setText(getSizeStr(newPs.codeSize));
+ changed = true;
+ }
+ if(newPs.dataSize != mSizeInfo.dataSize) {
+ mDataSize.setText(getSizeStr(newPs.dataSize));
+ changed = true;
+ }
+ if(newPs.cacheSize != mSizeInfo.cacheSize) {
+ mCacheSize.setText(getSizeStr(newPs.cacheSize));
+ changed = true;
+ }
+ if(changed) {
+ mSizeInfo = newPs;
+ }
+ }
+
+ long data = mSizeInfo.dataSize;
+ // Disable button if data is 0
+ if(mAppButtonState != AppButtonStates.NONE){
+ mAppButton.setText(mAppButtonText);
+ if((mAppButtonState == AppButtonStates.CLEAR_DATA) && (data == 0)) {
+ mAppButton.setEnabled(false);
+ } else {
+ mAppButton.setEnabled(true);
+ mAppButton.setOnClickListener(this);
+ }
+ }
+ refreshCacheInfo(newPs.cacheSize);
+ }
+
+ private void refreshCacheInfo(long cacheSize) {
+ // Set cache info
+ mCacheSize.setText(getSizeStr(cacheSize));
+ if (cacheSize <= 0) {
+ mClearCacheButton.setEnabled(false);
+ } else {
+ mClearCacheButton.setOnClickListener(this);
+ }
+ }
+
+ /*
+ * Private method to handle clear message notification from observer when
+ * the async operation from PackageManager is complete
+ */
+ private void processClearMsg(Message msg) {
+ int result = msg.arg1;
+ String packageName = mAppInfo.packageName;
+ if(result == OP_SUCCESSFUL) {
+ Log.i(TAG, "Cleared user data for system package:"+packageName);
+ mPm.getPackageSizeInfo(packageName, mSizeObserver);
+ } else {
+ mAppButton.setText(R.string.clear_user_data_text);
+ mAppButton.setEnabled(true);
+ }
+ }
+
+ /*
+ * Private method to initiate clearing user data when the user clicks the clear data
+ * button for a system package
+ */
+ private void initiateClearUserDataForSysPkg() {
+ mAppButton.setEnabled(false);
+ //invoke uninstall or clear user data based on sysPackage
+ String packageName = mAppInfo.packageName;
+ Log.i(TAG, "Clearing user data for system package");
+ if(mClearDataObserver == null) {
+ mClearDataObserver = new ClearUserDataObserver();
+ }
+ ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
+ if(!res) {
+ //doesnt initiate clear. some error. should not happen but just log error for now
+ Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
+ displayErrorDialog(R.string.clear_data_failed, false, false);
+ } else {
+ mAppButton.setText(R.string.recompute_size);
+ }
+ }
+
+ /*
+ * Method implementing functionality of buttons clicked
+ * @see android.view.View.OnClickListener#onClick(android.view.View)
+ */
+ public void onClick(View v) {
+ String packageName = mAppInfo.packageName;
+ if(v == mAppButton) {
+ if(mCanUninstall) {
+ //display confirmation dialog
+ new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.clear_data_dlg_title))
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(getString(R.string.clear_data_dlg_text))
+ .setPositiveButton(R.string.dlg_ok, this)
+ .setNegativeButton(R.string.dlg_cancel, this)
+ .show();
+ } else {
+ //create new intent to launch Uninstaller activity
+ Uri packageURI = Uri.parse("package:"+packageName);
+ Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
+ startActivity(uninstallIntent);
+ setIntentAndFinish(true, true);
+ }
+ } else if(v == mActivitiesButton) {
+ mPm.clearPackagePreferredActivities(packageName);
+ mActivitiesButton.setEnabled(false);
+ } else if(v == mManageSpaceButton) {
+ Intent intent = new Intent(Intent.ACTION_DEFAULT);
+ intent.setClassName(mAppInfo.packageName, mAppInfo.manageSpaceActivityName);
+ startActivityForResult(intent, -1);
+ } else if (v == mClearCacheButton) {
+ // Lazy initialization of observer
+ if (mClearCacheObserver == null) {
+ mClearCacheObserver = new ClearCacheObserver();
+ }
+ mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
+ } else if (v == mForceStopButton) {
+ ActivityManager am = (ActivityManager)getSystemService(
+ Context.ACTIVITY_SERVICE);
+ am.restartPackage(packageName);
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if(which == AlertDialog.BUTTON_POSITIVE) {
+ //invoke uninstall or clear user data based on sysPackage
+ initiateClearUserDataForSysPkg();
+ } else {
+ //cancel do nothing just retain existing screen
+ }
+ }
+}
+
diff --git a/src/com/android/settings/LanguageSettings.java b/src/com/android/settings/LanguageSettings.java
new file mode 100644
index 00000000000..8463d26fcac
--- /dev/null
+++ b/src/com/android/settings/LanguageSettings.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2008 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.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.provider.Settings.System;
+import android.text.TextUtils;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.HashSet;
+import java.util.List;
+
+public class LanguageSettings extends PreferenceActivity {
+
+ private final String[] mSettingsUiKey = {
+ "auto_caps",
+ "auto_replace",
+ "auto_punctuate",
+ };
+
+ // Note: Order of this array should correspond to the order of the above array
+ private final String[] mSettingsSystemId = {
+ System.TEXT_AUTO_CAPS,
+ System.TEXT_AUTO_REPLACE,
+ System.TEXT_AUTO_PUNCTUATE,
+ };
+
+ // Note: Order of this array should correspond to the order of the above array
+ private final int[] mSettingsDefault = {
+ 1,
+ 1,
+ 1,
+ };
+
+ private List mInputMethodProperties;
+
+ final TextUtils.SimpleStringSplitter mStringColonSplitter
+ = new TextUtils.SimpleStringSplitter(':');
+
+ private String mLastInputMethodId;
+ private String mLastTickedInputMethodId;
+
+ static public String getInputMethodIdFromKey(String key) {
+ return key;
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.language_settings);
+
+ if (getAssets().getLocales().length == 1) {
+ getPreferenceScreen().
+ removePreference(findPreference("language_category"));
+ }
+
+ Configuration config = getResources().getConfiguration();
+ if (config.keyboard != Configuration.KEYBOARD_QWERTY) {
+ getPreferenceScreen().removePreference(
+ getPreferenceScreen().findPreference("hardkeyboard_category"));
+ } else {
+ ContentResolver resolver = getContentResolver();
+ for (int i = 0; i < mSettingsUiKey.length; i++) {
+ CheckBoxPreference pref = (CheckBoxPreference) findPreference(mSettingsUiKey[i]);
+ pref.setChecked(System.getInt(resolver, mSettingsSystemId[i],
+ mSettingsDefault[i]) > 0);
+ }
+ }
+
+ onCreateIMM();
+ }
+
+ private void onCreateIMM() {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ mInputMethodProperties = imm.getInputMethodList();
+
+ mLastInputMethodId = Settings.Secure.getString(getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD);
+
+ PreferenceGroup textCategory = (PreferenceGroup) findPreference("text_category");
+
+ int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties
+ .size());
+ for (int i = 0; i < N; ++i) {
+ InputMethodInfo property = mInputMethodProperties.get(i);
+ String prefKey = property.getId();
+
+ CharSequence label = property.loadLabel(getPackageManager());
+
+ // Add a check box.
+ CheckBoxPreference chkbxPref = new CheckBoxPreference(this);
+ chkbxPref.setKey(prefKey);
+ chkbxPref.setTitle(label);
+ textCategory.addPreference(chkbxPref);
+
+ // If setting activity is available, add a setting screen entry.
+ if (null != property.getSettingsActivity()) {
+ PreferenceScreen prefScreen = new PreferenceScreen(this, null);
+ prefScreen.setKey(property.getSettingsActivity());
+ CharSequence settingsLabel = getResources().getString(
+ R.string.input_methods_settings_label_format, label);
+ prefScreen.setTitle(settingsLabel);
+ textCategory.addPreference(prefScreen);
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ final HashSet enabled = new HashSet();
+ String enabledStr = Settings.Secure.getString(getContentResolver(),
+ Settings.Secure.ENABLED_INPUT_METHODS);
+ if (enabledStr != null) {
+ final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+ splitter.setString(enabledStr);
+ while (splitter.hasNext()) {
+ enabled.add(splitter.next());
+ }
+ }
+
+ // Update the statuses of the Check Boxes.
+ int N = mInputMethodProperties.size();
+ for (int i = 0; i < N; ++i) {
+ final String id = mInputMethodProperties.get(i).getId();
+ CheckBoxPreference pref = (CheckBoxPreference) findPreference(mInputMethodProperties
+ .get(i).getId());
+ pref.setChecked(enabled.contains(id));
+ }
+ mLastTickedInputMethodId = null;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ StringBuilder builder = new StringBuilder(256);
+
+ boolean haveLastInputMethod = false;
+
+ int firstEnabled = -1;
+ int N = mInputMethodProperties.size();
+ for (int i = 0; i < N; ++i) {
+ final String id = mInputMethodProperties.get(i).getId();
+ CheckBoxPreference pref = (CheckBoxPreference) findPreference(id);
+ boolean hasIt = id.equals(mLastInputMethodId);
+ if (pref.isChecked()) {
+ if (builder.length() > 0) builder.append(':');
+ builder.append(id);
+ if (firstEnabled < 0) {
+ firstEnabled = i;
+ }
+ if (hasIt) haveLastInputMethod = true;
+ } else if (hasIt) {
+ mLastInputMethodId = mLastTickedInputMethodId;
+ }
+ }
+
+ // If the last input method is unset, set it as the first enabled one.
+ if (null == mLastInputMethodId || "".equals(mLastInputMethodId)) {
+ if (firstEnabled >= 0) {
+ mLastInputMethodId = mInputMethodProperties.get(firstEnabled).getId();
+ } else {
+ mLastInputMethodId = null;
+ }
+ }
+
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD, mLastInputMethodId);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+
+ // Physical keyboard stuff
+ for (int i = 0; i < mSettingsUiKey.length; i++) {
+ if (mSettingsUiKey[i].equals(preference.getKey())) {
+ System.putInt(getContentResolver(), mSettingsSystemId[i],
+ ((CheckBoxPreference)preference).isChecked()? 1 : 0);
+ return true;
+ }
+ }
+
+ // Input Method stuff
+ // Those monkeys kept committing suicide, so we add this property
+ // to disable this functionality
+ if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
+ return false;
+ }
+
+ if (preference instanceof CheckBoxPreference) {
+ CheckBoxPreference chkPref = (CheckBoxPreference) preference;
+ String id = getInputMethodIdFromKey(chkPref.getKey());
+ if (chkPref.isChecked()) {
+ mLastTickedInputMethodId = id;
+ } else if (id.equals(mLastTickedInputMethodId)) {
+ mLastTickedInputMethodId = null;
+ }
+ } else if (preference instanceof PreferenceScreen) {
+ if (preference.getIntent() == null) {
+ PreferenceScreen pref = (PreferenceScreen) preference;
+ String activityName = pref.getKey();
+ String packageName = activityName.substring(0, activityName
+ .lastIndexOf("."));
+ if (activityName.length() > 0) {
+ Intent i = new Intent(Intent.ACTION_MAIN);
+ i.setClassName(packageName, activityName);
+ startActivity(i);
+ }
+ }
+ }
+
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
+ }
+
+}
diff --git a/src/com/android/settings/LauncherGadgetBinder.java b/src/com/android/settings/LauncherGadgetBinder.java
new file mode 100644
index 00000000000..f7b5a61bb46
--- /dev/null
+++ b/src/com/android/settings/LauncherGadgetBinder.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2008 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.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.gadget.GadgetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class LauncherGadgetBinder extends Activity {
+ private static final String TAG = "LauncherGadgetBinder";
+ private static final boolean LOGD = true;
+
+ static final String AUTHORITY = "com.android.launcher.settings";
+ static final String TABLE_FAVORITES = "favorites";
+
+ static final String EXTRA_BIND_SOURCES = "com.android.launcher.settings.bindsources";
+ static final String EXTRA_BIND_TARGETS = "com.android.launcher.settings.bindtargets";
+
+ static final String EXTRA_GADGET_BITMAPS = "com.android.camera.gadgetbitmaps";
+
+ /**
+ * {@link ContentProvider} constants pulled over from Launcher
+ */
+ static final class LauncherProvider implements BaseColumns {
+ static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + TABLE_FAVORITES);
+
+ static final String ITEM_TYPE = "itemType";
+ static final String GADGET_ID = "gadgetId";
+ static final String ICON = "icon";
+
+ static final int ITEM_TYPE_GADGET = 4;
+ static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
+ static final int ITEM_TYPE_WIDGET_SEARCH = 1001;
+ static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002;
+ }
+
+ static final String[] BIND_PROJECTION = new String[] {
+ LauncherProvider._ID,
+ LauncherProvider.ITEM_TYPE,
+ LauncherProvider.GADGET_ID,
+ LauncherProvider.ICON,
+ };
+
+ static final int INDEX_ID = 0;
+ static final int INDEX_ITEM_TYPE = 1;
+ static final int INDEX_GADGET_ID = 2;
+ static final int INDEX_ICON = 3;
+
+ static final ComponentName BIND_PHOTO_GADGET = new ComponentName("com.android.camera",
+ "com.android.camera.PhotoGadgetBind");
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ finish();
+
+ // This helper reaches into the Launcher database and binds any unlinked
+ // gadgets. If will remove any items that can't be bound successfully.
+ // We protect this binder at the manifest level by asserting the caller
+ // has the Launcher WRITE_SETTINGS permission.
+
+ final Intent intent = getIntent();
+ final Bundle extras = intent.getExtras();
+
+ int[] bindSources = null;
+ ArrayList bindTargets = null;
+ Exception exception = null;
+
+ try {
+ bindSources = extras.getIntArray(EXTRA_BIND_SOURCES);
+ bindTargets = intent.getParcelableArrayListExtra(EXTRA_BIND_TARGETS);
+ } catch (ClassCastException ex) {
+ exception = ex;
+ }
+
+ if (exception != null || bindSources == null || bindTargets == null ||
+ bindSources.length != bindTargets.size()) {
+ Log.w(TAG, "Problem reading incoming bind request, or invalid request", exception);
+ return;
+ }
+
+ final String selectWhere = buildOrWhereString(LauncherProvider.ITEM_TYPE, bindSources);
+
+ final ContentResolver resolver = getContentResolver();
+ final GadgetManager gadgetManager = GadgetManager.getInstance(this);
+
+ boolean foundPhotoGadgets = false;
+ final ArrayList photoGadgetIds = new ArrayList();
+ final ArrayList photoBitmaps = new ArrayList();
+
+ Cursor c = null;
+
+ try {
+ c = resolver.query(LauncherProvider.CONTENT_URI,
+ BIND_PROJECTION, selectWhere, null, null);
+
+ if (LOGD) Log.d(TAG, "found bind cursor count="+c.getCount());
+
+ final ContentValues values = new ContentValues();
+ while (c != null && c.moveToNext()) {
+ long favoriteId = c.getLong(INDEX_ID);
+ int itemType = c.getInt(INDEX_ITEM_TYPE);
+ int gadgetId = c.getInt(INDEX_GADGET_ID);
+ byte[] iconData = c.getBlob(INDEX_ICON);
+
+ // Find the binding target for this type
+ ComponentName targetGadget = null;
+ for (int i = 0; i < bindSources.length; i++) {
+ if (bindSources[i] == itemType) {
+ targetGadget = bindTargets.get(i);
+ break;
+ }
+ }
+
+ if (LOGD) Log.d(TAG, "found matching targetGadget="+targetGadget.toString()+" for favoriteId="+favoriteId);
+
+ boolean bindSuccess = false;
+ try {
+ gadgetManager.bindGadgetId(gadgetId, targetGadget);
+ bindSuccess = true;
+ } catch (RuntimeException ex) {
+ Log.w(TAG, "Problem binding gadget", ex);
+ }
+
+ // Handle special case of photo gadget by loading bitmap and
+ // preparing for later binding
+ if (bindSuccess && iconData != null &&
+ itemType == LauncherProvider.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
+ Bitmap bitmap = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
+
+ photoGadgetIds.add(gadgetId);
+ photoBitmaps.add(bitmap);
+ foundPhotoGadgets = true;
+ }
+
+ if (LOGD) Log.d(TAG, "after finished, success="+bindSuccess);
+
+ // Depending on success, update launcher or remove item
+ Uri favoritesUri = ContentUris.withAppendedId(LauncherProvider.CONTENT_URI, favoriteId);
+ if (bindSuccess) {
+ values.clear();
+ values.put(LauncherProvider.ITEM_TYPE, LauncherProvider.ITEM_TYPE_GADGET);
+ values.putNull(LauncherProvider.ICON);
+ resolver.update(favoritesUri, values, null, null);
+ } else {
+ resolver.delete(favoritesUri, null, null);
+ }
+
+ }
+ } catch (SQLException ex) {
+ Log.w(TAG, "Problem while binding gadgetIds for Launcher", ex);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ if (foundPhotoGadgets) {
+ // Convert gadgetIds into int[]
+ final int N = photoGadgetIds.size();
+ final int[] photoGadgetIdsArray = new int[N];
+ for (int i = 0; i < N; i++) {
+ photoGadgetIdsArray[i] = photoGadgetIds.get(i);
+ }
+
+ // Launch intent over to handle bitmap binding, but we don't need to
+ // wait around for the result.
+ final Intent bindIntent = new Intent();
+ bindIntent.setComponent(BIND_PHOTO_GADGET);
+
+ final Bundle bindExtras = new Bundle();
+ bindExtras.putIntArray(GadgetManager.EXTRA_GADGET_IDS, photoGadgetIdsArray);
+ bindExtras.putParcelableArrayList(EXTRA_GADGET_BITMAPS, photoBitmaps);
+ bindIntent.putExtras(bindExtras);
+
+ startActivity(bindIntent);
+ }
+
+ if (LOGD) Log.d(TAG, "completely finished with binding for Launcher");
+ }
+
+ /**
+ * Build a query string that will match any row where the column matches
+ * anything in the values list.
+ */
+ static String buildOrWhereString(String column, int[] values) {
+ StringBuilder selectWhere = new StringBuilder();
+ for (int i = values.length - 1; i >= 0; i--) {
+ selectWhere.append(column).append("=").append(values[i]);
+ if (i > 0) {
+ selectWhere.append(" OR ");
+ }
+ }
+ return selectWhere.toString();
+ }
+
+}
diff --git a/src/com/android/settings/LocalePicker.java b/src/com/android/settings/LocalePicker.java
new file mode 100644
index 00000000000..386d7e0f4d2
--- /dev/null
+++ b/src/com/android/settings/LocalePicker.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2007 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.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.ListActivity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class LocalePicker extends ListActivity {
+ private static final String TAG = "LocalePicker";
+
+ Loc[] mLocales;
+
+ private static class Loc {
+ String label;
+ Locale locale;
+
+ public Loc(String label, Locale locale) {
+ this.label = label;
+ this.locale = locale;
+ }
+
+ @Override
+ public String toString() {
+ return this.label;
+ }
+ }
+
+ int getContentView() {
+ return R.layout.locale_picker;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(getContentView());
+
+ String[] locales = getAssets().getLocales();
+ Arrays.sort(locales);
+
+ final int origSize = locales.length;
+ Loc[] preprocess = new Loc[origSize];
+ int finalSize = 0;
+ for (int i = 0 ; i < origSize; i++ ) {
+ String s = locales[i];
+ int len = s.length();
+ if (len == 2) {
+ Locale l = new Locale(s);
+ preprocess[finalSize++] = new Loc(toTitleCase(l.getDisplayLanguage()), l);
+ } else if (len == 5) {
+ String language = s.substring(0, 2);
+ String country = s.substring(3, 5);
+ Locale l = new Locale(language, country);
+
+ if (finalSize == 0) {
+ preprocess[finalSize++] = new Loc(toTitleCase(l.getDisplayLanguage()), l);
+ } else {
+ // check previous entry:
+ // same lang and no country -> overwrite it with a lang-only name
+ // same lang and a country -> upgrade to full name and
+ // insert ours with full name
+ // diff lang -> insert ours with lang-only name
+ if (preprocess[finalSize-1].locale.getLanguage().equals(language)) {
+ String prevCountry = preprocess[finalSize-1].locale.getCountry();
+ if (prevCountry.length() == 0) {
+ preprocess[finalSize-1].locale = l;
+ preprocess[finalSize-1].label = toTitleCase(l.getDisplayLanguage());
+ } else {
+ preprocess[finalSize-1].label = toTitleCase(preprocess[finalSize-1].locale.getDisplayName());
+ preprocess[finalSize++] = new Loc(toTitleCase(l.getDisplayName()), l);
+ }
+ } else {
+ String displayName;
+ if (s.equals("zz_ZZ")) {
+ displayName = "Pseudo...";
+ } else {
+ displayName = toTitleCase(l.getDisplayLanguage());
+ }
+ preprocess[finalSize++] = new Loc(displayName, l);
+ }
+ }
+ }
+ }
+ mLocales = new Loc[finalSize];
+ for (int i = 0; i < finalSize ; i++) {
+ mLocales[i] = preprocess[i];
+ }
+ int layoutId = R.layout.locale_picker_item;
+ int fieldId = R.id.locale;
+ ArrayAdapter adapter = new ArrayAdapter(this, layoutId, fieldId, mLocales);
+ getListView().setAdapter(adapter);
+ }
+
+ private static String toTitleCase(String s) {
+ if (s.length() == 0) {
+ return s;
+ }
+
+ return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ getListView().requestFocus();
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ try {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ Configuration config = am.getConfiguration();
+
+ Loc loc = mLocales[position];
+ config.locale = loc.locale;
+
+ // indicate this isn't some passing default - the user wants this remembered
+ config.userSetLocale = true;
+
+ am.updateConfiguration(config);
+ } catch (RemoteException e) {
+ // Intentionally left blank
+ }
+ finish();
+ }
+}
diff --git a/src/com/android/settings/LocalePickerInSetupWizard.java b/src/com/android/settings/LocalePickerInSetupWizard.java
new file mode 100644
index 00000000000..b160e899b88
--- /dev/null
+++ b/src/com/android/settings/LocalePickerInSetupWizard.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007 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.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.ListActivity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class LocalePickerInSetupWizard extends LocalePicker {
+
+ @Override
+ int getContentView() {
+ return R.layout.locale_picker_in_setupwizard;
+ }
+
+}
diff --git a/src/com/android/settings/ManageApplications.java b/src/com/android/settings/ManageApplications.java
new file mode 100644
index 00000000000..74957ed430f
--- /dev/null
+++ b/src/com/android/settings/ManageApplications.java
@@ -0,0 +1,1307 @@
+/*
+ * Copyright (C) 2006 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.settings.R;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.format.Formatter;
+import android.util.Config;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Activity to pick an application that will be used to display installation information and
+ * options to uninstall/delete user data for system applications. This activity
+ * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
+ * intent.
+ * Initially a compute in progress message is displayed while the application retrieves
+ * the list of application information from the PackageManager. The size information
+ * for each package is refreshed to the screen. The resource(app description and
+ * icon) information for each package is not available yet, so some default values for size
+ * icon and descriptions are used initially. Later the resource information for each
+ * application is retrieved and dynamically updated on the screen.
+ * A Broadcast receiver registers for package additions or deletions when the activity is
+ * in focus. If the user installs or deletes packages when the activity has focus, the receiver
+ * gets notified and proceeds to add/delete these packages from the list on the screen.
+ * This is an unlikely scenario but could happen. The entire list gets created every time
+ * the activity's onStart gets invoked. This is to avoid having the receiver for the entire
+ * life cycle of the application.
+ * The applications can be sorted either alphabetically or
+ * based on size(descending). If this activity gets launched under low memory
+ * situations(A low memory notification dispatches intent
+ * ACTION_MANAGE_PACKAGE_STORAGE) the list is sorted per size.
+ * If the user selects an application, extended info(like size, uninstall/clear data options,
+ * permissions info etc.,) is displayed via the InstalledAppDetails activity.
+ */
+public class ManageApplications extends ListActivity implements
+ OnItemClickListener, DialogInterface.OnCancelListener,
+ DialogInterface.OnClickListener {
+ // TAG for this activity
+ private static final String TAG = "ManageApplications";
+
+ // Log information boolean
+ private boolean localLOGV = Config.LOGV || false;
+
+ // attributes used as keys when passing values to InstalledAppDetails activity
+ public static final String APP_PKG_PREFIX = "com.android.settings.";
+ public static final String APP_PKG_NAME = APP_PKG_PREFIX+"ApplicationPkgName";
+ public static final String APP_CHG = APP_PKG_PREFIX+"changed";
+
+ // attribute name used in receiver for tagging names of added/deleted packages
+ private static final String ATTR_PKG_NAME="PackageName";
+ private static final String ATTR_APP_PKG_STATS="ApplicationPackageStats";
+
+ // constant value that can be used to check return code from sub activity.
+ private static final int INSTALLED_APP_DETAILS = 1;
+
+ // sort order that can be changed through the menu can be sorted alphabetically
+ // or size(descending)
+ private static final int MENU_OPTIONS_BASE = 0;
+ public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 0;
+ public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 1;
+ // Filter options used for displayed list of applications
+ public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 2;
+ public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 3;
+ public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 4;
+ public static final int FILTER_OPTIONS = MENU_OPTIONS_BASE + 5;
+ // Alert Dialog presented to user to find out the filter option
+ AlertDialog mAlertDlg;
+ // sort order
+ private int mSortOrder = SORT_ORDER_ALPHA;
+ // Filter value
+ int mFilterApps = FILTER_APPS_ALL;
+
+ // Custom Adapter used for managing items in the list
+ private AppInfoAdapter mAppInfoAdapter;
+
+ // messages posted to the handler
+ private static final int HANDLER_MESSAGE_BASE = 0;
+ private static final int INIT_PKG_INFO = HANDLER_MESSAGE_BASE+1;
+ private static final int COMPUTE_PKG_SIZE_DONE = HANDLER_MESSAGE_BASE+2;
+ private static final int REMOVE_PKG = HANDLER_MESSAGE_BASE+3;
+ private static final int REORDER_LIST = HANDLER_MESSAGE_BASE+4;
+ private static final int ADD_PKG_START = HANDLER_MESSAGE_BASE+5;
+ private static final int ADD_PKG_DONE = HANDLER_MESSAGE_BASE+6;
+ private static final int REFRESH_ICONS = HANDLER_MESSAGE_BASE+7;
+ private static final int NEXT_LOAD_STEP = HANDLER_MESSAGE_BASE+8;
+
+ // observer object used for computing pkg sizes
+ private PkgSizeObserver mObserver;
+ // local handle to PackageManager
+ private PackageManager mPm;
+ // Broadcast Receiver object that receives notifications for added/deleted
+ // packages
+ private PackageIntentReceiver mReceiver;
+ // atomic variable used to track if computing pkg sizes is in progress. should be volatile?
+
+ private boolean mComputeSizes = false;
+ // default icon thats used when displaying applications initially before resource info is
+ // retrieved
+ private Drawable mDefaultAppIcon;
+
+ // temporary dialog displayed while the application info loads
+ private static final int DLG_BASE = 0;
+ private static final int DLG_LOADING = DLG_BASE + 1;
+
+ // compute index used to track the application size computations
+ private int mComputeIndex;
+
+ // Size resource used for packages whose size computation failed for some reason
+ private CharSequence mInvalidSizeStr;
+ private CharSequence mComputingSizeStr;
+
+ // map used to store list of added and removed packages. Immutable Boolean
+ // variables indicate if a package has been added or removed. If a package is
+ // added or deleted multiple times a single entry with the latest operation will
+ // be recorded in the map.
+ private Map mAddRemoveMap;
+
+ // layout inflater object used to inflate views
+ private LayoutInflater mInflater;
+
+ // invalid size value used initially and also when size retrieval through PackageManager
+ // fails for whatever reason
+ private static final int SIZE_INVALID = -1;
+
+ // debug boolean variable to test delays from PackageManager API's
+ private boolean DEBUG_PKG_DELAY = false;
+
+ // Thread to load resources
+ ResourceLoaderThread mResourceThread;
+
+ String mCurrentPkgName;
+
+ //TODO implement a cache system
+ private Map mAppPropCache;
+
+ // empty message displayed when list is empty
+ private TextView mEmptyView;
+
+ // Boolean variables indicating state
+ private boolean mLoadLabels = false;
+ private boolean mSizesFirst = false;
+ // ListView used to display list
+ private ListView mListView;
+ // State variables used to figure out menu options and also
+ // initiate the first computation and loading of resources
+ private boolean mJustCreated = true;
+ private boolean mFirst = false;
+
+ /*
+ * Handler class to handle messages for various operations
+ * Most of the operations that effect Application related data
+ * are posted as messages to the handler to avoid synchronization
+ * when accessing these structures.
+ * When the size retrieval gets kicked off for the first time, a COMPUTE_PKG_SIZE_START
+ * message is posted to the handler which invokes the getSizeInfo for the pkg at index 0
+ * When the PackageManager's asynchronous call back through
+ * PkgSizeObserver.onGetStatsCompleted gets invoked, the application resources like
+ * label, description, icon etc., is loaded in the same thread and these values are
+ * set on the observer. The observer then posts a COMPUTE_PKG_SIZE_DONE message
+ * to the handler. This information is updated on the AppInfoAdapter associated with
+ * the list view of this activity and size info retrieval is initiated for the next package as
+ * indicated by mComputeIndex
+ * When a package gets added while the activity has focus, the PkgSizeObserver posts
+ * ADD_PKG_START message to the handler. If the computation is not in progress, the size
+ * is retrieved for the newly added package through the observer object and the newly
+ * installed app info is updated on the screen. If the computation is still in progress
+ * the package is added to an internal structure and action deferred till the computation
+ * is done for all the packages.
+ * When a package gets deleted, REMOVE_PKG is posted to the handler
+ * if computation is not in progress(as indicated by
+ * mDoneIniting), the package is deleted from the displayed list of apps. If computation is
+ * still in progress the package is added to an internal structure and action deferred till
+ * the computation is done for all packages.
+ * When the sizes of all packages is computed, the newly
+ * added or removed packages are processed in order.
+ * If the user changes the order in which these applications are viewed by hitting the
+ * menu key, REORDER_LIST message is posted to the handler. this sorts the list
+ * of items based on the sort order.
+ */
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ PackageStats ps;
+ ApplicationInfo info;
+ Bundle data;
+ String pkgName = null;
+ AppInfo appInfo;
+ data = msg.getData();
+ if(data != null) {
+ pkgName = data.getString(ATTR_PKG_NAME);
+ }
+ switch (msg.what) {
+ case INIT_PKG_INFO:
+ if(localLOGV) Log.i(TAG, "Message INIT_PKG_INFO");
+ // Retrieve the package list and init some structures
+ initAppList(mFilterApps);
+ mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
+ break;
+ case COMPUTE_PKG_SIZE_DONE:
+ if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
+ if(pkgName == null) {
+ Log.w(TAG, "Ignoring message");
+ break;
+ }
+ ps = data.getParcelable(ATTR_APP_PKG_STATS);
+ if(ps == null) {
+ Log.i(TAG, "Invalid package stats for package:"+pkgName);
+ } else {
+ int pkgId = mAppInfoAdapter.getIndex(pkgName);
+ if(mComputeIndex != pkgId) {
+ //spurious call from stale observer
+ Log.w(TAG, "Stale call back from PkgSizeObserver");
+ break;
+ }
+ mAppInfoAdapter.updateAppSize(pkgName, ps);
+ }
+ mComputeIndex++;
+ if (mComputeIndex < mAppInfoAdapter.getCount()) {
+ // initiate compute package size for next pkg in list
+ mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
+ mComputeIndex),
+ COMPUTE_PKG_SIZE_DONE);
+ } else {
+ // check for added/removed packages
+ Set keys = mAddRemoveMap.keySet();
+ Iterator iter = keys.iterator();
+ List removeList = new ArrayList();
+ boolean added = false;
+ boolean removed = false;
+ while (iter.hasNext()) {
+ String key = iter.next();
+ if (mAddRemoveMap.get(key) == Boolean.TRUE) {
+ // add
+ try {
+ info = mPm.getApplicationInfo(key, 0);
+ mAppInfoAdapter.addApplicationInfo(info);
+ added = true;
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Invalid added package:"+key+" Ignoring entry");
+ }
+ } else {
+ // remove
+ removeList.add(key);
+ removed = true;
+ }
+ }
+ // remove uninstalled packages from list
+ if (removed) {
+ mAppInfoAdapter.removeFromList(removeList);
+ }
+ // handle newly installed packages
+ if (added) {
+ mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
+ mComputeIndex),
+ COMPUTE_PKG_SIZE_DONE);
+ } else {
+ // end computation here
+ mComputeSizes = true;
+ mFirst = true;
+ mAppInfoAdapter.sortList(mSortOrder);
+ mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
+ }
+ }
+ break;
+ case REMOVE_PKG:
+ if(localLOGV) Log.i(TAG, "Message REMOVE_PKG");
+ if(pkgName == null) {
+ Log.w(TAG, "Ignoring message:REMOVE_PKG for null pkgName");
+ break;
+ }
+ if (!mComputeSizes) {
+ Boolean currB = mAddRemoveMap.get(pkgName);
+ if (currB == null || (currB.equals(Boolean.TRUE))) {
+ mAddRemoveMap.put(pkgName, Boolean.FALSE);
+ }
+ break;
+ }
+ List pkgList = new ArrayList();
+ pkgList.add(pkgName);
+ mAppInfoAdapter.removeFromList(pkgList);
+ break;
+ case REORDER_LIST:
+ if(localLOGV) Log.i(TAG, "Message REORDER_LIST");
+ int menuOption = msg.arg1;
+ if((menuOption == SORT_ORDER_ALPHA) ||
+ (menuOption == SORT_ORDER_SIZE)) {
+ // Option to sort list
+ if (menuOption != mSortOrder) {
+ mSortOrder = menuOption;
+ if (localLOGV) Log.i(TAG, "Changing sort order to "+mSortOrder);
+ mAppInfoAdapter.sortList(mSortOrder);
+ }
+ } else if(menuOption != mFilterApps) {
+ // Option to filter list
+ mFilterApps = menuOption;
+ boolean ret = mAppInfoAdapter.resetAppList(mFilterApps,
+ getInstalledApps(mFilterApps));
+ if(!ret) {
+ // Reset cache
+ mAppPropCache = null;
+ mFilterApps = FILTER_APPS_ALL;
+ mHandler.sendEmptyMessage(INIT_PKG_INFO);
+ sendMessageToHandler(REORDER_LIST, menuOption);
+ }
+ }
+ break;
+ case ADD_PKG_START:
+ if(localLOGV) Log.i(TAG, "Message ADD_PKG_START");
+ if(pkgName == null) {
+ Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
+ break;
+ }
+ if (!mComputeSizes) {
+ Boolean currB = mAddRemoveMap.get(pkgName);
+ if (currB == null || (currB.equals(Boolean.FALSE))) {
+ mAddRemoveMap.put(pkgName, Boolean.TRUE);
+ }
+ break;
+ }
+ try {
+ info = mPm.getApplicationInfo(pkgName, 0);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Couldnt find application info for:"+pkgName);
+ break;
+ }
+ mObserver.invokeGetSizeInfo(info, ADD_PKG_DONE);
+ break;
+ case ADD_PKG_DONE:
+ if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
+ if(pkgName == null) {
+ Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
+ break;
+ }
+ ps = data.getParcelable(ATTR_APP_PKG_STATS);
+ mAppInfoAdapter.addToList(pkgName, ps);
+ break;
+ case REFRESH_ICONS:
+ Map iconMap = (Map) msg.obj;
+ if(iconMap == null) {
+ Log.w(TAG, "Error loading icons for applications");
+ } else {
+ mAppInfoAdapter.updateAppsResourceInfo(iconMap);
+ }
+ mLoadLabels = true;
+ mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
+ break;
+ case NEXT_LOAD_STEP:
+ if (mComputeSizes && mLoadLabels) {
+ doneLoadingData();
+ } else if (!mComputeSizes && !mLoadLabels) {
+ // Either load the package labels or initiate get size info
+ if (mSizesFirst) {
+ initComputeSizes();
+ } else {
+ initResourceThread();
+ }
+ } else {
+ // Create list view from the adapter here. Wait till the sort order
+ // of list is defined. its either by label or by size. so atleast one of the
+ // first steps should be complete before filling the list
+ if (mJustCreated) {
+ // Set the adapter here.
+ mJustCreated = false;
+ mListView.setAdapter(mAppInfoAdapter);
+ dismissLoadingMsg();
+ }
+ if (!mComputeSizes) {
+ initComputeSizes();
+ } else if (!mLoadLabels) {
+ initResourceThread();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+
+
+ private void doneLoadingData() {
+ setProgressBarIndeterminateVisibility(false);
+ }
+
+ List getInstalledApps(int filterOption) {
+ List installedAppList = mPm.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ if (installedAppList == null) {
+ return new ArrayList ();
+ }
+ if (filterOption == FILTER_APPS_THIRD_PARTY) {
+ List appList =new ArrayList ();
+ for (ApplicationInfo appInfo : installedAppList) {
+ boolean flag = false;
+ if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ // Updated system app
+ flag = true;
+ } else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ // Non-system app
+ flag = true;
+ }
+ if (flag) {
+ appList.add(appInfo);
+ }
+ }
+ return appList;
+ } else if (filterOption == FILTER_APPS_RUNNING) {
+ List appList =new ArrayList ();
+ List procList = getRunningAppProcessesList();
+ if ((procList == null) || (procList.size() == 0)) {
+ return appList;
+ }
+ // Retrieve running processes from ActivityManager
+ for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
+ if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
+ int size = appProcInfo.pkgList.length;
+ for (int i = 0; i < size; i++) {
+ ApplicationInfo appInfo = null;
+ try {
+ appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
+ continue;
+ }
+ if(appInfo != null) {
+ appList.add(appInfo);
+ }
+ }
+ }
+ }
+ return appList;
+ } else {
+ return installedAppList;
+ }
+ }
+
+ private List getRunningAppProcessesList() {
+ ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ return am.getRunningAppProcesses();
+ }
+
+ // some initialization code used when kicking off the size computation
+ private void initAppList(int filterOption) {
+ setProgressBarIndeterminateVisibility(true);
+ mComputeIndex = 0;
+ mComputeSizes = false;
+ mLoadLabels = false;
+ // Initialize lists
+ List appList = getInstalledApps(filterOption);
+ mAddRemoveMap = new TreeMap();
+ mAppInfoAdapter.resetAppList(filterOption, appList);
+ }
+
+ // Utility method to start a thread to read application labels and icons
+ private void initResourceThread() {
+ //load resources now
+ if(mResourceThread.isAlive()) {
+ mResourceThread.interrupt();
+ }
+ mResourceThread.loadAllResources(mAppInfoAdapter.getAppList());
+ }
+
+ private void initComputeSizes() {
+ // initiate compute pkg sizes
+ if (localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
+ if (mAppInfoAdapter.getCount() > 0) {
+ mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(0),
+ COMPUTE_PKG_SIZE_DONE);
+ } else {
+ mComputeSizes = true;
+ }
+ }
+
+ private void showEmptyViewIfListEmpty() {
+ if (localLOGV) Log.i(TAG, "Checking for empty view");
+ if (mAppInfoAdapter.getCount() > 0) {
+ mListView.setVisibility(View.VISIBLE);
+ mEmptyView.setVisibility(View.GONE);
+ } else {
+ mListView.setVisibility(View.GONE);
+ mEmptyView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ // internal structure used to track added and deleted packages when
+ // the activity has focus
+ class AddRemoveInfo {
+ String pkgName;
+ boolean add;
+ public AddRemoveInfo(String pPkgName, boolean pAdd) {
+ pkgName = pPkgName;
+ add = pAdd;
+ }
+ }
+
+ class ResourceLoaderThread extends Thread {
+ List mAppList;
+
+ void loadAllResources(List appList) {
+ mAppList = appList;
+ start();
+ }
+
+ public void run() {
+ Map iconMap = new HashMap();
+ if(mAppList == null || mAppList.size() <= 0) {
+ Log.w(TAG, "Empty or null application list");
+ } else {
+ for (ApplicationInfo appInfo : mAppList) {
+ CharSequence appName = appInfo.loadLabel(mPm);
+ Drawable appIcon = appInfo.loadIcon(mPm);
+ iconMap.put(appInfo.packageName,
+ new AppInfo(appInfo.packageName, appName, appIcon));
+ }
+ }
+ Message msg = mHandler.obtainMessage(REFRESH_ICONS);
+ msg.obj = iconMap;
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /* Internal class representing an application or packages displayable attributes
+ *
+ */
+ class AppInfo {
+ public String pkgName;
+ int index;
+ public CharSequence appName;
+ public Drawable appIcon;
+ public CharSequence appSize;
+ public PackageStats appStats;
+
+ public void refreshIcon(AppInfo pInfo) {
+ appName = pInfo.appName;
+ appIcon = pInfo.appIcon;
+ }
+
+ public AppInfo(String pName, CharSequence aName, Drawable aIcon) {
+ index = -1;
+ pkgName = pName;
+ appName = aName;
+ appIcon = aIcon;
+ appStats = null;
+ appSize = mComputingSizeStr;
+ }
+
+ public AppInfo(String pName, int pIndex, CharSequence aName, Drawable aIcon,
+ PackageStats ps) {
+ index = pIndex;
+ pkgName = pName;
+ appName = aName;
+ appIcon = aIcon;
+ if(ps == null) {
+ appSize = mComputingSizeStr;
+ } else {
+ appStats = ps;
+ appSize = getSizeStr();
+ }
+ }
+ public void setSize(PackageStats ps) {
+ appStats = ps;
+ if (ps != null) {
+ appSize = getSizeStr();
+ }
+ }
+ public long getTotalSize() {
+ PackageStats ps = appStats;
+ if (ps != null) {
+ return ps.cacheSize+ps.codeSize+ps.dataSize;
+ }
+ return SIZE_INVALID;
+ }
+
+ private String getSizeStr() {
+ PackageStats ps = appStats;
+ String retStr = "";
+ // insert total size information into map to display in view
+ // at this point its guaranteed that ps is not null. but checking anyway
+ if (ps != null) {
+ long size = getTotalSize();
+ if (size == SIZE_INVALID) {
+ return mInvalidSizeStr.toString();
+ }
+ return Formatter.formatFileSize(ManageApplications.this, size);
+ }
+ return retStr;
+ }
+ }
+
+ // View Holder used when displaying views
+ static class AppViewHolder {
+ TextView appName;
+ ImageView appIcon;
+ TextView appSize;
+ }
+
+ /* Custom adapter implementation for the ListView
+ * This adapter maintains a map for each displayed application and its properties
+ * An index value on each AppInfo object indicates the correct position or index
+ * in the list. If the list gets updated dynamically when the user is viewing the list of
+ * applications, we need to return the correct index of position. This is done by mapping
+ * the getId methods via the package name into the internal maps and indices.
+ * The order of applications in the list is mirrored in mAppLocalList
+ */
+ class AppInfoAdapter extends BaseAdapter {
+ private Map mAppPropMap;
+ private List mAppLocalList;
+ ApplicationInfo.DisplayNameComparator mAlphaComparator;
+ AppInfoComparator mSizeComparator;
+
+ private AppInfo getFromCache(String packageName) {
+ if(mAppPropCache == null) {
+ return null;
+ }
+ return mAppPropCache.get(packageName);
+ }
+
+ public AppInfoAdapter(Context c, List appList) {
+ mAppLocalList = appList;
+ boolean useCache = false;
+ int sortOrder = SORT_ORDER_ALPHA;
+ int imax = mAppLocalList.size();
+ if(mAppPropCache != null) {
+ useCache = true;
+ // Activity has been resumed. can use the cache to populate values initially
+ mAppPropMap = mAppPropCache;
+ sortOrder = mSortOrder;
+ }
+ sortAppList(sortOrder);
+ // Recreate property map
+ mAppPropMap = new TreeMap();
+ for (int i = 0; i < imax; i++) {
+ ApplicationInfo info = mAppLocalList.get(i);
+ AppInfo aInfo = getFromCache(info.packageName);
+ if(aInfo == null){
+ aInfo = new AppInfo(info.packageName, i,
+ info.packageName, mDefaultAppIcon, null);
+ } else {
+ aInfo.index = i;
+ }
+ mAppPropMap.put(info.packageName, aInfo);
+ }
+ }
+
+ public int getCount() {
+ return mAppLocalList.size();
+ }
+
+ public Object getItem(int position) {
+ return mAppLocalList.get(position);
+ }
+
+ /*
+ * This method returns the index of the package position in the application list
+ */
+ public int getIndex(String pkgName) {
+ if(pkgName == null) {
+ Log.w(TAG, "Getting index of null package in List Adapter");
+ }
+ int imax = mAppLocalList.size();
+ ApplicationInfo appInfo;
+ for(int i = 0; i < imax; i++) {
+ appInfo = mAppLocalList.get(i);
+ if(appInfo.packageName.equalsIgnoreCase(pkgName)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public ApplicationInfo getApplicationInfo(int position) {
+ int imax = mAppLocalList.size();
+ if( (position < 0) || (position >= imax)) {
+ Log.w(TAG, "Position out of bounds in List Adapter");
+ return null;
+ }
+ return mAppLocalList.get(position);
+ }
+
+ public void addApplicationInfo(ApplicationInfo info) {
+ if(info == null) {
+ Log.w(TAG, "Ignoring null add in List Adapter");
+ return;
+ }
+ mAppLocalList.add(info);
+ }
+
+ public long getItemId(int position) {
+ int imax = mAppLocalList.size();
+ if( (position < 0) || (position >= imax)) {
+ Log.w(TAG, "Position out of bounds in List Adapter");
+ return -1;
+ }
+ return mAppPropMap.get(mAppLocalList.get(position).packageName).index;
+ }
+
+ public List getAppList() {
+ return mAppLocalList;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (position >= mAppLocalList.size()) {
+ Log.w(TAG, "Invalid view position:"+position+", actual size is:"+mAppLocalList.size());
+ return null;
+ }
+ // A ViewHolder keeps references to children views to avoid unneccessary calls
+ // to findViewById() on each row.
+ AppViewHolder holder;
+
+ // When convertView is not null, we can reuse it directly, there is no need
+ // to reinflate it. We only inflate a new View when the convertView supplied
+ // by ListView is null.
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.manage_applications_item, null);
+
+ // Creates a ViewHolder and store references to the two children views
+ // we want to bind data to.
+ holder = new AppViewHolder();
+ holder.appName = (TextView) convertView.findViewById(R.id.app_name);
+ holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
+ holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
+ convertView.setTag(holder);
+ } else {
+ // Get the ViewHolder back to get fast access to the TextView
+ // and the ImageView.
+ holder = (AppViewHolder) convertView.getTag();
+ }
+
+ // Bind the data efficiently with the holder
+ ApplicationInfo appInfo = mAppLocalList.get(position);
+ AppInfo mInfo = mAppPropMap.get(appInfo.packageName);
+ if(mInfo != null) {
+ if(mInfo.appName != null) {
+ holder.appName.setText(mInfo.appName);
+ }
+ if(mInfo.appIcon != null) {
+ holder.appIcon.setImageDrawable(mInfo.appIcon);
+ }
+ if (mInfo.appSize != null) {
+ holder.appSize.setText(mInfo.appSize);
+ }
+ } else {
+ Log.w(TAG, "No info for package:"+appInfo.packageName+" in property map");
+ }
+ return convertView;
+ }
+
+ private void adjustIndex() {
+ int imax = mAppLocalList.size();
+ ApplicationInfo info;
+ for (int i = 0; i < imax; i++) {
+ info = mAppLocalList.get(i);
+ mAppPropMap.get(info.packageName).index = i;
+ }
+ }
+
+ public void sortAppList(int sortOrder) {
+ Collections.sort(mAppLocalList, getAppComparator(sortOrder));
+ }
+
+ public void sortList(int sortOrder) {
+ sortAppList(sortOrder);
+ adjustIndex();
+ notifyDataSetChanged();
+ }
+
+ public boolean resetAppList(int filterOption, List appList) {
+ // Create application list based on the filter value
+ mAppLocalList = appList;
+ // Check for all properties in map before sorting. Populate values from cache
+ for(ApplicationInfo applicationInfo : mAppLocalList) {
+ AppInfo appInfo = mAppPropMap.get(applicationInfo.packageName);
+ if(appInfo == null) {
+ AppInfo rInfo = getFromCache(applicationInfo.packageName);
+ if(rInfo == null) {
+ // Need to load resources again. Inconsistency somewhere
+ return false;
+ }
+ mAppPropMap.put(applicationInfo.packageName, rInfo);
+ }
+ }
+ if (mAppLocalList.size() > 0) {
+ sortList(mSortOrder);
+ } else {
+ notifyDataSetChanged();
+ }
+ showEmptyViewIfListEmpty();
+ return true;
+ }
+
+ private Comparator getAppComparator(int sortOrder) {
+ if (sortOrder == SORT_ORDER_ALPHA) {
+ // Lazy initialization
+ if (mAlphaComparator == null) {
+ mAlphaComparator = new ApplicationInfo.DisplayNameComparator(mPm);
+ }
+ return mAlphaComparator;
+ }
+ // Lazy initialization
+ if(mSizeComparator == null) {
+ mSizeComparator = new AppInfoComparator(mAppPropMap);
+ }
+ return mSizeComparator;
+ }
+
+ public void updateAppsResourceInfo(Map iconMap) {
+ if(iconMap == null) {
+ Log.w(TAG, "Null iconMap when refreshing icon in List Adapter");
+ return;
+ }
+ boolean changed = false;
+ for (ApplicationInfo info : mAppLocalList) {
+ AppInfo pInfo = iconMap.get(info.packageName);
+ if(pInfo != null) {
+ AppInfo aInfo = mAppPropMap.get(info.packageName);
+ if (aInfo != null) {
+ aInfo.refreshIcon(pInfo);
+ } else {
+ mAppPropMap.put(info.packageName, pInfo);
+ }
+ changed = true;
+ }
+ }
+ if(changed) {
+ notifyDataSetChanged();
+ }
+ }
+
+ public void addToList(String pkgName, PackageStats ps) {
+ if(pkgName == null) {
+ Log.w(TAG, "Adding null pkg to List Adapter");
+ return;
+ }
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(pkgName, 0);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Ignoring non-existent package:"+pkgName);
+ return;
+ }
+ if(info == null) {
+ // Nothing to do log error message and return
+ Log.i(TAG, "Null ApplicationInfo for package:"+pkgName);
+ return;
+ }
+ // Binary search returns a negative index (ie --index) of the position where
+ // this might be inserted.
+ int newIdx = Collections.binarySearch(mAppLocalList, info,
+ getAppComparator(mSortOrder));
+ if(newIdx >= 0) {
+ Log.i(TAG, "Strange. Package:"+pkgName+" is not new");
+ return;
+ }
+ // New entry
+ newIdx = -newIdx-1;
+ mAppLocalList.add(newIdx, info);
+ mAppPropMap.put(info.packageName, new AppInfo(pkgName, newIdx,
+ info.loadLabel(mPm), info.loadIcon(mPm), ps));
+ adjustIndex();
+ notifyDataSetChanged();
+ }
+
+ public void removeFromList(List pkgNames) {
+ if(pkgNames == null) {
+ Log.w(TAG, "Removing null pkg list from List Adapter");
+ return;
+ }
+ int imax = mAppLocalList.size();
+ boolean found = false;
+ ApplicationInfo info;
+ int i, k;
+ String pkgName;
+ int kmax = pkgNames.size();
+ if(kmax <= 0) {
+ Log.w(TAG, "Removing empty pkg list from List Adapter");
+ return;
+ }
+ int idxArr[] = new int[kmax];
+ for (k = 0; k < kmax; k++) {
+ idxArr[k] = -1;
+ }
+ for (i = 0; i < imax; i++) {
+ info = mAppLocalList.get(i);
+ for (k = 0; k < kmax; k++) {
+ pkgName = pkgNames.get(k);
+ if (info.packageName.equalsIgnoreCase(pkgName)) {
+ idxArr[k] = i;
+ found = true;
+ break;
+ }
+ }
+ }
+ // Sort idxArr
+ Arrays.sort(idxArr);
+ // remove the packages based on decending indices
+ for (k = kmax-1; k >= 0; k--) {
+ // Check if package has been found in the list of existing apps first
+ if(idxArr[k] == -1) {
+ break;
+ }
+ info = mAppLocalList.get(idxArr[k]);
+ mAppLocalList.remove(idxArr[k]);
+ mAppPropMap.remove(info.packageName);
+ if (localLOGV) Log.i(TAG, "Removed pkg:"+info.packageName+ " list");
+ }
+ if (found) {
+ adjustIndex();
+ notifyDataSetChanged();
+ }
+ }
+
+ public void updateAppSize(String pkgName, PackageStats ps) {
+ if(pkgName == null) {
+ return;
+ }
+ AppInfo entry = mAppPropMap.get(pkgName);
+ if (entry == null) {
+ Log.w(TAG, "Entry for package:"+pkgName+"doesnt exist in map");
+ return;
+ }
+ // Copy the index into the newly updated entry
+ entry.setSize(ps);
+ notifyDataSetChanged();
+ }
+
+ public PackageStats getAppStats(String pkgName) {
+ if(pkgName == null) {
+ return null;
+ }
+ AppInfo entry = mAppPropMap.get(pkgName);
+ if (entry == null) {
+ return null;
+ }
+ return entry.appStats;
+ }
+ }
+
+ /*
+ * Utility method to clear messages to Handler
+ * We need'nt synchronize on the Handler since posting messages is guaranteed
+ * to be thread safe. Even if the other thread that retrieves package sizes
+ * posts a message, we do a cursory check of validity on mAppInfoAdapter's applist
+ */
+ private void clearMessagesInHandler() {
+ mHandler.removeMessages(INIT_PKG_INFO);
+ mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE);
+ mHandler.removeMessages(REMOVE_PKG);
+ mHandler.removeMessages(REORDER_LIST);
+ mHandler.removeMessages(ADD_PKG_START);
+ mHandler.removeMessages(ADD_PKG_DONE);
+ }
+
+ private void sendMessageToHandler(int msgId, int arg1) {
+ Message msg = mHandler.obtainMessage(msgId);
+ msg.arg1 = arg1;
+ mHandler.sendMessage(msg);
+ }
+
+ private void sendMessageToHandler(int msgId, Bundle data) {
+ Message msg = mHandler.obtainMessage(msgId);
+ msg.setData(data);
+ mHandler.sendMessage(msg);
+ }
+
+ private void sendMessageToHandler(int msgId) {
+ mHandler.sendEmptyMessage(msgId);
+ }
+
+ /*
+ * Stats Observer class used to compute package sizes and retrieve size information
+ * PkgSizeOberver is the call back thats used when invoking getPackageSizeInfo on
+ * PackageManager. The values in call back onGetStatsCompleted are validated
+ * and the specified message is passed to mHandler. The package name
+ * and the AppInfo object corresponding to the package name are set on the message
+ */
+ class PkgSizeObserver extends IPackageStatsObserver.Stub {
+ private ApplicationInfo mAppInfo;
+ private int mMsgId;
+ public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
+ if(DEBUG_PKG_DELAY) {
+ try {
+ Thread.sleep(10*1000);
+ } catch (InterruptedException e) {
+ }
+ }
+ AppInfo appInfo = null;
+ Bundle data = new Bundle();
+ data.putString(ATTR_PKG_NAME, mAppInfo.packageName);
+ if(pSucceeded && pStats != null) {
+ if (localLOGV) Log.i(TAG, "onGetStatsCompleted::"+pStats.packageName+", ("+
+ pStats.cacheSize+","+
+ pStats.codeSize+", "+pStats.dataSize);
+ data.putParcelable(ATTR_APP_PKG_STATS, pStats);
+ } else {
+ Log.w(TAG, "Invalid package stats from PackageManager");
+ }
+ //post message to Handler
+ Message msg = mHandler.obtainMessage(mMsgId, data);
+ msg.setData(data);
+ mHandler.sendMessage(msg);
+ }
+
+ public void invokeGetSizeInfo(ApplicationInfo pAppInfo, int msgId) {
+ if(pAppInfo == null || pAppInfo.packageName == null) {
+ return;
+ }
+ if(localLOGV) Log.i(TAG, "Invoking getPackageSizeInfo for package:"+
+ pAppInfo.packageName);
+ mMsgId = msgId;
+ mAppInfo = pAppInfo;
+ mPm.getPackageSizeInfo(pAppInfo.packageName, this);
+ }
+ }
+
+ /**
+ * Receives notifications when applications are added/removed.
+ */
+ private class PackageIntentReceiver extends BroadcastReceiver {
+ void registerReceiver() {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ ManageApplications.this.registerReceiver(this, filter);
+ }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String actionStr = intent.getAction();
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ if (localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName);
+ updatePackageList(actionStr, pkgName);
+ }
+ }
+
+ private void updatePackageList(String actionStr, String pkgName) {
+ // technically we dont have to invoke handler since onReceive is invoked on
+ // the main thread but doing it here for better clarity
+ if (Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
+ Bundle data = new Bundle();
+ data.putString(ATTR_PKG_NAME, pkgName);
+ sendMessageToHandler(ADD_PKG_START, data);
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) {
+ Bundle data = new Bundle();
+ data.putString(ATTR_PKG_NAME, pkgName);
+ sendMessageToHandler(REMOVE_PKG, data);
+ }
+ }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent lIntent = getIntent();
+ String action = lIntent.getAction();
+ if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
+ mSortOrder = SORT_ORDER_SIZE;
+ mSizesFirst = true;
+ }
+ mPm = getPackageManager();
+ // initialize some window features
+ requestWindowFeature(Window.FEATURE_RIGHT_ICON);
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ setContentView(R.layout.compute_sizes);
+ mDefaultAppIcon =Resources.getSystem().getDrawable(
+ com.android.internal.R.drawable.sym_def_app_icon);
+ mInvalidSizeStr = getText(R.string.invalid_size_value);
+ mComputingSizeStr = getText(R.string.computing_size);
+ // initialize the inflater
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mReceiver = new PackageIntentReceiver();
+ mEmptyView = (TextView) findViewById(R.id.empty_view);
+ mObserver = new PkgSizeObserver();
+ // Create adapter and list view here
+ List appList = getInstalledApps(mSortOrder);
+ mAppInfoAdapter = new AppInfoAdapter(this, appList);
+ ListView lv= (ListView) findViewById(android.R.id.list);
+ //lv.setAdapter(mAppInfoAdapter);
+ lv.setOnItemClickListener(this);
+ lv.setSaveEnabled(true);
+ lv.setItemsCanFocus(true);
+ lv.setOnItemClickListener(this);
+ mListView = lv;
+ showLoadingMsg();
+ }
+
+ @Override
+ public Dialog onCreateDialog(int id) {
+ if (id == DLG_LOADING) {
+ ProgressDialog dlg = new ProgressDialog(this);
+ dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ dlg.setMessage(getText(R.string.loading));
+ dlg.setIndeterminate(true);
+ dlg.setOnCancelListener(this);
+ return dlg;
+ }
+ return null;
+ }
+
+
+ private void showLoadingMsg() {
+ showDialog(DLG_LOADING);
+ if(localLOGV) Log.i(TAG, "Displaying Loading message");
+ }
+
+ private void dismissLoadingMsg() {
+ if(localLOGV) Log.i(TAG, "Dismissing Loading message");
+ dismissDialog(DLG_LOADING);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // Create a thread to load resources
+ mResourceThread = new ResourceLoaderThread();
+ sendMessageToHandler(INIT_PKG_INFO);
+ // register receiver
+ mReceiver.registerReceiver();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ // clear all messages related to application list
+ clearMessagesInHandler();
+ // register receiver here
+ unregisterReceiver(mReceiver);
+ mAppPropCache = mAppInfoAdapter.mAppPropMap;
+ }
+
+ // Avoid the restart and pause when orientation changes
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+ /*
+ * comparator class used to sort AppInfo objects based on size
+ */
+ public static class AppInfoComparator implements Comparator {
+ public AppInfoComparator(Map pAppPropMap) {
+ mAppPropMap= pAppPropMap;
+ }
+
+ public final int compare(ApplicationInfo a, ApplicationInfo b) {
+ AppInfo ainfo = mAppPropMap.get(a.packageName);
+ AppInfo binfo = mAppPropMap.get(b.packageName);
+ long atotal = ainfo.getTotalSize();
+ long btotal = binfo.getTotalSize();
+ long ret = atotal - btotal;
+ // negate result to sort in descending order
+ if (ret < 0) {
+ return 1;
+ }
+ if (ret == 0) {
+ return 0;
+ }
+ return -1;
+ }
+ private Map mAppPropMap;
+ }
+
+ // utility method used to start sub activity
+ private void startApplicationDetailsActivity() {
+ // Create intent to start new activity
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setClass(this, InstalledAppDetails.class);
+ intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
+ // start new activity to display extended information
+ startActivityForResult(intent, INSTALLED_APP_DETAILS);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
+ .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
+ menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
+ .setIcon(android.R.drawable.ic_menu_sort_by_size);
+ menu.add(0, FILTER_OPTIONS, 3, R.string.filter)
+ .setIcon(R.drawable.ic_menu_filter_settings);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (mFirst) {
+ menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
+ menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
+ menu.findItem(FILTER_OPTIONS).setVisible(true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int menuId = item.getItemId();
+ if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
+ sendMessageToHandler(REORDER_LIST, menuId);
+ } else if (menuId == FILTER_OPTIONS) {
+ if (mAlertDlg == null) {
+ mAlertDlg = new AlertDialog.Builder(this).
+ setTitle(R.string.filter_dlg_title).
+ setNeutralButton(R.string.cancel, this).
+ setSingleChoiceItems(new CharSequence[] {getText(R.string.filter_apps_all),
+ getText(R.string.filter_apps_running),
+ getText(R.string.filter_apps_third_party)},
+ -1, this).
+ create();
+ }
+ mAlertDlg.show();
+ }
+ return true;
+ }
+
+ public void onItemClick(AdapterView> parent, View view, int position,
+ long id) {
+ ApplicationInfo info = (ApplicationInfo)mAppInfoAdapter.getItem(position);
+ mCurrentPkgName = info.packageName;
+ startApplicationDetailsActivity();
+ }
+
+ // Finish the activity if the user presses the back button to cancel the activity
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ int newOption;
+ switch (which) {
+ // Make sure that values of 0, 1, 2 match options all, running, third_party when
+ // created via the AlertDialog.Builder
+ case 0:
+ newOption = FILTER_APPS_ALL;
+ break;
+ case 1:
+ newOption = FILTER_APPS_RUNNING;
+ break;
+ case 2:
+ newOption = FILTER_APPS_THIRD_PARTY;
+ break;
+ default:
+ return;
+ }
+ mAlertDlg.dismiss();
+ sendMessageToHandler(REORDER_LIST, newOption);
+ }
+}
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
new file mode 100644
index 00000000000..38ad6081850
--- /dev/null
+++ b/src/com/android/settings/MasterClear.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 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.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.ICheckinService;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * Confirm and execute a reset of the device to a clean "just out of the box"
+ * state. Multiple confirmations are required: first, a general "are you sure
+ * you want to do this?" prompt, followed by a keyguard pattern trace if the user
+ * has defined one, followed by a final strongly-worded "THIS WILL ERASE EVERYTHING
+ * ON THE PHONE" prompt. If at any time the phone is allowed to go to sleep, is
+ * locked, et cetera, then the confirmation sequence is abandoned.
+ */
+public class MasterClear extends Activity {
+
+ private static final int KEYGUARD_REQUEST = 55;
+
+ private LayoutInflater mInflater;
+ private LockPatternUtils mLockUtils;
+
+ private View mInitialView;
+ private Button mInitiateButton;
+
+ private View mFinalView;
+ private Button mFinalButton;
+
+ /**
+ * The user has gone through the multiple confirmation, so now we go ahead
+ * and invoke the Checkin Service to reset the device to its factory-default
+ * state (rebooting in the process).
+ */
+ private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
+ public void onClick(View v) {
+
+ // Those monkeys kept committing suicide, so we add this property
+ // to disable going through with the master clear
+ if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
+ return;
+ }
+
+ ICheckinService service =
+ ICheckinService.Stub.asInterface(ServiceManager.getService("checkin"));
+ if (service != null) {
+ try {
+ // This RPC should never return
+ service.masterClear();
+ } catch (android.os.RemoteException e) {
+ // Intentionally blank - there's nothing we can do here
+ Log.w("MasterClear", "Unable to invoke ICheckinService.masterClear()");
+ }
+ } else {
+ Log.w("MasterClear", "Unable to locate ICheckinService");
+ }
+
+ /* If we reach this point, the master clear didn't happen -- the
+ * service might have been unregistered with the ServiceManager,
+ * the RPC might have thrown an exception, or for some reason
+ * the implementation of masterClear() may have returned instead
+ * of resetting the device.
+ */
+ new AlertDialog.Builder(MasterClear.this)
+ .setMessage(getText(R.string.master_clear_failed))
+ .setPositiveButton(getText(android.R.string.ok), null)
+ .show();
+ }
+ };
+
+ /**
+ * Keyguard validation is run using the standard {@link ConfirmLockPattern}
+ * component as a subactivity
+ */
+ private void runKeyguardConfirmation() {
+ final Intent intent = new Intent();
+ intent.setClassName("com.android.settings",
+ "com.android.settings.ConfirmLockPattern");
+ // supply header and footer text in the intent
+ intent.putExtra(ConfirmLockPattern.HEADER_TEXT,
+ getText(R.string.master_clear_gesture_prompt));
+ intent.putExtra(ConfirmLockPattern.FOOTER_TEXT,
+ getText(R.string.master_clear_gesture_explanation));
+ startActivityForResult(intent, KEYGUARD_REQUEST);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode != KEYGUARD_REQUEST) {
+ return;
+ }
+
+ // If the user entered a valid keyguard trace, present the final
+ // confirmation prompt; otherwise, go back to the initial state.
+ if (resultCode == Activity.RESULT_OK) {
+ establishFinalConfirmationState();
+ } else {
+ establishInitialState();
+ }
+ }
+
+ /**
+ * If the user clicks to begin the reset sequence, we next require a
+ * keyguard confirmation if the user has currently enabled one. If there
+ * is no keyguard available, we simply go to the final confirmation prompt.
+ */
+ private Button.OnClickListener mInitiateListener = new Button.OnClickListener() {
+ public void onClick(View v) {
+ if (mLockUtils.isLockPatternEnabled()) {
+ runKeyguardConfirmation();
+ } else {
+ establishFinalConfirmationState();
+ }
+ }
+ };
+
+ /**
+ * Configure the UI for the final confirmation interaction
+ */
+ private void establishFinalConfirmationState() {
+ if (mFinalView == null) {
+ mFinalView = mInflater.inflate(R.layout.master_clear_final, null);
+ mFinalButton =
+ (Button) mFinalView.findViewById(R.id.execute_master_clear);
+ mFinalButton.setOnClickListener(mFinalClickListener);
+ }
+
+ setContentView(mFinalView);
+ }
+
+ /**
+ * In its initial state, the activity presents a button for the user to
+ * click in order to initiate a confirmation sequence. This method is
+ * called from various other points in the code to reset the activity to
+ * this base state.
+ *
+ * Reinflating views from resources is expensive and prevents us from
+ * caching widget pointers, so we use a single-inflate pattern: we lazy-
+ * inflate each view, caching all of the widget pointers we'll need at the
+ * time, then simply reuse the inflated views directly whenever we need
+ * to change contents.
+ */
+ private void establishInitialState() {
+ if (mInitialView == null) {
+ mInitialView = mInflater.inflate(R.layout.master_clear_primary, null);
+ mInitiateButton =
+ (Button) mInitialView.findViewById(R.id.initiate_master_clear);
+ mInitiateButton.setOnClickListener(mInitiateListener);
+ }
+
+ setContentView(mInitialView);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ mInitialView = null;
+ mFinalView = null;
+ mInflater = LayoutInflater.from(this);
+ mLockUtils = new LockPatternUtils(getContentResolver());
+
+ establishInitialState();
+ }
+
+ /** Abandon all progress through the confirmation sequence by returning
+ * to the initial view any time the activity is interrupted (e.g. by
+ * idle timeout).
+ */
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ establishInitialState();
+ }
+
+}
diff --git a/src/com/android/settings/MediaFormat.java b/src/com/android/settings/MediaFormat.java
new file mode 100644
index 00000000000..3594572a927
--- /dev/null
+++ b/src/com/android/settings/MediaFormat.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008 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.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IMountService;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.Environment;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * Confirm and execute a format of the sdcard.
+ * Multiple confirmations are required: first, a general "are you sure
+ * you want to do this?" prompt, followed by a keyguard pattern trace if the user
+ * has defined one, followed by a final strongly-worded "THIS WILL ERASE EVERYTHING
+ * ON THE SD CARD" prompt. If at any time the phone is allowed to go to sleep, is
+ * locked, et cetera, then the confirmation sequence is abandoned.
+ */
+public class MediaFormat extends Activity {
+
+ private static final int KEYGUARD_REQUEST = 55;
+
+ private LayoutInflater mInflater;
+ private LockPatternUtils mLockUtils;
+
+ private View mInitialView;
+ private Button mInitiateButton;
+
+ private View mFinalView;
+ private Button mFinalButton;
+
+ /**
+ * The user has gone through the multiple confirmation, so now we go ahead
+ * and invoke the Mount Service to format the SD card.
+ */
+ private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
+ public void onClick(View v) {
+
+ // Those monkeys kept committing suicide, so we add this property
+ // to disable going through with the format
+ if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
+ return;
+ }
+ IMountService service =
+ IMountService.Stub.asInterface(ServiceManager.getService("mount"));
+ if (service != null) {
+ try {
+ service.formatMedia(Environment.getExternalStorageDirectory().toString());
+ } catch (android.os.RemoteException e) {
+ // Intentionally blank - there's nothing we can do here
+ Log.w("MediaFormat", "Unable to invoke IMountService.formatMedia()");
+ }
+ } else {
+ Log.w("MediaFormat", "Unable to locate IMountService");
+ }
+ finish();
+ }
+ };
+
+ /**
+ * Keyguard validation is run using the standard {@link ConfirmLockPattern}
+ * component as a subactivity
+ */
+ private void runKeyguardConfirmation() {
+ final Intent intent = new Intent();
+ intent.setClassName("com.android.settings",
+ "com.android.settings.ConfirmLockPattern");
+ // supply header and footer text in the intent
+ intent.putExtra(ConfirmLockPattern.HEADER_TEXT,
+ getText(R.string.media_format_gesture_prompt));
+ intent.putExtra(ConfirmLockPattern.FOOTER_TEXT,
+ getText(R.string.media_format_gesture_explanation));
+ startActivityForResult(intent, KEYGUARD_REQUEST);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode != KEYGUARD_REQUEST) {
+ return;
+ }
+
+ // If the user entered a valid keyguard trace, present the final
+ // confirmation prompt; otherwise, go back to the initial state.
+ if (resultCode == Activity.RESULT_OK) {
+ establishFinalConfirmationState();
+ } else {
+ establishInitialState();
+ }
+ }
+
+ /**
+ * If the user clicks to begin the reset sequence, we next require a
+ * keyguard confirmation if the user has currently enabled one. If there
+ * is no keyguard available, we simply go to the final confirmation prompt.
+ */
+ private Button.OnClickListener mInitiateListener = new Button.OnClickListener() {
+ public void onClick(View v) {
+ if (mLockUtils.isLockPatternEnabled()) {
+ runKeyguardConfirmation();
+ } else {
+ establishFinalConfirmationState();
+ }
+ }
+ };
+
+ /**
+ * Configure the UI for the final confirmation interaction
+ */
+ private void establishFinalConfirmationState() {
+ if (mFinalView == null) {
+ mFinalView = mInflater.inflate(R.layout.media_format_final, null);
+ mFinalButton =
+ (Button) mFinalView.findViewById(R.id.execute_media_format);
+ mFinalButton.setOnClickListener(mFinalClickListener);
+ }
+
+ setContentView(mFinalView);
+ }
+
+ /**
+ * In its initial state, the activity presents a button for the user to
+ * click in order to initiate a confirmation sequence. This method is
+ * called from various other points in the code to reset the activity to
+ * this base state.
+ *
+ *
Reinflating views from resources is expensive and prevents us from
+ * caching widget pointers, so we use a single-inflate pattern: we lazy-
+ * inflate each view, caching all of the widget pointers we'll need at the
+ * time, then simply reuse the inflated views directly whenever we need
+ * to change contents.
+ */
+ private void establishInitialState() {
+ if (mInitialView == null) {
+ mInitialView = mInflater.inflate(R.layout.media_format_primary, null);
+ mInitiateButton =
+ (Button) mInitialView.findViewById(R.id.initiate_media_format);
+ mInitiateButton.setOnClickListener(mInitiateListener);
+ }
+
+ setContentView(mInitialView);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ mInitialView = null;
+ mFinalView = null;
+ mInflater = LayoutInflater.from(this);
+ mLockUtils = new LockPatternUtils(getContentResolver());
+
+ establishInitialState();
+ }
+
+ /** Abandon all progress through the confirmation sequence by returning
+ * to the initial view any time the activity is interrupted (e.g. by
+ * idle timeout).
+ */
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ establishInitialState();
+ }
+
+}
diff --git a/src/com/android/settings/ProgressCategory.java b/src/com/android/settings/ProgressCategory.java
new file mode 100644
index 00000000000..15810b33824
--- /dev/null
+++ b/src/com/android/settings/ProgressCategory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 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.Context;
+import android.preference.PreferenceCategory;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.Map;
+
+public class ProgressCategory extends PreferenceCategory {
+
+ private boolean mProgress = false;
+
+ public ProgressCategory(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setLayoutResource(R.layout.preference_progress_category);
+ }
+
+ @Override
+ public void onBindView(View view) {
+ super.onBindView(view);
+ View textView = view.findViewById(R.id.scanning_text);
+ View progressBar = view.findViewById(R.id.scanning_progress);
+
+ int visibility = mProgress ? View.VISIBLE : View.INVISIBLE;
+ textView.setVisibility(visibility);
+ progressBar.setVisibility(visibility);
+ }
+
+ /**
+ * Turn on/off the progress indicator and text on the right.
+ * @param progressOn whether or not the progress should be displayed
+ */
+ public void setProgress(boolean progressOn) {
+ mProgress = progressOn;
+ notifyChanged();
+ }
+}
+
diff --git a/src/com/android/settings/ProxySelector.java b/src/com/android/settings/ProxySelector.java
new file mode 100644
index 00000000000..80fe3c90c98
--- /dev/null
+++ b/src/com/android/settings/ProxySelector.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2006 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.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.net.Proxy;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * To start the Proxy Selector activity, create the following intent.
+ *
+ *
+ * Intent intent = new Intent();
+ * intent.setClassName("com.android.browser.ProxySelector");
+ * startActivity(intent);
+ *
+ *
+ * you can add extra options to the intent by using
+ *
+ *
+ * intent.putExtra(key, value);
+ *
+ *
+ * the extra options are:
+ *
+ * button-label: a string label to display for the okay button
+ * title: the title of the window
+ * error-text: If not null, will be used as the label of the error message.
+ */
+public class ProxySelector extends Activity
+{
+ private final static String LOGTAG = "Settings";
+
+ EditText mHostnameField;
+ EditText mPortField;
+ Button mOKButton;
+
+ // Matches blank input, ips, and domain names
+ private static final String HOSTNAME_REGEXP = "^$|^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*$";
+ private static final Pattern HOSTNAME_PATTERN;
+ static {
+ HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
+ }
+
+
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ if (android.util.Config.LOGV) Log.v(LOGTAG, "[ProxySelector] onStart");
+
+ setContentView(R.layout.proxy);
+ initView();
+ populateFields(false);
+ }
+
+ protected void showError(int error) {
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.proxy_error)
+ .setMessage(error)
+ .setPositiveButton(R.string.proxy_error_dismiss, null)
+ .show();
+ }
+
+ void initView() {
+
+ mHostnameField = (EditText)findViewById(R.id.hostname);
+ mHostnameField.setOnFocusChangeListener(mOnFocusChangeHandler);
+
+ mPortField = (EditText)findViewById(R.id.port);
+ mPortField.setOnClickListener(mOKHandler);
+ mPortField.setOnFocusChangeListener(mOnFocusChangeHandler);
+
+ mOKButton = (Button)findViewById(R.id.action);
+ mOKButton.setOnClickListener(mOKHandler);
+
+ Button b = (Button)findViewById(R.id.clear);
+ b.setOnClickListener(mClearHandler);
+
+ b = (Button)findViewById(R.id.defaultView);
+ b.setOnClickListener(mDefaultHandler);
+ }
+
+ void populateFields(boolean useDefault) {
+ String hostname = null;
+ int port = -1;
+ if (useDefault) {
+ // Use the default proxy settings provided by the carrier
+ hostname = Proxy.getDefaultHost();
+ port = Proxy.getDefaultPort();
+ } else {
+ // Use the last setting given by the user
+ hostname = Proxy.getHost(this);
+ port = Proxy.getPort(this);
+ }
+
+ if (hostname == null) {
+ hostname = "";
+ }
+
+ mHostnameField.setText(hostname);
+
+ String portStr = port == -1 ? "" : Integer.toString(port);
+ mPortField.setText(portStr);
+
+ Intent intent = getIntent();
+
+ String buttonLabel = intent.getStringExtra("button-label");
+ if (!TextUtils.isEmpty(buttonLabel)) {
+ mOKButton.setText(buttonLabel);
+ }
+
+ String title = intent.getStringExtra("title");
+ if (!TextUtils.isEmpty(title)) {
+ setTitle(title);
+ }
+ }
+
+ /**
+ * validate syntax of hostname and port entries
+ * @return 0 on success, string resource ID on failure
+ */
+ int validate(String hostname, String port) {
+ Matcher match = HOSTNAME_PATTERN.matcher(hostname);
+
+ if (!match.matches()) return R.string.proxy_error_invalid_host;
+
+ if (hostname.length() > 0 && port.length() == 0) {
+ return R.string.proxy_error_empty_port;
+ }
+
+ if (port.length() > 0) {
+ if (hostname.length() == 0) {
+ return R.string.proxy_error_empty_host_set_port;
+ }
+ int portVal = -1;
+ try {
+ portVal = Integer.parseInt(port);
+ } catch (NumberFormatException ex) {
+ return R.string.proxy_error_invalid_port;
+ }
+ if (portVal <= 0 || portVal > 0xFFFF) {
+ return R.string.proxy_error_invalid_port;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * returns true on success, false if the user must correct something
+ */
+ boolean saveToDb() {
+
+ String hostname = mHostnameField.getText().toString().trim();
+ String portStr = mPortField.getText().toString().trim();
+ int port = -1;
+
+ int result = validate(hostname, portStr);
+ if (result > 0) {
+ showError(result);
+ return false;
+ }
+
+ if (portStr.length() > 0) {
+ try {
+ port = Integer.parseInt(portStr);
+ } catch (NumberFormatException ex) {
+ return false;
+ }
+ }
+
+ // FIXME: The best solution would be to make a better UI that would
+ // disable editing of the text boxes if the user chooses to use the
+ // default settings. i.e. checking a box to always use the default
+ // carrier. http:/b/issue?id=756480
+ // FIXME: This currently will not work if the default host is blank and
+ // the user has cleared the input boxes in order to not use a proxy.
+ // This is a UI problem and can be solved with some better form
+ // controls.
+ // FIXME: If the user types in a proxy that matches the default, should
+ // we keep that setting? Can be fixed with a new UI.
+ ContentResolver res = getContentResolver();
+ if (hostname.equals(Proxy.getDefaultHost())
+ && port == Proxy.getDefaultPort()) {
+ // If the user hit the default button and didn't change any of
+ // the input boxes, treat it as if the user has not specified a
+ // proxy.
+ hostname = null;
+ }
+
+ if (!TextUtils.isEmpty(hostname)) {
+ hostname += ':' + portStr;
+ }
+ Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY, hostname);
+ sendBroadcast(new Intent(Proxy.PROXY_CHANGE_ACTION));
+
+ return true;
+ }
+
+ OnClickListener mOKHandler = new OnClickListener() {
+ public void onClick(View v) {
+ if (saveToDb()) {
+ finish();
+ }
+ }
+ };
+
+ OnClickListener mClearHandler = new OnClickListener() {
+ public void onClick(View v) {
+ mHostnameField.setText("");
+ mPortField.setText("");
+ }
+ };
+
+ OnClickListener mDefaultHandler = new OnClickListener() {
+ public void onClick(View v) {
+ populateFields(true);
+ }
+ };
+
+ OnFocusChangeListener mOnFocusChangeHandler = new OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ TextView textView = (TextView) v;
+ Selection.selectAll((Spannable) textView.getText());
+ }
+ }
+ };
+}
diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java
new file mode 100644
index 00000000000..dbad45dcaaf
--- /dev/null
+++ b/src/com/android/settings/RadioInfo.java
@@ -0,0 +1,1184 @@
+/*
+ * Copyright (C) 2006 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.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.INetStatService;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.preference.PreferenceManager;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.NeighboringCellInfo;
+import android.telephony.gsm.GsmCellLocation;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.EditText;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.PhoneStateIntentReceiver;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.gsm.PdpConnection;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RadioInfo extends Activity {
+ private final String TAG = "phone";
+
+ private static final int EVENT_PHONE_STATE_CHANGED = 100;
+ private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 200;
+ private static final int EVENT_SERVICE_STATE_CHANGED = 300;
+ private static final int EVENT_CFI_CHANGED = 302;
+
+ private static final int EVENT_QUERY_PREFERRED_TYPE_DONE = 1000;
+ private static final int EVENT_SET_PREFERRED_TYPE_DONE = 1001;
+ private static final int EVENT_QUERY_NEIGHBORING_CIDS_DONE = 1002;
+ private static final int EVENT_SET_QXDMLOG_DONE = 1003;
+ private static final int EVENT_SET_CIPHER_DONE = 1004;
+ private static final int EVENT_QUERY_SMSC_DONE = 1005;
+ private static final int EVENT_UPDATE_SMSC_DONE = 1006;
+
+ private static final int MENU_ITEM_SELECT_BAND = 0;
+ private static final int MENU_ITEM_VIEW_ADN = 1;
+ private static final int MENU_ITEM_VIEW_FDN = 2;
+ private static final int MENU_ITEM_VIEW_SDN = 3;
+ private static final int MENU_ITEM_GET_PDP_LIST = 4;
+ private static final int MENU_ITEM_TOGGLE_DATA = 5;
+ private static final int MENU_ITEM_TOGGLE_DATA_ON_BOOT = 6;
+
+ private TextView mImei;
+ private TextView number;
+ private TextView callState;
+ private TextView operatorName;
+ private TextView roamingState;
+ private TextView gsmState;
+ private TextView gprsState;
+ private TextView network;
+ private TextView dBm;
+ private TextView mMwi;
+ private TextView mCfi;
+ private TextView mLocation;
+ private TextView mNeighboringCids;
+ private TextView resets;
+ private TextView attempts;
+ private TextView successes;
+ private TextView disconnects;
+ private TextView sentSinceReceived;
+ private TextView sent;
+ private TextView received;
+ private TextView mPingIpAddr;
+ private TextView mPingHostname;
+ private TextView mHttpClientTest;
+ private TextView cipherState;
+ private TextView dnsCheckState;
+ private EditText smsc;
+ private Button radioPowerButton;
+ private Button qxdmLogButton;
+ private Button cipherToggleButton;
+ private Button dnsCheckToggleButton;
+ private Button pingTestButton;
+ private Button updateSmscButton;
+ private Button refreshSmscButton;
+ private Spinner preferredNetworkType;
+
+ private TelephonyManager mTelephonyManager;
+ private Phone phone = null;
+ private PhoneStateIntentReceiver mPhoneStateReceiver;
+ private INetStatService netstat;
+
+ private OemCommands mOem = null;
+ private boolean mQxdmLogEnabled;
+ // The requested cipher state
+ private boolean mCipherOn;
+
+ private String mPingIpAddrResult;
+ private String mPingHostnameResult;
+ private String mHttpClientTestResult;
+ private boolean mMwiValue = false;
+ private boolean mCfiValue = false;
+
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onDataConnectionStateChanged(int state) {
+ updateDataState();
+ updateDataStats();
+ updatePdpList();
+ updateNetworkType();
+ }
+
+ @Override
+ public void onDataActivity(int direction) {
+ updateDataStats2();
+ }
+
+ @Override
+ public void onCellLocationChanged(CellLocation location) {
+ updateLocation(location);
+ }
+
+ @Override
+ public void onMessageWaitingIndicatorChanged(boolean mwi) {
+ mMwiValue = mwi;
+ updateMessageWaiting();
+ }
+
+ @Override
+ public void onCallForwardingIndicatorChanged(boolean cfi) {
+ mCfiValue = cfi;
+ updateCallRedirect();
+ }
+ };
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_PHONE_STATE_CHANGED:
+ updatePhoneState();
+ break;
+
+ case EVENT_SIGNAL_STRENGTH_CHANGED:
+ updateSignalStrength();
+ break;
+
+ case EVENT_SERVICE_STATE_CHANGED:
+ updateServiceState();
+ updatePowerState();
+ break;
+
+ case EVENT_QUERY_PREFERRED_TYPE_DONE:
+ ar= (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ int type = ((int[])ar.result)[0];
+ preferredNetworkType.setSelection(type, true);
+ } else {
+ preferredNetworkType.setSelection(3, true);
+ }
+ break;
+ case EVENT_SET_PREFERRED_TYPE_DONE:
+ ar= (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ phone.getPreferredNetworkType(
+ obtainMessage(EVENT_QUERY_PREFERRED_TYPE_DONE));
+ }
+ break;
+ case EVENT_QUERY_NEIGHBORING_CIDS_DONE:
+ ar= (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ updateNeighboringCids((ArrayList)ar.result);
+ } else {
+ mNeighboringCids.setText("unknown");
+ }
+ break;
+ case EVENT_SET_QXDMLOG_DONE:
+ ar= (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ mQxdmLogEnabled = !mQxdmLogEnabled;
+
+ updateQxdmState(mQxdmLogEnabled);
+ displayQxdmEnableResult();
+ }
+ break;
+ case EVENT_SET_CIPHER_DONE:
+ ar= (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ setCiphPref(mCipherOn);
+ }
+ updateCiphState();
+ break;
+ case EVENT_QUERY_SMSC_DONE:
+ ar= (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ smsc.setText("refresh error");
+ } else {
+ byte[] buf = (byte[]) ar.result;
+ smsc.setText(new String(buf));
+ }
+ break;
+ case EVENT_UPDATE_SMSC_DONE:
+ updateSmscButton.setEnabled(true);
+ ar= (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ smsc.setText("update error");
+ }
+ break;
+ default:
+ break;
+
+ }
+ }
+ };
+
+ private class OemCommands {
+
+ public final int OEM_QXDM_SDLOG_DEFAULT_FILE_SIZE = 32;
+ public final int OEM_QXDM_SDLOG_DEFAULT_MASK = 0;
+ public final int OEM_QXDM_SDLOG_DEFAULT_MAX_INDEX = 8;
+
+ final int SIZE_OF_INT = 4;
+ final int OEM_FEATURE_ENABLE = 1;
+ final int OEM_FEATURE_DISABLE = 0;
+ final int OEM_SIMPE_FEAUTURE_LEN = 1;
+
+ final int OEM_QXDM_SDLOG_FUNCTAG = 0x00010000;
+ final int OEM_QXDM_SDLOG_LEN = 4;
+ final int OEM_PS_AUTO_ATTACH_FUNCTAG = 0x00020000;
+ final int OEM_CIPHERING_FUNCTAG = 0x00020001;
+ final int OEM_SMSC_UPDATE_FUNCTAG = 0x00020002;
+ final int OEM_SMSC_QUERY_FUNCTAG = 0x00020003;
+ final int OEM_SMSC_QUERY_LEN = 0;
+
+ /**
+ * The OEM interface to store QXDM to SD.
+ *
+ * To start/stop logging QXDM logs to SD card, use tag
+ * OEM_RIL_HOOK_QXDM_SD_LOG_SETUP 0x00010000
+ *
+ * "data" is a const oem_ril_hook_qxdm_sdlog_setup_data_st *
+ * ((const oem_ril_hook_qxdm_sdlog_setup_data_st *)data)->head.func_tag
+ * should be OEM_RIL_HOOK_QXDM_SD_LOG_SETUP
+ * ((const oem_ril_hook_qxdm_sdlog_setup_data_st *)data)->head.len
+ * should be "sizeof(unsigned int) * 4"
+ * ((const oem_ril_hook_qxdm_sdlog_setup_data_st *)data)->mode
+ * could be 0 for 'stop logging', or 1 for 'start logging'
+ * ((const oem_ril_hook_qxdm_sdlog_setup_data_st *)data)->log_file_size
+ * will assign the size of each log file, and it could be a value between
+ * 1 and 512 (in megabytes, default value is recommended to set as 32).
+ * This value will be ignored when mode == 0.
+ * ((const oem_ril_hook_qxdm_sdlog_setup_data_st *)data)->log_mask will
+ * assign the rule to filter logs, and it is a bitmask (bit0 is for MsgAll,
+ * bit1 is for LogAll, and bit2 is for EventAll) recommended to be set as 0
+ * by default. This value will be ignored when mode == 0.
+ * ((const oem_ril_hook_qxdm_sdlog_setup_data_st *)data)->log_max_fileindex
+ * set the how many logfiles will storted before roll over. This value will
+ * be ignored when mode == 0.
+ *
+ * "response" is NULL
+ *
+ * typedef struct _oem_ril_hook_raw_head_st {
+ * unsigned int func_tag;
+ * unsigned int len;
+ * } oem_ril_hook_raw_head_st;
+ *
+ * typedef struct _oem_ril_hook_qxdm_sdlog_setup_data_st {
+ * oem_ril_hook_raw_head_st head;
+ * unsigned int mode;
+ * unsigned int log_file_size;
+ * unsigned int log_mask;
+ * unsigned int log_max_fileindex;
+ * } oem_ril_hook_qxdm_sdlog_setup_data_st;
+ *
+ * @param enable set true to start logging QXDM in SD card
+ * @param fileSize is the log file size in MB
+ * @param mask is the log mask to filter
+ * @param maxIndex is the maximum roll-over file number
+ * @return byteArray to use in RIL RAW command
+ */
+ byte[] getQxdmSdlogData(boolean enable, int fileSize, int mask, int maxIndex) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ try {
+ writeIntLittleEndian(dos, OEM_QXDM_SDLOG_FUNCTAG);
+ writeIntLittleEndian(dos, OEM_QXDM_SDLOG_LEN * SIZE_OF_INT);
+ writeIntLittleEndian(dos, enable ?
+ OEM_FEATURE_ENABLE : OEM_FEATURE_DISABLE);
+ writeIntLittleEndian(dos, fileSize);
+ writeIntLittleEndian(dos, mask);
+ writeIntLittleEndian(dos, maxIndex);
+ } catch (IOException e) {
+ return null;
+ }
+ return bos.toByteArray();
+ }
+
+ byte[] getSmscQueryData() {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ try {
+ writeIntLittleEndian(dos, OEM_SMSC_QUERY_FUNCTAG);
+ writeIntLittleEndian(dos, OEM_SMSC_QUERY_LEN * SIZE_OF_INT);
+ } catch (IOException e) {
+ return null;
+ }
+ return bos.toByteArray();
+ }
+
+ byte[] getSmscUpdateData(String smsc) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ try {
+ byte[] smsc_bytes = smsc.getBytes();
+ writeIntLittleEndian(dos, OEM_SMSC_UPDATE_FUNCTAG);
+ writeIntLittleEndian(dos, smsc_bytes.length);
+ dos.write(smsc_bytes);
+ } catch (IOException e) {
+ return null;
+ }
+ return bos.toByteArray();
+ }
+
+ byte[] getPsAutoAttachData(boolean enable) {
+ return getSimpleFeatureData(OEM_PS_AUTO_ATTACH_FUNCTAG, enable);
+ }
+
+ byte[] getCipheringData(boolean enable) {
+ return getSimpleFeatureData(OEM_CIPHERING_FUNCTAG, enable);
+ }
+
+ private byte[] getSimpleFeatureData(int tag, boolean enable) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ try {
+ writeIntLittleEndian(dos, tag);
+ writeIntLittleEndian(dos, OEM_SIMPE_FEAUTURE_LEN * SIZE_OF_INT);
+ writeIntLittleEndian(dos, enable ?
+ OEM_FEATURE_ENABLE : OEM_FEATURE_DISABLE);
+ } catch (IOException e) {
+ return null;
+ }
+ return bos.toByteArray();
+ }
+
+ private void writeIntLittleEndian(DataOutputStream dos, int val)
+ throws IOException {
+ dos.writeByte(val);
+ dos.writeByte(val >> 8);
+ dos.writeByte(val >> 16);
+ dos.writeByte(val >> 24);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.radio_info);
+
+ mTelephonyManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
+ phone = PhoneFactory.getDefaultPhone();
+
+ mImei = (TextView) findViewById(R.id.imei);
+ number = (TextView) findViewById(R.id.number);
+ callState = (TextView) findViewById(R.id.call);
+ operatorName = (TextView) findViewById(R.id.operator);
+ roamingState = (TextView) findViewById(R.id.roaming);
+ gsmState = (TextView) findViewById(R.id.gsm);
+ gprsState = (TextView) findViewById(R.id.gprs);
+ network = (TextView) findViewById(R.id.network);
+ dBm = (TextView) findViewById(R.id.dbm);
+ mMwi = (TextView) findViewById(R.id.mwi);
+ mCfi = (TextView) findViewById(R.id.cfi);
+ mLocation = (TextView) findViewById(R.id.location);
+ mNeighboringCids = (TextView) findViewById(R.id.neighboring);
+
+ resets = (TextView) findViewById(R.id.resets);
+ attempts = (TextView) findViewById(R.id.attempts);
+ successes = (TextView) findViewById(R.id.successes);
+ disconnects = (TextView) findViewById(R.id.disconnects);
+ sentSinceReceived = (TextView) findViewById(R.id.sentSinceReceived);
+ sent = (TextView) findViewById(R.id.sent);
+ received = (TextView) findViewById(R.id.received);
+ cipherState = (TextView) findViewById(R.id.ciphState);
+ smsc = (EditText) findViewById(R.id.smsc);
+ dnsCheckState = (TextView) findViewById(R.id.dnsCheckState);
+
+ mPingIpAddr = (TextView) findViewById(R.id.pingIpAddr);
+ mPingHostname = (TextView) findViewById(R.id.pingHostname);
+ mHttpClientTest = (TextView) findViewById(R.id.httpClientTest);
+
+ preferredNetworkType = (Spinner) findViewById(R.id.preferredNetworkType);
+ ArrayAdapter adapter = new ArrayAdapter (this,
+ android.R.layout.simple_spinner_item, mPreferredNetworkLabels);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ preferredNetworkType.setAdapter(adapter);
+ preferredNetworkType.setOnItemSelectedListener(mPreferredNetworkHandler);
+
+ radioPowerButton = (Button) findViewById(R.id.radio_power);
+ radioPowerButton.setOnClickListener(mPowerButtonHandler);
+
+ qxdmLogButton = (Button) findViewById(R.id.qxdm_log);
+ qxdmLogButton.setOnClickListener(mQxdmButtonHandler);
+
+ cipherToggleButton = (Button) findViewById(R.id.ciph_toggle);
+ cipherToggleButton.setOnClickListener(mCipherButtonHandler);
+ pingTestButton = (Button) findViewById(R.id.ping_test);
+ pingTestButton.setOnClickListener(mPingButtonHandler);
+ updateSmscButton = (Button) findViewById(R.id.update_smsc);
+ updateSmscButton.setOnClickListener(mUpdateSmscButtonHandler);
+ refreshSmscButton = (Button) findViewById(R.id.refresh_smsc);
+ refreshSmscButton.setOnClickListener(mRefreshSmscButtonHandler);
+ dnsCheckToggleButton = (Button) findViewById(R.id.dns_check_toggle);
+ dnsCheckToggleButton.setOnClickListener(mDnsCheckButtonHandler);
+
+ mPhoneStateReceiver = new PhoneStateIntentReceiver(this, mHandler);
+ mPhoneStateReceiver.notifySignalStrength(EVENT_SIGNAL_STRENGTH_CHANGED);
+ mPhoneStateReceiver.notifyServiceState(EVENT_SERVICE_STATE_CHANGED);
+ mPhoneStateReceiver.notifyPhoneCallState(EVENT_PHONE_STATE_CHANGED);
+
+ updateQxdmState(null);
+ mOem = new OemCommands();
+
+ phone.getPreferredNetworkType(
+ mHandler.obtainMessage(EVENT_QUERY_PREFERRED_TYPE_DONE));
+ phone.getNeighboringCids(
+ mHandler.obtainMessage(EVENT_QUERY_NEIGHBORING_CIDS_DONE));
+
+ netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat"));
+
+ CellLocation.requestLocationUpdate();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ updatePhoneState();
+ updateSignalStrength();
+ updateMessageWaiting();
+ updateCallRedirect();
+ updateServiceState();
+ updateLocation(mTelephonyManager.getCellLocation());
+ updateDataState();
+ updateDataStats();
+ updateDataStats2();
+ updatePowerState();
+ updateQxdmState(null);
+ updateProperties();
+ updateCiphState();
+ updateDnsCheckState();
+
+ Log.i(TAG, "[RadioInfo] onResume: register phone & data intents");
+
+ mPhoneStateReceiver.registerIntent();
+ mTelephonyManager.listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_DATA_ACTIVITY
+ | PhoneStateListener.LISTEN_CELL_LOCATION
+ | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+ | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ Log.i(TAG, "[RadioInfo] onPause: unregister phone & data intents");
+
+ mPhoneStateReceiver.unregisterIntent();
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_ITEM_SELECT_BAND, 0, R.string.radio_info_band_mode_label).setOnMenuItemClickListener(mSelectBandCallback)
+ .setAlphabeticShortcut('b');
+ menu.add(1, MENU_ITEM_VIEW_ADN, 0,
+ R.string.radioInfo_menu_viewADN).setOnMenuItemClickListener(mViewADNCallback);
+ menu.add(1, MENU_ITEM_VIEW_FDN, 0,
+ R.string.radioInfo_menu_viewFDN).setOnMenuItemClickListener(mViewFDNCallback);
+ menu.add(1, MENU_ITEM_VIEW_SDN, 0,
+ R.string.radioInfo_menu_viewSDN).setOnMenuItemClickListener(mViewSDNCallback);
+ menu.add(1, MENU_ITEM_GET_PDP_LIST,
+ 0, R.string.radioInfo_menu_getPDP).setOnMenuItemClickListener(mGetPdpList);
+ menu.add(1, MENU_ITEM_TOGGLE_DATA,
+ 0, R.string.radioInfo_menu_disableData).setOnMenuItemClickListener(mToggleData);
+ menu.add(1, MENU_ITEM_TOGGLE_DATA_ON_BOOT,
+ 0, R.string.radioInfo_menu_disableDataOnBoot).setOnMenuItemClickListener(mToggleDataOnBoot);
+ return true;
+ }
+
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu)
+ {
+ // Get the TOGGLE DATA menu item in the right state.
+ MenuItem item = menu.findItem(MENU_ITEM_TOGGLE_DATA);
+ int state = mTelephonyManager.getDataState();
+ boolean visible = true;
+
+ switch (state) {
+ case TelephonyManager.DATA_CONNECTED:
+ case TelephonyManager.DATA_SUSPENDED:
+ item.setTitle(R.string.radioInfo_menu_disableData);
+ break;
+ case TelephonyManager.DATA_DISCONNECTED:
+ item.setTitle(R.string.radioInfo_menu_enableData);
+ break;
+ default:
+ visible = false;
+ break;
+ }
+ item.setVisible(visible);
+
+ // Get the toggle-data-on-boot menu item in the right state.
+ item = menu.findItem(MENU_ITEM_TOGGLE_DATA_ON_BOOT);
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this.getApplication());
+ boolean value = sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false);
+ if (value) {
+ item.setTitle(R.string.radioInfo_menu_enableDataOnBoot);
+ } else {
+ item.setTitle(R.string.radioInfo_menu_disableDataOnBoot);
+ }
+ return true;
+ }
+
+ private boolean isRadioOn() {
+ return phone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
+ }
+
+ private void updatePowerState() {
+ //log("updatePowerState");
+ String buttonText = isRadioOn() ?
+ getString(R.string.turn_off_radio) :
+ getString(R.string.turn_on_radio);
+ radioPowerButton.setText(buttonText);
+ }
+
+ private void updateQxdmState(Boolean newQxdmStatus) {
+ SharedPreferences sp =
+ PreferenceManager.getDefaultSharedPreferences(this.getApplication());
+ mQxdmLogEnabled = sp.getBoolean("qxdmstatus", false);
+ // This is called from onCreate, onResume, and the handler when the status
+ // is updated.
+ if (newQxdmStatus != null) {
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putBoolean("qxdmstatus", newQxdmStatus);
+ editor.commit();
+ mQxdmLogEnabled = newQxdmStatus;
+ }
+
+ String buttonText = mQxdmLogEnabled ?
+ getString(R.string.turn_off_qxdm) :
+ getString(R.string.turn_on_qxdm);
+ qxdmLogButton.setText(buttonText);
+ }
+
+ private void setCiphPref(boolean value) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this.getApplication());
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putBoolean(GSMPhone.CIPHERING_KEY, value);
+ editor.commit();
+ }
+
+ private boolean getCiphPref() {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this.getApplication());
+ boolean ret = sp.getBoolean(GSMPhone.CIPHERING_KEY, true);
+ return ret;
+ }
+
+ private void updateCiphState() {
+ cipherState.setText(getCiphPref() ? "Ciphering ON" : "Ciphering OFF");
+ }
+
+ private void updateDnsCheckState() {
+ GSMPhone gsmPhone = (GSMPhone) phone;
+ dnsCheckState.setText(gsmPhone.isDnsCheckDisabled() ?
+ "0.0.0.0 allowed" :"0.0.0.0 not allowed");
+ }
+
+ private final void
+ updateSignalStrength() {
+ int state =
+ mPhoneStateReceiver.getServiceState().getState();
+ Resources r = getResources();
+
+ if ((ServiceState.STATE_OUT_OF_SERVICE == state) ||
+ (ServiceState.STATE_POWER_OFF == state)) {
+ dBm.setText("0");
+ }
+
+ int signalDbm = mPhoneStateReceiver.getSignalStrengthDbm();
+
+ if (-1 == signalDbm) signalDbm = 0;
+
+ int signalAsu = mPhoneStateReceiver.getSignalStrength();
+
+ if (-1 == signalAsu) signalAsu = 0;
+
+ dBm.setText(String.valueOf(signalDbm) + " "
+ + r.getString(R.string.radioInfo_display_dbm) + " "
+ + String.valueOf(signalAsu) + " "
+ + r.getString(R.string.radioInfo_display_asu));
+ }
+
+ private final void updateLocation(CellLocation location) {
+ GsmCellLocation loc = (GsmCellLocation)location;
+ Resources r = getResources();
+
+ int lac = loc.getLac();
+ int cid = loc.getCid();
+
+ mLocation.setText(r.getString(R.string.radioInfo_lac) + " = "
+ + ((lac == -1) ? "unknown" : Integer.toHexString(lac))
+ + " "
+ + r.getString(R.string.radioInfo_cid) + " = "
+ + ((cid == -1) ? "unknown" : Integer.toHexString(cid)));
+ }
+
+ private final void updateNeighboringCids(ArrayList cids) {
+ String neighborings = "";
+ if (cids != null) {
+ if ( cids.isEmpty() ) {
+ neighborings = "no neighboring cells";
+ } else {
+ for (NeighboringCellInfo cell : cids) {
+ neighborings += "{" + Integer.toHexString(cell.getCid())
+ + "@" + cell.getRssi() + "} ";
+ }
+ }
+ } else {
+ neighborings = "unknown";
+ }
+ mNeighboringCids.setText(neighborings);
+ }
+
+ private final void
+ updateMessageWaiting() {
+ mMwi.setText(String.valueOf(mMwiValue));
+ }
+
+ private final void
+ updateCallRedirect() {
+ mCfi.setText(String.valueOf(mCfiValue));
+ }
+
+
+ private final void
+ updateServiceState() {
+ ServiceState serviceState = mPhoneStateReceiver.getServiceState();
+ int state = serviceState.getState();
+ Resources r = getResources();
+ String display = r.getString(R.string.radioInfo_unknown);
+
+ switch (state) {
+ case ServiceState.STATE_IN_SERVICE:
+ display = r.getString(R.string.radioInfo_service_in);
+ break;
+ case ServiceState.STATE_OUT_OF_SERVICE:
+ case ServiceState.STATE_EMERGENCY_ONLY:
+ display = r.getString(R.string.radioInfo_service_emergency);
+ break;
+ case ServiceState.STATE_POWER_OFF:
+ display = r.getString(R.string.radioInfo_service_off);
+ break;
+ }
+
+ gsmState.setText(display);
+
+ if (serviceState.getRoaming()) {
+ roamingState.setText(R.string.radioInfo_roaming_in);
+ } else {
+ roamingState.setText(R.string.radioInfo_roaming_not);
+ }
+
+ operatorName.setText(serviceState.getOperatorAlphaLong());
+ }
+
+ private final void
+ updatePhoneState() {
+ Phone.State state = mPhoneStateReceiver.getPhoneState();
+ Resources r = getResources();
+ String display = r.getString(R.string.radioInfo_unknown);
+
+ switch (state) {
+ case IDLE:
+ display = r.getString(R.string.radioInfo_phone_idle);
+ break;
+ case RINGING:
+ display = r.getString(R.string.radioInfo_phone_ringing);
+ break;
+ case OFFHOOK:
+ display = r.getString(R.string.radioInfo_phone_offhook);
+ break;
+ }
+
+ callState.setText(display);
+ }
+
+ private final void
+ updateDataState() {
+ int state = mTelephonyManager.getDataState();
+ Resources r = getResources();
+ String display = r.getString(R.string.radioInfo_unknown);
+
+ switch (state) {
+ case TelephonyManager.DATA_CONNECTED:
+ display = r.getString(R.string.radioInfo_data_connected);
+ break;
+ case TelephonyManager.DATA_CONNECTING:
+ display = r.getString(R.string.radioInfo_data_connecting);
+ break;
+ case TelephonyManager.DATA_DISCONNECTED:
+ display = r.getString(R.string.radioInfo_data_disconnected);
+ break;
+ case TelephonyManager.DATA_SUSPENDED:
+ display = r.getString(R.string.radioInfo_data_suspended);
+ break;
+ }
+
+ gprsState.setText(display);
+ }
+
+ private final void updateNetworkType() {
+ Resources r = getResources();
+ String display = SystemProperties.get(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
+ r.getString(R.string.radioInfo_unknown));
+
+ network.setText(display);
+ }
+
+ private final void
+ updateProperties() {
+ String s;
+ Resources r = getResources();
+
+ s = phone.getDeviceId();
+ if (s == null) s = r.getString(R.string.radioInfo_unknown);
+ mImei.setText(s);
+
+ s = phone.getLine1Number();
+ if (s == null) s = r.getString(R.string.radioInfo_unknown);
+ number.setText(s);
+ }
+
+ private final void updateDataStats() {
+ String s;
+
+ s = SystemProperties.get("net.gsm.radio-reset", "0");
+ resets.setText(s);
+
+ s = SystemProperties.get("net.gsm.attempt-gprs", "0");
+ attempts.setText(s);
+
+ s = SystemProperties.get("net.gsm.succeed-gprs", "0");
+ successes.setText(s);
+
+ //s = SystemProperties.get("net.gsm.disconnect", "0");
+ //disconnects.setText(s);
+
+ s = SystemProperties.get("net.ppp.reset-by-timeout", "0");
+ sentSinceReceived.setText(s);
+ }
+
+ private final void updateDataStats2() {
+ Resources r = getResources();
+
+ try {
+ long txPackets = netstat.getMobileTxPackets();
+ long rxPackets = netstat.getMobileRxPackets();
+ long txBytes = netstat.getMobileTxBytes();
+ long rxBytes = netstat.getMobileRxBytes();
+
+ String packets = r.getString(R.string.radioInfo_display_packets);
+ String bytes = r.getString(R.string.radioInfo_display_bytes);
+
+ sent.setText(txPackets + " " + packets + ", " + txBytes + " " + bytes);
+ received.setText(rxPackets + " " + packets + ", " + rxBytes + " " + bytes);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Ping a IP address.
+ */
+ private final void pingIpAddr() {
+ try {
+ // This is hardcoded IP addr. This is for testing purposes.
+ // We would need to get rid of this before release.
+ String ipAddress = "74.125.47.104";
+ Process p = Runtime.getRuntime().exec("ping -c 1 " + ipAddress);
+ int status = p.waitFor();
+ if (status == 0) {
+ mPingIpAddrResult = "Pass";
+ } else {
+ mPingIpAddrResult = "Fail: IP addr not reachable";
+ }
+ } catch (IOException e) {
+ mPingIpAddrResult = "Fail: IOException";
+ } catch (InterruptedException e) {
+ mPingIpAddrResult = "Fail: InterruptedException";
+ }
+ }
+
+ /**
+ * Ping a host name
+ */
+ private final void pingHostname() {
+ try {
+ Process p = Runtime.getRuntime().exec("ping -c 1 www.google.com");
+ int status = p.waitFor();
+ if (status == 0) {
+ mPingHostnameResult = "Pass";
+ } else {
+ mPingHostnameResult = "Fail: Host unreachable";
+ }
+ } catch (UnknownHostException e) {
+ mPingHostnameResult = "Fail: Unknown Host";
+ } catch (IOException e) {
+ mPingHostnameResult= "Fail: IOException";
+ } catch (InterruptedException e) {
+ mPingHostnameResult = "Fail: InterruptedException";
+ }
+ }
+
+ /**
+ * This function checks for basic functionality of HTTP Client.
+ */
+ private void httpClientTest() {
+ HttpClient client = new DefaultHttpClient();
+ try {
+ HttpGet request = new HttpGet("http://www.google.com");
+ HttpResponse response = client.execute(request);
+ if (response.getStatusLine().getStatusCode() == 200) {
+ mHttpClientTestResult = "Pass";
+ } else {
+ mHttpClientTestResult = "Fail: Code: " + String.valueOf(response);
+ }
+ request.abort();
+ } catch (IOException e) {
+ mHttpClientTestResult = "Fail: IOException";
+ }
+ }
+
+ private void refreshSmsc() {
+ byte[] data = mOem.getSmscQueryData();
+ if (data == null) return;
+ phone.invokeOemRilRequestRaw(data,
+ mHandler.obtainMessage(EVENT_QUERY_SMSC_DONE));
+ }
+
+ private final void updatePingState() {
+ final Handler handler = new Handler();
+ // Set all to unknown since the threads will take a few secs to update.
+ mPingIpAddrResult = getResources().getString(R.string.radioInfo_unknown);
+ mPingHostnameResult = getResources().getString(R.string.radioInfo_unknown);
+ mHttpClientTestResult = getResources().getString(R.string.radioInfo_unknown);
+
+ mPingIpAddr.setText(mPingIpAddrResult);
+ mPingHostname.setText(mPingHostnameResult);
+ mHttpClientTest.setText(mHttpClientTestResult);
+
+ final Runnable updatePingResults = new Runnable() {
+ public void run() {
+ mPingIpAddr.setText(mPingIpAddrResult);
+ mPingHostname.setText(mPingHostnameResult);
+ mHttpClientTest.setText(mHttpClientTestResult);
+ }
+ };
+ Thread ipAddr = new Thread() {
+ @Override
+ public void run() {
+ pingIpAddr();
+ handler.post(updatePingResults);
+ }
+ };
+ ipAddr.start();
+
+ Thread hostname = new Thread() {
+ @Override
+ public void run() {
+ pingHostname();
+ handler.post(updatePingResults);
+ }
+ };
+ hostname.start();
+
+ Thread httpClient = new Thread() {
+ @Override
+ public void run() {
+ httpClientTest();
+ handler.post(updatePingResults);
+ }
+ };
+ httpClient.start();
+ }
+
+ private final void updatePdpList() {
+ StringBuilder sb = new StringBuilder("========DATA=======\n");
+
+ List pdps = phone.getCurrentPdpList();
+
+ for (PdpConnection pdp : pdps) {
+ sb.append(" State: ").append(pdp.getState().toString()).append("\n");
+ if (pdp.getState().isActive()) {
+ long timeElapsed =
+ (System.currentTimeMillis() - pdp.getConnectionTime())/1000;
+ sb.append(" connected at ")
+ .append(DateUtils.timeString(pdp.getConnectionTime()))
+ .append(" and elapsed ")
+ .append(DateUtils.formatElapsedTime(timeElapsed))
+ .append("\n to ")
+ .append(pdp.getApn().toString())
+ .append("\ninterface: ")
+ .append(phone.getInterfaceName(phone.getActiveApnTypes()[0]))
+ .append("\naddress: ")
+ .append(phone.getIpAddress(phone.getActiveApnTypes()[0]))
+ .append("\ngateway: ")
+ .append(phone.getGateway(phone.getActiveApnTypes()[0]));
+ String[] dns = phone.getDnsServers(phone.getActiveApnTypes()[0]);
+ if (dns != null) {
+ sb.append("\ndns: ").append(dns[0]).append(", ").append(dns[1]);
+ }
+ } else if (pdp.getState().isInactive()) {
+ sb.append(" disconnected with last try at ")
+ .append(DateUtils.timeString(pdp.getLastFailTime()))
+ .append("\n fail because ")
+ .append(pdp.getLastFailCause().toString());
+ } else {
+ sb.append(" is connecting to ")
+ .append(pdp.getApn().toString());
+ }
+ sb.append("\n===================");
+ }
+
+
+ disconnects.setText(sb.toString());
+ }
+
+ private void displayQxdmEnableResult() {
+ String status = mQxdmLogEnabled ? "Start QXDM Log" : "Stop QXDM Log";
+
+ DialogInterface mProgressPanel = new AlertDialog.
+ Builder(this).setMessage(status).show();
+
+ mHandler.postDelayed(
+ new Runnable() {
+ public void run() {
+ finish();
+ }
+ }, 2000);
+ }
+
+ private MenuItem.OnMenuItemClickListener mViewADNCallback = new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ // XXX We need to specify the component here because if we don't
+ // the activity manager will try to resolve the type by calling
+ // the content provider, which causes it to be loaded in a process
+ // other than the Dialer process, which causes a lot of stuff to
+ // break.
+ intent.setClassName("com.android.phone",
+ "com.android.phone.SimContacts");
+ startActivity(intent);
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mViewFDNCallback = new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ // XXX We need to specify the component here because if we don't
+ // the activity manager will try to resolve the type by calling
+ // the content provider, which causes it to be loaded in a process
+ // other than the Dialer process, which causes a lot of stuff to
+ // break.
+ intent.setClassName("com.android.phone",
+ "com.android.phone.FdnList");
+ startActivity(intent);
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mViewSDNCallback = new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(
+ Intent.ACTION_VIEW, Uri.parse("content://sim/sdn"));
+ // XXX We need to specify the component here because if we don't
+ // the activity manager will try to resolve the type by calling
+ // the content provider, which causes it to be loaded in a process
+ // other than the Dialer process, which causes a lot of stuff to
+ // break.
+ intent.setClassName("com.android.phone",
+ "com.android.phone.ADNList");
+ startActivity(intent);
+ return true;
+ }
+ };
+
+ private void toggleDataDisabledOnBoot() {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this.getApplication());
+ SharedPreferences.Editor editor = sp.edit();
+ boolean value = sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false);
+ editor.putBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, !value);
+ byte[] data = mOem.getPsAutoAttachData(value);
+ if (data == null) {
+ // don't commit
+ return;
+ }
+
+ editor.commit();
+ phone.invokeOemRilRequestRaw(data, null);
+ }
+
+ private MenuItem.OnMenuItemClickListener mToggleDataOnBoot = new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ toggleDataDisabledOnBoot();
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mToggleData = new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ int state = mTelephonyManager.getDataState();
+ switch (state) {
+ case TelephonyManager.DATA_CONNECTED:
+ phone.disableDataConnectivity();
+ break;
+ case TelephonyManager.DATA_DISCONNECTED:
+ phone.enableDataConnectivity();
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mGetPdpList = new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ phone.getPdpContextList(null);
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mSelectBandCallback = new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent();
+ intent.setClass(RadioInfo.this, BandMode.class);
+ startActivity(intent);
+ return true;
+ }
+ };
+
+ OnClickListener mPowerButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ //log("toggle radio power: currently " + (isRadioOn()?"on":"off"));
+ phone.setRadioPower(!isRadioOn());
+ }
+ };
+
+ OnClickListener mCipherButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ mCipherOn = !getCiphPref();
+ byte[] data = mOem.getCipheringData(mCipherOn);
+
+ if (data == null)
+ return;
+
+ cipherState.setText("Setting...");
+ phone.invokeOemRilRequestRaw(data,
+ mHandler.obtainMessage(EVENT_SET_CIPHER_DONE));
+ }
+ };
+
+ OnClickListener mDnsCheckButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ GSMPhone gsmPhone = (GSMPhone) phone;
+ gsmPhone.disableDnsCheck(!gsmPhone.isDnsCheckDisabled());
+ updateDnsCheckState();
+ }
+ };
+
+ OnClickListener mPingButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ updatePingState();
+ }
+ };
+
+ OnClickListener mUpdateSmscButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ updateSmscButton.setEnabled(false);
+ byte[] data = mOem.getSmscUpdateData(smsc.getText().toString());
+ if (data == null) return;
+ phone.invokeOemRilRequestRaw(data,
+ mHandler.obtainMessage(EVENT_UPDATE_SMSC_DONE));
+ }
+ };
+
+ OnClickListener mRefreshSmscButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ refreshSmsc();
+ }
+ };
+
+ OnClickListener mQxdmButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ byte[] data = mOem.getQxdmSdlogData(
+ !mQxdmLogEnabled,
+ mOem.OEM_QXDM_SDLOG_DEFAULT_FILE_SIZE,
+ mOem.OEM_QXDM_SDLOG_DEFAULT_MASK,
+ mOem.OEM_QXDM_SDLOG_DEFAULT_MAX_INDEX);
+
+ if (data == null)
+ return;
+
+ phone.invokeOemRilRequestRaw(data,
+ mHandler.obtainMessage(EVENT_SET_QXDMLOG_DONE));
+ }
+ };
+
+ AdapterView.OnItemSelectedListener
+ mPreferredNetworkHandler = new AdapterView.OnItemSelectedListener() {
+ public void onItemSelected(AdapterView parent, View v, int pos, long id) {
+ Message msg = mHandler.obtainMessage(EVENT_SET_PREFERRED_TYPE_DONE);
+ if (pos>=0 && pos<=2) {
+ phone.setPreferredNetworkType(pos, msg);
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+ };
+
+ private String[] mPreferredNetworkLabels = {
+ "WCDMA preferred", "GSM only", "WCDMA only", "Unknown"};
+}
diff --git a/src/com/android/settings/RingerVolumePreference.java b/src/com/android/settings/RingerVolumePreference.java
new file mode 100644
index 00000000000..2d21ec6e997
--- /dev/null
+++ b/src/com/android/settings/RingerVolumePreference.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 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.Context;
+import android.media.AudioManager;
+import android.preference.VolumePreference;
+import android.preference.VolumePreference.SeekBarVolumizer;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+/**
+ * Special preference type that allows configuration of both the ring volume and
+ * notification volume.
+ */
+public class RingerVolumePreference extends VolumePreference implements
+ CheckBox.OnCheckedChangeListener {
+ private static final String TAG = "RingerVolumePreference";
+
+ private CheckBox mNotificationsUseRingVolumeCheckbox;
+ private SeekBarVolumizer mNotificationSeekBarVolumizer;
+ private TextView mNotificationVolumeTitle;
+
+ public RingerVolumePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // The always visible seekbar is for ring volume
+ setStreamType(AudioManager.STREAM_RING);
+
+ setDialogLayoutResource(R.layout.preference_dialog_ringervolume);
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ mNotificationsUseRingVolumeCheckbox =
+ (CheckBox) view.findViewById(R.id.same_notification_volume);
+ mNotificationsUseRingVolumeCheckbox.setOnCheckedChangeListener(this);
+ mNotificationsUseRingVolumeCheckbox.setChecked(Settings.System.getInt(
+ getContext().getContentResolver(),
+ Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1);
+
+ final SeekBar seekBar = (SeekBar) view.findViewById(R.id.notification_volume_seekbar);
+ mNotificationSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar,
+ AudioManager.STREAM_NOTIFICATION);
+
+ mNotificationVolumeTitle = (TextView) view.findViewById(R.id.notification_volume_title);
+
+ setNotificationVolumeVisibility(!mNotificationsUseRingVolumeCheckbox.isChecked());
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (!positiveResult && mNotificationSeekBarVolumizer != null) {
+ mNotificationSeekBarVolumizer.revertVolume();
+ }
+
+ cleanup();
+ }
+
+ @Override
+ public void onActivityStop() {
+ super.onActivityStop();
+ cleanup();
+ }
+
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setNotificationVolumeVisibility(!isChecked);
+
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATIONS_USE_RING_VOLUME, isChecked ? 1 : 0);
+
+ if (isChecked) {
+ // The user wants the notification to be same as ring, so do a
+ // one-time sync right now
+ AudioManager audioManager = (AudioManager) getContext()
+ .getSystemService(Context.AUDIO_SERVICE);
+ audioManager.setStreamVolume(AudioManager.STREAM_NOTIFICATION,
+ audioManager.getStreamVolume(AudioManager.STREAM_RING), 0);
+ }
+ }
+
+ @Override
+ protected void onSampleStarting(SeekBarVolumizer volumizer) {
+ super.onSampleStarting(volumizer);
+
+ if (mNotificationSeekBarVolumizer != null && volumizer != mNotificationSeekBarVolumizer) {
+ mNotificationSeekBarVolumizer.stopSample();
+ }
+ }
+
+ private void setNotificationVolumeVisibility(boolean visible) {
+ if (mNotificationSeekBarVolumizer != null) {
+ mNotificationSeekBarVolumizer.getSeekBar().setVisibility(
+ visible ? View.VISIBLE : View.GONE);
+ mNotificationVolumeTitle.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ private void cleanup() {
+ if (mNotificationSeekBarVolumizer != null) {
+ mNotificationSeekBarVolumizer.stop();
+ mNotificationSeekBarVolumizer = null;
+ }
+ }
+
+}
diff --git a/src/com/android/settings/SdCardIntentReceiver.java b/src/com/android/settings/SdCardIntentReceiver.java
new file mode 100644
index 00000000000..9648ec1132c
--- /dev/null
+++ b/src/com/android/settings/SdCardIntentReceiver.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 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.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.util.Config;
+import android.util.Log;
+
+/**
+ *
+ */
+public class SdCardIntentReceiver extends BroadcastReceiver {
+
+ private static final int SDCARD_STATUS = 1;
+ private static final String TAG = "SdCardIntentReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ NotificationManager nm = (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ String action = intent.getAction();
+ if (Config.LOGD) Log.d(TAG, "onReceiveIntent " + action);
+
+ if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
+ nm.cancel(SDCARD_STATUS);
+
+ Intent statusIntent = new Intent(Intent.ACTION_MAIN, null);
+ statusIntent.setClass(context, SdCardSettings.class);
+ nm.notify(SDCARD_STATUS, new Notification(context,
+ android.R.drawable.stat_notify_sdcard,
+ null,
+ System.currentTimeMillis(),
+ context.getText(R.string.sdcard_setting),
+ null,
+ statusIntent));
+ } else if (action.equals(Intent.ACTION_MEDIA_REMOVED)) {
+ nm.cancel(SDCARD_STATUS);
+ } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
+ nm.cancel(SDCARD_STATUS);
+
+ Intent statusIntent = new Intent(Intent.ACTION_MAIN, null);
+ statusIntent.setClass(context, SdCardSettings.class);
+ nm.notify(SDCARD_STATUS, new Notification(context,
+ android.R.drawable.stat_notify_sdcard_usb,
+ null,
+ System.currentTimeMillis(),
+ "SD Card",
+ null,
+ statusIntent));
+ }
+ }
+}
diff --git a/src/com/android/settings/SdCardSettings.java b/src/com/android/settings/SdCardSettings.java
new file mode 100644
index 00000000000..b6935a2fe4a
--- /dev/null
+++ b/src/com/android/settings/SdCardSettings.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.Environment;
+import android.os.IMountService;
+import android.os.ServiceManager;
+import android.os.StatFs;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import java.io.File;
+
+
+public class SdCardSettings extends Activity
+{
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.sdcard_settings_screen);
+
+ mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
+
+ mRemovedLayout = findViewById(R.id.removed);
+ mMountedLayout = findViewById(R.id.mounted);
+ mUnmountedLayout = findViewById(R.id.unmounted);
+ mScanningLayout = findViewById(R.id.scanning);
+ mSharedLayout = findViewById(R.id.shared);
+ mBadRemovalLayout = findViewById(R.id.bad_removal);
+ mReadOnlyStatus = findViewById(R.id.read_only);
+
+ mMassStorage = (CheckBox)findViewById(R.id.mass_storage);
+ mMassStorage.setOnClickListener(mMassStorageListener);
+
+ Button unmountButton = (Button)findViewById(R.id.sdcard_unmount);
+ unmountButton.setOnClickListener(mUnmountButtonHandler);
+
+ Button formatButton = (Button)findViewById(R.id.sdcard_format);
+ formatButton.setOnClickListener(mFormatButtonHandler);
+
+ mTotalSize = (TextView)findViewById(R.id.total);
+ mUsedSize = (TextView)findViewById(R.id.used);
+ mAvailableSize = (TextView)findViewById(R.id.available);
+
+ // install an intent filter to receive SD card related events.
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_REMOVED);
+ intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
+ intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
+ intentFilter.addAction(Intent.ACTION_MEDIA_SHARED);
+ intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING);
+ intentFilter.addAction(Intent.ACTION_MEDIA_NOFS);
+ intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
+ intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
+ intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
+ intentFilter.addDataScheme("file");
+ registerReceiver(mReceiver, intentFilter);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ update();
+ }
+
+ private void setLayout(View layout) {
+ mRemovedLayout.setVisibility(layout == mRemovedLayout ? View.VISIBLE : View.GONE);
+ mMountedLayout.setVisibility(layout == mMountedLayout ? View.VISIBLE : View.GONE);
+ mUnmountedLayout.setVisibility(layout == mUnmountedLayout ? View.VISIBLE : View.GONE);
+ mScanningLayout.setVisibility(layout == mScanningLayout ? View.VISIBLE : View.GONE);
+ mSharedLayout.setVisibility(layout == mSharedLayout ? View.VISIBLE : View.GONE);
+ mBadRemovalLayout.setVisibility(layout == mBadRemovalLayout ? View.VISIBLE : View.GONE);
+ }
+
+ private void update() {
+ try {
+ mMassStorage.setChecked(mMountService.getMassStorageEnabled());
+ } catch (RemoteException ex) {
+ }
+
+ String scanVolume = null; // this no longer exists: SystemProperties.get(MediaScanner.CURRENT_VOLUME_PROPERTY, "");
+ boolean scanning = "external".equals(scanVolume);
+
+ if (scanning) {
+ setLayout(mScanningLayout);
+ } else {
+ String status = Environment.getExternalStorageState();
+ boolean readOnly = false;
+
+ if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
+ status = Environment.MEDIA_MOUNTED;
+ readOnly = true;
+ }
+
+ if (status.equals(Environment.MEDIA_MOUNTED)) {
+ try {
+ File path = Environment.getExternalStorageDirectory();
+ StatFs stat = new StatFs(path.getPath());
+ long blockSize = stat.getBlockSize();
+ long totalBlocks = stat.getBlockCount();
+ long availableBlocks = stat.getAvailableBlocks();
+
+ mTotalSize.setText(formatSize(totalBlocks * blockSize));
+ mUsedSize.setText(formatSize((totalBlocks - availableBlocks) * blockSize));
+ mAvailableSize.setText(formatSize(availableBlocks * blockSize));
+ } catch (IllegalArgumentException e) {
+ // this can occur if the SD card is removed, but we haven't received the
+ // ACTION_MEDIA_REMOVED Intent yet.
+ status = Environment.MEDIA_REMOVED;
+ }
+
+ mReadOnlyStatus.setVisibility(readOnly ? View.VISIBLE : View.GONE);
+ setLayout(mMountedLayout);
+ } else if (status.equals(Environment.MEDIA_UNMOUNTED)) {
+ setLayout(mUnmountedLayout);
+ } else if (status.equals(Environment.MEDIA_REMOVED)) {
+ setLayout(mRemovedLayout);
+ } else if (status.equals(Environment.MEDIA_SHARED)) {
+ setLayout(mSharedLayout);
+ } else if (status.equals(Environment.MEDIA_BAD_REMOVAL)) {
+ setLayout(mBadRemovalLayout);
+ }
+ }
+ }
+
+ private String formatSize(long size) {
+ String suffix = null;
+
+ // add K or M suffix if size is greater than 1K or 1M
+ if (size >= 1024) {
+ suffix = "K";
+ size /= 1024;
+ if (size >= 1024) {
+ suffix = "M";
+ size /= 1024;
+ }
+ }
+
+ StringBuilder resultBuffer = new StringBuilder(Long.toString(size));
+
+ int commaOffset = resultBuffer.length() - 3;
+ while (commaOffset > 0) {
+ resultBuffer.insert(commaOffset, ',');
+ commaOffset -= 3;
+ }
+
+ if (suffix != null)
+ resultBuffer.append(suffix);
+ return resultBuffer.toString();
+ }
+
+ OnClickListener mMassStorageListener = new OnClickListener() {
+ public void onClick(View v) {
+ try {
+ mMountService.setMassStorageEnabled(mMassStorage.isChecked());
+ } catch (RemoteException ex) {
+ }
+ }
+ };
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ update();
+ }
+ };
+
+ OnClickListener mUnmountButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ try {
+ mMountService.unmountMedia(Environment.getExternalStorageDirectory().toString());
+ } catch (RemoteException ex) {
+ }
+ }
+ };
+
+ OnClickListener mFormatButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ try {
+ mMountService.formatMedia(Environment.getExternalStorageDirectory().toString());
+ } catch (RemoteException ex) {
+ }
+ }
+ };
+
+
+ private int mStatus;
+ private IMountService mMountService;
+
+ private CheckBox mMassStorage;
+
+ private TextView mTotalSize;
+ private TextView mUsedSize;
+ private TextView mAvailableSize;
+
+ private View mRemovedLayout;
+ private View mMountedLayout;
+ private View mUnmountedLayout;
+ private View mScanningLayout;
+ private View mSharedLayout;
+ private View mBadRemovalLayout;
+ private View mReadOnlyStatus;
+}
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
new file mode 100644
index 00000000000..cd26492a944
--- /dev/null
+++ b/src/com/android/settings/SecuritySettings.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2007 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.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.util.Config;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * Gesture lock pattern settings.
+ */
+public class SecuritySettings extends PreferenceActivity
+ implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ // Lock Settings
+
+ private static final String KEY_LOCK_ENABLED = "lockenabled";
+ private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
+ private static final String KEY_TACTILE_FEEDBACK_ENABLED = "tactilefeedback";
+ private static final int CONFIRM_PATTERN_THEN_DISABLE_REQUEST_CODE = 55;
+ private static final int CONFIRM_PATTERN_THEN_ENABLE_REQUEST_CODE = 56;
+
+ private LockPatternUtils mLockPatternUtils;
+ private CheckBoxPreference mLockEnabled;
+ private CheckBoxPreference mVisiblePattern;
+ private CheckBoxPreference mTactileFeedback;
+ private Preference mChoosePattern;
+
+ private CheckBoxPreference mShowPassword;
+
+ // Location Settings
+
+ private static final String LOCATION_NETWORK = "location_network";
+ private static final String LOCATION_GPS = "location_gps";
+
+ private CheckBoxPreference mNetwork;
+ private CheckBoxPreference mGps;
+ private LocationManager mLocationManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.security_settings);
+
+ mLockPatternUtils = new LockPatternUtils(getContentResolver());
+
+ createPreferenceHierarchy();
+
+ // Get the available location providers
+ mLocationManager = (LocationManager)
+ getSystemService(Context.LOCATION_SERVICE);
+
+ mNetwork = (CheckBoxPreference) getPreferenceScreen().findPreference(LOCATION_NETWORK);
+ mGps = (CheckBoxPreference) getPreferenceScreen().findPreference(LOCATION_GPS);
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ updateToggles();
+ }
+
+ private PreferenceScreen createPreferenceHierarchy() {
+ // Root
+ PreferenceScreen root = this.getPreferenceScreen();
+
+ // Inline preferences
+ PreferenceCategory inlinePrefCat = new PreferenceCategory(this);
+ inlinePrefCat.setTitle(R.string.lock_settings_title);
+ root.addPreference(inlinePrefCat);
+
+ // autolock toggle
+ mLockEnabled = new LockEnabledPref(this);
+ mLockEnabled.setTitle(R.string.lockpattern_settings_enable_title);
+ mLockEnabled.setSummary(R.string.lockpattern_settings_enable_summary);
+ mLockEnabled.setKey(KEY_LOCK_ENABLED);
+ inlinePrefCat.addPreference(mLockEnabled);
+
+ // visible pattern
+ mVisiblePattern = new CheckBoxPreference(this);
+ mVisiblePattern.setKey(KEY_VISIBLE_PATTERN);
+ mVisiblePattern.setTitle(R.string.lockpattern_settings_enable_visible_pattern_title);
+ inlinePrefCat.addPreference(mVisiblePattern);
+
+ // tactile feedback
+ mTactileFeedback = new CheckBoxPreference(this);
+ mTactileFeedback.setKey(KEY_TACTILE_FEEDBACK_ENABLED);
+ mTactileFeedback.setTitle(R.string.lockpattern_settings_enable_tactile_feedback_title);
+ inlinePrefCat.addPreference(mTactileFeedback);
+
+ // change pattern lock
+ Intent intent = new Intent();
+ intent.setClassName("com.android.settings",
+ "com.android.settings.ChooseLockPatternTutorial");
+ mChoosePattern = getPreferenceManager().createPreferenceScreen(this);
+ mChoosePattern.setIntent(intent);
+ inlinePrefCat.addPreference(mChoosePattern);
+
+ PreferenceScreen simLockPreferences = getPreferenceManager()
+ .createPreferenceScreen(this);
+ simLockPreferences.setTitle(R.string.sim_lock_settings_category);
+ // Intent to launch SIM lock settings
+ intent = new Intent();
+ intent.setClassName("com.android.settings", "com.android.settings.SimLockSettings");
+ simLockPreferences.setIntent(intent);
+
+ PreferenceCategory simLockCat = new PreferenceCategory(this);
+ simLockCat.setTitle(R.string.sim_lock_settings_title);
+ root.addPreference(simLockCat);
+ simLockCat.addPreference(simLockPreferences);
+
+ // Passwords
+ PreferenceCategory passwordsCat = new PreferenceCategory(this);
+ passwordsCat.setTitle(R.string.security_passwords_title);
+ root.addPreference(passwordsCat);
+
+ CheckBoxPreference showPassword = mShowPassword = new CheckBoxPreference(this);
+ showPassword.setKey("show_password");
+ showPassword.setTitle(R.string.show_password);
+ showPassword.setSummary(R.string.show_password_summary);
+ showPassword.setPersistent(false);
+ passwordsCat.addPreference(showPassword);
+
+ return root;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ boolean patternExists = mLockPatternUtils.savedPatternExists();
+ mLockEnabled.setEnabled(patternExists);
+ mVisiblePattern.setEnabled(patternExists);
+ mTactileFeedback.setEnabled(patternExists);
+
+ mLockEnabled.setChecked(mLockPatternUtils.isLockPatternEnabled());
+ mVisiblePattern.setChecked(mLockPatternUtils.isVisiblePatternEnabled());
+ mTactileFeedback.setChecked(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ int chooseStringRes = mLockPatternUtils.savedPatternExists() ?
+ R.string.lockpattern_settings_change_lock_pattern :
+ R.string.lockpattern_settings_choose_lock_pattern;
+ mChoosePattern.setTitle(chooseStringRes);
+
+ mShowPassword
+ .setChecked(Settings.System.getInt(getContentResolver(),
+ Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+ Preference preference) {
+ final String key = preference.getKey();
+
+ if (KEY_LOCK_ENABLED.equals(key)) {
+ mLockPatternUtils.setLockPatternEnabled(isToggled(preference));
+ } else if (KEY_VISIBLE_PATTERN.equals(key)) {
+ mLockPatternUtils.setVisiblePatternEnabled(isToggled(preference));
+ } else if (KEY_TACTILE_FEEDBACK_ENABLED.equals(key)) {
+ mLockPatternUtils.setTactileFeedbackEnabled(isToggled(preference));
+ } else if (preference == mShowPassword) {
+ Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD,
+ mShowPassword.isChecked() ? 1 : 0);
+ }
+
+ return false;
+ }
+
+ /*
+ * Creates toggles for each available location provider
+ */
+ private void updateToggles() {
+ String providers = getAllowedProviders();
+ mNetwork.setChecked(providers.contains(LocationManager.NETWORK_PROVIDER));
+ mGps.setChecked(providers.contains(LocationManager.GPS_PROVIDER));
+ }
+
+ private void updateProviders() {
+ String preferredProviders = "";
+ if (mNetwork.isChecked()) {
+ preferredProviders += LocationManager.NETWORK_PROVIDER;
+ }
+ if (mGps.isChecked()) {
+ preferredProviders += "," + LocationManager.GPS_PROVIDER;
+ }
+ setProviders(preferredProviders);
+ }
+
+ private void setProviders(String providers) {
+ // Update the secure setting LOCATION_PROVIDERS_ALLOWED
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED, providers);
+ if (Config.LOGV) {
+ Log.v("Location Accuracy", "Setting LOCATION_PROVIDERS_ALLOWED = " + providers);
+ }
+ // Inform the location manager about the changes
+ mLocationManager.updateProviders();
+ }
+
+ /**
+ * @return string containing a list of providers that have been enabled for use
+ */
+ private String getAllowedProviders() {
+ String allowedProviders =
+ Settings.Secure.getString(getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
+ if (allowedProviders == null) {
+ allowedProviders = "";
+ }
+ return allowedProviders;
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
+ if (LOCATION_NETWORK.equals(key) || LOCATION_GPS.equals(key)) {
+ updateProviders();
+ }
+ }
+
+ private boolean isToggled(Preference pref) {
+ return ((CheckBoxPreference) pref).isChecked();
+ }
+
+
+ /**
+ * For the user to disable keyguard, we first make them verify their
+ * existing pattern.
+ */
+ private class LockEnabledPref extends CheckBoxPreference {
+
+ public LockEnabledPref(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onClick() {
+ if (mLockPatternUtils.savedPatternExists()) {
+ if (isChecked()) {
+ confirmPatternThenDisable();
+ } else {
+ confirmPatternThenEnable();
+ }
+ } else {
+ super.onClick();
+ }
+ }
+ }
+
+ private void confirmPatternThenEnable() {
+ final Intent intent = new Intent();
+ intent.setClassName("com.android.settings", "com.android.settings.ConfirmLockPattern");
+ startActivityForResult(intent, CONFIRM_PATTERN_THEN_ENABLE_REQUEST_CODE);
+ }
+
+ /**
+ * Launch screen to confirm the existing lock pattern.
+ * @see #onActivityResult(int, int, android.content.Intent)
+ */
+ private void confirmPatternThenDisable() {
+ final Intent intent = new Intent();
+ intent.setClassName("com.android.settings", "com.android.settings.ConfirmLockPattern");
+ startActivityForResult(intent, CONFIRM_PATTERN_THEN_DISABLE_REQUEST_CODE);
+ }
+
+ /**
+ * @see #confirmPatternThenDisable
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ final boolean resultOk = resultCode == Activity.RESULT_OK;
+
+ if ((requestCode == CONFIRM_PATTERN_THEN_DISABLE_REQUEST_CODE) && resultOk) {
+ mLockPatternUtils.setLockPatternEnabled(false);
+ } else if ((requestCode == CONFIRM_PATTERN_THEN_ENABLE_REQUEST_CODE) && resultOk) {
+ mLockPatternUtils.setLockPatternEnabled(true);
+ }
+ }
+}
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
new file mode 100644
index 00000000000..0c4545eef40
--- /dev/null
+++ b/src/com/android/settings/Settings.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 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.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.provider.Settings.System;
+
+public class Settings extends PreferenceActivity {
+
+ private static final String KEY_PARENT = "parent";
+ private static final String KEY_CALL_SETTINGS = "call_settings";
+ private static final String KEY_SYNC_SETTINGS = "sync_settings";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.settings);
+
+ PreferenceGroup parent = (PreferenceGroup) findPreference(KEY_PARENT);
+ Utils.updatePreferenceToSpecificActivityOrRemove(this, parent, KEY_SYNC_SETTINGS, 0);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ findPreference(KEY_CALL_SETTINGS).setEnabled(!AirplaneModeEnabler.isAirplaneModeOn(this));
+ }
+
+}
diff --git a/src/com/android/settings/SettingsLicenseActivity.java b/src/com/android/settings/SettingsLicenseActivity.java
new file mode 100644
index 00000000000..0b809e19a9d
--- /dev/null
+++ b/src/com/android/settings/SettingsLicenseActivity.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 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.os.Bundle;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Config;
+import android.util.Log;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Toast;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.zip.GZIPInputStream;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+/**
+ * The "dialog" that shows from "License" in the Settings app.
+ */
+public class SettingsLicenseActivity extends AlertActivity {
+
+ private static final String TAG = "SettingsLicenseActivity";
+ private static final boolean LOGV = false || Config.LOGV;
+
+ private static final String DEFAULT_LICENSE_PATH = "/system/etc/NOTICE.html.gz";
+ private static final String PROPERTY_LICENSE_PATH = "ro.config.license_path";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ String fileName = SystemProperties.get(PROPERTY_LICENSE_PATH, DEFAULT_LICENSE_PATH);
+ if (TextUtils.isEmpty(fileName)) {
+ Log.e(TAG, "The system property for the license file is empty.");
+ showErrorAndFinish();
+ return;
+ }
+
+ InputStreamReader inputReader = null;
+ StringBuilder data = null;
+ try {
+ data = new StringBuilder(2048);
+ char tmp[] = new char[2048];
+ int numRead;
+ if (fileName.endsWith(".gz")) {
+ inputReader = new InputStreamReader(
+ new GZIPInputStream(new FileInputStream(fileName)));
+ } else {
+ inputReader = new FileReader(fileName);
+ }
+ while ((numRead = inputReader.read(tmp)) >= 0) {
+ data.append(tmp, 0, numRead);
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "License HTML file not found at " + fileName, e);
+ showErrorAndFinish();
+ return;
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading license HTML file at " + fileName, e);
+ showErrorAndFinish();
+ return;
+ } finally {
+ try {
+ if (inputReader != null) {
+ inputReader.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+
+ if (TextUtils.isEmpty(data)) {
+ Log.e(TAG, "License HTML is empty (from " + fileName + ")");
+ showErrorAndFinish();
+ return;
+ }
+
+ WebView webView = new WebView(this);
+
+ // Begin the loading. This will be done in a separate thread in WebView.
+ webView.loadDataWithBaseURL(null, data.toString(), "text/html", "utf-8", null);
+ webView.setWebViewClient(new WebViewClient() {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ // Change from 'Loading...' to the real title
+ mAlert.setTitle(getString(R.string.settings_license_activity_title));
+ }
+ });
+
+ final AlertController.AlertParams p = mAlertParams;
+ p.mTitle = getString(R.string.settings_license_activity_loading);
+ p.mView = webView;
+ p.mForceInverseBackground = true;
+ setupAlert();
+ }
+
+ private void showErrorAndFinish() {
+ Toast.makeText(this, R.string.settings_license_activity_unavailable, Toast.LENGTH_LONG)
+ .show();
+ finish();
+ }
+
+}
diff --git a/src/com/android/settings/SimLockSettings.java b/src/com/android/settings/SimLockSettings.java
new file mode 100644
index 00000000000..286e3d6f1d0
--- /dev/null
+++ b/src/com/android/settings/SimLockSettings.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2008 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.Context;
+import android.content.res.Resources;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.CheckBoxPreference;
+import android.preference.PreferenceScreen;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import android.widget.Toast;
+
+/**
+ * Implements the preference screen to enable/disable SIM lock and
+ * also the dialogs to change the SIM PIN. In the former case, enabling/disabling
+ * the SIM lock will prompt the user for the current PIN.
+ * In the Change PIN case, it prompts the user for old pin, new pin and new pin
+ * again before attempting to change it. Calls the SimCard interface to execute
+ * these operations.
+ *
+ */
+public class SimLockSettings extends PreferenceActivity
+ implements EditPinPreference.OnPinEnteredListener {
+
+ private static final int OFF_MODE = 0;
+ // State when enabling/disabling SIM lock
+ private static final int SIM_LOCK_MODE = 1;
+ // State when entering the old pin
+ private static final int SIM_OLD_MODE = 2;
+ // State when entering the new pin - first time
+ private static final int SIM_NEW_MODE = 3;
+ // State when entering the new pin - second time
+ private static final int SIM_REENTER_MODE = 4;
+
+ // Keys in xml file
+ private static final String PIN_DIALOG = "sim_pin";
+ private static final String PIN_TOGGLE = "sim_toggle";
+ // Keys in icicle
+ private static final String DIALOG_STATE = "dialogState";
+ private static final String DIALOG_PIN = "dialogPin";
+ private static final String DIALOG_ERROR = "dialogError";
+ private static final String ENABLE_TO_STATE = "enableState";
+
+ private static final int MIN_PIN_LENGTH = 4;
+ private static final int MAX_PIN_LENGTH = 8;
+ // Which dialog to show next when popped up
+ private int mDialogState = OFF_MODE;
+
+ private String mPin;
+ private String mOldPin;
+ private String mNewPin;
+ private String mError;
+ // Are we trying to enable or disable SIM lock?
+ private boolean mToState;
+
+ private Phone mPhone;
+
+ private EditPinPreference mPinDialog;
+ private CheckBoxPreference mPinToggle;
+
+ private Resources mRes;
+
+ // For async handler to identify request type
+ private static final int ENABLE_SIM_PIN_COMPLETE = 100;
+ private static final int CHANGE_SIM_PIN_COMPLETE = 101;
+
+ // For replies from SimCard interface
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ switch (msg.what) {
+ case ENABLE_SIM_PIN_COMPLETE:
+ simLockChanged(ar.exception == null);
+ break;
+ case CHANGE_SIM_PIN_COMPLETE:
+ simPinChanged(ar.exception == null);
+ break;
+ }
+
+ return;
+ }
+ };
+
+ // For top-level settings screen to query
+ static boolean isSimLockEnabled() {
+ return PhoneFactory.getDefaultPhone().getSimCard().getSimLockEnabled();
+ }
+
+ static String getSummary(Context context) {
+ Resources res = context.getResources();
+ String summary = isSimLockEnabled()
+ ? res.getString(R.string.sim_lock_on)
+ : res.getString(R.string.sim_lock_off);
+ return summary;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.sim_lock_settings);
+
+ mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG);
+ mPinToggle = (CheckBoxPreference) findPreference(PIN_TOGGLE);
+ if (savedInstanceState != null && savedInstanceState.containsKey(DIALOG_STATE)) {
+ mDialogState = savedInstanceState.getInt(DIALOG_STATE);
+ mPin = savedInstanceState.getString(DIALOG_PIN);
+ mError = savedInstanceState.getString(DIALOG_ERROR);
+ mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE);
+ }
+
+ mPinDialog.setOnPinEnteredListener(this);
+
+ // Don't need any changes to be remembered
+ getPreferenceScreen().setPersistent(false);
+
+ mPhone = PhoneFactory.getDefaultPhone();
+ mRes = getResources();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mPinToggle.setChecked(mPhone.getSimCard().getSimLockEnabled());
+
+ if (mDialogState != OFF_MODE) {
+ showPinDialog();
+ } else {
+ // Prep for standard click on "Change PIN"
+ resetDialogState();
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle out) {
+ // Need to store this state for slider open/close
+ // There is one case where the dialog is popped up by the preference
+ // framework. In that case, let the preference framework store the
+ // dialog state. In other cases, where this activity manually launches
+ // the dialog, store the state of the dialog.
+ if (mPinDialog.isDialogOpen()) {
+ out.putInt(DIALOG_STATE, mDialogState);
+ out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString());
+ out.putString(DIALOG_ERROR, mError);
+ out.putBoolean(ENABLE_TO_STATE, mToState);
+ } else {
+ super.onSaveInstanceState(out);
+ }
+ }
+
+ private void showPinDialog() {
+ if (mDialogState == OFF_MODE) {
+ return;
+ }
+ setDialogValues();
+
+ mPinDialog.showPinDialog();
+ }
+
+ private void setDialogValues() {
+ mPinDialog.setText(mPin);
+ String message = "";
+ switch (mDialogState) {
+ case SIM_LOCK_MODE:
+ message = mRes.getString(R.string.sim_enter_pin);
+ mPinDialog.setDialogTitle(mToState
+ ? mRes.getString(R.string.sim_enable_sim_lock)
+ : mRes.getString(R.string.sim_disable_sim_lock));
+ break;
+ case SIM_OLD_MODE:
+ message = mRes.getString(R.string.sim_enter_old);
+ mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
+ break;
+ case SIM_NEW_MODE:
+ message = mRes.getString(R.string.sim_enter_new);
+ mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
+ break;
+ case SIM_REENTER_MODE:
+ message = mRes.getString(R.string.sim_reenter_new);
+ mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
+ break;
+ }
+ if (mError != null) {
+ message = mError + "\n" + message;
+ mError = null;
+ }
+ mPinDialog.setDialogMessage(message);
+ }
+
+ public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
+ if (!positiveResult) {
+ resetDialogState();
+ return;
+ }
+
+ mPin = preference.getText();
+ if (!reasonablePin(mPin)) {
+ // inject error message and display dialog again
+ mError = mRes.getString(R.string.sim_bad_pin);
+ showPinDialog();
+ return;
+ }
+ switch (mDialogState) {
+ case SIM_LOCK_MODE:
+ tryChangeSimLockState();
+ break;
+ case SIM_OLD_MODE:
+ mOldPin = mPin;
+ mDialogState = SIM_NEW_MODE;
+ mError = null;
+ mPin = null;
+ showPinDialog();
+ break;
+ case SIM_NEW_MODE:
+ mNewPin = mPin;
+ mDialogState = SIM_REENTER_MODE;
+ mPin = null;
+ showPinDialog();
+ break;
+ case SIM_REENTER_MODE:
+ if (!mPin.equals(mNewPin)) {
+ mError = mRes.getString(R.string.sim_pins_dont_match);
+ mDialogState = SIM_NEW_MODE;
+ mPin = null;
+ showPinDialog();
+ } else {
+ mError = null;
+ tryChangePin();
+ }
+ break;
+ }
+ }
+
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ if (preference == mPinToggle) {
+ // Get the new, preferred state
+ mToState = mPinToggle.isChecked();
+ // Flip it back and pop up pin dialog
+ mPinToggle.setChecked(!mToState);
+ mDialogState = SIM_LOCK_MODE;
+ showPinDialog();
+ }
+ return true;
+ }
+
+ private void tryChangeSimLockState() {
+ // Try to change sim lock. If it succeeds, toggle the lock state and
+ // reset dialog state. Else inject error message and show dialog again.
+ Message callback = Message.obtain(mHandler, ENABLE_SIM_PIN_COMPLETE);
+ mPhone.getSimCard().setSimLockEnabled(mToState, mPin, callback);
+
+ }
+
+ private void simLockChanged(boolean success) {
+ if (success) {
+ mPinToggle.setChecked(mToState);
+ } else {
+ // TODO: I18N
+ Toast.makeText(this, mRes.getString(R.string.sim_lock_failed), Toast.LENGTH_SHORT)
+ .show();
+ }
+ resetDialogState();
+ }
+
+ private void simPinChanged(boolean success) {
+ if (!success) {
+ // TODO: I18N
+ Toast.makeText(this, mRes.getString(R.string.sim_change_failed),
+ Toast.LENGTH_SHORT)
+ .show();
+ } else {
+ Toast.makeText(this, mRes.getString(R.string.sim_change_succeeded),
+ Toast.LENGTH_SHORT)
+ .show();
+
+ }
+ resetDialogState();
+ }
+
+ private void tryChangePin() {
+ Message callback = Message.obtain(mHandler, CHANGE_SIM_PIN_COMPLETE);
+ mPhone.getSimCard().changeSimLockPassword(mOldPin,
+ mNewPin, callback);
+ }
+
+ private boolean reasonablePin(String pin) {
+ if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ private void resetDialogState() {
+ mError = null;
+ mDialogState = SIM_OLD_MODE; // Default for when Change PIN is clicked
+ mPin = "";
+ setDialogValues();
+ }
+}
diff --git a/src/com/android/settings/SoundAndDisplaySettings.java b/src/com/android/settings/SoundAndDisplaySettings.java
new file mode 100644
index 00000000000..53912e34875
--- /dev/null
+++ b/src/com/android/settings/SoundAndDisplaySettings.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2007 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 static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.IMountService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.preference.CheckBoxPreference;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.IWindowManager;
+
+public class SoundAndDisplaySettings extends PreferenceActivity implements
+ Preference.OnPreferenceChangeListener {
+ private static final String TAG = "SoundAndDisplaysSettings";
+
+ /** If there is no setting in the provider, use this. */
+ private static final int FALLBACK_SCREEN_TIMEOUT_VALUE = 30000;
+
+ private static final String KEY_SILENT = "silent";
+ private static final String KEY_VIBRATE = "vibrate";
+ private static final String KEY_SCREEN_TIMEOUT = "screen_timeout";
+ private static final String KEY_DTMF_TONE = "dtmf_tone";
+ private static final String KEY_SOUND_EFFECTS = "sound_effects";
+ private static final String KEY_ANIMATIONS = "animations";
+ private static final String KEY_PLAY_MEDIA_NOTIFICATION_SOUNDS = "play_media_notification_sounds";
+
+ private CheckBoxPreference mSilent;
+
+ private CheckBoxPreference mPlayMediaNotificationSounds;
+
+ private IMountService mMountService = null;
+
+ /*
+ * If we are currently in one of the silent modes (the ringer mode is set to either
+ * "silent mode" or "vibrate mode"), then toggling the "Phone vibrate"
+ * preference will switch between "silent mode" and "vibrate mode".
+ * Otherwise, it will adjust the normal ringer mode's ring or ring+vibrate
+ * setting.
+ */
+ private CheckBoxPreference mVibrate;
+ private CheckBoxPreference mDtmfTone;
+ private CheckBoxPreference mSoundEffects;
+ private CheckBoxPreference mAnimations;
+ private float[] mAnimationScales;
+
+ private AudioManager mAudioManager;
+
+ private IWindowManager mWindowManager;
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateState(false);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ContentResolver resolver = getContentResolver();
+
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+
+ mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
+
+ addPreferencesFromResource(R.xml.sound_and_display_settings);
+
+ mSilent = (CheckBoxPreference) findPreference(KEY_SILENT);
+ mPlayMediaNotificationSounds = (CheckBoxPreference) findPreference(KEY_PLAY_MEDIA_NOTIFICATION_SOUNDS);
+
+ mVibrate = (CheckBoxPreference) findPreference(KEY_VIBRATE);
+ mDtmfTone = (CheckBoxPreference) findPreference(KEY_DTMF_TONE);
+ mDtmfTone.setPersistent(false);
+ mDtmfTone.setChecked(Settings.System.getInt(resolver,
+ Settings.System.DTMF_TONE_WHEN_DIALING, 1) != 0);
+ mSoundEffects = (CheckBoxPreference) findPreference(KEY_SOUND_EFFECTS);
+ mSoundEffects.setPersistent(false);
+ mSoundEffects.setChecked(Settings.System.getInt(resolver,
+ Settings.System.SOUND_EFFECTS_ENABLED, 0) != 0);
+ mAnimations = (CheckBoxPreference) findPreference(KEY_ANIMATIONS);
+ mAnimations.setPersistent(false);
+
+ ListPreference screenTimeoutPreference =
+ (ListPreference) findPreference(KEY_SCREEN_TIMEOUT);
+ screenTimeoutPreference.setValue(String.valueOf(Settings.System.getInt(
+ resolver, SCREEN_OFF_TIMEOUT, FALLBACK_SCREEN_TIMEOUT_VALUE)));
+ screenTimeoutPreference.setOnPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ updateState(true);
+
+ IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ registerReceiver(mReceiver, filter);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ unregisterReceiver(mReceiver);
+ }
+
+ private void updateState(boolean force) {
+ final int ringerMode = mAudioManager.getRingerMode();
+ final boolean silentOrVibrateMode =
+ ringerMode != AudioManager.RINGER_MODE_NORMAL;
+
+ if (silentOrVibrateMode != mSilent.isChecked() || force) {
+ mSilent.setChecked(silentOrVibrateMode);
+ }
+
+ try {
+ mPlayMediaNotificationSounds.setChecked(mMountService.getPlayNotificationSounds());
+ } catch (RemoteException e) {
+ }
+
+ boolean vibrateSetting;
+ if (silentOrVibrateMode) {
+ vibrateSetting = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
+ } else {
+ vibrateSetting = mAudioManager.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER)
+ == AudioManager.VIBRATE_SETTING_ON;
+ }
+ if (vibrateSetting != mVibrate.isChecked() || force) {
+ mVibrate.setChecked(vibrateSetting);
+ }
+
+ boolean animations = true;
+ try {
+ mAnimationScales = mWindowManager.getAnimationScales();
+ } catch (RemoteException e) {
+ }
+ if (mAnimationScales != null) {
+ for (int i=0; i {
+ Map mAppLabelList;
+ AppNameComparator(Map appList) {
+ mAppLabelList = appList;
+ }
+ public final int compare(PkgUsageStats a, PkgUsageStats b) {
+ String alabel = mAppLabelList.get(a.packageName).toString();
+ String blabel = mAppLabelList.get(b.packageName).toString();
+ return alabel.compareTo(blabel);
+ }
+ }
+
+ public static class LaunchCountComparator implements Comparator {
+ public final int compare(PkgUsageStats a, PkgUsageStats b) {
+ // return by descending order
+ return b.launchCount - a.launchCount;
+ }
+ }
+
+ public static class UsageTimeComparator implements Comparator {
+ public final int compare(PkgUsageStats a, PkgUsageStats b) {
+ long ret = a.usageTime-b.usageTime;
+ if (ret == 0) {
+ return 0;
+ }
+ if (ret < 0) {
+ return 1;
+ }
+ return -1;
+ }
+ }
+
+ // View Holder used when displaying views
+ static class AppViewHolder {
+ TextView pkgName;
+ TextView launchCount;
+ 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_LAUNCH_COUNT = 1;
+ private static final int _DISPLAY_ORDER_APP_NAME = 2;
+
+ private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME;
+ private List mUsageStats;
+ private LaunchCountComparator mLaunchCountComparator;
+ private UsageTimeComparator mUsageTimeComparator;
+ private AppNameComparator mAppLabelComparator;
+ private HashMap mAppLabelMap;
+
+ UsageStatsAdapter() {
+ mUsageStats = new ArrayList();
+ mAppLabelMap = new HashMap();
+ PkgUsageStats[] stats;
+ try {
+ stats = mUsageStatsService.getAllPkgUsageStats();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed initializing usage stats service");
+ return;
+ }
+ if (stats == null) {
+ return;
+ }
+ for (PkgUsageStats ps : stats) {
+ mUsageStats.add(ps);
+ // load application labels for each application
+ CharSequence label;
+ try {
+ ApplicationInfo appInfo = mPm.getApplicationInfo(ps.packageName, 0);
+ label = appInfo.loadLabel(mPm);
+ } catch (NameNotFoundException e) {
+ label = ps.packageName;
+ }
+ mAppLabelMap.put(ps.packageName, label);
+ }
+ // Sort list
+ mLaunchCountComparator = new LaunchCountComparator();
+ mUsageTimeComparator = new UsageTimeComparator();
+ mAppLabelComparator = new AppNameComparator(mAppLabelMap);
+ sortList();
+ }
+ public int getCount() {
+ return mUsageStats.size();
+ }
+
+ public Object getItem(int position) {
+ return mUsageStats.get(position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // A ViewHolder keeps references to children views to avoid unneccessary calls
+ // to findViewById() on each row.
+ AppViewHolder holder;
+
+ // When convertView is not null, we can reuse it directly, there is no need
+ // to reinflate it. We only inflate a new View when the convertView supplied
+ // by ListView is null.
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.usage_stats_item, null);
+
+ // Creates a ViewHolder and store references to the two children views
+ // we want to bind data to.
+ holder = new AppViewHolder();
+ holder.pkgName = (TextView) convertView.findViewById(R.id.package_name);
+ holder.launchCount = (TextView) convertView.findViewById(R.id.launch_count);
+ holder.usageTime = (TextView) convertView.findViewById(R.id.usage_time);
+ convertView.setTag(holder);
+ } else {
+ // Get the ViewHolder back to get fast access to the TextView
+ // and the ImageView.
+ holder = (AppViewHolder) convertView.getTag();
+ }
+
+ // Bind the data efficiently with the holder
+ PkgUsageStats pkgStats = mUsageStats.get(position);
+ if (pkgStats != null) {
+ CharSequence label = mAppLabelMap.get(pkgStats.packageName);
+ holder.pkgName.setText(label);
+ holder.launchCount.setText(String.valueOf(pkgStats.launchCount));
+ holder.usageTime.setText(String.valueOf(pkgStats.usageTime)+" ms");
+ } else {
+ Log.w(TAG, "No usage stats info for package:"+pkgStats.packageName);
+ }
+ return convertView;
+ }
+
+ void sortList(int sortOrder) {
+ if (mDisplayOrder == sortOrder) {
+ // do nothing
+ return;
+ }
+ mDisplayOrder= sortOrder;
+ sortList();
+ }
+ private void sortList() {
+ if (mDisplayOrder == _DISPLAY_ORDER_USAGE_TIME) {
+ if (localLOGV) Log.i(TAG, "Sorting by usage time");
+ Collections.sort(mUsageStats, mUsageTimeComparator);
+ } else if (mDisplayOrder == _DISPLAY_ORDER_LAUNCH_COUNT) {
+ if (localLOGV) Log.i(TAG, "Sorting launch count");
+ Collections.sort(mUsageStats, mLaunchCountComparator);
+ } else if (mDisplayOrder == _DISPLAY_ORDER_APP_NAME) {
+ if (localLOGV) Log.i(TAG, "Sorting by application name");
+ Collections.sort(mUsageStats, mAppLabelComparator);
+ }
+ notifyDataSetChanged();
+ }
+ }
+
+ /** Called when the activity is first created. */
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mUsageStatsService = IUsageStats.Stub.asInterface(ServiceManager.getService("usagestats"));
+ if (mUsageStatsService == null) {
+ Log.e(TAG, "Failed to retrieve usagestats service");
+ return;
+ }
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mPm = getPackageManager();
+
+ setContentView(R.layout.usage_stats);
+ mTypeSpinner = (Spinner) findViewById(R.id.typeSpinner);
+ mTypeSpinner.setOnItemSelectedListener(this);
+
+ mListView = (ListView) findViewById(R.id.pkg_list);
+ // Initialize the inflater
+
+ mAdapter = new UsageStatsAdapter();
+ mListView.setAdapter(mAdapter);
+ }
+
+ public void onItemSelected(AdapterView> parent, View view, int position,
+ long id) {
+ mAdapter.sortList(position);
+ }
+
+ public void onNothingSelected(AdapterView> parent) {
+ // do nothing
+ }
+}
+
diff --git a/src/com/android/settings/UserDictionarySettings.java b/src/com/android/settings/UserDictionarySettings.java
new file mode 100644
index 00000000000..aeddcf7a913
--- /dev/null
+++ b/src/com/android/settings/UserDictionarySettings.java
@@ -0,0 +1,271 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.UserDictionary;
+import android.text.InputType;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AlphabetIndexer;
+import android.widget.EditText;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+import java.util.Locale;
+
+public class UserDictionarySettings extends ListActivity {
+
+ private static final String INSTANCE_KEY_DIALOG_EDITING_WORD = "DIALOG_EDITING_WORD";
+ private static final String INSTANCE_KEY_ADDED_WORD = "DIALOG_ADDED_WORD";
+
+ private static final String[] QUERY_PROJECTION = {
+ UserDictionary.Words._ID, UserDictionary.Words.WORD
+ };
+
+ // Either the locale is empty (means the word is applicable to all locales)
+ // or the word equals our current locale
+ private static final String QUERY_SELECTION = UserDictionary.Words.LOCALE + "=? OR "
+ + UserDictionary.Words.LOCALE + " is null";
+
+ private static final String DELETE_SELECTION = UserDictionary.Words.WORD + "=?";
+
+ private static final String EXTRA_WORD = "word";
+
+ private static final int CONTEXT_MENU_EDIT = Menu.FIRST;
+ private static final int CONTEXT_MENU_DELETE = Menu.FIRST + 1;
+
+ private static final int OPTIONS_MENU_ADD = Menu.FIRST;
+
+ private static final int DIALOG_ADD_OR_EDIT = 0;
+
+ /** The word being edited in the dialog (null means the user is adding a word). */
+ private String mDialogEditingWord;
+
+ private Cursor mCursor;
+
+ private boolean mAddedWordAlready;
+ private boolean mAutoReturn;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.list_content_with_empty_view);
+
+ mCursor = createCursor();
+ setListAdapter(createAdapter());
+
+ TextView emptyView = (TextView) findViewById(R.id.empty);
+ emptyView.setText(R.string.user_dict_settings_empty_text);
+
+ ListView listView = getListView();
+ listView.setFastScrollEnabled(true);
+ listView.setEmptyView(emptyView);
+
+ registerForContextMenu(listView);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (!mAddedWordAlready
+ && getIntent().getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) {
+ String word = getIntent().getStringExtra(EXTRA_WORD);
+ mAutoReturn = true;
+ if (word != null) {
+ showAddOrEditDialog(word);
+ }
+ }
+ }
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ mDialogEditingWord = state.getString(INSTANCE_KEY_DIALOG_EDITING_WORD);
+ mAddedWordAlready = state.getBoolean(INSTANCE_KEY_ADDED_WORD, false);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(INSTANCE_KEY_DIALOG_EDITING_WORD, mDialogEditingWord);
+ outState.putBoolean(INSTANCE_KEY_ADDED_WORD, mAddedWordAlready);
+ }
+
+ private Cursor createCursor() {
+ String currentLocale = Locale.getDefault().toString();
+ // Case-insensitive sort
+ return managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
+ QUERY_SELECTION, new String[] { currentLocale },
+ "UPPER(" + UserDictionary.Words.WORD + ")");
+ }
+
+ private ListAdapter createAdapter() {
+ return new MyAdapter(this,
+ android.R.layout.simple_list_item_1, mCursor,
+ new String[] { UserDictionary.Words.WORD },
+ new int[] { android.R.id.text1 });
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ showAddOrEditDialog(getWord(position));
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ if (!(menuInfo instanceof AdapterContextMenuInfo)) return;
+
+ AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
+ menu.setHeaderTitle(getWord(adapterMenuInfo.position));
+ menu.add(0, CONTEXT_MENU_EDIT, 0, R.string.user_dict_settings_context_menu_edit_title);
+ menu.add(0, CONTEXT_MENU_DELETE, 0, R.string.user_dict_settings_context_menu_delete_title);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ ContextMenuInfo menuInfo = item.getMenuInfo();
+ if (!(menuInfo instanceof AdapterContextMenuInfo)) return false;
+
+ AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
+ String word = getWord(adapterMenuInfo.position);
+
+ switch (item.getItemId()) {
+ case CONTEXT_MENU_DELETE:
+ deleteWord(word);
+ return true;
+
+ case CONTEXT_MENU_EDIT:
+ showAddOrEditDialog(word);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title)
+ .setIcon(R.drawable.ic_menu_add);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ showAddOrEditDialog(null);
+ return true;
+ }
+
+ private void showAddOrEditDialog(String editingWord) {
+ mDialogEditingWord = editingWord;
+ showDialog(DIALOG_ADD_OR_EDIT);
+ }
+
+ private String getWord(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getString(
+ mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD));
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ View content = getLayoutInflater().inflate(R.layout.dialog_edittext, null);
+ final EditText editText = (EditText) content.findViewById(R.id.edittext);
+ // No prediction in soft keyboard mode. TODO: Create a better way to disable prediction
+ editText.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
+
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.user_dict_settings_add_dialog_title)
+ .setView(content)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ onAddOrEditFinished(editText.getText().toString());
+ if (mAutoReturn) finish();
+ }})
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (mAutoReturn) finish();
+ }})
+ .create();
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog d) {
+ AlertDialog dialog = (AlertDialog) d;
+ EditText editText = (EditText) dialog.findViewById(R.id.edittext);
+ editText.setText(mDialogEditingWord);
+ }
+
+ private void onAddOrEditFinished(String word) {
+ if (mDialogEditingWord != null) {
+ // The user was editing a word, so do a delete/add
+ deleteWord(mDialogEditingWord);
+ }
+
+ // Disallow duplicates
+ deleteWord(word);
+
+ // TODO: present UI for picking whether to add word to all locales, or current.
+ UserDictionary.Words.addWord(this, word.toString(),
+ 250, UserDictionary.Words.LOCALE_TYPE_ALL);
+ mCursor.requery();
+ mAddedWordAlready = true;
+ }
+
+ private void deleteWord(String word) {
+ getContentResolver().delete(UserDictionary.Words.CONTENT_URI, DELETE_SELECTION,
+ new String[] { word });
+ }
+
+ private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer {
+ private AlphabetIndexer mIndexer;
+
+ public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
+ super(context, layout, c, from, to);
+
+ int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD);
+ String alphabet = context.getString(com.android.internal.R.string.fast_scroll_alphabet);
+ mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet);
+ }
+
+ public int getPositionForSection(int section) {
+ return mIndexer.getPositionForSection(section);
+ }
+
+ public int getSectionForPosition(int position) {
+ return mIndexer.getSectionForPosition(position);
+ }
+
+ public Object[] getSections() {
+ return mIndexer.getSections();
+ }
+ }
+}
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
new file mode 100644
index 00000000000..a23272b81a6
--- /dev/null
+++ b/src/com/android/settings/Utils.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.preference.Preference;
+import android.preference.PreferenceGroup;
+
+import java.util.List;
+
+public class Utils {
+
+ /**
+ * Set the preference's title to the matching activity's label.
+ */
+ public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
+
+ /**
+ * Finds a matching activity for a preference's intent. If a matching
+ * activity is not found, it will remove the preference.
+ *
+ * @param context The context.
+ * @param parentPreferenceGroup The preference group that contains the
+ * preference whose intent is being resolved.
+ * @param preferenceKey The key of the preference whose intent is being
+ * resolved.
+ * @param flags 0 or one or more of
+ * {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
+ * .
+ * @return Whether an activity was found. If false, the preference was
+ * removed.
+ */
+ public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
+ PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
+
+ Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
+ if (preference == null) {
+ return false;
+ }
+
+ Intent intent = preference.getIntent();
+ if (intent != null) {
+ // Find the activity that is in the system image
+ PackageManager pm = context.getPackageManager();
+ List list = pm.queryIntentActivities(intent, 0);
+ int listSize = list.size();
+ for (int i = 0; i < listSize; i++) {
+ ResolveInfo resolveInfo = list.get(i);
+ if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
+ != 0) {
+
+ // Replace the intent with this specific activity
+ preference.setIntent(new Intent().setClassName(
+ resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name));
+
+ if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
+ // Set the preference title to the activity's label
+ preference.setTitle(resolveInfo.loadLabel(pm));
+ }
+
+ return true;
+ }
+ }
+ }
+
+ // Did not find a matching activity, so remove the preference
+ parentPreferenceGroup.removePreference(preference);
+
+ return true;
+ }
+
+}
diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java
new file mode 100644
index 00000000000..d1129150b99
--- /dev/null
+++ b/src/com/android/settings/WirelessSettings.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2007 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.settings.bluetooth.BluetoothEnabler;
+import com.android.settings.wifi.WifiEnabler;
+
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.preference.CheckBoxPreference;
+
+public class WirelessSettings extends PreferenceActivity {
+
+ private static final String KEY_TOGGLE_AIRPLANE = "toggle_airplane";
+ private static final String KEY_TOGGLE_BLUETOOTH = "toggle_bluetooth";
+ private static final String KEY_TOGGLE_WIFI = "toggle_wifi";
+
+ private WifiEnabler mWifiEnabler;
+ private AirplaneModeEnabler mAirplaneModeEnabler;
+ private BluetoothEnabler mBtEnabler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.wireless_settings);
+
+ initToggles();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mWifiEnabler.resume();
+ mAirplaneModeEnabler.resume();
+ mBtEnabler.resume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ mWifiEnabler.pause();
+ mAirplaneModeEnabler.pause();
+ mBtEnabler.pause();
+ }
+
+ private void initToggles() {
+
+ mWifiEnabler = new WifiEnabler(
+ this,
+ (WifiManager) getSystemService(WIFI_SERVICE),
+ (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI));
+
+ mAirplaneModeEnabler = new AirplaneModeEnabler(
+ this,
+ (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE));
+
+ mBtEnabler = new BluetoothEnabler(
+ this,
+ (CheckBoxPreference) findPreference(KEY_TOGGLE_BLUETOOTH));
+ }
+
+}
diff --git a/src/com/android/settings/ZoneList.java b/src/com/android/settings/ZoneList.java
new file mode 100644
index 00000000000..2877f0039f0
--- /dev/null
+++ b/src/com/android/settings/ZoneList.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2006 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.AlarmManager;
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * This activity displays a list of time zones that match a filter string
+ * such as "Africa", "Europe", etc. Choosing an item from the list will set
+ * the time zone. Pressing Back without choosing from the list will not
+ * result in a change in the time zone setting.
+ */
+public class ZoneList extends ListActivity {
+
+ private static final String TAG = "ZoneList";
+ private static final String KEY_ID = "id";
+ private static final String KEY_DISPLAYNAME = "name";
+ private static final String KEY_GMT = "gmt";
+ private static final String KEY_OFFSET = "offset";
+ private static final String XMLTAG_TIMEZONE = "timezone";
+
+ private static final int HOURS_1 = 60 * 60000;
+ private static final int HOURS_24 = 24 * HOURS_1;
+ private static final int HOURS_HALF = HOURS_1 / 2;
+
+ private static final int MENU_TIMEZONE = Menu.FIRST+1;
+ private static final int MENU_ALPHABETICAL = Menu.FIRST;
+
+ // Initial focus position
+ private int mDefault;
+
+ private boolean mSortedByTimezone;
+
+ private SimpleAdapter mTimezoneSortedAdapter;
+ private SimpleAdapter mAlphabeticalAdapter;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ String[] from = new String[] {KEY_DISPLAYNAME, KEY_GMT};
+ int[] to = new int[] {android.R.id.text1, android.R.id.text2};
+
+ MyComparator comparator = new MyComparator(KEY_OFFSET);
+
+ List timezoneSortedList = getZones();
+ Collections.sort(timezoneSortedList, comparator);
+ mTimezoneSortedAdapter = new SimpleAdapter(this,
+ (List) timezoneSortedList,
+ android.R.layout.simple_list_item_2,
+ from,
+ to);
+
+ List alphabeticalList = new ArrayList(timezoneSortedList);
+ comparator.setSortingKey(KEY_DISPLAYNAME);
+ Collections.sort(alphabeticalList, comparator);
+ mAlphabeticalAdapter = new SimpleAdapter(this,
+ (List) alphabeticalList,
+ android.R.layout.simple_list_item_2,
+ from,
+ to);
+
+ // Sets the adapter
+ setSorting(true);
+
+ // If current timezone is in this list, move focus to it
+ setSelection(mDefault);
+
+ // Assume user may press Back
+ setResult(RESULT_CANCELED);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_ALPHABETICAL, 0, R.string.zone_list_menu_sort_alphabetically)
+ .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
+ menu.add(0, MENU_TIMEZONE, 0, R.string.zone_list_menu_sort_by_timezone)
+ .setIcon(R.drawable.ic_menu_3d_globe);
+
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+
+ if (mSortedByTimezone) {
+ menu.findItem(MENU_TIMEZONE).setVisible(false);
+ menu.findItem(MENU_ALPHABETICAL).setVisible(true);
+ } else {
+ menu.findItem(MENU_TIMEZONE).setVisible(true);
+ menu.findItem(MENU_ALPHABETICAL).setVisible(false);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+
+ case MENU_TIMEZONE:
+ setSorting(true);
+ return true;
+
+ case MENU_ALPHABETICAL:
+ setSorting(false);
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private void setSorting(boolean timezone) {
+ setListAdapter(timezone ? mTimezoneSortedAdapter : mAlphabeticalAdapter);
+ mSortedByTimezone = timezone;
+ }
+
+ private List getZones() {
+ List myData = new ArrayList();
+ long date = Calendar.getInstance().getTimeInMillis();
+ try {
+ XmlResourceParser xrp = getResources().getXml(R.xml.timezones);
+ while (xrp.next() != XmlResourceParser.START_TAG)
+ ;
+ xrp.next();
+ while (xrp.getEventType() != XmlResourceParser.END_TAG) {
+ while (xrp.getEventType() != XmlResourceParser.START_TAG) {
+ if (xrp.getEventType() == XmlResourceParser.END_DOCUMENT) {
+ return myData;
+ }
+ xrp.next();
+ }
+ if (xrp.getName().equals(XMLTAG_TIMEZONE)) {
+ String id = xrp.getAttributeValue(0);
+ String displayName = xrp.nextText();
+ addItem(myData, id, displayName, date);
+ }
+ while (xrp.getEventType() != XmlResourceParser.END_TAG) {
+ xrp.next();
+ }
+ xrp.next();
+ }
+ xrp.close();
+ } catch (XmlPullParserException xppe) {
+ Log.e(TAG, "Ill-formatted timezones.xml file");
+ } catch (java.io.IOException ioe) {
+ Log.e(TAG, "Unable to read timezones.xml file");
+ }
+
+ return myData;
+ }
+
+ protected void addItem(List myData, String id, String displayName,
+ long date) {
+ HashMap map = new HashMap();
+ map.put(KEY_ID, id);
+ map.put(KEY_DISPLAYNAME, displayName);
+ TimeZone tz = TimeZone.getTimeZone(id);
+ int offset = tz.getOffset(date);
+ int p = Math.abs(offset);
+ StringBuilder name = new StringBuilder();
+ name.append("GMT");
+
+ if (offset < 0) {
+ name.append('-');
+ } else {
+ name.append('+');
+ }
+
+ name.append(p / (HOURS_1));
+ name.append(':');
+
+ int min = p / 60000;
+ min %= 60;
+
+ if (min < 10) {
+ name.append('0');
+ }
+ name.append(min);
+
+ map.put(KEY_GMT, name.toString());
+ map.put(KEY_OFFSET, offset);
+
+ if (id.equals(TimeZone.getDefault().getID())) {
+ mDefault = myData.size();
+ }
+
+ myData.add(map);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ Map map = (Map) l.getItemAtPosition(position);
+ // Update the system timezone value
+ AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ alarm.setTimeZone((String) map.get(KEY_ID));
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ private static class MyComparator implements Comparator {
+ private String mSortingKey;
+
+ public MyComparator(String sortingKey) {
+ mSortingKey = sortingKey;
+ }
+
+ public void setSortingKey(String sortingKey) {
+ mSortingKey = sortingKey;
+ }
+
+ public int compare(HashMap map1, HashMap map2) {
+ Object value1 = map1.get(mSortingKey);
+ Object value2 = map2.get(mSortingKey);
+
+ /*
+ * This should never happen, but just in-case, put non-comparable
+ * items at the end.
+ */
+ if (!isComparable(value1)) {
+ return isComparable(value2) ? 1 : 0;
+ } else if (!isComparable(value2)) {
+ return -1;
+ }
+
+ return ((Comparable) value1).compareTo(value2);
+ }
+
+ private boolean isComparable(Object value) {
+ return (value != null) && (value instanceof Comparable);
+ }
+ }
+
+}
diff --git a/src/com/android/settings/ZonePicker.java b/src/com/android/settings/ZonePicker.java
new file mode 100644
index 00000000000..def5036ae18
--- /dev/null
+++ b/src/com/android/settings/ZonePicker.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2006 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.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ZonePicker extends ListActivity {
+
+ private ArrayAdapter mFilterAdapter;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mFilterAdapter = ArrayAdapter.createFromResource(this,
+ R.array.timezone_filters, android.R.layout.simple_list_item_1);
+ setListAdapter(mFilterAdapter);
+ }
+
+ protected void addItem(List