1a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal/**
2a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * Copyright (c) 2011, Google Inc.
3a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal *
4a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * Licensed under the Apache License, Version 2.0 (the "License");
5a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * you may not use this file except in compliance with the License.
6a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * You may obtain a copy of the License at
7a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal *
8a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal *     http://www.apache.org/licenses/LICENSE-2.0
9a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal *
10a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * Unless required by applicable law or agreed to in writing, software
11a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * distributed under the License is distributed on an "AS IS" BASIS,
12a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * See the License for the specific language governing permissions and
14a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * limitations under the License.
15a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal */
16a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal
17a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwalpackage com.android.mail.utils;
18a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal
19a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwalimport android.content.res.Resources;
2069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwalimport android.text.TextUtils;
21a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal
22a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwalimport com.android.mail.R;
2369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwalimport com.android.mail.providers.Account;
2469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwalimport com.android.mail.providers.AccountObserver;
2569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwalimport com.android.mail.ui.AccountController;
2669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
27a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwalimport java.util.regex.Pattern;
28a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal
29a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal/**
30a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * A veiled email address is where we don't want to display the email address, because the address
31a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * might be throw-away, or temporary. For these veiled addresses, we want to display some alternate
32a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal * information. To find if an email address is veiled, call the method
3369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal * {@link #isVeiledAddress(String)}
34a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal */
3569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwalpublic final class VeiledAddressMatcher{
36a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    /**
37a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * Resource for the regex pattern that specifies a veiled addresses.
38a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     */
39a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    private static final int VEILED_RESOURCE = R.string.veiled_address;
40a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal
41a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    /**
42a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * Resource that specifies whether veiled address matching is enabled.
43a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     */
44a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    private static final int VEILED_MATCHING_ENABLED = R.bool.veiled_address_enabled;
45a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal
46a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    /**
47a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * Similar to {@link #VEILED_ALTERNATE_TEXT} except this is for addresses where we don't have
48a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * the name corresponding to the veiled address. Since we don't show the address, we should
49a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * indicate that the recipient is unknown to us.
50a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     */
51a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    public static final int VEILED_ALTERNATE_TEXT_UNKNOWN_PERSON =
52a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal            R.string.veiled_alternate_text_unknown_person;
53a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal
54a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    /**
55a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * When we show a veiled address, we should show an alternate string rather than the email
56a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * address. This is the resource that specifies the alternate string.
57a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     */
58a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    public static final int VEILED_ALTERNATE_TEXT = R.string.veiled_alternate_text;
59a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal
60a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    /**
617faa4d0a0ee25200f4fdc6dee30bb98b8e18b24aVikram Aggarwal     * A summary string (short-string) for an unknown veiled recipient.
627faa4d0a0ee25200f4fdc6dee30bb98b8e18b24aVikram Aggarwal     */
637faa4d0a0ee25200f4fdc6dee30bb98b8e18b24aVikram Aggarwal    public static final int VEILED_SUMMARY_UNKNOWN = R.string.veiled_summary_unknown_person;
647faa4d0a0ee25200f4fdc6dee30bb98b8e18b24aVikram Aggarwal
657faa4d0a0ee25200f4fdc6dee30bb98b8e18b24aVikram Aggarwal    /**
6669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * Private object that does the actual matching.
6769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
6869a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    private Pattern mMatcher = null;
6969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
7069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
7169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * True if veiled address matching is enabled, false otherwise.
7269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
7369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    protected boolean mVeiledMatchingEnabled = false;
7469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
7569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
7669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * The hash code of the last profile pattern retrieved . This allows us to avoid recompiling the
7769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * patterns when nothing has changed.
7869a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
7969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    private int mProfilePatternLastHash = -1;
8069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
8169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    private final AccountObserver mObserver = new AccountObserver() {
8269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        @Override
8369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        public void onChanged(Account newAccount) {
8469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal            loadPattern(newAccount.settings.veiledAddressPattern);
8569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        }
8669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    };
8769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
8869a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
8969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * Make instantiation impossible.
9069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
9169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    private VeiledAddressMatcher() {
9269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        // Do nothing.
9369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    }
9469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
9569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
9669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * Loads the regular expression that corresponds to veiled addresses. It is safe to call this
9769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * method repeatedly with the same pattern. If the pattern has not changed, little extra work
9869a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * is done.
9969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * @param pattern
10069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
10169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    private final void loadPattern(String pattern) {
10269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        if (!TextUtils.isEmpty(pattern)) {
10369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal            final int hashCode = pattern.hashCode();
10469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal            if (hashCode != mProfilePatternLastHash) {
10569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal                mProfilePatternLastHash = hashCode;
10669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal                mMatcher = Pattern.compile(pattern);
10769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal                // Since we have a non-empty pattern now, enable pattern matching.
10869a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal                mVeiledMatchingEnabled = true;
10969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal            }
11069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        }
11169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    }
11269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
11369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
11469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * Default constructor
11569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * @return
11669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
11769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    public static final VeiledAddressMatcher newInstance(Resources resources) {
11869a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        final VeiledAddressMatcher instance = new VeiledAddressMatcher();
11969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        instance.mVeiledMatchingEnabled = resources.getBoolean(VEILED_MATCHING_ENABLED);
12069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        if (instance.mVeiledMatchingEnabled) {
12169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal            instance.loadPattern(resources.getString(VEILED_RESOURCE));
12269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        }
12369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        return instance;
12469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    }
12569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
12669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
12769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * Initialize the object to listen for account changes. Without this, we cannot obtain updated
12869a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * values of the veiled address pattern and the value is read once from resources.
12969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * @param controller
13069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
13169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    public final void initialize(AccountController controller) {
13269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        mObserver.initialize(controller);
13369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    }
13469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
13569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
136a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * Returns true if the given email address is a throw-away (or veiled) address. Such addresses
137a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * are created using special server-side logic for the purpose of keeping the real address of
138a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * the user hidden.
139a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * @param address
140a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     * @return true if the address is veiled, false otherwise.
141a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal     */
14269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    public final boolean isVeiledAddress (String address) {
14369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        if (!mVeiledMatchingEnabled || mMatcher == null) {
14469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal            // Veiled address matching is explicitly disabled: Match nothing.
145a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal            return false;
146a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal        }
147a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal        return mMatcher.matcher(address).matches();
148a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal    }
149a8dc86faac4d1ac07901be75dc92b7a877ca46ffVikram Aggarwal}
150