BrowserBookmarksPage.java revision b7775e1e46a6c8ce0f07747d44c1b1792bb0ce1c
1/*
2 * Copyright (C) 2006 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
17package com.android.browser;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.DialogInterface;
22import android.content.Intent;
23import android.graphics.Bitmap;
24import android.net.Uri;
25import android.os.Bundle;
26import android.os.Handler;
27import android.os.Message;
28import android.os.ServiceManager;
29import android.provider.Browser;
30import android.text.IClipboard;
31import android.util.Log;
32import android.view.ContextMenu;
33import android.view.KeyEvent;
34import android.view.Menu;
35import android.view.MenuInflater;
36import android.view.MenuItem;
37import android.view.View;
38import android.view.ViewGroup;
39import android.view.ContextMenu.ContextMenuInfo;
40import android.widget.AdapterView;
41import android.widget.ListView;
42
43/**
44 *  View showing the user's bookmarks in the browser.
45 */
46public class BrowserBookmarksPage extends Activity implements
47        View.OnCreateContextMenuListener {
48
49    private BrowserBookmarksAdapter mBookmarksAdapter;
50    private static final int        BOOKMARKS_SAVE = 1;
51    private boolean                 mMaxTabsOpen;
52    private BookmarkItem            mContextHeader;
53    private AddNewBookmark          mAddHeader;
54    private boolean                 mCanceled = false;
55    private boolean                 mCreateShortcut;
56    // XXX: There is no public string defining this intent so if Home changes
57    // the value, we have to update this string.
58    private static final String     INSTALL_SHORTCUT =
59            "com.android.launcher.action.INSTALL_SHORTCUT";
60
61    private final static String LOGTAG = "browser";
62
63
64    @Override
65    public boolean onContextItemSelected(MenuItem item) {
66        // It is possible that the view has been canceled when we get to
67        // this point as back has a higher priority
68        if (mCanceled) {
69            return true;
70        }
71        AdapterView.AdapterContextMenuInfo i =
72            (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
73        // If we have no menu info, we can't tell which item was selected.
74        if (i == null) {
75            return true;
76        }
77
78        switch (item.getItemId()) {
79        case R.id.new_context_menu_id:
80            saveCurrentPage();
81            break;
82        case R.id.open_context_menu_id:
83            loadUrl(i.position);
84            break;
85        case R.id.edit_context_menu_id:
86            editBookmark(i.position);
87            break;
88        case R.id.shortcut_context_menu_id:
89            final Intent send = createShortcutIntent(getUrl(i.position),
90                    getBookmarkTitle(i.position));
91            send.setAction(INSTALL_SHORTCUT);
92            sendBroadcast(send);
93            break;
94        case R.id.delete_context_menu_id:
95            displayRemoveBookmarkDialog(i.position);
96            break;
97        case R.id.new_window_context_menu_id:
98            openInNewWindow(i.position);
99            break;
100        case R.id.send_context_menu_id:
101            Browser.sendString(BrowserBookmarksPage.this, getUrl(i.position));
102            break;
103        case R.id.copy_url_context_menu_id:
104            copy(getUrl(i.position));
105
106        default:
107            return super.onContextItemSelected(item);
108        }
109        return true;
110    }
111
112    @Override
113    public void onCreateContextMenu(ContextMenu menu, View v,
114                ContextMenuInfo menuInfo) {
115            AdapterView.AdapterContextMenuInfo i =
116                    (AdapterView.AdapterContextMenuInfo) menuInfo;
117
118            MenuInflater inflater = getMenuInflater();
119            inflater.inflate(R.menu.bookmarkscontext, menu);
120
121            if (0 == i.position) {
122                menu.setGroupVisible(R.id.CONTEXT_MENU, false);
123                if (mAddHeader == null) {
124                    mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this);
125                } else if (mAddHeader.getParent() != null) {
126                    ((ViewGroup) mAddHeader.getParent()).
127                            removeView(mAddHeader);
128                }
129                ((AddNewBookmark) i.targetView).copyTo(mAddHeader);
130                menu.setHeaderView(mAddHeader);
131                return;
132            }
133            menu.setGroupVisible(R.id.ADD_MENU, false);
134            BookmarkItem b = (BookmarkItem) i.targetView;
135            if (mContextHeader == null) {
136                mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
137            } else if (mContextHeader.getParent() != null) {
138                ((ViewGroup) mContextHeader.getParent()).
139                        removeView(mContextHeader);
140            }
141            b.copyTo(mContextHeader);
142            menu.setHeaderView(mContextHeader);
143
144            if (mMaxTabsOpen) {
145                menu.findItem(R.id.new_window_context_menu_id).setVisible(
146                        false);
147            }
148        }
149
150    /**
151     *  Create a new BrowserBookmarksPage.
152     */
153    @Override
154    protected void onCreate(Bundle icicle) {
155        super.onCreate(icicle);
156
157        setContentView(R.layout.browser_bookmarks_page);
158        setTitle(R.string.browser_bookmarks_page_bookmarks_text);
159
160        if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
161            mCreateShortcut = true;
162        }
163
164        mBookmarksAdapter = new BrowserBookmarksAdapter(this,
165                getIntent().getStringExtra("url"), mCreateShortcut);
166        mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
167
168        ListView listView = (ListView) findViewById(R.id.list);
169        listView.setAdapter(mBookmarksAdapter);
170        listView.setDrawSelectorOnTop(false);
171        listView.setVerticalScrollBarEnabled(true);
172        listView.setOnItemClickListener(mListener);
173
174        if (!mCreateShortcut) {
175            listView.setOnCreateContextMenuListener(this);
176        }
177    }
178
179    private static final int SAVE_CURRENT_PAGE = 1000;
180    private final Handler mHandler = new Handler() {
181        @Override
182        public void handleMessage(Message msg) {
183            if (msg.what == SAVE_CURRENT_PAGE) {
184                saveCurrentPage();
185            }
186        }
187    };
188
189    private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
190        public void onItemClick(AdapterView parent, View v, int position, long id) {
191            // It is possible that the view has been canceled when we get to
192            // this point as back has a higher priority
193            if (mCanceled) {
194                android.util.Log.e("browser", "item clicked when dismising");
195                return;
196            }
197            if (!mCreateShortcut) {
198                if (0 == position) {
199                    // XXX: Work-around for a framework issue.
200                    mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
201                } else {
202                    loadUrl(position);
203                }
204            } else {
205                final Intent intent = createShortcutIntent(getUrl(position),
206                        getBookmarkTitle(position));
207                setResultToParent(RESULT_OK, intent);
208                finish();
209            }
210        }
211    };
212
213    private Intent createShortcutIntent(String url, String title) {
214        final Intent i = new Intent();
215        i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_VIEW,
216                    Uri.parse(url)));
217        i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
218        i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
219                Intent.ShortcutIconResource.fromContext(BrowserBookmarksPage.this,
220                        R.drawable.ic_launcher_browser));
221        // Do not allow duplicate items
222        i.putExtra("duplicate", false);
223        return i;
224    }
225
226    private void saveCurrentPage() {
227        Intent i = new Intent(BrowserBookmarksPage.this,
228                AddBookmarkPage.class);
229        i.putExtras(getIntent());
230        startActivityForResult(i, BOOKMARKS_SAVE);
231    }
232
233    private void loadUrl(int position) {
234        Intent intent = (new Intent()).setAction(getUrl(position));
235        setResultToParent(RESULT_OK, intent);
236        finish();
237    }
238
239    @Override
240    public boolean onCreateOptionsMenu(Menu menu) {
241        boolean result = super.onCreateOptionsMenu(menu);
242        if (!mCreateShortcut) {
243            MenuInflater inflater = getMenuInflater();
244            inflater.inflate(R.menu.bookmarks, menu);
245            return true;
246        }
247        return result;
248    }
249
250    @Override
251    public boolean onOptionsItemSelected(MenuItem item) {
252        switch (item.getItemId()) {
253            case R.id.new_context_menu_id:
254                saveCurrentPage();
255                break;
256
257            default:
258                return super.onOptionsItemSelected(item);
259        }
260        return true;
261    }
262
263    private void openInNewWindow(int position) {
264        Bundle b = new Bundle();
265        b.putBoolean("new_window", true);
266        setResultToParent(RESULT_OK,
267                (new Intent()).setAction(getUrl(position)).putExtras(b));
268
269        finish();
270    }
271
272
273    private void editBookmark(int position) {
274        Intent intent = new Intent(BrowserBookmarksPage.this,
275            AddBookmarkPage.class);
276        intent.putExtra("bookmark", getRow(position));
277        startActivityForResult(intent, BOOKMARKS_SAVE);
278    }
279
280    @Override
281    protected void onActivityResult(int requestCode, int resultCode,
282                                    Intent data) {
283        switch(requestCode) {
284            case BOOKMARKS_SAVE:
285                if (resultCode == RESULT_OK) {
286                    Bundle extras;
287                    if (data != null && (extras = data.getExtras()) != null) {
288                        // If there are extras, then we need to save
289                        // the edited bookmark. This is done in updateRow()
290                        String title = extras.getString("title");
291                        String url = extras.getString("url");
292                        if (title != null && url != null) {
293                            mBookmarksAdapter.updateRow(extras);
294                        }
295                    } else {
296                        // extras == null then a new bookmark was added to
297                        // the database.
298                        refreshList();
299                    }
300                }
301                break;
302            default:
303                break;
304        }
305    }
306
307    private void displayRemoveBookmarkDialog(int position) {
308        // Put up a dialog asking if the user really wants to
309        // delete the bookmark
310        final int deletePos = position;
311        new AlertDialog.Builder(this)
312                .setTitle(R.string.delete_bookmark)
313                .setIcon(android.R.drawable.ic_dialog_alert)
314                .setMessage(getText(R.string.delete_bookmark_warning).toString().replace(
315                        "%s", getBookmarkTitle(deletePos)))
316                .setPositiveButton(R.string.ok,
317                        new DialogInterface.OnClickListener() {
318                            public void onClick(DialogInterface dialog, int whichButton) {
319                                deleteBookmark(deletePos);
320                            }
321                        })
322                .setNegativeButton(R.string.cancel, null)
323                .show();
324    }
325
326    /**
327     *  Refresh the shown list after the database has changed.
328     */
329    public void refreshList() {
330        mBookmarksAdapter.refreshList();
331    }
332
333    /**
334     *  Return a hashmap representing the currently highlighted row.
335     */
336    public Bundle getRow(int position) {
337        return mBookmarksAdapter.getRow(position);
338    }
339
340    /**
341     *  Return the url of the currently highlighted row.
342     */
343    public String getUrl(int position) {
344        return mBookmarksAdapter.getUrl(position);
345    }
346
347    private void copy(CharSequence text) {
348        try {
349            IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
350            if (clip != null) {
351                clip.setClipboardText(text);
352            }
353        } catch (android.os.RemoteException e) {
354            Log.e(LOGTAG, "Copy failed", e);
355        }
356    }
357
358    public String getBookmarkTitle(int position) {
359        return mBookmarksAdapter.getTitle(position);
360    }
361
362    /**
363     *  Delete the currently highlighted row.
364     */
365    public void deleteBookmark(int position) {
366        mBookmarksAdapter.deleteRow(position);
367    }
368
369    public boolean dispatchKeyEvent(KeyEvent event) {
370        if (event.getKeyCode() ==  KeyEvent.KEYCODE_BACK && event.isDown()) {
371            setResultToParent(RESULT_CANCELED, null);
372            mCanceled = true;
373        }
374        return super.dispatchKeyEvent(event);
375    }
376
377    // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
378    // that situation, we need to pass our result code up to our parent.
379    // However, if someone calls this Activity directly, then this has no
380    // parent, and it needs to set it on itself.
381    private void setResultToParent(int resultCode, Intent data) {
382        Activity a = getParent() == null ? this : getParent();
383        a.setResult(resultCode, data);
384    }
385}
386