平台:Android6.0 TV
问题:添加关机动画
解决方法:此处选择方法2添加动画
@Override
public void onClick(View v) {
/*if (v == soundIntelligentOV) {
ShutDownHelper.gotoSmartBox(this);
//Toast.makeText(this, getString(R.string.smart_box), Toast.LENGTH_LONG).show();
} else */if (v == soundBlueToothOV) {
ShutDownHelper.gotoBluetoohBox(this);
//Toast.makeText(this, getString(R.string.bluetooth_box), Toast.LENGTH_LONG).show();
finish();
//关机按钮操作
} else if (v == shutDownOV) {
//Toast.makeText(this, getString(R.string.shutdown), Toast.LENGTH_LONG).show();
ShutDownHelper.shutdown(this);
finish();
//重启按钮操作
} else if (v == restartOV) {
ShutDownHelper.reboot(this);
//Toast.makeText(this, getString(R.string.reboot), Toast.LENGTH_LONG).show();
ShutDownHelper.reboot(this);
finish();
} else if (v == delayShutDownOV) {
if (delayShutDownOV.getTitle().equals(getString(R.string.description_cancel))) {
mAutoDelayOptionView.cancleAutoDelay();
finish();
}else {
Toast.makeText(this, getString(R.string.delay_shutdown), Toast.LENGTH_LONG).show();
mAutoDelayOptionView.DelayShutDown();
}
Log.d("sgq","ssss***"+34/15);
}
}
由上可以看到按下shutDownOV按钮时进入ShutDownHelper.shutdown(this);
public static void shutdown(Context context) {
// PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
// pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
// ((Activity)context).finish();
// try {
// Runtime.getRuntime().exec("reboot");
// } catch (IOException e) {
// e.printStackTrace();
// }
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
pm.shutdown(false,false);
//change by xzh,reboot enter AC on standby
}
通过PowerManager对象调用其shutdown方法
2. 进入frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
/**
* Shuts down the device.
*
* @param confirm If true, shows a shutdown confirmation dialog.
* @param wait If true, this call waits for the shutdown to complete and does not return.
*/
@Override // Binder call
public void shutdown(boolean confirm, boolean wait) {
//权限的获取
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(true, confirm, null, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
此处传入两个参数,confirm-表示按下关机键时关机确认对话框是否弹出;
private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
final String reason, boolean wait) {
if (mHandler == null || !mSystemReady) {
throw new IllegalStateException("Too early to call shutdown() or reboot()");
}
//根据bool值shutdown判断应该执行关机还是重启
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
if (shutdown) {
ShutdownThread.shutdown(mContext, confirm);
} else {
ShutdownThread.reboot(mContext, reason, confirm);
}
}
}
};
// ShutdownThread must run on a looper capable of displaying the UI.
Message msg = Message.obtain(mHandler, runnable);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
// PowerManager.reboot() is documented not to return so just wait for the inevitable.
if (wait) {
synchronized (runnable) {
while (true) {
try {
runnable.wait();
} catch (InterruptedException e) {
}
}
}
}
}
/**
* Request a clean shutdown, waiting for subsystems to clean up their
* state etc. Must be called from a Looper thread in which its UI
* is shown.
*
* @param context Context used to display the shutdown progress dialog.
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}
static void shutdownInner(final Context context, boolean confirm) {
// ensure that only one thread is trying to power down.
// any additional calls are just returned
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.");
return;
}
}
...
//confirm判断是否弹出关机确认对话框
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
}
//创建关机确认对话框
sConfirmDialog = new AlertDialog.Builder(context)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
: com.android.internal.R.string.power_off)
.setMessage(resourceId)
.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//执行关机
beginShutdownSequence(context);
}
})
.setNegativeButton(com.android.internal.R.string.no, null)
.create();
closer.dialog = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer);
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
sConfirmDialog.show();
} else {
//无需对话框直接执行关机
beginShutdownSequence(context);
}
}
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
}
// Throw up a system dialog to indicate the device is rebooting / shutting down.
ProgressDialog pd = new ProgressDialog(context);
// Path 1: Reboot to recovery and install the update
// Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True
// (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
// UI: progress bar
//
// Path 2: Reboot to recovery for factory reset
// Condition: mRebootReason == REBOOT_RECOVERY
// UI: spinning circle only (no progress bar)
//
// Path 3: Regular reboot / shutdown
// Condition: Otherwise
// UI: spinning circle only (no progress bar)
if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
if (mRebootUpdate) {
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_update_prepare));
pd.setMax(100);
pd.setProgressNumberFormat(null);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setProgress(0);
pd.setIndeterminate(false);
//add by shaoguangqing for shutdown UI on
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
pd.show();
//add by shaoguangqing for shutdown UI off
}
}
//初始化关机线程
sInstance.mProgressDialog = pd;
Log.i("shutdownthread qff","beginShutdownSequence sInstance.mProgressDialog= "+sInstance.mProgressDialog);
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
// make sure we never fall asleep again
sInstance.mCpuWakeLock = null;
try {
Log.i("shutdownthread qff","beginShutdownSequence sInstance.mCpuWakeLock---------");
sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
sInstance.mCpuWakeLock.setReferenceCounted(false);
sInstance.mCpuWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mCpuWakeLock = null;
}
// also make sure the screen stays on for better user experience
sInstance.mScreenWakeLock = null;
if (sInstance.mPowerManager.isScreenOn()) {
try {
sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
sInstance.mScreenWakeLock.setReferenceCounted(false);
sInstance.mScreenWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mScreenWakeLock = null;
}
}
// start the thread that initiates shutdown
// 启动关机线程
sInstance.mHandler = new Handler() {
};
sInstance.start();
}
/**
* Makes sure we handle the shutdown gracefully.
* Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
*/
public void run() {
BroadcastReceiver br = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
// We don't allow apps to cancel this, so ignore the result.
// 用于接收关机广播
actionDone();
}
};
/*
* Write a system property in case the system_server reboots before we
* get to the actual hardware restart. If that happens, we'll retry at
* the beginning of the SystemServer startup.
*/
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
/*
* If we are rebooting into safe mode, write a system property
* indicating so.
*/
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
//等待10s,前面定义的广播接收器收到关机广播时mActionDone设置为true,同时取消等待
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
while (!mActionDone) {
long delay = endTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown broadcast timed out");
break;
} else if (mRebootUpdate) {
int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
sInstance.setRebootProgress(status, null);
}
try {
mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
} catch (InterruptedException e) {
}
}
}
if (mRebootUpdate) {
sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
}
Log.i(TAG, "Shutting down activity manager...");
// 10s内关闭ActivityManager服务
final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
try {
am.shutdown(MAX_BROADCAST_TIME);
} catch (RemoteException e) {
}
}
if (mRebootUpdate) {
sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
}
Log.i(TAG, "Shutting down package manager...");
//关闭PackageManager服务
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
if (pm != null) {
pm.shutdown();
}
if (mRebootUpdate) {
sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
}
// Shutdown radios.
//12s内关闭收音机
shutdownRadios(MAX_RADIO_WAIT_TIME);
if (mRebootUpdate) {
sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
}
// Shutdown MountService to ensure media is in a safe state
IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
public void onShutDownComplete(int statusCode) throws RemoteException {
Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
actionDone();
}
};
Log.i(TAG, "Shutting down MountService");
// Set initial variables and time out time.
//20s内关闭MountService服务
mActionDone = false;
final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
synchronized (mActionDoneSync) {
try {
final IMountService mount = IMountService.Stub.asInterface(
ServiceManager.checkService("mount"));
if (mount != null) {
mount.shutdown(observer);
} else {
Log.w(TAG, "MountService unavailable for shutdown");
}
} catch (Exception e) {
Log.e(TAG, "Exception during MountService shutdown", e);
}
while (!mActionDone) {
long delay = endShutTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown wait timed out");
break;
} else if (mRebootUpdate) {
int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
(MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
MAX_SHUTDOWN_WAIT_TIME);
status += RADIO_STOP_PERCENT;
sInstance.setRebootProgress(status, null);
}
try {
mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
} catch (InterruptedException e) {
}
}
}
if (mRebootUpdate) {
sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
// If it's to reboot to install update, invoke uncrypt via init service.
uncrypt();
}
// 执行重启或关机
rebootOrShutdown(mContext, mReboot, mRebootReason);
}
run方法中主要完成的工作有:
/**
* Do not call this directly. Use {@link #reboot(Context, String, boolean)}
* or {@link #shutdown(Context, boolean)} instead.
*
* @param context Context used to vibrate or null without vibration
* @param reboot true to reboot or false to shutdown
* @param reason reason for reboot
*/
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
Log.e(TAG, "Reboot failed, will attempt shutdown instead");
} else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
// vibrate before shutting down
//关机前震动
Vibrator vibrator = new SystemVibrator(context);
try {
vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
} catch (Exception e) {
// Failure to vibrate shouldn't interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
}
// vibrator is asynchronous so we need to wait to avoid shutting down too soon.
// 先休眠一会,震动结束后再关机
try {
Thread.sleep(SHUTDOWN_VIBRATE_MS);
} catch (InterruptedException unused) {
}
}
// Shutdown power
Log.i(TAG, "Performing low-level shutdown...");
// 调用PowerManagerService的方法关闭电源
PowerManagerService.lowLevelShutdown();
}
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
*/
public static void lowLevelShutdown() {
// MStar Android Patch Begin
//SystemProperties.set("sys.powerctl", "shutdown");
nativeShutdown();
// MStar Android Patch End
}
根据上述源码分析,应该在/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java的shutdown()方法中添加对应的播放动画的方法,此处选择通过activity跳转的方式启动动画。在设计之前,原先的思路是放置在framework/base/package/systemui/的目录下,但是操作过程发现当按下power选择关机按钮后log中会出现
2140 04-19 17:14:31 4868 4997 I ActivityManager: moveTaskToBack: TaskRecord {6fbcc5 #4 A=com.android.systemui U=0 sz=2}
因此导致在播放动画之前就退出了的现象,另尝试在settings下添加该activity,可以实现播放,因此暂时先放在settings目录下
public class ShutdownAnimationGifView extends View {
private long movieStart;
private Movie movie;
//此处必须重写该构造方法
public ShutdownAnimationGifView(Context context,AttributeSet attributeSet) {
super(context,attributeSet);
//以文件流(InputStream)读取进gif图片资源
movie=Movie.decodeStream(getResources().openRawResource(R.drawable.shutdownanimation));
}
@Override
protected void onDraw(Canvas canvas) {
long curTime=android.os.SystemClock.uptimeMillis();
//第一次播放
if (movieStart == 0) {
movieStart = curTime;
}
if (movie != null) {
int duraction = movie.duration();
int relTime = (int) ((curTime-movieStart)%duraction);
movie.setTime(relTime);
movie.draw(canvas, 0, 0);
//强制重绘
invalidate();
}
super.onDraw(canvas);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:attr="http://schemas.android.com/apk/res/com.android.tv.settings"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.tv.settings.ShutdownAnimationGifView
android:id="@+id/shutanimation_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
LinearLayout>
public class ShutAnimationShow extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//设置activity为全屏显示
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
android.util.Log.i("ShutAnimationShow","qffffffffffffffff");
setContentView(R.layout.activity_shutanimation_show_ui);
}
}
//exported:是否能够被其他应用程序组件调用或跟它交互
//如果自己定义的某个Activity要通过隐式启动,在AndroidManifast.xm那么必须加上android.intent.category.DEFAULT,否则不起作用
<activity android:name=".ShutAnimationShow"
android:exported="true"
android:hardwareAccelerated="false">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
intent-filter>
activity>
android:hardwareAccelerated=“false” 该属性解决了gif动画不播放的问题,取消硬件加速器
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
try {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//参数是包名,类全限定名,注意直接用类名不行
ComponentName name = new ComponentName("com.android.tv.settings",
"com.android.tv.settings.ShutAnimationShow");
Log.i("shutdownthread qff","beginShutdownSequence--------- ");
intent.setComponent(name);
context.startActivity(intent);
//context.startActivityAsUser(intent, UserHandle.CURRENT);
sleep(3000);
}catch (Exception e){
e.printStackTrace();
}
shutdownInner(context, confirm);
}
这里使用组建名启动方式启动,跳转时会出现如下log信息:
04-19 14:37:53.463 4407 4530 W ContextImpl: Calling a method in the system process without a qualified user: android.app.ContextImpl.startActivity:658 com.android.server.power.ShutdownThread.beginShutdownSequence:310
此处为警告可以不做修改,若必须改可以修改为
context.startActivityAsUser(intent, UserHandle.CURRENT);
解释参考https://blog.csdn.net/love_xsq/article/details/50392093
注意
上处代码中添加sleep(3000),若不做休眠处理会出现activity界面闪一下的现象,因为跳转activity与接下来的关机流程几乎同步,故作休眠处理
以上的修改完成的关机动画的添加