public void init() {
    int iconSize = mContext.getResources().getDimensionPixelSize(R.dimen.abouthome_addon_icon_size);
    sIconBounds = new Rect(0, 0, iconSize, iconSize);
    sSubTitleSpan = new TextAppearanceSpan(mContext, R.style.AboutHome_TextAppearance_SubTitle);

    inflate();

    mAccountManager = AccountManager.get(mContext);

    // The listener will run on the background thread (see 2nd argument)
    mAccountManager.addOnAccountsUpdatedListener(
        mAccountListener =
            new OnAccountsUpdateListener() {
              public void onAccountsUpdated(Account[] accounts) {
                updateLayoutForSync();
              }
            },
        GeckoAppShell.getHandler(),
        false);

    mRemoteTabClickListener =
        new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            int flags = Tabs.LOADURL_NEW_TAB;
            if (Tabs.getInstance().getSelectedTab().isPrivate()) flags |= Tabs.LOADURL_PRIVATE;
            Tabs.getInstance().loadUrl((String) v.getTag(), flags);
          }
        };

    mPrelimPromoBoxType =
        (new Random()).nextFloat() < 0.5
            ? AboutHomePromoBox.Type.SYNC
            : AboutHomePromoBox.Type.APPS;
  }
Example #2
0
  /*
   * If the app has been launched a certain number of times, and we haven't asked for feedback before,
   * open a new tab with about:feedback when launching the app from the icon shortcut.
   */
  @Override
  protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);

    if (!Intent.ACTION_MAIN.equals(intent.getAction()) || !mInitialized) return;

    (new GeckoAsyncTask<Void, Void, Boolean>(mAppContext, GeckoAppShell.getHandler()) {
          @Override
          public synchronized Boolean doInBackground(Void... params) {
            // Check to see how many times the app has been launched.
            SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
            String keyName = getPackageName() + ".feedback_launch_count";
            int launchCount = settings.getInt(keyName, 0);
            if (launchCount >= FEEDBACK_LAUNCH_COUNT) return false;

            // Increment the launch count and store the new value.
            launchCount++;
            settings.edit().putInt(keyName, launchCount).commit();

            // If we've reached our magic number, show the feedback page.
            return launchCount == FEEDBACK_LAUNCH_COUNT;
          }

          @Override
          public void onPostExecute(Boolean shouldShowFeedbackPage) {
            if (shouldShowFeedbackPage) Tabs.getInstance().loadUrlInTab("about:feedback");
          }
        })
        .execute();
  }
Example #3
0
    protected void onPostExecute(Pair<GroupList, List<ChildrenList>> result) {
      mCursorAdapter =
          new HistoryListAdapter(
              mContext,
              result.first,
              R.layout.awesomebar_header_row,
              new String[] {URLColumns.TITLE},
              new int[] {R.id.title},
              result.second);

      if (mContentObserver == null) {
        // Register an observer to update the history tab contents if they change.
        mContentObserver =
            new ContentObserver(GeckoAppShell.getHandler()) {
              public void onChange(boolean selfChange) {
                mQueryTask = new HistoryQueryTask();
                mQueryTask.execute();
              }
            };
        BrowserDB.registerHistoryObserver(getContentResolver(), mContentObserver);
      }

      final ExpandableListView historyList = (ExpandableListView) getView();

      // Hack: force this to the main thread, even though it should already be on it
      GeckoApp.mAppContext.mMainHandler.post(
          new Runnable() {
            public void run() {
              historyList.setAdapter(mCursorAdapter);
              expandAllGroups(historyList);
            }
          });

      mQueryTask = null;
    }
