/* * Copyright (C) 2013 75py * * 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.nagopy.android.xposed.utilities; import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; import android.annotation.TargetApi; import android.content.Context; import android.content.IntentFilter; import android.content.res.XModuleResources; import android.graphics.Typeface; import android.os.Build; import android.util.TypedValue; import android.widget.TextClock; import android.widget.TextView; import com.nagopy.android.common.pref.FontListPreference; import com.nagopy.android.xposed.SettingChangedReceiver; import com.nagopy.android.xposed.util.XUtil; import com.nagopy.android.xposed.utilities.XposedModules.HandleInitPackageResources; import com.nagopy.android.xposed.utilities.XposedModules.InitZygote; import com.nagopy.android.xposed.utilities.XposedModules.XMinSdkVersion; import com.nagopy.android.xposed.utilities.XposedModules.XposedModule; import com.nagopy.android.xposed.utilities.setting.ModLockscreenClockSettingsGen; import com.nagopy.android.xposed.utilities.util.Const; import de.robv.android.xposed.IXposedHookZygoteInit.StartupParam; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodReplacement; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; import de.robv.android.xposed.callbacks.XC_LayoutInflated; /** * ロックスクリーンの時計をカスタマイズするモジュール. */ @XposedModule(setting = ModLockscreenClockSettingsGen.class) @XMinSdkVersion(Build.VERSION_CODES.JELLY_BEAN_MR1) @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public class ModLockscreenClock { /** キーガードのパッケージ名 */ private static final String PACKAGE_KEYGUARD = "com.android.keyguard"; @InitZygote public static void initZygote( StartupParam startupParam, ModLockscreenClockSettingsGen mSettings) throws Throwable { // chooseFormatをフック // フォーマットに「秒がある場合は秒更新を行う」ロジックがもともとあるので、それを使用するため XposedHelpers.findAndHookMethod(TextClock.class, "chooseFormat", boolean.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Object format = XposedHelpers .getAdditionalInstanceField(param.thisObject, Const.ADDITIONAL_DATE_FORMAT); if (format == null) { // 追加フィールドがない場合は何もしない return; } // このモジュールで追加した値がある場合は、mFormat12と24に値をセット // オリジナルのchooseFormatでmFormat24またはmFormat12がmFormatに入る SimpleDateFormat simpleDateFormat = (SimpleDateFormat) format; String formatString = simpleDateFormat.toPattern(); XposedHelpers.setObjectField(param.thisObject, "mFormat24", formatString); XposedHelpers.setObjectField(param.thisObject, "mFormat12", formatString); } }); // 時計更新メソッドを書き換え XposedHelpers.findAndHookMethod(TextClock.class, "onTimeChanged", new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { Object format = XposedHelpers .getAdditionalInstanceField(param.thisObject, Const.ADDITIONAL_DATE_FORMAT); if (format == null) { // 追加フィールドがない場合は通常の処理を実行 return XUtil.invokeOriginalMethod(param); } else { // 設定値を使用して時計を更新 TextClock textClock = (TextClock) param.thisObject; Calendar mTime = (Calendar) XposedHelpers .getObjectField(textClock, "mTime"); mTime.setTimeInMillis(System.currentTimeMillis()); SimpleDateFormat simpleDateFormat = (SimpleDateFormat) format; textClock.setText(simpleDateFormat.format(mTime.getTime())); return null; } } }); } @HandleInitPackageResources(targetPackage = PACKAGE_KEYGUARD) public static void handleInitPackageResources( final String modulePath, final InitPackageResourcesParam resparam, final ModLockscreenClockSettingsGen mSettings ) throws Throwable { // レイアウトをごにょごにょ resparam.res.hookLayout(PACKAGE_KEYGUARD, "layout", "keyguard_status_view", new XC_LayoutInflated() { @Override public void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable { // 時計のビューを取得 TextClock mDateView = (TextClock) liparam.view .findViewById(liparam.res.getIdentifier( "date_view", "id", PACKAGE_KEYGUARD)); TextClock mClockView = (TextClock) liparam.view .findViewById(liparam.res.getIdentifier( "clock_view", "id", PACKAGE_KEYGUARD)); // デフォルト値を保存 mSettings.defaultTimeTextSize = mClockView.getTextSize(); mSettings.defaultTimeTextColor = mClockView .getTextColors().getDefaultColor(); mSettings.defaultTimeTypeface = mClockView.getTypeface(); mSettings.defaultDateTextSize = mDateView.getTextSize(); mSettings.defaultDateTextColor = mDateView .getTextColors().getDefaultColor(); mSettings.defaultDateTypeface = mDateView.getTypeface(); // モジュールリソース取得用の値をDaoに追加 mSettings.moduleResources = XModuleResources .createInstance(modulePath, resparam.res); // モジュールの設定を保存 updateSettings(mDateView, mClockView, mSettings); // 時計を更新 update(mDateView, mClockView); // 設定変更をリアルタイムに反映させるためのレシーバーを登録 Context context = mClockView.getContext(); context.registerReceiver( new ModLockscreenClockSettingChangedReceiver( mDateView, mClockView, mSettings, Const.ACTION_LOCKSCREEN_CLOCK_SETTING_CHANGED), new IntentFilter(Const.ACTION_LOCKSCREEN_CLOCK_SETTING_CHANGED)); } }); } /** * 表示を更新する. * * @param mDateView * @param mClockView */ private static void update(TextView mDateView, TextView mClockView) { // initを実行 XposedHelpers.callMethod(mDateView, "init"); XposedHelpers.callMethod(mClockView, "init"); } /** * 時計の表示設定を変更する. * * @param mDateView * @param mClockView * @param setting {@link ModLockscreenClockSettingsGen} */ private static void updateSettings(TextView mDateView, TextView mClockView, ModLockscreenClockSettingsGen setting) { if (setting.masterModLockscreenClockEnable) { // モジュール有効の場合 // 時計の文字サイズ、色、フォント、フォーマットをセット mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, setting.lockscreenClockTimeTextSize / 100f * setting.defaultTimeTextSize); mClockView.setTextColor(setting.lockscreenClockTimeTextColor); Typeface timeTypeface = FontListPreference.makeTypeface( setting.moduleResources.getAssets(), setting.lockscreenClockTimeTypefaceKbn, setting.lockscreenClockTimeTypefaceName, setting.lockscreenClockTimeTypefaceStyle); mClockView.setTypeface(timeTypeface); SimpleDateFormat timeFormat = new SimpleDateFormat( setting.lockscreenClockTimeFormat, setting.lockscreenClockTimeForceEnglish ? Locale.ENGLISH : Locale.getDefault()); XposedHelpers.setAdditionalInstanceField(mClockView, Const.ADDITIONAL_DATE_FORMAT, timeFormat); // 日付の文字サイズ、色、フォント、フォーマットをセット mDateView.setTextSize(TypedValue.COMPLEX_UNIT_PX, setting.lockscreenClockDateTextSize / 100f * setting.defaultDateTextSize); mDateView.setTextColor(setting.lockscreenClockDateTextColor); Typeface dateTypeface = FontListPreference.makeTypeface( setting.moduleResources.getAssets(), setting.lockscreenClockDateTypefaceKbn, setting.lockscreenClockDateTypefaceName, setting.lockscreenClockDateTypefaceStyle); mDateView.setTypeface(dateTypeface); SimpleDateFormat dateFormat = new SimpleDateFormat( setting.lockscreenClockDateFormat, setting.lockscreenClockDateForceEnglish ? Locale.ENGLISH : Locale.getDefault()); XposedHelpers.setAdditionalInstanceField(mDateView, Const.ADDITIONAL_DATE_FORMAT, dateFormat); } else { // モジュール無効の場合 // 時計のデフォルト設定を反映 mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, setting.defaultTimeTextSize); mClockView.setTextColor(setting.defaultTimeTextColor); mClockView.setTypeface(setting.defaultTimeTypeface); XposedHelpers.removeAdditionalInstanceField(mClockView, Const.ADDITIONAL_DATE_FORMAT); // 日付のデフォルト設定を反映 mDateView.setTextSize(TypedValue.COMPLEX_UNIT_PX, setting.defaultDateTextSize); mDateView.setTextColor(setting.defaultDateTextColor); mDateView.setTypeface(setting.defaultDateTypeface); XposedHelpers.removeAdditionalInstanceField(mDateView, Const.ADDITIONAL_DATE_FORMAT); } } /** * 設定変更を受け取るレシーバー. */ public static class ModLockscreenClockSettingChangedReceiver extends SettingChangedReceiver { private WeakReference<TextView> mClockView; private WeakReference<TextView> mDateView; protected ModLockscreenClockSettingChangedReceiver(TextView mDateView, TextView mClockView, ModLockscreenClockSettingsGen dataObject, String action) { super(dataObject, action); this.mClockView = new WeakReference<TextView>(mClockView); this.mDateView = new WeakReference<TextView>(mDateView); } @Override protected void onDataChanged() { TextView mClockView = this.mClockView.get(); TextView mDateView = this.mDateView.get(); Object setting = this.dataObject.get(); if (isNotNull(mClockView, mDateView, setting)) { // 設定を反映し、表示を更新 updateSettings(mDateView, mClockView, (ModLockscreenClockSettingsGen) setting); update(mDateView, mClockView); } } } }