blob: 9a903e468a7249502d01d04cb96f7641b0770d77 [file] [log] [blame]
/**
* Copyright (C) 2012 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 android.service.dreams;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
import android.view.ActionMode;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.policy.PolicyManager;
/**
* Extend this class to implement a custom screensaver.
*/
public class Dream extends Service implements Window.Callback {
private final static boolean DEBUG = true;
private final static String TAG = "Dream";
/**
* The {@link Intent} that must be declared as handled by the service.
* To be supported, the service must also require the
* {@link android.Manifest.permission#BIND_WALLPAPER} permission so
* that other applications can not abuse it.
*/
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
"android.service.dreams.Dream";
private Window mWindow;
private WindowManager mWindowManager;
private IDreamManager mSandman;
private boolean mInteractive;
final Handler mHandler = new Handler();
boolean mFinished = false;
// begin Window.Callback methods
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (!mInteractive) {
finish();
return true;
}
return mWindow.superDispatchKeyEvent(event);
}
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
if (!mInteractive) {
finish();
return true;
}
return mWindow.superDispatchKeyShortcutEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (!mInteractive) {
finish();
return true;
}
return mWindow.superDispatchTouchEvent(event);
}
@Override
public boolean dispatchTrackballEvent(MotionEvent event) {
if (!mInteractive) {
finish();
return true;
}
return mWindow.superDispatchTrackballEvent(event);
}
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event) {
if (!mInteractive) {
finish();
return true;
}
return mWindow.superDispatchGenericMotionEvent(event);
}
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
return false;
}
@Override
public View onCreatePanelView(int featureId) {
return null;
}
@Override
public boolean onCreatePanelMenu(int featureId, Menu menu) {
return false;
}
@Override
public boolean onPreparePanel(int featureId, View view, Menu menu) {
return false;
}
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
return false;
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
return false;
}
@Override
public void onWindowAttributesChanged(LayoutParams attrs) {
}
@Override
public void onContentChanged() {
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
}
@Override
public void onAttachedToWindow() {
mWindow.addFlags(
WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
);
lightsOut();
}
@Override
public void onDetachedFromWindow() {
}
@Override
public void onPanelClosed(int featureId, Menu menu) {
}
@Override
public boolean onSearchRequested() {
return false;
}
@Override
public ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback callback) {
return null;
}
@Override
public void onActionModeStarted(ActionMode mode) {
}
@Override
public void onActionModeFinished(ActionMode mode) {
}
// end Window.Callback methods
public WindowManager getWindowManager() {
return mWindowManager;
}
public Window getWindow() {
return mWindow;
}
/**
* Called when this Dream is constructed. Place your initialization here.
*
* Subclasses must call through to the superclass implementation.
*/
@Override
public void onCreate() {
super.onCreate();
if (DEBUG) Slog.v(TAG, "Dream created on thread " + Thread.currentThread().getId());
mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams"));
}
/**
* Called when this Dream is started.
*/
public void onStart() {
// hook for subclasses
}
/**
* Inflate a layout resource and set it to be the content view for this Dream.
* Behaves similarly to {@link android.app.Activity#setContentView(int)}.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
}
/**
* Set a view to be the content view for this Dream.
* Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)},
* including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view.
*
* @param view The desired content to display.
*
* @see #setContentView(int)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(View view) {
getWindow().setContentView(view);
}
/**
* Set a view to be the content view for this Dream.
* Behaves similarly to
* {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}.
*
* @param view The desired content to display.
* @param params Layout parameters for the view.
*
* @see #setContentView(android.view.View)
* @see #setContentView(int)
*/
public void setContentView(View view, ViewGroup.LayoutParams params) {
getWindow().setContentView(view, params);
}
/**
* Add a view to the Dream's window, leaving other content views in place.
*
* @param view The desired content to display.
* @param params Layout parameters for the view.
*/
public void addContentView(View view, ViewGroup.LayoutParams params) {
getWindow().addContentView(view, params);
}
/**
* @param mInteractive the mInteractive to set
*/
public void setInteractive(boolean mInteractive) {
this.mInteractive = mInteractive;
}
/**
* @return the mInteractive
*/
public boolean isInteractive() {
return mInteractive;
}
/** Convenience method for setting View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. */
protected void lightsOut() {
// turn the lights down low
final View v = mWindow.getDecorView();
if (v != null) {
v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
}
/**
* Finds a view that was identified by the id attribute from the XML that
* was processed in {@link #onCreate}.
*
* @return The view if found or null otherwise.
*/
public View findViewById(int id) {
return getWindow().findViewById(id);
}
/**
* Called when this Dream is being removed from the screen and stopped.
*/
@Override
public void onDestroy() {
super.onDestroy();
mWindowManager.removeView(mWindow.getDecorView());
}
/**
* Creates a new dream window, attaches the current content view, and shows it.
*
* @param windowToken Binder to attach to the window to allow access to the correct window type.
* @hide
*/
final /*package*/ void attach(IBinder windowToken) {
if (DEBUG) Slog.v(TAG, "Dream attached on thread " + Thread.currentThread().getId());
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
if (DEBUG) Slog.v(TAG, "attaching window token: " + windowToken
+ " to window of type " + WindowManager.LayoutParams.TYPE_DREAM);
WindowManager.LayoutParams lp = mWindow.getAttributes();
lp.type = WindowManager.LayoutParams.TYPE_DREAM;
lp.token = windowToken;
lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
//WindowManagerImpl.getDefault().addView(mWindow.getDecorView(), lp);
if (DEBUG) Slog.v(TAG, "created and attached window: " + mWindow);
mWindow.setWindowManager(null, windowToken, "dream", true);
mWindowManager = mWindow.getWindowManager();
// now make it visible
mHandler.post(new Runnable(){
@Override
public void run() {
if (DEBUG) Slog.v(TAG, "Dream window added on thread " + Thread.currentThread().getId());
getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
// start it up
onStart();
}});
}
/**
* Stop the dream and wake up.
*
* After this method is called, the service will be stopped.
*/
public void finish() {
if (mFinished) return;
try {
mSandman.awaken(); // assuming we were started by the DreamManager
stopSelf(); // if launched via any other means
mFinished = true;
} catch (RemoteException ex) {
// sigh
}
}
class IDreamServiceWrapper extends IDreamService.Stub {
public IDreamServiceWrapper() {
}
public void attach(IBinder windowToken) {
Dream.this.attach(windowToken);
}
}
/**
* Implement to return the implementation of the internal accessibility
* service interface. Subclasses should not override.
*/
@Override
public final IBinder onBind(Intent intent) {
return new IDreamServiceWrapper();
}
}