Example #4
0
  public void unpinAllSites() {
    final ContentResolver resolver = mActivity.getContentResolver();

    // Clear the view quickly to make things appear responsive
    for (int i = 0; i < mTopSitesGrid.getChildCount(); i++) {
      View v = mTopSitesGrid.getChildAt(i);
      TopSitesViewHolder holder = (TopSitesViewHolder) v.getTag();
      clearThumbnail(holder);
    }

    (new GeckoAsyncTask<Void, Void, Void>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
          @Override
          public Void doInBackground(Void... params) {
            ContentResolver resolver = mActivity.getContentResolver();
            BrowserDB.unpinAllSites(resolver);
            return null;
          }

          @Override
          public void onPostExecute(Void v) {
            update(EnumSet.of(UpdateFlags.TOP_SITES));
          }
        })
        .execute();
  }
  // Helper method to check if there are any clients available
  public static void areClientsAvailable(
      final Context context, final OnClientsAvailableListener listener) {
    if (listener == null) return;

    (new GeckoAsyncTask<Void, Void, Boolean>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
          @Override
          protected Boolean doInBackground(Void... unused) {
            Uri uri = BrowserContract.Tabs.CONTENT_URI;
            uri = uri.buildUpon().appendQueryParameter(BrowserContract.PARAM_LIMIT, "1").build();

            Cursor cursor =
                context
                    .getContentResolver()
                    .query(uri, CLIENTS_AVAILABILITY_PROJECTION, CLIENTS_SELECTION, null, null);

            if (cursor == null) return false;

            try {
              return cursor.moveToNext();
            } finally {
              cursor.close();
            }
          }

          @Override
          protected void onPostExecute(Boolean availability) {
            listener.areAvailable(availability);
          }
        })
        .setPriority(GeckoAsyncTask.Priority.HIGH)
        .execute();
  }
  public void init() {
    int iconSize = mContext.getResources().getDimensionPixelSize(R.dimen.abouthome_addon_icon_size);
    sIconBounds = new Rect(0, 0, iconSize, iconSize);
    sSubTitleSpan = new TextAppearanceSpan(mContext, R.style.AboutHome_TextAppearance_SubTitle);

    inflate();

    // Reload the mobile homepage on inbound tab syncs
    // Because the tabs URI is coarse grained, this updates the
    // remote tabs component on *every* tab change
    // The observer will run on the background thread (see constructor argument)
    mTabsContentObserver =
        new ContentObserver(GeckoAppShell.getHandler()) {
          public void onChange(boolean selfChange) {
            update(EnumSet.of(AboutHomeContent.UpdateFlags.REMOTE_TABS));
          }
        };
    mActivity
        .getContentResolver()
        .registerContentObserver(BrowserContract.Tabs.CONTENT_URI, false, mTabsContentObserver);

    mRemoteTabClickListener =
        new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            int flags = Tabs.LOADURL_NEW_TAB;
            if (Tabs.getInstance().getSelectedTab().isPrivate()) flags |= Tabs.LOADURL_PRIVATE;
            Tabs.getInstance().loadUrl((String) v.getTag(), flags);
          }
        };
  }
  public void init() {
    inflate();

    mAccountManager = AccountManager.get(mContext);

    // The listener will run on the background thread (see 2nd argument)
    mAccountManager.addOnAccountsUpdatedListener(
        mAccountListener =
            new OnAccountsUpdateListener() {
              public void onAccountsUpdated(Account[] accounts) {
                updateLayoutForSync();
              }
            },
        GeckoAppShell.getHandler(),
        false);

    mRemoteTabClickListener =
        new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            Tabs.getInstance().loadUrl((String) v.getTag(), Tabs.LOADURL_NEW_TAB);
          }
        };

    mPrelimPromoBoxType =
        (new Random()).nextFloat() < 0.5
            ? AboutHomePromoBox.Type.SYNC
            : AboutHomePromoBox.Type.APPS;
  }
  public void pinSite() {
    final int position = mTopSitesGrid.getSelectedPosition();
    View v = mTopSitesGrid.getChildAt(position);

    TopSitesViewHolder holder = (TopSitesViewHolder) v.getTag();
    final String url = holder.url;
    final String title = holder.titleView.getText().toString();
    holder.setPinned(true);

    // update the database on a background thread
    (new GeckoAsyncTask<Void, Void, Void>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
          @Override
          public Void doInBackground(Void... params) {
            final ContentResolver resolver = mActivity.getContentResolver();
            BrowserDB.pinSite(
                resolver, url, (title == null || TextUtils.isEmpty(title) ? url : title), position);
            return null;
          }

          @Override
          public void onPostExecute(Void v) {
            update(EnumSet.of(UpdateFlags.TOP_SITES));
          }
        })
        .execute();
  }
 // GeckoEventListener implementation
 public void handleMessage(String event, final JSONObject message) {
   GeckoAppShell.getHandler()
       .post(
           new Runnable() {
             public void run() {
               processMessage(message);
             }
           });
 }
