1/**
2 * Copyright (c) 2011, Google Inc.
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
17package com.android.mail.utils;
18
19import android.content.res.Resources;
20import android.text.TextUtils;
21
22import com.android.mail.R;
23import com.android.mail.providers.Account;
24import com.android.mail.providers.AccountObserver;
25import com.android.mail.ui.AccountController;
26
27import java.util.regex.Pattern;
28
29/**
30 * A veiled email address is where we don't want to display the email address, because the address
31 * might be throw-away, or temporary. For these veiled addresses, we want to display some alternate
32 * information. To find if an email address is veiled, call the method
33 * {@link #isVeiledAddress(String)}
34 */
35public final class VeiledAddressMatcher{
36    /**
37     * Resource for the regex pattern that specifies a veiled addresses.
38     */
39    private static final int VEILED_RESOURCE = R.string.veiled_address;
40
41    /**
42     * Resource that specifies whether veiled address matching is enabled.
43     */
44    private static final int VEILED_MATCHING_ENABLED = R.bool.veiled_address_enabled;
45
46    /**
47     * Similar to {@link #VEILED_ALTERNATE_TEXT} except this is for addresses where we don't have
48     * the name corresponding to the veiled address. Since we don't show the address, we should
49     * indicate that the recipient is unknown to us.
50     */
51    public static final int VEILED_ALTERNATE_TEXT_UNKNOWN_PERSON =
52            R.string.veiled_alternate_text_unknown_person;
53
54    /**
55     * When we show a veiled address, we should show an alternate string rather than the email
56     * address. This is the resource that specifies the alternate string.
57     */
58    public static final int VEILED_ALTERNATE_TEXT = R.string.veiled_alternate_text;
59
60    /**
61     * A summary string (short-string) for an unknown veiled recipient.
62     */
63    public static final int VEILED_SUMMARY_UNKNOWN = R.string.veiled_summary_unknown_person;
64
65    /**
66     * Private object that does the actual matching.
67     */
68    private Pattern mMatcher = null;
69
70    /**
71     * True if veiled address matching is enabled, false otherwise.
72     */
73    protected boolean mVeiledMatchingEnabled = false;
74
75    /**
76     * The hash code of the last profile pattern retrieved . This allows us to avoid recompiling the
77     * patterns when nothing has changed.
78     */
79    private int mProfilePatternLastHash = -1;
80
81    private final AccountObserver mObserver = new AccountObserver() {
82        @Override
83        public void onChanged(Account newAccount) {
84            loadPattern(newAccount.settings.veiledAddressPattern);
85        }
86    };
87
88    /**
89     * Make instantiation impossible.
90     */
91    private VeiledAddressMatcher() {
92        // Do nothing.
93    }
94
95    /**
96     * Loads the regular expression that corresponds to veiled addresses. It is safe to call this
97     * method repeatedly with the same pattern. If the pattern has not changed, little extra work
98     * is done.
99     * @param pattern
100     */
101    private final void loadPattern(String pattern) {
102        if (!TextUtils.isEmpty(pattern)) {
103            final int hashCode = pattern.hashCode();
104            if (hashCode != mProfilePatternLastHash) {
105                mProfilePatternLastHash = hashCode;
106                mMatcher = Pattern.compile(pattern);
107                // Since we have a non-empty pattern now, enable pattern matching.
108                mVeiledMatchingEnabled = true;
109            }
110        }
111    }
112
113    /**
114     * Default constructor
115     * @return
116     */
117    public static final VeiledAddressMatcher newInstance(Resources resources) {
118        final VeiledAddressMatcher instance = new VeiledAddressMatcher();
119        instance.mVeiledMatchingEnabled = resources.getBoolean(VEILED_MATCHING_ENABLED);
120        if (instance.mVeiledMatchingEnabled) {
121            instance.loadPattern(resources.getString(VEILED_RESOURCE));
122        }
123        return instance;
124    }
125
126    /**
127     * Initialize the object to listen for account changes. Without this, we cannot obtain updated
128     * values of the veiled address pattern and the value is read once from resources.
129     * @param controller
130     */
131    public final void initialize(AccountController controller) {
132        mObserver.initialize(controller);
133    }
134
135    /**
136     * Returns true if the given email address is a throw-away (or veiled) address. Such addresses
137     * are created using special server-side logic for the purpose of keeping the real address of
138     * the user hidden.
139     * @param address
140     * @return true if the address is veiled, false otherwise.
141     */
142    public final boolean isVeiledAddress (String address) {
143        if (!mVeiledMatchingEnabled || mMatcher == null) {
144            // Veiled address matching is explicitly disabled: Match nothing.
145            return false;
146        }
147        return mMatcher.matcher(address).matches();
148    }
149}
150