/* * Copyright (C) 2014 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.Date; 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.view.ViewGroup; import android.widget.TextView; import com.nagopy.android.common.pref.FontListPreference; import com.nagopy.android.common.util.VersionUtil; import com.nagopy.android.xposed.SettingChangedReceiver; import com.nagopy.android.xposed.util.XConst; import com.nagopy.android.xposed.util.XUtil; import com.nagopy.android.xposed.utilities.XposedModules.HandleInitPackageResources; import com.nagopy.android.xposed.utilities.XposedModules.HandleLoadPackage; import com.nagopy.android.xposed.utilities.XposedModules.XMinSdkVersion; import com.nagopy.android.xposed.utilities.XposedModules.XposedModule; import com.nagopy.android.xposed.utilities.setting.ModNotificationExpandedClockSettingsGen; import com.nagopy.android.xposed.utilities.util.Const; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodReplacement; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; import de.robv.android.xposed.callbacks.XC_LayoutInflated; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; /** * 通知ヘッダーの時計をカスタマイズするモジュール. */ @XMinSdkVersion(Build.VERSION_CODES.JELLY_BEAN_MR1) @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @XposedModule(setting = ModNotificationExpandedClockSettingsGen.class) public class ModNotificationExpandedClock { @HandleLoadPackage(targetPackage = XConst.PKG_SYSTEM_UI) public static void handleLoadPackage( String modulePath, LoadPackageParam lpparam, ModNotificationExpandedClockSettingsGen mSettings) throws Throwable { // Clockのクラスを取得 final Class<?> clockClass = XposedHelpers.findClass( "com.android.systemui.statusbar.policy.Clock", lpparam.classLoader); // 時計の文字を返すメソッドを書き換え XposedHelpers.findAndHookMethod(clockClass, "getSmallTime", new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { Object additionalInstanceField = XposedHelpers .getAdditionalInstanceField(param.thisObject, Const.ADDITIONAL_DATE_FORMAT); if (additionalInstanceField == null) { // モジュールで追加した値がない場合は元のメソッドを実行 return XUtil.invokeOriginalMethod(param); } // モジュールで設定したフォーマットを使用して時計の文字を作成する Calendar mCalendar = (Calendar) XposedHelpers .getObjectField(param.thisObject, "mCalendar"); SimpleDateFormat mClockFormat = (SimpleDateFormat) additionalInstanceField; return mClockFormat.format(mCalendar.getTime()); } }); // 日付フォーマットを反映できるようごにょごにょ Class<?> clsDateView = XposedHelpers.findClass( "com.android.systemui.statusbar.policy.DateView", lpparam.classLoader); if (VersionUtil.isKitKatOrLator()) { // KitKatの場合はフックで済ませる XposedHelpers.findAndHookMethod(clsDateView, "updateClock", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Object mDateFormat = XposedHelpers.getObjectField(param.thisObject, "mDateFormat"); if (mDateFormat == null) { // フォーマットを更新 Object additionalInstanceField = XposedHelpers .getAdditionalInstanceField(param.thisObject, Const.ADDITIONAL_DATE_FORMAT); if (additionalInstanceField == null) { // モジュールで追加した値がない場合は元のメソッドを実行 return; } // フォーマットをセット XposedHelpers.setObjectField(param.thisObject, "mDateFormat", additionalInstanceField); } } }); } else { // JBの場合は諦めて書き換える XposedHelpers.findAndHookMethod(clsDateView, "updateClock", new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { Object additionalInstanceField = XposedHelpers .getAdditionalInstanceField(param.thisObject, Const.ADDITIONAL_DATE_FORMAT); if (additionalInstanceField == null) { // モジュールで追加した値がない場合は元のメソッドを実行 return XUtil.invokeOriginalMethod(param); } TextView thisTextView = (TextView) param.thisObject; Date date = new Date(); SimpleDateFormat dateFormat = (SimpleDateFormat) additionalInstanceField; thisTextView.setText(dateFormat.format(date)); return null; } }); } } @HandleInitPackageResources(targetPackage = XConst.PKG_SYSTEM_UI) public static void handleInitPackageResources( final String modulePath, final InitPackageResourcesParam resparam, final ModNotificationExpandedClockSettingsGen mSettings) throws Throwable { // レイアウトをごにょごにょ resparam.res.hookLayout(XConst.PKG_SYSTEM_UI, "layout", "super_status_bar", new XC_LayoutInflated() { @Override public void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable { // 時計のビューを取得 int id_datetime = liparam.res.getIdentifier( "datetime", "id", XConst.PKG_SYSTEM_UI); ViewGroup datetimeViewGroup = (ViewGroup) liparam.view .findViewById(id_datetime); int id_date = liparam.res.getIdentifier("date", "id", XConst.PKG_SYSTEM_UI); TextView mDateView = (TextView) datetimeViewGroup.findViewById(id_date); int id_clock = liparam.res.getIdentifier( "clock", "id", XConst.PKG_SYSTEM_UI); TextView mClockView = (TextView) datetimeViewGroup.findViewById(id_clock); // デフォルト値を保存 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(); // モジュールリソース取得用の値を保存 mSettings.moduleResources = XModuleResources .createInstance(modulePath, resparam.res); // モジュールの設定を保存 updateSettings(mDateView, mClockView, mSettings); // 時計を更新 // ごにょごにょ直後は何もしない(エラー対策) // update(mDateView, mClockView); // 設定変更をリアルタイムに反映させるためのレシーバーを登録 Context context = mClockView.getContext(); context.registerReceiver( new ModNotificationExpandedClockSettingChangedReceiver( mDateView, mClockView, mSettings, Const.ACTION_NOTIFICATION_EXPANDED_CLOCK_SETTING_CHANGED), new IntentFilter( Const.ACTION_NOTIFICATION_EXPANDED_CLOCK_SETTING_CHANGED)); } }); } /** * 表示を更新する. * * @param mDateView * @param mClockView */ private static void update(TextView mDateView, TextView mClockView) { try { XposedHelpers.setObjectField(mDateView, "mDateFormat", null); XposedHelpers.callMethod(mDateView, "updateClock"); XposedHelpers.callMethod(mClockView, "updateClock"); } catch (Throwable t) { XposedBridge.log(t); } } /** * 時計の表示設定を変更する. * * @param mDateView {@link TextView}(Clockクラスのインスタンス) * @param clockModDao {@link GenModStatusBarClockDao} */ private static void updateSettings(TextView mDateView, TextView mClockView, ModNotificationExpandedClockSettingsGen setting) { if (setting.masterModNotificationExpandedClockEnable) { // モジュール有効の場合 // 時計の文字サイズ、色、フォント、フォーマットをセット mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, setting.notificationExpandedClockTimeTextSize / 100f * setting.defaultTimeTextSize); mClockView.setTextColor(setting.notificationExpandedClockTimeTextColor); Typeface timeTypeface = FontListPreference.makeTypeface( setting.moduleResources.getAssets(), setting.notificationExpandedClockTimeTypefaceKbn, setting.notificationExpandedClockTimeTypefaceName, setting.notificationExpandedClockTimeTypefaceStyle); mClockView.setTypeface(timeTypeface); SimpleDateFormat timeFormat = new SimpleDateFormat( setting.notificationExpandedClockTimeFormat, setting.notificationExpandedClockTimeForceEnglish ? Locale.ENGLISH : Locale.getDefault()); XposedHelpers.setAdditionalInstanceField(mClockView, Const.ADDITIONAL_DATE_FORMAT, timeFormat); // 日付の文字サイズ、色、フォント、フォーマットをセット mDateView.setTextSize(TypedValue.COMPLEX_UNIT_PX, setting.notificationExpandedClockDateTextSize / 100f * setting.defaultDateTextSize); mDateView.setTextColor(setting.notificationExpandedClockDateTextColor); Typeface dateTypeface = FontListPreference.makeTypeface( setting.moduleResources.getAssets(), setting.notificationExpandedClockDateTypefaceKbn, setting.notificationExpandedClockDateTypefaceName, setting.notificationExpandedClockDateTypefaceStyle); mDateView.setTypeface(dateTypeface); SimpleDateFormat dateFormat = new SimpleDateFormat( setting.notificationExpandedClockDateFormat, setting.notificationExpandedClockDateForceEnglish ? 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 ModNotificationExpandedClockSettingChangedReceiver extends SettingChangedReceiver { private WeakReference<TextView> mClockView; private WeakReference<TextView> mDateView; protected ModNotificationExpandedClockSettingChangedReceiver(TextView mDateView, TextView mClockView, ModNotificationExpandedClockSettingsGen dataObject, String action) { super(dataObject, action); this.mDateView = new WeakReference<TextView>(mDateView); this.mClockView = new WeakReference<TextView>(mClockView); } @Override protected void onDataChanged() { TextView mDateView = this.mDateView.get(); TextView mClockView = this.mClockView.get(); Object setting = this.dataObject.get(); if (isNotNull(mClockView, mDateView, setting)) { // 設定を反映し、表示を更新 updateSettings(mDateView, mClockView, (ModNotificationExpandedClockSettingsGen) setting); update(mDateView, mClockView); } } } }