1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.notification.zen; 18 19 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS; 20 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; 21 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; 22 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE; 23 24 import android.app.ActivityManager; 25 import android.app.AutomaticZenRule; 26 import android.app.NotificationManager; 27 import android.content.Context; 28 import android.database.Cursor; 29 import android.icu.text.MessageFormat; 30 import android.net.Uri; 31 import android.provider.ContactsContract; 32 import android.provider.Settings; 33 import android.service.notification.ZenAdapters; 34 import android.service.notification.ZenModeConfig; 35 import android.service.notification.ZenPolicy; 36 import android.util.Log; 37 38 import androidx.annotation.VisibleForTesting; 39 40 import com.android.settings.R; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.Comparator; 45 import java.util.HashMap; 46 import java.util.List; 47 import java.util.Locale; 48 import java.util.Map; 49 50 public class ZenModeBackend { 51 @VisibleForTesting 52 protected static final String ZEN_MODE_FROM_ANYONE = "zen_mode_from_anyone"; 53 @VisibleForTesting 54 protected static final String ZEN_MODE_FROM_CONTACTS = "zen_mode_from_contacts"; 55 @VisibleForTesting 56 protected static final String ZEN_MODE_FROM_STARRED = "zen_mode_from_starred"; 57 @VisibleForTesting 58 protected static final String ZEN_MODE_FROM_NONE = "zen_mode_from_none"; 59 protected static final int SOURCE_NONE = -1; 60 61 private static ZenModeBackend sInstance; 62 63 protected int mZenMode; 64 /** gets policy last set by updatePolicy **/ 65 protected NotificationManager.Policy mPolicy; 66 private final NotificationManager mNotificationManager; 67 68 private static final String TAG = "ZenModeSettingsBackend"; 69 private final Context mContext; 70 getInstance(Context context)71 public static ZenModeBackend getInstance(Context context) { 72 if (sInstance == null) { 73 sInstance = new ZenModeBackend(context); 74 } 75 return sInstance; 76 } 77 ZenModeBackend(Context context)78 public ZenModeBackend(Context context) { 79 mContext = context; 80 mNotificationManager = (NotificationManager) context.getSystemService( 81 Context.NOTIFICATION_SERVICE); 82 updateZenMode(); 83 updatePolicy(); 84 } 85 updatePolicy()86 protected void updatePolicy() { 87 if (mNotificationManager != null) { 88 mPolicy = mNotificationManager.getNotificationPolicy(); 89 } 90 } 91 updateZenMode()92 protected void updateZenMode() { 93 mZenMode = Settings.Global.getInt(mContext.getContentResolver(), 94 Settings.Global.ZEN_MODE, mZenMode); 95 } 96 updateZenRule(String id, AutomaticZenRule rule)97 protected boolean updateZenRule(String id, AutomaticZenRule rule) { 98 if (android.app.Flags.modesApi()) { 99 return mNotificationManager.updateAutomaticZenRule(id, rule, /* fromUser= */ true); 100 } else { 101 return NotificationManager.from(mContext).updateAutomaticZenRule(id, rule); 102 } 103 } 104 setZenMode(int zenMode)105 protected void setZenMode(int zenMode) { 106 if (android.app.Flags.modesApi()) { 107 mNotificationManager.setZenMode(zenMode, null, TAG, /* fromUser= */ true); 108 } else { 109 NotificationManager.from(mContext).setZenMode(zenMode, null, TAG); 110 } 111 mZenMode = getZenMode(); 112 } 113 setZenModeForDuration(int minutes)114 protected void setZenModeForDuration(int minutes) { 115 Uri conditionId = ZenModeConfig.toTimeCondition(mContext, minutes, 116 ActivityManager.getCurrentUser(), true).id; 117 if (android.app.Flags.modesApi()) { 118 mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, 119 conditionId, TAG, /* fromUser= */ true); 120 } else { 121 mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, 122 conditionId, TAG); 123 } 124 mZenMode = getZenMode(); 125 } 126 getZenMode()127 protected int getZenMode() { 128 mZenMode = Settings.Global.getInt(mContext.getContentResolver(), 129 Settings.Global.ZEN_MODE, mZenMode); 130 return mZenMode; 131 } 132 isVisualEffectSuppressed(int visualEffect)133 protected boolean isVisualEffectSuppressed(int visualEffect) { 134 return (mPolicy.suppressedVisualEffects & visualEffect) != 0; 135 } 136 isPriorityCategoryEnabled(int categoryType)137 protected boolean isPriorityCategoryEnabled(int categoryType) { 138 return (mPolicy.priorityCategories & categoryType) != 0; 139 } 140 getNewDefaultPriorityCategories(boolean allow, int categoryType)141 protected int getNewDefaultPriorityCategories(boolean allow, int categoryType) { 142 int priorityCategories = mPolicy.priorityCategories; 143 if (allow) { 144 priorityCategories |= categoryType; 145 } else { 146 priorityCategories &= ~categoryType; 147 } 148 return priorityCategories; 149 } 150 getPriorityCallSenders()151 protected int getPriorityCallSenders() { 152 if (isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS)) { 153 return mPolicy.priorityCallSenders; 154 } 155 156 return SOURCE_NONE; 157 } 158 getPriorityMessageSenders()159 protected int getPriorityMessageSenders() { 160 if (isPriorityCategoryEnabled( 161 NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)) { 162 return mPolicy.priorityMessageSenders; 163 } 164 return SOURCE_NONE; 165 } 166 getPriorityConversationSenders()167 protected int getPriorityConversationSenders() { 168 if (isPriorityCategoryEnabled(PRIORITY_CATEGORY_CONVERSATIONS)) { 169 return mPolicy.priorityConversationSenders; 170 } 171 return CONVERSATION_SENDERS_NONE; 172 } 173 saveVisualEffectsPolicy(int category, boolean suppress)174 protected void saveVisualEffectsPolicy(int category, boolean suppress) { 175 Settings.Secure.putInt(mContext.getContentResolver(), 176 Settings.Secure.ZEN_SETTINGS_UPDATED, 1); 177 178 int suppressedEffects = getNewSuppressedEffects(suppress, category); 179 savePolicy(mPolicy.priorityCategories, mPolicy.priorityCallSenders, 180 mPolicy.priorityMessageSenders, suppressedEffects, 181 mPolicy.priorityConversationSenders); 182 } 183 saveSoundPolicy(int category, boolean allow)184 protected void saveSoundPolicy(int category, boolean allow) { 185 int priorityCategories = getNewDefaultPriorityCategories(allow, category); 186 savePolicy(priorityCategories, mPolicy.priorityCallSenders, 187 mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects, 188 mPolicy.priorityConversationSenders); 189 } 190 savePolicy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders, int suppressedVisualEffects, int priorityConversationSenders)191 protected void savePolicy(int priorityCategories, int priorityCallSenders, 192 int priorityMessageSenders, int suppressedVisualEffects, 193 int priorityConversationSenders) { 194 mPolicy = new NotificationManager.Policy(priorityCategories, priorityCallSenders, 195 priorityMessageSenders, suppressedVisualEffects, priorityConversationSenders); 196 if (android.app.Flags.modesApi()) { 197 mNotificationManager.setNotificationPolicy(mPolicy, /* fromUser= */ true); 198 } else { 199 mNotificationManager.setNotificationPolicy(mPolicy); 200 } 201 } 202 203 getNewSuppressedEffects(boolean suppress, int effectType)204 private int getNewSuppressedEffects(boolean suppress, int effectType) { 205 int effects = mPolicy.suppressedVisualEffects; 206 207 if (suppress) { 208 effects |= effectType; 209 } else { 210 effects &= ~effectType; 211 } 212 213 return clearDeprecatedEffects(effects); 214 } 215 clearDeprecatedEffects(int effects)216 private int clearDeprecatedEffects(int effects) { 217 return effects & ~(SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF); 218 } 219 isEffectAllowed(int effect)220 protected boolean isEffectAllowed(int effect) { 221 return (mPolicy.suppressedVisualEffects & effect) == 0; 222 } 223 saveSenders(int category, int val)224 protected void saveSenders(int category, int val) { 225 int priorityCallSenders = getPriorityCallSenders(); 226 int priorityMessagesSenders = getPriorityMessageSenders(); 227 int categorySenders = getPrioritySenders(category); 228 229 final boolean allowSenders = val != SOURCE_NONE; 230 final int allowSendersFrom = val == SOURCE_NONE ? categorySenders : val; 231 232 String stringCategory = ""; 233 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) { 234 stringCategory = "Calls"; 235 priorityCallSenders = allowSendersFrom; 236 } 237 238 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) { 239 stringCategory = "Messages"; 240 priorityMessagesSenders = allowSendersFrom; 241 } 242 243 savePolicy(getNewDefaultPriorityCategories(allowSenders, category), 244 priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects, 245 mPolicy.priorityConversationSenders); 246 247 if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allow" + 248 stringCategory + "=" + allowSenders + " allow" + stringCategory + "From=" 249 + ZenModeConfig.sourceToString(allowSendersFrom)); 250 } 251 saveConversationSenders(int val)252 protected void saveConversationSenders(int val) { 253 final boolean allowSenders = val != CONVERSATION_SENDERS_NONE; 254 255 savePolicy(getNewDefaultPriorityCategories(allowSenders, PRIORITY_CATEGORY_CONVERSATIONS), 256 mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders, 257 mPolicy.suppressedVisualEffects, val); 258 259 } 260 getPrioritySenders(int category)261 private int getPrioritySenders(int category) { 262 int categorySenders = -1; 263 264 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) { 265 return getPriorityCallSenders(); 266 } 267 268 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) { 269 return getPriorityMessageSenders(); 270 } 271 272 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) { 273 return getPriorityConversationSenders(); 274 } 275 276 return categorySenders; 277 } 278 getKeyFromZenPolicySetting(int contactType)279 protected static String getKeyFromZenPolicySetting(int contactType) { 280 switch (contactType) { 281 case ZenPolicy.PEOPLE_TYPE_ANYONE: 282 return ZEN_MODE_FROM_ANYONE; 283 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 284 return ZEN_MODE_FROM_CONTACTS; 285 case ZenPolicy.PEOPLE_TYPE_STARRED: 286 return ZEN_MODE_FROM_STARRED; 287 case ZenPolicy.PEOPLE_TYPE_NONE: 288 default: 289 return ZEN_MODE_FROM_NONE; 290 } 291 } 292 getKeyFromSetting(int contactType)293 protected static String getKeyFromSetting(int contactType) { 294 switch (contactType) { 295 case NotificationManager.Policy.PRIORITY_SENDERS_ANY: 296 return ZEN_MODE_FROM_ANYONE; 297 case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS: 298 return ZEN_MODE_FROM_CONTACTS; 299 case NotificationManager.Policy.PRIORITY_SENDERS_STARRED: 300 return ZEN_MODE_FROM_STARRED; 301 case SOURCE_NONE: 302 default: 303 return ZEN_MODE_FROM_NONE; 304 } 305 } 306 getContactSettingFromZenPolicySetting(int setting)307 protected static int getContactSettingFromZenPolicySetting(int setting) { 308 switch (setting) { 309 case ZenPolicy.PEOPLE_TYPE_ANYONE: 310 return NotificationManager.Policy.PRIORITY_SENDERS_ANY; 311 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 312 return NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS; 313 case ZenPolicy.PEOPLE_TYPE_STARRED: 314 return NotificationManager.Policy.PRIORITY_SENDERS_STARRED; 315 case ZenPolicy.PEOPLE_TYPE_NONE: 316 default: 317 return SOURCE_NONE; 318 } 319 } 320 getAlarmsTotalSilencePeopleSummary(int category)321 protected int getAlarmsTotalSilencePeopleSummary(int category) { 322 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) { 323 return R.string.zen_mode_none_messages; 324 } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS){ 325 return R.string.zen_mode_none_calls; 326 } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) { 327 return R.string.zen_mode_from_no_conversations; 328 } 329 return R.string.zen_mode_from_no_conversations; 330 } 331 getContactsCallsSummary(ZenPolicy policy)332 protected int getContactsCallsSummary(ZenPolicy policy) { 333 int peopleType = policy.getPriorityCallSenders(); 334 switch (peopleType) { 335 case ZenPolicy.PEOPLE_TYPE_ANYONE: 336 return R.string.zen_mode_from_anyone; 337 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 338 return R.string.zen_mode_from_contacts; 339 case ZenPolicy.PEOPLE_TYPE_STARRED: 340 return R.string.zen_mode_from_starred; 341 case ZenPolicy.PEOPLE_TYPE_NONE: 342 default: 343 return R.string.zen_mode_none_calls; 344 } 345 } 346 getContactsMessagesSummary(ZenPolicy policy)347 protected int getContactsMessagesSummary(ZenPolicy policy) { 348 int peopleType = policy.getPriorityMessageSenders(); 349 switch (peopleType) { 350 case ZenPolicy.PEOPLE_TYPE_ANYONE: 351 return R.string.zen_mode_from_anyone; 352 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 353 return R.string.zen_mode_from_contacts; 354 case ZenPolicy.PEOPLE_TYPE_STARRED: 355 return R.string.zen_mode_from_starred; 356 case ZenPolicy.PEOPLE_TYPE_NONE: 357 default: 358 return R.string.zen_mode_none_messages; 359 } 360 } 361 getZenPolicySettingFromPrefKey(String key)362 protected static int getZenPolicySettingFromPrefKey(String key) { 363 switch (key) { 364 case ZEN_MODE_FROM_ANYONE: 365 return ZenPolicy.PEOPLE_TYPE_ANYONE; 366 case ZEN_MODE_FROM_CONTACTS: 367 return ZenPolicy.PEOPLE_TYPE_CONTACTS; 368 case ZEN_MODE_FROM_STARRED: 369 return ZenPolicy.PEOPLE_TYPE_STARRED; 370 case ZEN_MODE_FROM_NONE: 371 default: 372 return ZenPolicy.PEOPLE_TYPE_NONE; 373 } 374 } 375 removeZenRule(String ruleId)376 public boolean removeZenRule(String ruleId) { 377 if (android.app.Flags.modesApi()) { 378 return mNotificationManager.removeAutomaticZenRule(ruleId, /* fromUser= */ true); 379 } else { 380 return NotificationManager.from(mContext).removeAutomaticZenRule(ruleId); 381 } 382 } 383 getConsolidatedPolicy()384 public NotificationManager.Policy getConsolidatedPolicy() { 385 return NotificationManager.from(mContext).getConsolidatedNotificationPolicy(); 386 } 387 addZenRule(AutomaticZenRule rule)388 protected String addZenRule(AutomaticZenRule rule) { 389 try { 390 if (android.app.Flags.modesApi()) { 391 return mNotificationManager.addAutomaticZenRule(rule, /* fromUser= */ true); 392 } else { 393 return NotificationManager.from(mContext).addAutomaticZenRule(rule); 394 } 395 } catch (Exception e) { 396 return null; 397 } 398 } 399 setDefaultZenPolicy(ZenPolicy zenPolicy)400 ZenPolicy setDefaultZenPolicy(ZenPolicy zenPolicy) { 401 int calls; 402 if (mPolicy.allowCalls()) { 403 calls = ZenAdapters.prioritySendersToPeopleType( 404 mPolicy.allowCallsFrom()); 405 } else { 406 calls = ZenPolicy.PEOPLE_TYPE_NONE; 407 } 408 409 int messages; 410 if (mPolicy.allowMessages()) { 411 messages = ZenAdapters.prioritySendersToPeopleType( 412 mPolicy.allowMessagesFrom()); 413 } else { 414 messages = ZenPolicy.PEOPLE_TYPE_NONE; 415 } 416 417 int conversations; 418 if (mPolicy.allowConversations()) { 419 // unlike the above, no mapping is needed because the values are the same 420 conversations = mPolicy.allowConversationsFrom(); 421 } else { 422 conversations = CONVERSATION_SENDERS_NONE; 423 } 424 425 return new ZenPolicy.Builder(zenPolicy) 426 .allowAlarms(mPolicy.allowAlarms()) 427 .allowCalls(calls) 428 .allowEvents(mPolicy.allowEvents()) 429 .allowMedia(mPolicy.allowMedia()) 430 .allowMessages(messages) 431 .allowConversations(conversations) 432 .allowReminders(mPolicy.allowReminders()) 433 .allowRepeatCallers(mPolicy.allowRepeatCallers()) 434 .allowSystem(mPolicy.allowSystem()) 435 .showFullScreenIntent(mPolicy.showFullScreenIntents()) 436 .showLights(mPolicy.showLights()) 437 .showInAmbientDisplay(mPolicy.showAmbient()) 438 .showInNotificationList(mPolicy.showInNotificationList()) 439 .showBadges(mPolicy.showBadges()) 440 .showPeeking(mPolicy.showPeeking()) 441 .showStatusBarIcons(mPolicy.showStatusBarIcons()) 442 .build(); 443 } 444 getAutomaticZenRules()445 protected Map.Entry<String, AutomaticZenRule>[] getAutomaticZenRules() { 446 Map<String, AutomaticZenRule> ruleMap = 447 NotificationManager.from(mContext).getAutomaticZenRules(); 448 final Map.Entry<String, AutomaticZenRule>[] rt = ruleMap.entrySet().toArray( 449 new Map.Entry[ruleMap.size()]); 450 Arrays.sort(rt, RULE_COMPARATOR); 451 return rt; 452 } 453 getAutomaticZenRule(String id)454 protected AutomaticZenRule getAutomaticZenRule(String id) { 455 return NotificationManager.from(mContext).getAutomaticZenRule(id); 456 } 457 getDefaultRuleIds()458 private static List<String> getDefaultRuleIds() { 459 return ZenModeConfig.DEFAULT_RULE_IDS; 460 } 461 toNotificationPolicy(ZenPolicy policy)462 NotificationManager.Policy toNotificationPolicy(ZenPolicy policy) { 463 ZenModeConfig config = new ZenModeConfig(); 464 return config.toNotificationPolicy(policy); 465 } 466 467 @VisibleForTesting getStarredContacts(Cursor cursor)468 List<String> getStarredContacts(Cursor cursor) { 469 List<String> starredContacts = new ArrayList<>(); 470 if (cursor != null && cursor.moveToFirst()) { 471 do { 472 String contact = cursor.getString(0); 473 starredContacts.add(contact != null ? contact : 474 mContext.getString(R.string.zen_mode_starred_contacts_empty_name)); 475 476 } while (cursor.moveToNext()); 477 } 478 return starredContacts; 479 } 480 getStarredContacts()481 private List<String> getStarredContacts() { 482 Cursor cursor = null; 483 try { 484 cursor = queryStarredContactsData(); 485 return getStarredContacts(cursor); 486 } finally { 487 if (cursor != null) { 488 cursor.close(); 489 } 490 } 491 } 492 getStarredContactsSummary(Context context)493 String getStarredContactsSummary(Context context) { 494 List<String> starredContacts = getStarredContacts(); 495 int numStarredContacts = starredContacts.size(); 496 MessageFormat msgFormat = new MessageFormat( 497 mContext.getString(R.string.zen_mode_starred_contacts_summary_contacts), 498 Locale.getDefault()); 499 Map<String, Object> args = new HashMap<>(); 500 args.put("count", numStarredContacts); 501 if (numStarredContacts >= 1) { 502 args.put("contact_1", starredContacts.get(0)); 503 if (numStarredContacts >= 2) { 504 args.put("contact_2", starredContacts.get(1)); 505 if (numStarredContacts == 3) { 506 args.put("contact_3", starredContacts.get(2)); 507 } 508 } 509 } 510 return msgFormat.format(args); 511 } 512 getContactsNumberSummary(Context context)513 String getContactsNumberSummary(Context context) { 514 MessageFormat msgFormat = new MessageFormat( 515 mContext.getString(R.string.zen_mode_contacts_count), 516 Locale.getDefault()); 517 Map<String, Object> args = new HashMap<>(); 518 args.put("count", queryAllContactsData().getCount()); 519 return msgFormat.format(args); 520 } 521 queryStarredContactsData()522 private Cursor queryStarredContactsData() { 523 return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 524 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY}, 525 ContactsContract.Data.STARRED + "=1", null, 526 ContactsContract.Data.TIMES_CONTACTED); 527 } 528 queryAllContactsData()529 private Cursor queryAllContactsData() { 530 return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 531 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY}, 532 null, null, null); 533 } 534 535 @VisibleForTesting 536 public static final Comparator<Map.Entry<String, AutomaticZenRule>> RULE_COMPARATOR = 537 new Comparator<Map.Entry<String, AutomaticZenRule>>() { 538 @Override 539 public int compare(Map.Entry<String, AutomaticZenRule> lhs, 540 Map.Entry<String, AutomaticZenRule> rhs) { 541 // if it's a default rule, should be at the top of automatic rules 542 boolean lhsIsDefaultRule = getDefaultRuleIds().contains(lhs.getKey()); 543 boolean rhsIsDefaultRule = getDefaultRuleIds().contains(rhs.getKey()); 544 if (lhsIsDefaultRule != rhsIsDefaultRule) { 545 return lhsIsDefaultRule ? -1 : 1; 546 } 547 548 int byDate = Long.compare(lhs.getValue().getCreationTime(), 549 rhs.getValue().getCreationTime()); 550 if (byDate != 0) { 551 return byDate; 552 } else { 553 return key(lhs.getValue()).compareTo(key(rhs.getValue())); 554 } 555 } 556 557 private String key(AutomaticZenRule rule) { 558 final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId()) 559 ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId()) 560 ? 2 : 3; 561 return type + rule.getName().toString(); 562 } 563 }; 564 } 565