summaryrefslogtreecommitdiffstats
path: root/policy
diff options
context:
space:
mode:
authorDvTonder <david.vantonder@gmail.com>2012-07-15 10:28:15 -0400
committerRicardo Cerqueira <cyanogenmod@cerqueira.org>2012-11-21 00:25:18 +0000
commit0b636d08439c291322339e02be605be54f589b91 (patch)
tree1abb1921eab3532617ddda7e07939f99de093280 /policy
parent8415ee214d09ed7f399d8ff68d4f78c76c68fe08 (diff)
downloadframeworks_base-0b636d08439c291322339e02be605be54f589b91.zip
frameworks_base-0b636d08439c291322339e02be605be54f589b91.tar.gz
frameworks_base-0b636d08439c291322339e02be605be54f589b91.tar.bz2
Framework: Port CM9 features to CM10
This commit includes: - Power menu Reboot - Power menu screenshot - Profiles - Lock screen Calendar - Lock screen Weather - Notification light customization - Battery light customization - IME Selector notification toggle - and a few more things to support Settings Change-Id: Ibd63116df90b06f6ce6adb8a0343059bbb999bfb
Diffstat (limited to 'policy')
-rw-r--r--policy/src/com/android/internal/policy/impl/GlobalActions.java236
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java154
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java368
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java51
4 files changed, 765 insertions, 44 deletions
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index d1f8ef1..6c439cc 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2012 CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +26,8 @@ import com.android.internal.R;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.Profile;
+import android.app.ProfileManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -71,6 +74,17 @@ import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
+
+/**
+ * Needed for takeScreenshot
+ */
+import android.content.ServiceConnection;
+import android.content.ComponentName;
+import android.os.IBinder;
+import android.os.Messenger;
+import android.os.RemoteException;
+
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that
@@ -85,6 +99,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private final Context mContext;
private final WindowManagerFuncs mWindowManagerFuncs;
+
private final AudioManager mAudioManager;
private final IDreamManager mDreamManager;
@@ -103,6 +118,9 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private boolean mHasTelephony;
private boolean mHasVibrator;
+ private Profile mChosenProfile;
+
+
/**
* @param context everything needs a context :(
*/
@@ -242,6 +260,22 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mWindowManagerFuncs.shutdown(true);
}
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+ });
+
+ // next: reboot
+ mItems.add(
+ new SinglePressAction(R.drawable.ic_lock_reboot, R.string.global_action_reboot) {
+ public void onPress() {
+ mWindowManagerFuncs.reboot();
+ }
+
public boolean onLongPress() {
mWindowManagerFuncs.rebootSafeMode(true);
return true;
@@ -256,6 +290,43 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
});
+
+ // next: profile
+ mItems.add(
+ new ProfileChooseAction() {
+ public void onPress() {
+ createProfileDialog();
+ }
+
+ public boolean onLongPress() {
+ return true;
+ }
+
+ public boolean showDuringKeyguard() {
+ return false;
+ }
+
+ public boolean showBeforeProvisioning() {
+ return false;
+ }
+ });
+
+ // next: screenshot
+ mItems.add(
+ new SinglePressAction(R.drawable.ic_lock_screenshot, R.string.global_action_screenshot) {
+ public void onPress() {
+ takeScreenshot();
+ }
+
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+ });
+
// next: airplane mode
mItems.add(mAirplaneModeOn);
@@ -385,11 +456,141 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
}
+ private void createProfileDialog(){
+ final ProfileManager profileManager = (ProfileManager)mContext.getSystemService(Context.PROFILE_SERVICE);
+
+ final Profile[] profiles = profileManager.getProfiles();
+ UUID activeProfile = profileManager.getActiveProfile().getUuid();
+ final CharSequence[] names = new CharSequence[profiles.length];
+
+ int i=0;
+ int checkedItem = 0;
+
+ for(Profile profile : profiles) {
+ if(profile.getUuid().equals(activeProfile)) {
+ checkedItem = i;
+ mChosenProfile = profile;
+ }
+ names[i++] = profile.getName();
+ }
+
+ final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
+
+ AlertDialog dialog = ab
+ .setTitle(R.string.global_action_choose_profile)
+ .setSingleChoiceItems(names, checkedItem, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which < 0)
+ return;
+ mChosenProfile = profiles[which];
+ }
+ })
+ .setPositiveButton(com.android.internal.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ profileManager.setActiveProfile(mChosenProfile.getUuid());
+ }
+ })
+ .setNegativeButton(com.android.internal.R.string.no,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ }).create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+ dialog.show();
+ }
+
+ /**
+ * functions needed for taking screenhots.
+ * This leverages the built in ICS screenshot functionality
+ */
+ final Object mScreenshotLock = new Object();
+ ServiceConnection mScreenshotConnection = null;
+
+ final Runnable mScreenshotTimeout = new Runnable() {
+ @Override public void run() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ }
+ }
+ }
+ };
+
+ private void takeScreenshot() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ return;
+ }
+ ComponentName cn = new ComponentName("com.android.systemui",
+ "com.android.systemui.screenshot.TakeScreenshotService");
+ Intent intent = new Intent();
+ intent.setComponent(cn);
+ ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != this) {
+ return;
+ }
+ Messenger messenger = new Messenger(service);
+ Message msg = Message.obtain(null, 1);
+ final ServiceConnection myConn = this;
+ Handler h = new Handler(mHandler.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection == myConn) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ mHandler.removeCallbacks(mScreenshotTimeout);
+ }
+ }
+ }
+ };
+ msg.replyTo = new Messenger(h);
+ msg.arg1 = msg.arg2 = 0;
+
+ /* remove for the time being
+ if (mStatusBar != null && mStatusBar.isVisibleLw())
+ msg.arg1 = 1;
+ if (mNavigationBar != null && mNavigationBar.isVisibleLw())
+ msg.arg2 = 1;
+ */
+
+ /* wait for the dialog box to close */
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
+ }
+
+ /* take the screenshot */
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {}
+ };
+ if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
+ mScreenshotConnection = conn;
+ mHandler.postDelayed(mScreenshotTimeout, 10000);
+ }
+ }
+ }
+
private void prepareDialog() {
refreshSilentMode();
mAirplaneModeOn.updateState(mAirplaneState);
mAdapter.notifyDataSetChanged();
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+
+ mDialog.setTitle(R.string.global_actions);
+
if (SHOW_SILENT_TOGGLE) {
IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
mContext.registerReceiver(mRingerModeReceiver, filter);
@@ -587,6 +788,41 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
/**
+ * A single press action maintains no state, just responds to a press
+ * and takes an action.
+ */
+ private abstract class ProfileChooseAction implements Action {
+ private ProfileManager mProfileManager;
+
+ protected ProfileChooseAction() {
+ mProfileManager = (ProfileManager)mContext.getSystemService(Context.PROFILE_SERVICE);
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ abstract public void onPress();
+
+ public View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
+ View v = (convertView != null) ?
+ convertView :
+ inflater.inflate(R.layout.global_actions_item, parent, false);
+
+ ImageView icon = (ImageView) v.findViewById(R.id.icon);
+ TextView messageView = (TextView) v.findViewById(R.id.message);
+ TextView statusView = (TextView) v.findViewById(R.id.status);
+ statusView.setVisibility(View.VISIBLE);
+ statusView.setText(mProfileManager.getActiveProfile().getName());
+
+ icon.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_lock_profile));
+ messageView.setText(R.string.global_action_choose_profile);
+
+ return v;
+ }
+ }
+
+ /**
* A toggle action knows whether it is on or off, and displays an icon
* and status message accordingly.
*/
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index d9c07f8..a48e411 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -324,6 +324,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mFocusedWindow;
IApplicationToken mFocusedApp;
+ // Behavior of volume wake
+ boolean mVolumeWakeScreen;
+
+ // Behavior of volbtn music controls
+ boolean mVolBtnMusicControls;
+ boolean mIsLongPress;
+
private static final class PointerLocationInputEventReceiver extends InputEventReceiver {
private final PointerLocationView mView;
@@ -526,6 +533,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.VOLUME_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.VOLBTN_MUSIC_CONTROLS), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.ACCELEROMETER_ROTATION), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
@@ -675,6 +688,53 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ /**
+ * When a volumeup-key longpress expires, skip songs based on key press
+ */
+ Runnable mVolumeUpLongPress = new Runnable() {
+ public void run() {
+ // set the long press flag to true
+ mIsLongPress = true;
+
+ // Shamelessly copied from Kmobs LockScreen controls, works for Pandora, etc...
+ sendMediaButtonEvent(KeyEvent.KEYCODE_MEDIA_NEXT);
+ };
+ };
+
+ /**
+ * When a volumedown-key longpress expires, skip songs based on key press
+ */
+ Runnable mVolumeDownLongPress = new Runnable() {
+ public void run() {
+ // set the long press flag to true
+ mIsLongPress = true;
+
+ // Shamelessly copied from Kmobs LockScreen controls, works for Pandora, etc...
+ sendMediaButtonEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
+ };
+ };
+
+ private void sendMediaButtonEvent(int code) {
+ long eventtime = SystemClock.uptimeMillis();
+ Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+ KeyEvent keyEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN, code, 0);
+ keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ mContext.sendOrderedBroadcast(keyIntent, null);
+ keyEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP);
+ keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ mContext.sendOrderedBroadcast(keyIntent, null);
+ }
+
+ void handleVolumeLongPress(int keycode) {
+ mHandler.postDelayed(keycode == KeyEvent.KEYCODE_VOLUME_UP ? mVolumeUpLongPress :
+ mVolumeDownLongPress, ViewConfiguration.getLongPressTimeout());
+ }
+
+ void handleVolumeLongPressAbort() {
+ mHandler.removeCallbacks(mVolumeUpLongPress);
+ mHandler.removeCallbacks(mVolumeDownLongPress);
+ }
+
private void interceptScreenshotChord() {
if (mScreenshotChordEnabled
&& mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {
@@ -1070,11 +1130,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT,
UserHandle.USER_CURRENT);
+ mVolumeWakeScreen = (Settings.System.getIntForUser(resolver,
+ Settings.System.VOLUME_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1);
+ mVolBtnMusicControls = (Settings.System.getIntForUser(resolver,
+ Settings.System.VOLBTN_MUSIC_CONTROLS, 1, UserHandle.USER_CURRENT) == 1);
// Configure rotation lock.
int userRotation = Settings.System.getIntForUser(resolver,
Settings.System.USER_ROTATION, Surface.ROTATION_0,
UserHandle.USER_CURRENT);
+
if (mUserRotation != userRotation) {
mUserRotation = userRotation;
updateRotation = true;
@@ -3340,7 +3405,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
- final int keyCode = event.getKeyCode();
+ int keyCode = event.getKeyCode();
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
@@ -3356,8 +3421,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (keyCode == KeyEvent.KEYCODE_POWER) {
policyFlags |= WindowManagerPolicy.FLAG_WAKE;
}
- final boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE
- | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
+ final boolean isWakeKey = (policyFlags
+ & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
if (DEBUG_INPUT) {
Log.d(TAG, "interceptKeyTq keycode=" + keyCode
@@ -3391,7 +3456,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (keyguardActive) {
// If the keyguard is showing, let it wake the device when ready.
mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
- } else {
+ } else if ((keyCode != KeyEvent.KEYCODE_VOLUME_UP) && (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN)) {
// Otherwise, wake the device ourselves.
result |= ACTION_WAKE_UP;
}
@@ -3400,6 +3465,35 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Handle special keys.
switch (keyCode) {
+ case KeyEvent.KEYCODE_ENDCALL: {
+ result &= ~ACTION_PASS_TO_USER;
+ if (down) {
+ ITelephony telephonyService = getTelephonyService();
+ boolean hungUp = false;
+ if (telephonyService != null) {
+ try {
+ hungUp = telephonyService.endCall();
+ } catch (RemoteException ex) {
+ Log.w(TAG, "ITelephony threw RemoteException", ex);
+ }
+ }
+ interceptPowerKeyDown(!isScreenOn || hungUp);
+ } else {
+ if (interceptPowerKeyUp(canceled)) {
+ if ((mEndcallBehavior
+ & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
+ if (goHome()) {
+ break;
+ }
+ }
+ if ((mEndcallBehavior
+ & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
+ result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
+ }
+ }
+ }
+ break;
+ }
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
@@ -3464,45 +3558,33 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Log.w(TAG, "ITelephony threw RemoteException", ex);
}
}
-
- if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {
- // If music is playing but we decided not to pass the key to the
- // application, handle the volume change here.
- handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
- break;
- }
}
- break;
- }
-
- case KeyEvent.KEYCODE_ENDCALL: {
- result &= ~ACTION_PASS_TO_USER;
- if (down) {
- ITelephony telephonyService = getTelephonyService();
- boolean hungUp = false;
- if (telephonyService != null) {
- try {
- hungUp = telephonyService.endCall();
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
- }
- }
- interceptPowerKeyDown(!isScreenOn || hungUp);
- } else {
- if (interceptPowerKeyUp(canceled)) {
- if ((mEndcallBehavior
- & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
- if (goHome()) {
+ if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {
+ if (mVolBtnMusicControls && down && (keyCode != KeyEvent.KEYCODE_VOLUME_MUTE)) {
+ mIsLongPress = false;
+ handleVolumeLongPress(keyCode);
+ break;
+ } else {
+ if (mVolBtnMusicControls && !down) {
+ handleVolumeLongPressAbort();
+ if (mIsLongPress) {
break;
}
}
- if ((mEndcallBehavior
- & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
- result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
+ if (!isScreenOn && !mVolumeWakeScreen) {
+ handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
}
}
}
- break;
+ if (isScreenOn || !mVolumeWakeScreen) {
+ break;
+ } else if (keyguardActive) {
+ keyCode = KeyEvent.KEYCODE_POWER;
+ mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
+ } else {
+ result |= ACTION_WAKE_UP;
+ break;
+ }
}
case KeyEvent.KEYCODE_POWER: {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
index b6ffde0..d1651fc 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012 The CyanogenMod Project (Weather, Calendar)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +30,12 @@ import libcore.util.MutableInt;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationManager;
+import android.os.Handler;
+import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateFormat;
@@ -36,8 +43,27 @@ import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.android.internal.R;
+import com.android.internal.util.weather.HttpRetriever;
+import com.android.internal.util.weather.WeatherInfo;
+import com.android.internal.util.weather.WeatherXmlParser;
+import com.android.internal.util.weather.YahooPlaceFinder;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.TransportControlView;
+
+import org.w3c.dom.Document;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+
+import libcore.util.MutableInt;
+
/***
* Manages a number of views inside of LockScreen layouts. See below for a list of widgets
*
@@ -71,6 +97,11 @@ class KeyguardStatusViewManager implements OnClickListener {
private TextView mOwnerInfoView;
private TextView mAlarmStatusView;
private TransportControlView mTransportView;
+ private RelativeLayout mWeatherPanel, mWeatherTempsPanel;
+ private TextView mWeatherCity, mWeatherCondition, mWeatherLowHigh, mWeatherTemp, mWeatherUpdateTime;
+ private ImageView mWeatherImage;
+ private LinearLayout mCalendarPanel;
+ private TextView mCalendarEventTitle, mCalendarEventDetails;
// Top-level container view for above views
private View mContainer;
@@ -185,6 +216,32 @@ class KeyguardStatusViewManager implements OnClickListener {
mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen;
mDigitalClock = (DigitalClock) findViewById(R.id.time);
+ // Weather panel
+ mWeatherPanel = (RelativeLayout) findViewById(R.id.weather_panel);
+ mWeatherCity = (TextView) findViewById(R.id.weather_city);
+ mWeatherCondition = (TextView) findViewById(R.id.weather_condition);
+ mWeatherImage = (ImageView) findViewById(R.id.weather_image);
+ mWeatherTemp = (TextView) findViewById(R.id.weather_temp);
+ mWeatherLowHigh = (TextView) findViewById(R.id.weather_low_high);
+ mWeatherUpdateTime = (TextView) findViewById(R.id.update_time);
+ mWeatherTempsPanel = (RelativeLayout) findViewById(R.id.weather_temps_panel);
+
+ // Hide Weather panel view until we know we need to show it.
+ if (mWeatherPanel != null) {
+ mWeatherPanel.setVisibility(View.GONE);
+ mWeatherPanel.setOnClickListener(this);
+ }
+
+ // Calendar panel
+ mCalendarPanel = (LinearLayout) findViewById(R.id.calendar_panel);
+ mCalendarEventTitle = (TextView) findViewById(R.id.calendar_event_title);
+ mCalendarEventDetails = (TextView) findViewById(R.id.calendar_event_details);
+
+ // Hide calendar panel view until we know we need to show it.
+ if (mCalendarPanel != null) {
+ mCalendarPanel.setVisibility(View.GONE);
+ }
+
// Hide transport control view until we know we need to show it.
if (mTransportView != null) {
mTransportView.setVisibility(View.GONE);
@@ -204,10 +261,12 @@ class KeyguardStatusViewManager implements OnClickListener {
resetStatusInfo();
refreshDate();
updateOwnerInfo();
+ refreshWeather();
+ refreshCalendar();
// Required to get Marquee to work.
final View scrollableViews[] = { mCarrierView, mDateView, mStatus1View, mOwnerInfoView,
- mAlarmStatusView };
+ mAlarmStatusView, mCalendarEventDetails, mWeatherCity, mWeatherCondition };
for (View v : scrollableViews) {
if (v != null) {
v.setSelected(true);
@@ -215,6 +274,303 @@ class KeyguardStatusViewManager implements OnClickListener {
}
}
+ /*
+ * CyanogenMod Lock screen Weather related functionality
+ */
+ private static final String URL_YAHOO_API_WEATHER = "http://weather.yahooapis.com/forecastrss?w=%s&u=";
+ private static WeatherInfo mWeatherInfo = new WeatherInfo();
+ private static final int QUERY_WEATHER = 0;
+ private static final int UPDATE_WEATHER = 1;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case QUERY_WEATHER:
+ Thread queryWeather = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ LocationManager locationManager = (LocationManager) getContext().
+ getSystemService(Context.LOCATION_SERVICE);
+ final ContentResolver resolver = getContext().getContentResolver();
+ boolean useCustomLoc = Settings.System.getInt(resolver,
+ Settings.System.WEATHER_USE_CUSTOM_LOCATION, 0) == 1;
+ String customLoc = Settings.System.getString(resolver,
+ Settings.System.WEATHER_CUSTOM_LOCATION);
+ String woeid = null;
+
+ // custom location
+ if (customLoc != null && useCustomLoc) {
+ try {
+ woeid = YahooPlaceFinder.GeoCode(getContext().getApplicationContext(), customLoc);
+ if (DEBUG)
+ Log.d(TAG, "Yahoo location code for " + customLoc + " is " + woeid);
+ } catch (Exception e) {
+ Log.e(TAG, "ERROR: Could not get Location code");
+ e.printStackTrace();
+ }
+ // network location
+ } else {
+ Criteria crit = new Criteria();
+ crit.setAccuracy(Criteria.ACCURACY_COARSE);
+ String bestProvider = locationManager.getBestProvider(crit, true);
+ Location loc = null;
+ if (bestProvider != null) {
+ loc = locationManager.getLastKnownLocation(bestProvider);
+ } else {
+ loc = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
+ }
+ try {
+ woeid = YahooPlaceFinder.reverseGeoCode(getContext(), loc.getLatitude(),
+ loc.getLongitude());
+ if (DEBUG)
+ Log.d(TAG, "Yahoo location code for current geolocation is " + woeid);
+ } catch (Exception e) {
+ Log.e(TAG, "ERROR: Could not get Location code");
+ e.printStackTrace();
+ }
+ }
+ Message msg = Message.obtain();
+ msg.what = UPDATE_WEATHER;
+ msg.obj = woeid;
+ mHandler.sendMessage(msg);
+ }
+ });
+ queryWeather.setPriority(Thread.MIN_PRIORITY);
+ queryWeather.start();
+ break;
+ case UPDATE_WEATHER:
+ String woeid = (String) msg.obj;
+ if (woeid != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Location code is " + woeid);
+ }
+ WeatherInfo w = null;
+ try {
+ w = parseXml(getDocument(woeid));
+ } catch (Exception e) {
+ }
+ if (w == null) {
+ setNoWeatherData();
+ } else {
+ setWeatherData(w);
+ mWeatherInfo = w;
+ }
+ } else {
+ if (mWeatherInfo.temp.equals(WeatherInfo.NODATA)) {
+ setNoWeatherData();
+ } else {
+ setWeatherData(mWeatherInfo);
+ }
+ }
+ break;
+ }
+ }
+ };
+
+ /**
+ * Reload the weather forecast
+ */
+ private void refreshWeather() {
+ final ContentResolver resolver = getContext().getContentResolver();
+ boolean showWeather = Settings.System.getInt(resolver,Settings.System.LOCKSCREEN_WEATHER, 0) == 1;
+
+ if (showWeather) {
+ final long interval = Settings.System.getLong(resolver,
+ Settings.System.WEATHER_UPDATE_INTERVAL, 60); // Default to hourly
+ boolean manualSync = (interval == 0);
+ if (!manualSync && (((System.currentTimeMillis() - mWeatherInfo.last_sync) / 60000) >= interval)) {
+ mHandler.sendEmptyMessage(QUERY_WEATHER);
+ } else if (manualSync && mWeatherInfo.last_sync == 0) {
+ setNoWeatherData();
+ } else {
+ setWeatherData(mWeatherInfo);
+ }
+ } else {
+ // Hide the Weather panel view
+ if (mWeatherPanel != null) {
+ mWeatherPanel.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ /**
+ * Display the weather information
+ * @param w
+ */
+ private void setWeatherData(WeatherInfo w) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Resources res = getContext().getResources();
+ boolean showLocation = Settings.System.getInt(resolver,
+ Settings.System.WEATHER_SHOW_LOCATION, 1) == 1;
+ boolean showTimestamp = Settings.System.getInt(resolver,
+ Settings.System.WEATHER_SHOW_TIMESTAMP, 1) == 1;
+ boolean invertLowhigh = Settings.System.getInt(resolver,
+ Settings.System.WEATHER_INVERT_LOWHIGH, 0) == 1;
+
+ if (mWeatherPanel != null) {
+ if (mWeatherImage != null) {
+ String conditionCode = w.condition_code;
+ String condition_filename = "weather_" + conditionCode;
+ int resID = res.getIdentifier(condition_filename, "drawable",
+ getContext().getPackageName());
+
+ if (DEBUG)
+ Log.d("Weather", "Condition:" + conditionCode + " ID:" + resID);
+
+ if (resID != 0) {
+ mWeatherImage.setImageDrawable(res.getDrawable(resID));
+ } else {
+ mWeatherImage.setImageResource(R.drawable.weather_na);
+ }
+ }
+ if (mWeatherCity != null) {
+ mWeatherCity.setText(w.city);
+ mWeatherCity.setVisibility(showLocation ? View.VISIBLE : View.GONE);
+ }
+ if (mWeatherCondition != null) {
+ mWeatherCondition.setText(w.condition);
+ mWeatherCondition.setVisibility(View.VISIBLE);
+ }
+ if (mWeatherUpdateTime != null) {
+ Date lastTime = new Date(mWeatherInfo.last_sync);
+ String date = DateFormat.getDateFormat(getContext()).format(lastTime);
+ String time = DateFormat.getTimeFormat(getContext()).format(lastTime);
+ mWeatherUpdateTime.setText(date + " " + time);
+ mWeatherUpdateTime.setVisibility(showTimestamp ? View.VISIBLE : View.GONE);
+ }
+ if (mWeatherTempsPanel != null && mWeatherTemp != null && mWeatherLowHigh != null) {
+ mWeatherTemp.setText(w.temp);
+ mWeatherLowHigh.setText(invertLowhigh ? w.high + " | " + w.low : w.low + " | " + w.high);
+ mWeatherTempsPanel.setVisibility(View.VISIBLE);
+ }
+
+ // Show the Weather panel view
+ mWeatherPanel.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * There is no data to display, display 'empty' fields and the
+ * 'Tap to reload' message
+ */
+ private void setNoWeatherData() {
+
+ if (mWeatherPanel != null) {
+ if (mWeatherImage != null) {
+ mWeatherImage.setImageResource(R.drawable.weather_na);
+ }
+ if (mWeatherCity != null) {
+ mWeatherCity.setText(R.string.weather_no_data);
+ mWeatherCity.setVisibility(View.VISIBLE);
+ }
+ if (mWeatherCondition != null) {
+ mWeatherCondition.setText(R.string.weather_tap_to_refresh);
+ }
+ if (mWeatherUpdateTime != null) {
+ mWeatherUpdateTime.setVisibility(View.GONE);
+ }
+ if (mWeatherTempsPanel != null ) {
+ mWeatherTempsPanel.setVisibility(View.GONE);
+ }
+
+ // Show the Weather panel view
+ mWeatherPanel.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Get the weather forecast XML document for a specific location
+ * @param woeid
+ * @return
+ */
+ private Document getDocument(String woeid) {
+ try {
+ boolean celcius = Settings.System.getInt(getContext().getContentResolver(),
+ Settings.System.WEATHER_USE_METRIC, 1) == 1;
+ String urlWithDegreeUnit;
+
+ if (celcius) {
+ urlWithDegreeUnit = URL_YAHOO_API_WEATHER + "c";
+ } else {
+ urlWithDegreeUnit = URL_YAHOO_API_WEATHER + "f";
+ }
+
+ return new HttpRetriever().getDocumentFromURL(String.format(urlWithDegreeUnit, woeid));
+ } catch (IOException e) {
+ Log.e(TAG, "Error querying Yahoo weather");
+ }
+
+ return null;
+ }
+
+ /**
+ * Parse the weather XML document
+ * @param wDoc
+ * @return
+ */
+ private WeatherInfo parseXml(Document wDoc) {
+ try {
+ return new WeatherXmlParser(getContext()).parseWeatherResponse(wDoc);
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing Yahoo weather XML document");
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /*
+ * CyanogenMod Lock screen Calendar related functionality
+ */
+
+ private void refreshCalendar() {
+ if (mCalendarPanel != null) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ String[] nextCalendar = null;
+ boolean visible = false; // Assume we are not showing the view
+
+ // Load the settings
+ boolean lockCalendar = (Settings.System.getInt(resolver,
+ Settings.System.LOCKSCREEN_CALENDAR, 0) == 1);
+ String[] calendars = parseStoredValue(Settings.System.getString(
+ resolver, Settings.System.LOCKSCREEN_CALENDARS));
+ boolean lockCalendarRemindersOnly = (Settings.System.getInt(resolver,
+ Settings.System.LOCKSCREEN_CALENDAR_REMINDERS_ONLY, 0) == 1);
+ long lockCalendarLookahead = Settings.System.getLong(resolver,
+ Settings.System.LOCKSCREEN_CALENDAR_LOOKAHEAD, 10800000);
+
+ if (lockCalendar) {
+ nextCalendar = mLockPatternUtils.getNextCalendarAlarm(lockCalendarLookahead,
+ calendars, lockCalendarRemindersOnly);
+ if (nextCalendar[0] != null && mCalendarEventTitle != null) {
+ mCalendarEventTitle.setText(nextCalendar[0].toString());
+ if (nextCalendar[1] != null && mCalendarEventDetails != null) {
+ mCalendarEventDetails.setText(nextCalendar[1]);
+ }
+ visible = true;
+ }
+ }
+
+ mCalendarPanel.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ /**
+ * Split the MultiSelectListPreference string based on a separator of ',' and
+ * stripping off the start [ and the end ]
+ * @param val
+ * @return
+ */
+ private static String[] parseStoredValue(String val) {
+ if (val == null || val.isEmpty())
+ return null;
+ else {
+ // Strip off the start [ and the end ] before splitting
+ val = val.substring(1, val.length() -1);
+ return (val.split(","));
+ }
+ }
+
private boolean inWidgetMode() {
return mTransportView != null && mTransportView.getVisibility() == View.VISIBLE;
}
@@ -657,6 +1013,16 @@ class KeyguardStatusViewManager implements OnClickListener {
public void onClick(View v) {
if (v == mEmergencyCallButton) {
mCallback.takeEmergencyCallAction();
+ } else if (v == mWeatherPanel) {
+ // Indicate we are refreshing
+ if (mWeatherCondition != null) {
+ mWeatherCondition.setText(R.string.weather_refreshing);
+ }
+
+ mCallback.pokeWakelock();
+ if (!mHandler.hasMessages(QUERY_WEATHER)) {
+ mHandler.sendEmptyMessage(QUERY_WEATHER);
+ }
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java
index 3de1428..bf43199 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java
@@ -25,6 +25,8 @@ import com.android.internal.widget.LockPatternUtils;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.PendingIntent;
+import android.app.Profile;
+import android.app.ProfileManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -248,6 +250,8 @@ public class KeyguardViewMediator implements KeyguardViewCallback {
private int mUnlockSoundId;
private int mLockSoundStreamId;
+ private ProfileManager mProfileManager;
+
/**
* The volume applied to the lock/unlock sounds.
*/
@@ -369,6 +373,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback {
mWakeAndHandOff.setReferenceCounted(false);
mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
+ mProfileManager = (ProfileManager) context.getSystemService(Context.PROFILE_SERVICE);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -434,11 +439,21 @@ public class KeyguardViewMediator implements KeyguardViewCallback {
mScreenOn = false;
if (DEBUG) Log.d(TAG, "onScreenTurnedOff(" + why + ")");
- // Lock immediately based on setting if secure (user has a pin/pattern/password).
- // This also "locks" the device when not secure to provide easy access to the
- // camera while preventing unwanted input.
- final boolean lockImmediately =
- mLockPatternUtils.getPowerButtonInstantlyLocks() || !mLockPatternUtils.isSecure();
+ // Prepare for handling Lock/Slide lock delay and timeout
+ boolean lockImmediately = false;
+ final ContentResolver cr = mContext.getContentResolver();
+ boolean separateSlideLockTimeoutEnabled = Settings.System.getInt(cr,
+ Settings.System.SCREEN_LOCK_SLIDE_DELAY_TOGGLE, 0) == 1;
+ if (mLockPatternUtils.isSecure()) {
+ // Lock immediately based on setting if secure (user has a pin/pattern/password)
+ // This is retained as-is to ensue AOSP security integrity is maintained
+ lockImmediately = mLockPatternUtils.getPowerButtonInstantlyLocks();
+ } else {
+ // Unless a separate slide lock timeout is enabled, this "locks" the device when
+ // not secure to provide easy access to the camera while preventing unwanted input
+ lockImmediately = separateSlideLockTimeoutEnabled ? false
+ : mLockPatternUtils.getPowerButtonInstantlyLocks();
+ }
if (mExitSecureCallback != null) {
if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
@@ -457,7 +472,6 @@ public class KeyguardViewMediator implements KeyguardViewCallback {
// to enable it a little bit later (i.e, give the user a chance
// to turn the screen back on within a certain window without
// having to unlock the screen)
- final ContentResolver cr = mContext.getContentResolver();
// From DisplaySettings
long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT,
@@ -468,17 +482,33 @@ public class KeyguardViewMediator implements KeyguardViewCallback {
Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
KEYGUARD_LOCK_AFTER_DELAY_DEFAULT);
+ // From CyanogenMod specific Settings
+ int slideLockTimeoutDelay = (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT ? Settings.System
+ .getInt(cr, Settings.System.SCREEN_LOCK_SLIDE_TIMEOUT_DELAY,
+ KEYGUARD_LOCK_AFTER_DELAY_DEFAULT) : Settings.System.getInt(cr,
+ Settings.System.SCREEN_LOCK_SLIDE_SCREENOFF_DELAY, 0));
+
// From DevicePolicyAdmin
final long policyTimeout = mLockPatternUtils.getDevicePolicyManager()
.getMaximumTimeToLock(null);
+ if (DEBUG) Log.d(TAG, "Security lock screen timeout delay is " + lockAfterTimeout
+ + " ms; slide lock screen timeout delay is "
+ + slideLockTimeoutDelay
+ + " ms; Separate slide lock delay settings considered: "
+ + separateSlideLockTimeoutEnabled
+ + "; Policy timeout is "
+ + policyTimeout
+ + " ms");
+
long timeout;
if (policyTimeout > 0) {
// policy in effect. Make sure we don't go beyond policy limit.
displayTimeout = Math.max(displayTimeout, 0); // ignore negative values
timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout);
} else {
- timeout = lockAfterTimeout;
+ // Not sure lockAfterTimeout is needed any more but keeping it for AOSP compatibility
+ timeout = separateSlideLockTimeoutEnabled ? slideLockTimeoutDelay : lockAfterTimeout;
}
if (timeout <= 0) {
@@ -708,6 +738,13 @@ public class KeyguardViewMediator implements KeyguardViewCallback {
return;
}
+ // if the current profile has disabled us, don't show
+ if (!lockedOrMissing
+ && mProfileManager.getActiveProfile().getScreenLockMode() == Profile.LockMode.DISABLE) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because of profile override");
+ return;
+ }
+
if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
showLocked();
}