Example #10
0
 /** Query for suggestions, but don't show them yet. */
 private void primeSuggestions() {
   GeckoAppShell.getHandler()
       .post(
           new Runnable() {
             @Override
             public void run() {
               mSuggestClient.query(mSearchTerm);
             }
           });
 }
  // This method returns limited number of tabs from all remote clients,
  // ordered by most recent client first, most recent tab first
  public static void getTabs(
      final Context context, final int limit, final OnQueryTabsCompleteListener listener) {
    // If there is no listener, no point in doing work.
    if (listener == null) return;

    (new GeckoAsyncTask<Void, Void, List<RemoteTab>>(
            GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
          @Override
          protected List<RemoteTab> doInBackground(Void... unused) {
            Uri uri = BrowserContract.Tabs.CONTENT_URI;

            if (limit > 0) {
              uri =
                  uri.buildUpon()
                      .appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(limit))
                      .build();
            }

            Cursor cursor =
                context
                    .getContentResolver()
                    .query(uri, TABS_PROJECTION_COLUMNS, TABS_SELECTION, null, null);

            if (cursor == null) return null;

            RemoteTab tab;
            final ArrayList<RemoteTab> tabs = new ArrayList<RemoteTab>();
            try {
              while (cursor.moveToNext()) {
                tab = new RemoteTab();
                tab.title = cursor.getString(TABS_COLUMN.TITLE.ordinal());
                tab.url = cursor.getString(TABS_COLUMN.URL.ordinal());
                tab.guid = cursor.getString(TABS_COLUMN.GUID.ordinal());
                tab.name = cursor.getString(TABS_COLUMN.NAME.ordinal());

                tabs.add(tab);
              }
            } finally {
              cursor.close();
            }

            return Collections.unmodifiableList(tabs);
          }

          @Override
          protected void onPostExecute(List<RemoteTab> tabs) {
            listener.onQueryTabsComplete(tabs);
          }
        })
        .execute();
  }
  void update(final EnumSet<UpdateFlags> flags) {
    GeckoAppShell.getHandler()
        .post(
            new Runnable() {
              public void run() {
                if (flags.contains(UpdateFlags.TOP_SITES)) loadTopSites();

                if (flags.contains(UpdateFlags.PREVIOUS_TABS)) readLastTabs();

                if (flags.contains(UpdateFlags.RECOMMENDED_ADDONS)) readRecommendedAddons();

                if (flags.contains(UpdateFlags.REMOTE_TABS)) loadRemoteTabs();
              }
            });
  }
  private void loadTopSitesThumbnails(final ContentResolver cr) {
    final List<String> urls = getTopSitesUrls();
    if (urls.size() == 0) return;

    (new GeckoAsyncTask<Void, Void, Cursor>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
          @Override
          public Cursor doInBackground(Void... params) {
            return BrowserDB.getThumbnailsForUrls(cr, urls);
          }

          @Override
          public void onPostExecute(Cursor c) {
            updateTopSitesThumbnails(getThumbnailsFromCursor(c));
          }
        })
        .execute();
  }
