/** * DATP - Dismiss Alarm Tasker Plugin * Copyright (C) 2014 * * DATP is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * DATP is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @author Fero */ package fero.xposed.dismissalarm; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import static de.robv.android.xposed.XposedHelpers.getObjectField; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.IBinder; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.IXposedHookZygoteInit; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; /** * Xposed initialization class to ensure KitKat or greater compatibility and * to hook into the android Alarm Clock/Desk Clock setDismissState methods. * During reflection of the setDismissState, this class will create an intent * to relay and check an event plugin for Tasker. * * NOTE: Tasker event protocol was added in v4.3b5 and may require download * and installation from Tasker's beta page: * * http://tasker.dinglisch.net/beta.html */ public class DismissAlarm extends Service implements IXposedHookZygoteInit, IXposedHookLoadPackage { /** * Package name for the alarm/desk clock */ private static final String ALARM_CLOCK_PACKAGE_NAME = "com.google.android.deskclock"; /** * AlarmStateManager class string name */ private static final String ALARM_STATE_MANAGER_CLASS_NAME = "com.android.deskclock.alarms.AlarmStateManager"; /** * AlarmInstance class string name */ private static final String ALARM_INSTANCE_CLASS_NAME = "com.android.deskclock.provider.AlarmInstance"; @Override public void initZygote(StartupParam startupParam) throws Throwable { //Log hardware and software information for device Logger.Info("Hardware: " + Build.HARDWARE); Logger.Info("Product: " + Build.PRODUCT); Logger.Info("Device manufacturer: " + Build.MANUFACTURER); Logger.Info("Device brand: " + Build.BRAND); Logger.Info("Device model: " + Build.MODEL); Logger.Info("Android SDK: " + Build.VERSION.SDK_INT); Logger.Info("Android Release: " + Build.VERSION.RELEASE); Logger.Info("ROM: " + Build.DISPLAY); //Ensure that KitKat or greater is being used if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)) { Logger.Error("Module requires KitKat or greater"); throw new Exception("Dismiss Alarm Tasker Plugin module requires KitKat or greater"); } } /** * Called during each package (apk) load to handle hooking into methods for * the xposed module * * @param loadPackageParam Parameters for the package being loaded */ @Override public void handleLoadPackage(final LoadPackageParam loadPackageParam) throws Throwable { /* * Ensure that we are only handling customizations for the alarm/desk * clock package */ if (loadPackageParam.packageName.equals(ALARM_CLOCK_PACKAGE_NAME)) { //Find the AlarmStateManager and AlarmInstance classes final Class<?> alarmStateManager = XposedHelpers.findClass(ALARM_STATE_MANAGER_CLASS_NAME, loadPackageParam.classLoader); final Class<?> alarmInstance = XposedHelpers.findClass(ALARM_INSTANCE_CLASS_NAME, loadPackageParam.classLoader); Logger.Fine("Hooking AlarmStateManager::setDismissState(Context, AlarmInstance)"); findAndHookMethod(alarmStateManager, "setDismissState", Context.class, alarmInstance, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { //Get the alarm label from the method parameters String alarmLabel = getObjectField(param.args[1], "mLabel").toString(); Context context = (Context) param.args[0]; Logger.Info("Alarm Dismissed: " + alarmLabel); //Broadcast the event to tasker broadcastDismissStateChange(context, alarmLabel); super.beforeHookedMethod(param); } }); } } /** * Broadcast/Send intent for a dismissed alarm * * @param context Context to utilize for sending the intent * @param alarmLabel Label for the dismissed alarm */ private final void broadcastDismissStateChange(final Context context, final String alarmLabel) { //Create the intent for the dismissed alarm Intent intent = new Intent(Constants.DISMISS_ALARM_STATE_INTENT); intent.putExtra(Constants.DISMISS_ALARM_KEY_ALARM_LABEL, alarmLabel); //Broadcast the intent for the dismissed alarm context.sendBroadcast(intent); Logger.Fine("Broadcasting Dismiss Alarm Intent"); } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } }