summaryrefslogtreecommitdiffstats
path: root/mobile/android/base/java/org/mozilla/gecko/preferences/SearchEnginePreference.java
blob: 3ba80b562ddf85a47976eea4cca922ec078777b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.preferences;

import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.R;
import org.mozilla.gecko.SnackbarBuilder;
import org.mozilla.gecko.icons.IconCallback;
import org.mozilla.gecko.icons.IconDescriptor;
import org.mozilla.gecko.icons.IconResponse;
import org.mozilla.gecko.icons.Icons;
import org.mozilla.gecko.widget.FaviconView;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.design.widget.Snackbar;
import android.text.SpannableString;
import android.util.Log;
import android.view.View;

/**
 * Represents an element in the list of search engines on the preferences menu.
 */
public class SearchEnginePreference extends CustomListPreference {
    protected String LOGTAG = "SearchEnginePreference";

    protected static final int INDEX_REMOVE_BUTTON = 1;

    // The icon to display in the prompt when clicked.
    private BitmapDrawable mPromptIcon;

    // The bitmap backing the drawable above - needed separately for the FaviconView.
    private Bitmap mIconBitmap;
    private final Object bitmapLock = new Object();

    private FaviconView mFaviconView;

    // Search engine identifier specified by the gecko search service. This will be "other"
    // for engines that are not shipped with the app.
    private String mIdentifier;

    public SearchEnginePreference(Context context, SearchPreferenceCategory parentCategory) {
        super(context, parentCategory);
    }

    /**
     * Called by Android when we're bound to the custom view. Allows us to set the custom properties
     * of our custom view elements as we desire (We can now use findViewById on them).
     *
     * @param view The view instance for this Preference object.
     */
    @Override
    protected void onBindView(View view) {
        super.onBindView(view);

        // We synchronise to avoid a race condition between this and the favicon loading callback in
        // setSearchEngineFromJSON.
        synchronized (bitmapLock) {
            // Set the icon in the FaviconView.
            mFaviconView = ((FaviconView) view.findViewById(R.id.search_engine_icon));

            if (mIconBitmap != null) {
                mFaviconView.updateAndScaleImage(IconResponse.create(mIconBitmap));
            }
        }
    }

    @Override
    protected int getPreferenceLayoutResource() {
        return R.layout.preference_search_engine;
    }

    /**
     * Returns the strings to be displayed in the dialog.
     */
    @Override
    protected String[] createDialogItems() {
        return new String[] { LABEL_SET_AS_DEFAULT,
                              LABEL_REMOVE };
    }

    @Override
    public void showDialog() {
        // If this is the last engine, then we are the default, and none of the options
        // on this menu can do anything.
        if (mParentCategory.getPreferenceCount() == 1) {
            Activity activity = (Activity) getContext();

            SnackbarBuilder.builder(activity)
                    .message(R.string.pref_search_last_toast)
                    .duration(Snackbar.LENGTH_LONG)
                    .buildAndShow();

            return;
        }

        super.showDialog();
    }

    @Override
    protected void configureDialogBuilder(AlertDialog.Builder builder) {
        // Copy the icon from this object to the prompt we produce. We lazily create the drawable,
        // as the user may not ever actually tap this object.
        if (mPromptIcon == null && mIconBitmap != null) {
            mPromptIcon = new BitmapDrawable(getContext().getResources(), mFaviconView.getBitmap());
        }

        builder.setIcon(mPromptIcon);
    }

    @Override
    protected void onDialogIndexClicked(int index) {
        switch (index) {
            case INDEX_SET_DEFAULT_BUTTON:
                mParentCategory.setDefault(this);
                break;

            case INDEX_REMOVE_BUTTON:
                mParentCategory.uninstall(this);
                break;

            default:
                Log.w(LOGTAG, "Selected index out of range.");
                break;
        }
    }

    /**
     * @return Identifier of built-in search engine, or "other" if engine is not built-in.
     */
    public String getIdentifier() {
        return mIdentifier;
    }

    /**
     * Configure this Preference object from the Gecko search engine JSON object.
     * @param geckoEngineJSON The Gecko-formatted JSON object representing the search engine.
     * @throws JSONException If the JSONObject is invalid.
     */
    public void setSearchEngineFromJSON(JSONObject geckoEngineJSON) throws JSONException {
        mIdentifier = geckoEngineJSON.getString("identifier");

        // A null JS value gets converted into a string.
        if (mIdentifier.equals("null")) {
            mIdentifier = "other";
        }

        final String engineName = geckoEngineJSON.getString("name");
        final SpannableString titleSpannable = new SpannableString(engineName);

        setTitle(titleSpannable);

        final String iconURI = geckoEngineJSON.getString("iconURI");
        // Keep a reference to the bitmap - we'll need it later in onBindView.
        try {
            Icons.with(getContext())
                    .pageUrl(mIdentifier)
                    .icon(IconDescriptor.createGenericIcon(iconURI))
                    .privileged(true)
                    .build()
                    .execute(new IconCallback() {
                        @Override
                        public void onIconResponse(IconResponse response) {
                            mIconBitmap = response.getBitmap();

                            if (mFaviconView != null) {
                                mFaviconView.updateAndScaleImage(response);
                            }
                        }
                    });
        } catch (IllegalArgumentException e) {
            Log.e(LOGTAG, "IllegalArgumentException creating Bitmap. Most likely a zero-length bitmap.", e);
        } catch (NullPointerException e) {
            Log.e(LOGTAG, "NullPointerException creating Bitmap. Most likely a zero-length bitmap.", e);
        }
    }
}