Example #14
0
  private void loadFaviconsForCurrentResults() {
    final List<String> urls = getUrlsWithoutFavicon();
    if (urls.size() == 0) return;

    (new UiAsyncTask<Void, Void, Cursor>(GeckoAppShell.getHandler()) {
          @Override
          public Cursor doInBackground(Void... params) {
            return BrowserDB.getFaviconsForUrls(getContentResolver(), urls);
          }

          @Override
          public void onPostExecute(Cursor c) {
            storeFaviconsInMemCache(c);
            postUpdateFavicons();
          }
        })
        .execute();
  }
  public void pinSite() {
    final int position = mTopSitesGrid.getSelectedPosition();
    View v = mTopSitesGrid.getChildAt(position);

    final TopSitesViewHolder holder = (TopSitesViewHolder) v.getTag();
    holder.setPinned(true);

    // update the database on a background thread
    (new GeckoAsyncTask<Void, Void, Void>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
          @Override
          public Void doInBackground(Void... params) {
            final ContentResolver resolver = mActivity.getContentResolver();
            BrowserDB.pinSite(resolver, holder.getUrl(), holder.getTitle(), position);
            return null;
          }
        })
        .execute();
  }
 public void unpinSite(final UnpinFlags flags) {
   final int position = mTopSitesGrid.getSelectedPosition();
   final View v = mTopSitesGrid.getChildAt(position);
   final TopSitesViewHolder holder = (TopSitesViewHolder) v.getTag();
   final String url = holder.getUrl();
   // Quickly update the view so that there isn't as much lag between the request and response
   clearThumbnail(holder);
   (new GeckoAsyncTask<Void, Void, Void>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
         @Override
         public Void doInBackground(Void... params) {
           final ContentResolver resolver = mActivity.getContentResolver();
           BrowserDB.unpinSite(resolver, position);
           if (flags == UnpinFlags.REMOVE_HISTORY) {
             BrowserDB.removeHistoryEntry(resolver, url);
           }
           return null;
         }
       })
       .execute();
 }
Example #17
0
  @Override
  protected void onDialogClosed(boolean positiveResult) {
    if (!positiveResult) {
      // user cancelled; reset checkbox values to their previous state
      mValues = mPrevValues.clone();
      return;
    } else {
      mPrevValues = mValues.clone();
    }

    GeckoAppShell.getHandler()
        .post(
            new Runnable() {
              public void run() {
                for (int i = 0; i < mEntryKeys.length; i++) {
                  String key = mEntryKeys[i].toString();
                  persistBoolean(key, mValues[i]);
                }
              }
            });
  }
Example #18
0
  public void unpinSite() {
    final int position = mTopSitesGrid.getSelectedPosition();
    View v = mTopSitesGrid.getChildAt(position);
    TopSitesViewHolder holder = (TopSitesViewHolder) v.getTag();

    // Quickly update the view so that there isn't as much lag between the request and response
    clearThumbnail(holder);
    (new GeckoAsyncTask<Void, Void, Void>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
          @Override
          public Void doInBackground(Void... params) {
            ContentResolver resolver = mActivity.getContentResolver();
            BrowserDB.unpinSite(resolver, position);
            return null;
          }

          @Override
          public void onPostExecute(Void v) {
            update(EnumSet.of(UpdateFlags.TOP_SITES));
          }
        })
        .execute();
  }
Example #19
0
  /**
   * Loads persistent prefs from shared preferences. If the preferences aren't persistent or haven't
   * yet been stored, they will be set to their initial values.
   */
  private void loadPersistedValues() {
    if (mEntryKeys == null || mInitialValues == null) return;

    final int entryCount = mEntryKeys.length;
    if (entryCount != mEntries.length || entryCount != mInitialValues.length) {
      throw new IllegalStateException(
          "MultiChoicePreference entryKeys and initialValues arrays must be the same length");
    }

    mValues = new boolean[entryCount];
    GeckoAppShell.getHandler()
        .post(
            new Runnable() {
              public void run() {
                for (int i = 0; i < entryCount; i++) {
                  String key = mEntryKeys[i].toString();
                  boolean initialValue = mInitialValues[i].equals("true");
                  mValues[i] = getPersistedBoolean(key, initialValue);
                }
                mPrevValues = mValues.clone();
              }
            });
  }
Example #20
0
  private void getLastUrl() {
    (new GeckoAsyncTask<Void, Void, String>(mAppContext, GeckoAppShell.getHandler()) {
          @Override
          public synchronized String doInBackground(Void... params) {
            // Get the most recent URL stored in browser history.
            String url = "";
            Cursor c = BrowserDB.getRecentHistory(getContentResolver(), 1);
            if (c.moveToFirst()) {
              url = c.getString(c.getColumnIndexOrThrow(Combined.URL));
            }
            c.close();
            return url;
          }

          @Override
          public void onPostExecute(String url) {
            // Don't bother sending a message if there is no URL.
            if (url.length() > 0)
              GeckoAppShell.sendEventToGecko(
                  GeckoEvent.createBroadcastEvent("Feedback:LastUrl", url));
          }
        })
        .execute();
  }
Example #21
0
  private void addAddonMenuItem(final MenuItemInfo info) {
    if (mMenu == null) {
      if (mAddonMenuItemsCache == null) mAddonMenuItemsCache = new Vector<MenuItemInfo>();

      mAddonMenuItemsCache.add(info);
      return;
    }

    Menu menu;
    if (info.parent == 0) {
      menu = mMenu;
    } else {
      MenuItem parent = mMenu.findItem(info.parent);
      if (parent == null) return;

      if (!parent.hasSubMenu()) {
        mMenu.removeItem(parent.getItemId());
        menu = mMenu.addSubMenu(Menu.NONE, parent.getItemId(), Menu.NONE, parent.getTitle());
        if (parent.getIcon() != null) ((SubMenu) menu).getItem().setIcon(parent.getIcon());
      } else {
        menu = parent.getSubMenu();
      }
    }

    final MenuItem item = menu.add(Menu.NONE, info.id, Menu.NONE, info.label);
    item.setOnMenuItemClickListener(
        new MenuItem.OnMenuItemClickListener() {
          @Override
          public boolean onMenuItemClick(MenuItem item) {
            Log.i(LOGTAG, "menu item clicked");
            GeckoAppShell.sendEventToGecko(
                GeckoEvent.createBroadcastEvent(
                    "Menu:Clicked", Integer.toString(info.id - ADDON_MENU_OFFSET)));
            return true;
          }
        });

    if (info.icon != null) {
      if (info.icon.startsWith("data")) {
        BitmapDrawable drawable = new BitmapDrawable(BitmapUtils.getBitmapFromDataURI(info.icon));
        item.setIcon(drawable);
      } else if (info.icon.startsWith("jar:") || info.icon.startsWith("file://")) {
        GeckoAppShell.getHandler()
            .post(
                new Runnable() {
                  public void run() {
                    try {
                      URL url = new URL(info.icon);
                      InputStream is = (InputStream) url.getContent();
                      try {
                        Drawable drawable = Drawable.createFromStream(is, "src");
                        item.setIcon(drawable);
                      } finally {
                        is.close();
                      }
                    } catch (Exception e) {
                      Log.w(LOGTAG, "Unable to set icon", e);
                    }
                  }
                });
      }
    }

    item.setCheckable(info.checkable);
    item.setChecked(info.checked);
    item.setEnabled(info.enabled);
    item.setVisible(info.visible);
  }
  @Override
  public boolean onContextItemSelected(MenuItem item) {
    if (mContextMenuSubject == null) return false;

    final int id = mContextMenuSubject.id;
    final String url = mContextMenuSubject.url;
    final byte[] b = mContextMenuSubject.favicon;
    final String title = mContextMenuSubject.title;
    final String keyword = mContextMenuSubject.keyword;

    switch (item.getItemId()) {
      case R.id.open_new_tab:
        {
          if (url == null) {
            Log.e(LOGTAG, "Can't open in new tab because URL is null");
            break;
          }

          GeckoApp.mAppContext.loadUrl(url, AwesomeBar.Target.NEW_TAB);
          Toast.makeText(this, R.string.new_tab_opened, Toast.LENGTH_SHORT).show();
          break;
        }
      case R.id.open_in_reader:
        {
          if (url == null) {
            Log.e(LOGTAG, "Can't open in reader mode because URL is null");
            break;
          }

          openUrlAndFinish(ReaderModeUtils.getAboutReaderForUrl(url, true));
          break;
        }
      case R.id.edit_bookmark:
        {
          AlertDialog.Builder editPrompt = new AlertDialog.Builder(this);
          View editView = getLayoutInflater().inflate(R.layout.bookmark_edit, null);
          editPrompt.setTitle(R.string.bookmark_edit_title);
          editPrompt.setView(editView);

          final EditText nameText = ((EditText) editView.findViewById(R.id.edit_bookmark_name));
          final EditText locationText =
              ((EditText) editView.findViewById(R.id.edit_bookmark_location));
          final EditText keywordText =
              ((EditText) editView.findViewById(R.id.edit_bookmark_keyword));
          nameText.setText(title);
          locationText.setText(url);
          keywordText.setText(keyword);

          editPrompt.setPositiveButton(
              R.string.button_ok,
              new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                  (new GeckoAsyncTask<Void, Void, Void>(
                          GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
                        @Override
                        public Void doInBackground(Void... params) {
                          String newUrl = locationText.getText().toString().trim();
                          BrowserDB.updateBookmark(
                              mResolver,
                              id,
                              newUrl,
                              nameText.getText().toString(),
                              keywordText.getText().toString());
                          return null;
                        }

                        @Override
                        public void onPostExecute(Void result) {
                          Toast.makeText(
                                  AwesomeBar.this, R.string.bookmark_updated, Toast.LENGTH_SHORT)
                              .show();
                        }
                      })
                      .execute();
                }
              });

          editPrompt.setNegativeButton(
              R.string.button_cancel,
              new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                  // do nothing
                }
              });

          final AlertDialog dialog = editPrompt.create();

          // disable OK button if the URL is empty
          locationText.addTextChangedListener(
              new TextWatcher() {
                private boolean mEnabled = true;

                public void afterTextChanged(Editable s) {}

                public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

                public void onTextChanged(CharSequence s, int start, int before, int count) {
                  boolean enabled = (s.toString().trim().length() > 0);
                  if (mEnabled != enabled) {
                    dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);
                    mEnabled = enabled;
                  }
                }
              });

          dialog.show();
          break;
        }
      case R.id.remove_bookmark:
        {
          (new AsyncTask<Void, Void, Void>() {
                private boolean mInReadingList;

                @Override
                public void onPreExecute() {
                  mInReadingList = mAwesomeTabs.isInReadingList();
                }

                @Override
                public Void doInBackground(Void... params) {
                  BrowserDB.removeBookmark(mResolver, id);
                  return null;
                }

                @Override
                public void onPostExecute(Void result) {
                  int messageId = R.string.bookmark_removed;
                  if (mInReadingList) {
                    messageId = R.string.reading_list_removed;

                    GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", url);
                    GeckoAppShell.sendEventToGecko(e);
                  }

                  Toast.makeText(AwesomeBar.this, messageId, Toast.LENGTH_SHORT).show();
                }
              })
              .execute();
          break;
        }
      case R.id.remove_history:
        {
          (new GeckoAsyncTask<Void, Void, Void>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
                @Override
                public Void doInBackground(Void... params) {
                  BrowserDB.removeHistoryEntry(mResolver, id);
                  return null;
                }

                @Override
                public void onPostExecute(Void result) {
                  Toast.makeText(AwesomeBar.this, R.string.history_removed, Toast.LENGTH_SHORT)
                      .show();
                }
              })
              .execute();
          break;
        }
      case R.id.add_to_launcher:
        {
          if (url == null) {
            Log.e(LOGTAG, "Can't add to home screen because URL is null");
            break;
          }

          Bitmap bitmap = null;
          if (b != null) bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);

          String shortcutTitle =
              TextUtils.isEmpty(title) ? url.replaceAll("^([a-z]+://)?(www\\.)?", "") : title;
          GeckoAppShell.createShortcut(shortcutTitle, url, bitmap, "");
          break;
        }
      case R.id.share:
        {
          if (url == null) {
            Log.e(LOGTAG, "Can't share because URL is null");
            break;
          }

          GeckoAppShell.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, title);
          break;
        }
      default:
        {
          return super.onContextItemSelected(item);
        }
    }
    return true;
  }