/* * Copyright 2014 A.C.R. Development */ package acr.browser.lightning; import info.guardianproject.onionkit.ui.OrbotHelper; import info.guardianproject.onionkit.web.WebkitProxy; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Locale; import android.app.ActionBar; import android.app.Activity; import android.app.AlertDialog; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.MediaPlayer; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Message; import android.provider.Browser; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.text.TextUtils; import android.util.Log; import android.util.TypedValue; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnKeyListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.ValueCallback; import android.webkit.WebViewDatabase; import android.webkit.WebChromeClient.CustomViewCallback; import android.webkit.WebIconDatabase; import android.webkit.WebView; import android.webkit.WebView.HitTestResult; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.VideoView; public class BrowserActivity extends Activity implements BrowserController { private DrawerLayout mDrawerLayout; private ListView mDrawerList; private RelativeLayout mDrawer; private LinearLayout mDrawerRight; private ListView mDrawerListRight; private TextView mNewTab; private ActionBarDrawerToggle mDrawerToggle; private List mWebViews = new ArrayList(); private List mIdList = new ArrayList(); private LightningView mCurrentView; private int mIdGenerator; private LightningViewAdapter mTitleAdapter; private List mBookmarkList; private BookmarkViewAdapter mBookmarkAdapter; private AutoCompleteTextView mSearch; private ClickHandler mClickHandler; private ProgressBar mProgress; private boolean mSystemBrowser = false; private ValueCallback mUploadMessage; private View mCustomView; private int mOriginalOrientation; private int mActionBarSize; private ActionBar mActionBar; private boolean mFullScreen; private FrameLayout mBrowserFrame; private FullscreenHolder mFullscreenContainer; private CustomViewCallback mCustomViewCallback; private final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); private Bitmap mDefaultVideoPoster; private View mVideoProgressView; private DatabaseHandler mHistoryHandler; private SQLiteDatabase mHistoryDatabase; private SharedPreferences mPreferences; private SharedPreferences.Editor mEditPrefs; private Context mContext; private Bitmap mWebpageBitmap; private String mSearchText; private Activity mActivity; private CookieManager mCookieManager; private final int API = android.os.Build.VERSION.SDK_INT; private Drawable mDeleteIcon; private Drawable mRefreshIcon; private Drawable mCopyIcon; private Drawable mIcon; private int mActionBarSizeDp; private int mNumberIconColor; private String mHomepage; private boolean mIsNewIntent = false; private VideoView mVideoView; private static SearchAdapter mSearchAdapter; private boolean isIncognito = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initialize(); } private synchronized void initialize() { setContentView(R.layout.activity_main); TypedValue typedValue = new TypedValue(); Theme theme = getTheme(); theme.resolveAttribute(R.attr.numberColor, typedValue, true); mNumberIconColor = typedValue.data; mPreferences = getSharedPreferences(PreferenceConstants.PREFERENCES, 0); mEditPrefs = mPreferences.edit(); mContext = this; if (mIdList != null) { mIdList.clear(); } else { mIdList = new ArrayList(); } if (mWebViews != null) { mWebViews.clear(); } else { mWebViews = new ArrayList(); } mActivity = this; mClickHandler = new ClickHandler(this); mBrowserFrame = (FrameLayout) findViewById(R.id.content_frame); mProgress = (ProgressBar) findViewById(R.id.activity_bar); mProgress.setVisibility(View.GONE); mNewTab = (TextView) findViewById(R.id.new_tab_button); mDrawer = (RelativeLayout) findViewById(R.id.drawer); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerList = (ListView) findViewById(R.id.left_drawer); mDrawerList.setDivider(null); mDrawerList.setDividerHeight(0); mDrawerRight = (LinearLayout) findViewById(R.id.right_drawer); mDrawerListRight = (ListView) findViewById(R.id.right_drawer_list); mDrawerListRight.setDivider(null); mDrawerListRight.setDividerHeight(0); setNavigationDrawerWidth(); mWebpageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_webpage); mActionBar = getActionBar(); final TypedArray styledAttributes = mContext.getTheme() .obtainStyledAttributes( new int[] { android.R.attr.actionBarSize }); mActionBarSize = (int) styledAttributes.getDimension(0, 0); if (pixelsToDp(mActionBarSize) < 48) { mActionBarSize = getDp(48); } mActionBarSizeDp = pixelsToDp(mActionBarSize); styledAttributes.recycle(); mHomepage = mPreferences.getString(PreferenceConstants.HOMEPAGE, Constants.HOMEPAGE); mTitleAdapter = new LightningViewAdapter(this, R.layout.tab_list_item, mWebViews); mDrawerList.setAdapter(mTitleAdapter); mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); mDrawerList .setOnItemLongClickListener(new DrawerItemLongClickListener()); mBookmarkList = getBookmarks(); mBookmarkAdapter = new BookmarkViewAdapter(this, R.layout.bookmark_list_item, mBookmarkList); mDrawerListRight.setAdapter(mBookmarkAdapter); mDrawerListRight .setOnItemClickListener(new BookmarkItemClickListener()); mDrawerListRight .setOnItemLongClickListener(new BookmarkItemLongClickListener()); if (mHistoryHandler == null) { mHistoryHandler = new DatabaseHandler(this); } else if (!mHistoryHandler.isOpen()) { mHistoryHandler = new DatabaseHandler(this); } mHistoryDatabase = mHistoryHandler.getReadableDatabase(); // set display options of the ActionBar mActionBar.setDisplayShowTitleEnabled(false); mActionBar.setDisplayShowCustomEnabled(true); mActionBar.setDisplayShowHomeEnabled(true); mActionBar.setDisplayHomeAsUpEnabled(true); mActionBar.setCustomView(R.layout.search); RelativeLayout back = (RelativeLayout) findViewById(R.id.action_back); RelativeLayout forward = (RelativeLayout) findViewById(R.id.action_forward); if (back != null) { back.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mCurrentView != null) { if (mCurrentView.canGoBack()) { mCurrentView.goBack(); } else { deleteTab(mDrawerList.getCheckedItemPosition()); } } } }); } if (forward != null) { forward.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mCurrentView != null) { if (mCurrentView.canGoForward()) { mCurrentView.goForward(); } } } }); } // create the search EditText in the ActionBar mSearch = (AutoCompleteTextView) mActionBar.getCustomView() .findViewById(R.id.search); mDeleteIcon = getResources().getDrawable(R.drawable.ic_action_delete); mDeleteIcon.setBounds(0, 0, Utils.convertToDensityPixels(mContext, 24), Utils.convertToDensityPixels(mContext, 24)); mRefreshIcon = getResources().getDrawable(R.drawable.ic_action_refresh); mRefreshIcon.setBounds(0, 0, Utils.convertToDensityPixels(mContext, 24), Utils.convertToDensityPixels(mContext, 24)); mCopyIcon = getResources().getDrawable(R.drawable.ic_action_copy); mCopyIcon.setBounds(0, 0, Utils.convertToDensityPixels(mContext, 24), Utils.convertToDensityPixels(mContext, 24)); mIcon = mRefreshIcon; mSearch.setCompoundDrawables(null, null, mRefreshIcon, null); mSearch.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View arg0, int arg1, KeyEvent arg2) { switch (arg1) { case KeyEvent.KEYCODE_ENTER: InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); searchTheWeb(mSearch.getText().toString()); if (mCurrentView != null) { mCurrentView.requestFocus(); } return true; default: break; } return false; } }); mSearch.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus && mCurrentView != null) { if (mCurrentView != null) { if (mCurrentView.getProgress() < 100) { setIsLoading(); } else { setIsFinishedLoading(); } } updateUrl(mCurrentView.getUrl()); } else if (hasFocus) { mIcon = mCopyIcon; mSearch.setCompoundDrawables(null, null, mCopyIcon, null); } } }); mSearch.setOnEditorActionListener(new OnEditorActionListener() { @Override public boolean onEditorAction(TextView arg0, int actionId, KeyEvent arg2) { // hide the keyboard and search the web when the enter key // button is pressed if (actionId == EditorInfo.IME_ACTION_GO || actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT || actionId == EditorInfo.IME_ACTION_SEND || actionId == EditorInfo.IME_ACTION_SEARCH || (arg2.getAction() == KeyEvent.KEYCODE_ENTER)) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); searchTheWeb(mSearch.getText().toString()); if (mCurrentView != null) { mCurrentView.requestFocus(); } return true; } return false; } }); mSearch.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (mSearch.getCompoundDrawables()[2] != null) { boolean tappedX = event.getX() > (mSearch.getWidth() - mSearch.getPaddingRight() - mIcon .getIntrinsicWidth()); if (tappedX) { if (event.getAction() == MotionEvent.ACTION_UP) { if (mSearch.hasFocus()) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("label", mSearch.getText().toString()); clipboard.setPrimaryClip(clip); Utils.showToast( mContext, mContext.getResources().getString( R.string.message_text_copied)); } else { refreshOrStop(); } } return true; } } return false; } }); mSystemBrowser = getSystemBrowser(); Thread initialize = new Thread(new Runnable() { @Override public void run() { initializeSearchSuggestions(mSearch); } }); initialize.run(); // ActionBarDrawerToggle ties together the the proper interactions // between the sliding drawer and the action bar app icon mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ mDrawerLayout, /* DrawerLayout object */ R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */ R.string.drawer_open, /* "open drawer" description for accessibility */ R.string.drawer_close /* "close drawer" description for accessibility */ ) { public void onDrawerClosed(View view) { super.onDrawerClosed(view); if (view.equals(mDrawer)) { mDrawerLayout.setDrawerLockMode( DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerRight); } else if (view.equals(mDrawerRight)) { mDrawerLayout.setDrawerLockMode( DrawerLayout.LOCK_MODE_UNLOCKED, mDrawer); } } public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); if (drawerView.equals(mDrawer)) { mDrawerLayout.closeDrawer(mDrawerRight); mDrawerLayout.setDrawerLockMode( DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerRight); } else if (drawerView.equals(mDrawerRight)) { mDrawerLayout.closeDrawer(mDrawer); mDrawerLayout.setDrawerLockMode( DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawer); } } }; mNewTab.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { newTab(null, true); } }); mDrawerLayout.setDrawerListener(mDrawerToggle); mDrawerLayout.setDrawerShadow(R.drawable.drawer_right_shadow, GravityCompat.END); mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); initializePreferences(); initializeTabs(); if (API < 19) { WebIconDatabase.getInstance().open( getDir("icons", MODE_PRIVATE).getPath()); } checkForTor(); } /* * If Orbot/Tor is installed, prompt the user if they want to enable * proxying for this session */ public boolean checkForTor() { boolean useProxy = mPreferences.getBoolean( PreferenceConstants.USE_PROXY, false); OrbotHelper oh = new OrbotHelper(this); if (oh.isOrbotInstalled() && !mPreferences.getBoolean( PreferenceConstants.INITIAL_CHECK_FOR_TOR, false)) { mEditPrefs.putBoolean(PreferenceConstants.INITIAL_CHECK_FOR_TOR, true); mEditPrefs.apply(); DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: mPreferences .edit() .putBoolean(PreferenceConstants.USE_PROXY, true) .apply(); initializeTor(); break; case DialogInterface.BUTTON_NEGATIVE: mPreferences .edit() .putBoolean(PreferenceConstants.USE_PROXY, false).apply(); break; } } }; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(R.string.use_tor_prompt) .setPositiveButton(R.string.yes, dialogClickListener) .setNegativeButton(R.string.no, dialogClickListener).show(); return true; } else if (oh.isOrbotInstalled() & useProxy == true) { initializeTor(); return true; } else { mEditPrefs.putBoolean(PreferenceConstants.USE_PROXY, false); mEditPrefs.apply(); return false; } } /* * Initialize WebKit Proxying for Tor */ public void initializeTor() { OrbotHelper oh = new OrbotHelper(this); if (!oh.isOrbotRunning()) oh.requestOrbotStart(this); WebkitProxy wkp = new WebkitProxy(); try { String host = mPreferences.getString( PreferenceConstants.USE_PROXY_HOST, "localhost"); int port = mPreferences.getInt(PreferenceConstants.USE_PROXY_PORT, 8118); wkp.setProxy("acr.browser.lightning.BrowserApp", getApplicationContext(), host, port); } catch (Exception e) { Log.d(Constants.LOGTAG, "error enabling web proxying", e); } } public void setNavigationDrawerWidth() { int width = getResources().getDisplayMetrics().widthPixels * 3 / 4; int maxWidth = Utils.convertToDensityPixels(mContext, 300); if (width > maxWidth) { DrawerLayout.LayoutParams params = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawer .getLayoutParams(); params.width = maxWidth; mDrawer.setLayoutParams(params); DrawerLayout.LayoutParams paramsRight = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerRight .getLayoutParams(); paramsRight.width = maxWidth; mDrawerRight.setLayoutParams(paramsRight); } else { DrawerLayout.LayoutParams params = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawer .getLayoutParams(); params.width = width; mDrawer.setLayoutParams(params); DrawerLayout.LayoutParams paramsRight = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerRight .getLayoutParams(); paramsRight.width = width; mDrawerRight.setLayoutParams(paramsRight); } } /* * Override this class */ public synchronized void initializeTabs() { } public void restoreOrNewTab() { mIdGenerator = 0; String url = null; if (getIntent() != null) { url = getIntent().getDataString(); if (url != null) { if (url.startsWith(Constants.FILE)) { Utils.showToast( this, getResources().getString( R.string.message_blocked_local)); url = null; } } } if (mPreferences .getBoolean(PreferenceConstants.RESTORE_LOST_TABS, true)) { String mem = mPreferences.getString(PreferenceConstants.URL_MEMORY, ""); mEditPrefs.putString(PreferenceConstants.URL_MEMORY, ""); String[] array = Utils.getArray(mem); int count = 0; for (int n = 0; n < array.length; n++) { if (array[n].length() > 0) { newTab(array[n], true); count++; } } if (url != null) { newTab(url, true); } else if (count == 0) { newTab(null, true); } } else { newTab(url, true); } } public void initializePreferences() { if (mPreferences == null) { mPreferences = getSharedPreferences( PreferenceConstants.PREFERENCES, 0); } mFullScreen = mPreferences.getBoolean(PreferenceConstants.FULL_SCREEN, false); if (mPreferences.getBoolean(PreferenceConstants.HIDE_STATUS_BAR, false)) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } else { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } switch (mPreferences.getInt(PreferenceConstants.SEARCH, 1)) { case 0: mSearchText = mPreferences.getString( PreferenceConstants.SEARCH_URL, Constants.GOOGLE_SEARCH); if (!mSearchText.startsWith(Constants.HTTP) && !mSearchText.startsWith(Constants.HTTPS)) { mSearchText = Constants.GOOGLE_SEARCH; } break; case 1: mSearchText = Constants.GOOGLE_SEARCH; break; case 2: mSearchText = Constants.ANDROID_SEARCH; break; case 3: mSearchText = Constants.BING_SEARCH; break; case 4: mSearchText = Constants.YAHOO_SEARCH; break; case 5: mSearchText = Constants.STARTPAGE_SEARCH; break; case 6: mSearchText = Constants.STARTPAGE_MOBILE_SEARCH; break; case 7: mSearchText = Constants.DUCK_SEARCH; break; case 8: mSearchText = Constants.DUCK_LITE_SEARCH; break; case 9: mSearchText = Constants.BAIDU_SEARCH; break; case 10: mSearchText = Constants.YANDEX_SEARCH; break; } updateCookiePreference(); if (mPreferences.getBoolean(PreferenceConstants.USE_PROXY, false)) { initializeTor(); } else { try { WebkitProxy.resetProxy("acr.browser.lightning.BrowserApp", getApplicationContext()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /* * Override this if class overrides BrowserActivity */ public void updateCookiePreference() { } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER) { if (mSearch.hasFocus()) { searchTheWeb(mSearch.getText().toString()); } } return super.onKeyDown(keyCode, event); } @Override public boolean onCreateOptionsMenu(Menu menu) { return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { // The action bar home/up action should open or close the drawer. // ActionBarDrawerToggle will take care of this. if (mDrawerToggle.onOptionsItemSelected(item)) { if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { mDrawerLayout.closeDrawer(mDrawerRight); mDrawerLayout.openDrawer(mDrawer); } else if (mDrawerLayout.isDrawerOpen(mDrawer)) { mDrawerLayout.closeDrawer(mDrawer); } mDrawerToggle.syncState(); return true; } // Handle action buttons switch (item.getItemId()) { case android.R.id.home: if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { mDrawerLayout.closeDrawer(mDrawerRight); } mDrawerToggle.syncState(); return true; case R.id.action_back: if (mCurrentView != null) { if (mCurrentView.canGoBack()) mCurrentView.goBack(); } return true; case R.id.action_forward: if (mCurrentView != null) { if (mCurrentView.canGoForward()) mCurrentView.goForward(); } return true; case R.id.action_new_tab: newTab(null, true); return true; case R.id.action_incognito: startActivity(new Intent(Constants.INCOGNITO_INTENT)); return true; case R.id.action_share: if (!mCurrentView.getUrl().startsWith(Constants.FILE)) { Intent shareIntent = new Intent( android.content.Intent.ACTION_SEND); shareIntent.setType("text/plain"); shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, mCurrentView.getTitle()); String shareMessage = mCurrentView.getUrl(); shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareMessage); startActivity(Intent.createChooser(shareIntent, getResources() .getString(R.string.dialog_title_share))); } return true; case R.id.action_bookmarks: openBookmarks(); return true; case R.id.action_copy: if (mCurrentView != null) { if (!mCurrentView.getUrl().startsWith(Constants.FILE)) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("label", mCurrentView .getUrl().toString()); clipboard.setPrimaryClip(clip); Utils.showToast(mContext, mContext.getResources() .getString(R.string.message_link_copied)); } } return true; case R.id.action_settings: startActivity(new Intent(Constants.SETTINGS_INTENT)); return true; case R.id.action_history: openHistory(); return true; case R.id.action_add_bookmark: if (!mCurrentView.getUrl().startsWith(Constants.FILE)) { addBookmark(this, mCurrentView.getTitle(), mCurrentView.getUrl()); } return true; case R.id.action_find: findInPage(); return true; default: return super.onOptionsItemSelected(item); } } /** * refreshes the underlying list of the Bookmark adapter since the bookmark * adapter doesn't always change when notifyDataChanged gets called. */ private void notifyBookmarkDataSetChanged() { mBookmarkAdapter.clear(); mBookmarkAdapter.addAll(mBookmarkList); mBookmarkAdapter.notifyDataSetChanged(); } /** * method that shows a dialog asking what string the user wishes to search * for. It highlights the text entered. */ private void findInPage() { final AlertDialog.Builder finder = new AlertDialog.Builder(mActivity); finder.setTitle(getResources().getString(R.string.action_find)); final EditText getHome = new EditText(this); getHome.setHint(getResources().getString(R.string.search_hint)); finder.setView(getHome); finder.setPositiveButton( getResources().getString(R.string.search_hint), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String text = getHome.getText().toString(); if (mCurrentView != null) { mCurrentView.find(text); } } }); finder.show(); } /** The click listener for ListView in the navigation drawer */ private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { mIsNewIntent = false; selectItem(position); } } /** long click listener for Navigation Drawer */ private class DrawerItemLongClickListener implements ListView.OnItemLongClickListener { @Override public boolean onItemLongClick(AdapterView arg0, View arg1, int position, long arg3) { deleteTab(position); return false; } } private class BookmarkItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (mCurrentView != null) { mCurrentView.loadUrl(mBookmarkList.get(position).getUrl()); } mDrawerLayout.closeDrawer(mDrawerRight); } } private class BookmarkItemLongClickListener implements ListView.OnItemLongClickListener { @Override public boolean onItemLongClick(AdapterView arg0, View arg1, final int position, long arg3) { AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); builder.setTitle(mContext.getResources().getString( R.string.action_bookmarks)); builder.setMessage( getResources().getString(R.string.dialog_bookmark)) .setCancelable(true) .setPositiveButton( getResources().getString(R.string.action_new_tab), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { newTab(mBookmarkList.get(position).getUrl(), false); mDrawerLayout.closeDrawers(); } }) .setNegativeButton( getResources().getString(R.string.action_delete), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { deleteBookmark(mBookmarkList.get(position) .getUrl()); } }) .setNeutralButton( getResources().getString(R.string.action_edit), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { editBookmark(position); } }); AlertDialog alert = builder.create(); alert.show(); return true; } } /** * Takes in the id of which bookmark was selected and shows a dialog that * allows the user to rename and change the url of the bookmark * * @param id * which id in the list was chosen */ public synchronized void editBookmark(final int id) { final AlertDialog.Builder homePicker = new AlertDialog.Builder( mActivity); homePicker.setTitle(getResources().getString( R.string.title_edit_bookmark)); final EditText getTitle = new EditText(mContext); getTitle.setHint(getResources().getString(R.string.hint_title)); getTitle.setText(mBookmarkList.get(id).getTitle()); getTitle.setSingleLine(); final EditText getUrl = new EditText(mContext); getUrl.setHint(getResources().getString(R.string.hint_url)); getUrl.setText(mBookmarkList.get(id).getUrl()); getUrl.setSingleLine(); LinearLayout layout = new LinearLayout(mContext); layout.setOrientation(LinearLayout.VERTICAL); layout.addView(getTitle); layout.addView(getUrl); homePicker.setView(layout); homePicker.setPositiveButton( getResources().getString(R.string.action_ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mBookmarkList.get(id).setTitle( getTitle.getText().toString()); mBookmarkList.get(id).setUrl( getUrl.getText().toString()); notifyBookmarkDataSetChanged(); File book = new File(getFilesDir(), "bookmarks"); File bookUrl = new File(getFilesDir(), "bookurl"); try { BufferedWriter bookWriter = new BufferedWriter( new FileWriter(book)); BufferedWriter urlWriter = new BufferedWriter( new FileWriter(bookUrl)); Iterator iter = mBookmarkList .iterator(); HistoryItem item; while (iter.hasNext()) { item = iter.next(); bookWriter.write(item.getTitle()); urlWriter.write(item.getUrl()); bookWriter.newLine(); urlWriter.newLine(); } bookWriter.close(); urlWriter.close(); } catch (FileNotFoundException e) { } catch (IOException e) { } Collections.sort(mBookmarkList, new SortIgnoreCase()); notifyBookmarkDataSetChanged(); if (mCurrentView != null) { if (mCurrentView.getUrl() .startsWith(Constants.FILE) && mCurrentView.getUrl().endsWith( "bookmarks.html")) { openBookmarkPage(mCurrentView.getWebView()); } } } }); homePicker.show(); } /** * displays the WebView contained in the LightningView Also handles the * removal of previous views * * @param view * the LightningView to show */ private synchronized void showTab(LightningView view) { if (view == null) { return; } if (mCurrentView != null) { mCurrentView.onPause(); } mCurrentView = view; if (view.getWebView() != null) { updateUrl(view.getUrl()); updateProgress(view.getProgress()); } else { updateUrl(""); updateProgress(0); } mBrowserFrame.removeAllViews(); mCurrentView.onResume(); mBrowserFrame.addView(view.getWebView()); } /** * creates a new tab with the passed in URL if it isn't null */ @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); } public void handleNewIntent(Intent intent) { if (mCurrentView == null) { initialize(); } String url = null; if (intent != null) { url = intent.getDataString(); } int num = 0; if (intent != null && intent.getExtras() != null) num = intent.getExtras().getInt(getPackageName() + ".Origin"); if (num == 1) { mCurrentView.loadUrl(url); } else if (url != null) { if (url.startsWith(Constants.FILE)) { Utils.showToast(this, getResources() .getString(R.string.message_blocked_local)); url = null; } newTab(url, true); mIsNewIntent = true; } } private void selectItem(int position) { // update selected item and title, then close the drawer mDrawerList.setItemChecked(position, true); showTab(mWebViews.get(position)); mDrawerLayout.closeDrawer(mDrawer); } /** * When using the ActionBarDrawerToggle, you must call it during * onPostCreate() and onConfigurationChanged()... */ @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Pass any configuration change to the drawer toggls mDrawerToggle.onConfigurationChanged(newConfig); } protected synchronized void newTab(String url, boolean show) { mIsNewIntent = false; LightningView startingTab = new LightningView(mActivity, url, mCookieManager); if (mIdGenerator == 0) { startingTab.resumeTimers(); } mIdList.add(mIdGenerator); mIdGenerator++; mWebViews.add(startingTab); Drawable icon = writeOnDrawable(mWebViews.size()); mActionBar.setIcon(icon); mTitleAdapter.notifyDataSetChanged(); if (show) { mDrawerList.setItemChecked(mWebViews.size() - 1, true); showTab(startingTab); } } private synchronized void deleteTab(int position) { if (position >= mWebViews.size()) { return; } int current = mDrawerList.getCheckedItemPosition(); LightningView reference = mWebViews.get(position); if (reference == null) { return; } boolean isShown = reference.isShown(); if (current > position) { mIdList.remove(position); mWebViews.remove(position); mDrawerList.setItemChecked(current - 1, true); reference.onDestroy(); } else if (mWebViews.size() > position + 1) { mIdList.remove(position); showTab(mWebViews.get(position + 1)); mWebViews.remove(position); mDrawerList.setItemChecked(position, true); reference.onDestroy(); } else if (mWebViews.size() > 1) { mIdList.remove(position); showTab(mWebViews.get(position - 1)); mWebViews.remove(position); mDrawerList.setItemChecked(position - 1, true); reference.onDestroy(); } else { if (mCurrentView.getUrl().startsWith(Constants.FILE) || mCurrentView.getUrl().equals(mHomepage)) { closeActivity(); } else { mIdList.remove(position); mWebViews.remove(position); if (mPreferences.getBoolean( PreferenceConstants.CLEAR_CACHE_EXIT, false) && mCurrentView != null && !isIncognito()) { mCurrentView.clearCache(true); Log.i(Constants.LOGTAG, "Cache Cleared"); } if (mPreferences.getBoolean( PreferenceConstants.CLEAR_HISTORY_EXIT, false) && !isIncognito()) { clearHistory(); Log.i(Constants.LOGTAG, "History Cleared"); } if (mPreferences.getBoolean( PreferenceConstants.CLEAR_COOKIES_EXIT, false) && !isIncognito()) { clearCookies(); Log.i(Constants.LOGTAG, "Cookies Cleared"); } if (reference != null) { reference.pauseTimers(); reference.onDestroy(); } mCurrentView = null; mTitleAdapter.notifyDataSetChanged(); finish(); } } mTitleAdapter.notifyDataSetChanged(); Drawable icon = writeOnDrawable(mWebViews.size()); mActionBar.setIcon(icon); if (mIsNewIntent && isShown) { mIsNewIntent = false; closeActivity(); } Log.i(Constants.LOGTAG, "deleted tab"); } @Override public boolean onKeyLongPress(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (mPreferences.getBoolean(PreferenceConstants.CLEAR_CACHE_EXIT, false) && mCurrentView != null && !isIncognito()) { mCurrentView.clearCache(true); Log.i(Constants.LOGTAG, "Cache Cleared"); } if (mPreferences.getBoolean(PreferenceConstants.CLEAR_HISTORY_EXIT, false) && !isIncognito()) { clearHistory(); Log.i(Constants.LOGTAG, "History Cleared"); } if (mPreferences.getBoolean(PreferenceConstants.CLEAR_COOKIES_EXIT, false) && !isIncognito()) { clearCookies(); Log.i(Constants.LOGTAG, "Cookies Cleared"); } mCurrentView = null; for (int n = 0; n < mWebViews.size(); n++) { if (mWebViews.get(n) != null) mWebViews.get(n).onDestroy(); } mWebViews.clear(); mTitleAdapter.notifyDataSetChanged(); finish(); } return true; } public void clearHistory() { this.deleteDatabase(DatabaseHandler.DATABASE_NAME); WebViewDatabase m = WebViewDatabase.getInstance(this); m.clearFormData(); m.clearHttpAuthUsernamePassword(); if (API < 18) { m.clearUsernamePassword(); WebIconDatabase.getInstance().removeAllIcons(); } if (mSystemBrowser) { try { Browser.clearHistory(getContentResolver()); } catch (NullPointerException ignored) { } } SettingsController.setClearHistory(true); Utils.trimCache(this); } public void clearCookies() { CookieManager c = CookieManager.getInstance(); CookieSyncManager.createInstance(this); c.removeAllCookie(); } @Override public void onBackPressed() { if (!mActionBar.isShowing()) { mActionBar.show(); } if (mDrawerLayout.isDrawerOpen(mDrawer)) { mDrawerLayout.closeDrawer(mDrawer); } else if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { mDrawerLayout.closeDrawer(mDrawerRight); } else { if (mCurrentView != null) { Log.i(Constants.LOGTAG, "onBackPressed"); if (mCurrentView.canGoBack()) { if (!mCurrentView.isShown()) { onHideCustomView(); } else { mCurrentView.goBack(); } } else { deleteTab(mDrawerList.getCheckedItemPosition()); } } else { Log.e(Constants.LOGTAG, "So madness. Much confusion. Why happen."); super.onBackPressed(); } } } @Override protected void onPause() { super.onPause(); Log.i(Constants.LOGTAG, "onPause"); if (mCurrentView != null) { mCurrentView.pauseTimers(); mCurrentView.onPause(); } if (mHistoryDatabase != null) { if (mHistoryDatabase.isOpen()) mHistoryDatabase.close(); } if (mHistoryHandler != null) { if (mHistoryHandler.isOpen()) mHistoryHandler.close(); } } public void saveOpenTabs() { if (mPreferences .getBoolean(PreferenceConstants.RESTORE_LOST_TABS, true)) { String s = ""; for (int n = 0; n < mWebViews.size(); n++) { if (mWebViews.get(n).getUrl() != null) { s = s + mWebViews.get(n).getUrl() + "|$|SEPARATOR|$|"; } } mEditPrefs.putString(PreferenceConstants.URL_MEMORY, s); mEditPrefs.commit(); } } @Override protected void onDestroy() { Log.i(Constants.LOGTAG, "onDestroy"); if (mHistoryDatabase != null) { if (mHistoryDatabase.isOpen()) mHistoryDatabase.close(); } if (mHistoryHandler != null) { if (mHistoryHandler.isOpen()) mHistoryHandler.close(); } super.onDestroy(); } @Override protected void onResume() { super.onResume(); Log.i(Constants.LOGTAG, "onResume"); if (SettingsController.getClearHistory()) { } if (mSearchAdapter != null) { mSearchAdapter.refreshPreferences(); mSearchAdapter.refreshBookmarks(); } if (mActionBar != null) { if (!mActionBar.isShowing()) { mActionBar.show(); } } if (mCurrentView != null) { mCurrentView.resumeTimers(); mCurrentView.onResume(); if (mHistoryHandler == null) { mHistoryHandler = new DatabaseHandler(this); } else if (!mHistoryHandler.isOpen()) { mHistoryHandler = new DatabaseHandler(this); } mHistoryDatabase = mHistoryHandler.getReadableDatabase(); mBookmarkList = getBookmarks(); notifyBookmarkDataSetChanged(); } else { initialize(); } initializePreferences(); if (mWebViews != null) { for (int n = 0; n < mWebViews.size(); n++) { if (mWebViews.get(n) != null) { mWebViews.get(n).initializePreferences(this); } else { mWebViews.remove(n); } } } else { initialize(); } } /** * searches the web for the query fixing any and all problems with the input * checks if it is a search, url, etc. */ void searchTheWeb(String query) { if (query.equals("")) { return; } String SEARCH = mSearchText; query = query.trim(); mCurrentView.stopLoading(); if (query.startsWith("www.")) { query = Constants.HTTP + query; } else if (query.startsWith("ftp.")) { query = "ftp://" + query; } boolean containsPeriod = query.contains("."); boolean isIPAddress = (TextUtils.isDigitsOnly(query.replace(".", "")) && (query.replace(".", "").length() >= 4) && query .contains(".")); boolean aboutScheme = query.contains("about:"); boolean validURL = (query.startsWith("ftp://") || query.startsWith(Constants.HTTP) || query.startsWith(Constants.FILE) || query .startsWith(Constants.HTTPS)) || isIPAddress; boolean isSearch = ((query.contains(" ") || !containsPeriod) && !aboutScheme); if (isIPAddress && (!query.startsWith(Constants.HTTP) || !query .startsWith(Constants.HTTPS))) { query = Constants.HTTP + query; } if (isSearch) { try { query = URLEncoder.encode(query, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } mCurrentView.loadUrl(SEARCH + query); } else if (!validURL) { mCurrentView.loadUrl(Constants.HTTP + query); } else { mCurrentView.loadUrl(query); } } public void deleteBookmark(String url) { File book = new File(getFilesDir(), "bookmarks"); File bookUrl = new File(getFilesDir(), "bookurl"); try { BufferedWriter bookWriter = new BufferedWriter(new FileWriter(book)); BufferedWriter urlWriter = new BufferedWriter(new FileWriter( bookUrl)); Iterator iter = mBookmarkList.iterator(); HistoryItem item; int num = 0; int deleteIndex = -1; while (iter.hasNext()) { item = iter.next(); if (!item.getUrl().equalsIgnoreCase(url)) { bookWriter.write(item.getTitle()); urlWriter.write(item.getUrl()); bookWriter.newLine(); urlWriter.newLine(); } else { deleteIndex = num; } num++; } if (deleteIndex != -1) { mBookmarkList.remove(deleteIndex); } bookWriter.close(); urlWriter.close(); } catch (FileNotFoundException e) { } catch (IOException e) { } notifyBookmarkDataSetChanged(); mSearchAdapter.refreshBookmarks(); openBookmarks(); } /** * converts the int num into density pixels * * @param num * @return density pixels */ private int getDp(int num) { float scale = getResources().getDisplayMetrics().density; return (int) (num * scale + 0.5f); } private int pixelsToDp(int num) { float scale = getResources().getDisplayMetrics().density; return (int) ((num - 0.5f) / scale); } /** * writes the number of open tabs on the icon. */ public BitmapDrawable writeOnDrawable(int number) { Bitmap bm = Bitmap.createBitmap(mActionBarSize, mActionBarSize, Config.ARGB_8888); String text = number + ""; Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setAntiAlias(true); paint.setStyle(Style.FILL); paint.setColor(mNumberIconColor); if (number > 99) number = 99; // pixels, 36 dp if (mActionBarSizeDp < 50) { if (number > 9) { paint.setTextSize(mActionBarSize * 3 / 4); // originally // 40 // pixels, // 24 dp } else { paint.setTextSize(mActionBarSize * 9 / 10); // originally 50 // pixels, 30 dp } } else { paint.setTextSize(mActionBarSize * 3 / 4); } Canvas canvas = new Canvas(bm); // originally only vertical padding of 5 pixels int xPos = (canvas.getWidth() / 2); int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint .ascent()) / 2)); canvas.drawText(text, xPos, yPos, paint); return new BitmapDrawable(getResources(), bm); } public class LightningViewAdapter extends ArrayAdapter { Context context; int layoutResourceId; List data = null; public LightningViewAdapter(Context context, int layoutResourceId, List data) { super(context, layoutResourceId, data); this.layoutResourceId = layoutResourceId; this.context = context; this.data = data; } @Override public View getView(final int position, View convertView, ViewGroup parent) { View row = convertView; LightningViewHolder holder = null; if (row == null) { LayoutInflater inflater = ((Activity) context) .getLayoutInflater(); row = inflater.inflate(layoutResourceId, parent, false); holder = new LightningViewHolder(); holder.txtTitle = (TextView) row.findViewById(R.id.text1); holder.favicon = (ImageView) row.findViewById(R.id.favicon1); holder.exit = (ImageView) row.findViewById(R.id.delete1); holder.exit.setTag(position); row.setTag(holder); } else { holder = (LightningViewHolder) row.getTag(); } holder.exit.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { deleteTab(position); } }); LightningView web = data.get(position); holder.txtTitle.setText(web.getTitle()); Bitmap favicon = web.getFavicon(); holder.favicon.setImageBitmap(favicon); return row; } class LightningViewHolder { TextView txtTitle; ImageView favicon; ImageView exit; } } public class BookmarkViewAdapter extends ArrayAdapter { Context context; int layoutResourceId; List data = null; public BookmarkViewAdapter(Context context, int layoutResourceId, List data) { super(context, layoutResourceId, data); this.layoutResourceId = layoutResourceId; this.context = context; this.data = data; } @Override public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; BookmarkViewHolder holder = null; if (row == null) { LayoutInflater inflater = ((Activity) context) .getLayoutInflater(); row = inflater.inflate(layoutResourceId, parent, false); holder = new BookmarkViewHolder(); holder.txtTitle = (TextView) row.findViewById(R.id.text1); holder.favicon = (ImageView) row.findViewById(R.id.favicon1); row.setTag(holder); } else { holder = (BookmarkViewHolder) row.getTag(); } HistoryItem web = data.get(position); holder.txtTitle.setText(web.getTitle()); holder.favicon.setImageBitmap(mWebpageBitmap); if (web.getBitmap() == null) { getImage(holder.favicon, web); } else { holder.favicon.setImageBitmap(web.getBitmap()); } return row; } class BookmarkViewHolder { TextView txtTitle; ImageView favicon; } } public void getImage(ImageView image, HistoryItem web) { try { new DownloadImageTask(image, web).execute(Constants.HTTP + getDomainName(web.getUrl()) + "/favicon.ico"); } catch (URISyntaxException e) { new DownloadImageTask(image, web) .execute("https://www.google.com/s2/favicons?domain_url=" + web.getUrl()); e.printStackTrace(); } } private class DownloadImageTask extends AsyncTask { ImageView bmImage; HistoryItem mWeb; public DownloadImageTask(ImageView bmImage, HistoryItem web) { this.bmImage = bmImage; this.mWeb = web; } protected Bitmap doInBackground(String... urls) { String urldisplay = urls[0]; Bitmap mIcon = null; // unique path for each url that is bookmarked. String hash = String.valueOf(urldisplay.hashCode()); File image = new File(mContext.getCacheDir(), hash + ".png"); // checks to see if the image exists if (!image.exists()) { try { // if not, download it... InputStream in = new java.net.URL(urldisplay).openStream(); if (in != null) { mIcon = BitmapFactory.decodeStream(in); } // ...and cache it if (mIcon != null) { FileOutputStream fos = new FileOutputStream(image); mIcon.compress(Bitmap.CompressFormat.PNG, 85, fos); fos.flush(); fos.close(); } } catch (Exception e) { } } else { // if it exists, retrieve it from the cache mIcon = BitmapFactory.decodeFile(image.getPath()); } if (mIcon == null) { try { // if not, download it... InputStream in = new java.net.URL( "https://www.google.com/s2/favicons?domain_url=" + urldisplay).openStream(); if (in != null) { mIcon = BitmapFactory.decodeStream(in); } // ...and cache it if (mIcon != null) { FileOutputStream fos = new FileOutputStream(image); mIcon.compress(Bitmap.CompressFormat.PNG, 85, fos); fos.flush(); fos.close(); } } catch (Exception e) { } } if (mIcon == null) { return mWebpageBitmap; } else { return mIcon; } } protected void onPostExecute(Bitmap result) { bmImage.setImageBitmap(result); mWeb.setBitmap(result); notifyBookmarkDataSetChanged(); } } static String getDomainName(String url) throws URISyntaxException { URI uri = new URI(url); String domain = uri.getHost(); if (domain == null) { return url; } return domain.startsWith("www.") ? domain.substring(4) : domain; } @Override public void updateUrl(String url) { if (url == null) return; url = url.replaceFirst(Constants.HTTP, ""); if (url.startsWith(Constants.FILE)) { url = ""; } mSearch.setText(url); } @Override public void updateProgress(int n) { if (!mProgress.isShown()) mProgress.setVisibility(View.VISIBLE); mProgress.setProgress(n); if (n == 100) { mProgress.setVisibility(View.INVISIBLE); setIsFinishedLoading(); } else { setIsLoading(); } } @Override public void updateHistory(final String title, final String url) { } public void addItemToHistory(final String title, final String url) { Runnable update = new Runnable() { @Override public void run() { if (isSystemBrowserAvailable()) { try { Browser.updateVisitedHistory(getContentResolver(), url, true); } catch (NullPointerException ignored) { } } try { StringBuilder sb = new StringBuilder("url" + " = "); DatabaseUtils.appendEscapedSQLString(sb, url); if (mHistoryHandler == null) { mHistoryHandler = new DatabaseHandler(mContext); mHistoryDatabase = mHistoryHandler .getReadableDatabase(); } else if (!mHistoryHandler.isOpen()) { mHistoryHandler = new DatabaseHandler(mContext); mHistoryDatabase = mHistoryHandler .getReadableDatabase(); } else if (mHistoryDatabase == null) { mHistoryDatabase = mHistoryHandler .getReadableDatabase(); } else if (!mHistoryDatabase.isOpen()) { mHistoryDatabase = mHistoryHandler .getReadableDatabase(); } Cursor cursor = mHistoryDatabase.query( DatabaseHandler.TABLE_HISTORY, new String[] { "id", "url", "title" }, sb.toString(), null, null, null, null); if (!cursor.moveToFirst()) { mHistoryHandler.addHistoryItem(new HistoryItem(url, title)); } else { mHistoryHandler.delete(url); mHistoryHandler.addHistoryItem(new HistoryItem(url, title)); } cursor.close(); cursor = null; } catch (IllegalStateException e) { Log.e(Constants.LOGTAG, "IllegalStateException in updateHistory"); } catch (NullPointerException e) { Log.e(Constants.LOGTAG, "NullPointerException in updateHistory"); } catch (SQLiteException e) { Log.e(Constants.LOGTAG, "SQLiteException in updateHistory"); } } }; if (url != null) { if (!url.startsWith(Constants.FILE)) { new Thread(update).start(); } } } /** * 1, 2, 3, testing... is there a system browser that has some nice * bookmarks for us? */ public boolean isSystemBrowserAvailable() { return mSystemBrowser; } /** * 1, 2, 3, testing... is there a system browser that has some nice * bookmarks for us? helper method for isSystemBrowserAvailable */ public boolean getSystemBrowser() { Cursor c = null; String[] columns = new String[] { "url", "title" }; boolean browserFlag = false; try { Uri bookmarks = Browser.BOOKMARKS_URI; c = getContentResolver() .query(bookmarks, columns, null, null, null); } catch (SQLiteException ignored) { } catch (IllegalStateException ignored) { } catch (NullPointerException ignored) { } if (c != null) { Log.i("Browser", "System Browser Available"); browserFlag = true; } else { Log.e("Browser", "System Browser Unavailable"); browserFlag = false; } if (c != null) { c.close(); c = null; } mEditPrefs.putBoolean("SystemBrowser", browserFlag); mEditPrefs.commit(); return browserFlag; } /** * method to generate search suggestions for the AutoCompleteTextView from * previously searched URLs */ private void initializeSearchSuggestions(final AutoCompleteTextView getUrl) { getUrl.setThreshold(1); getUrl.setDropDownWidth(-1); getUrl.setDropDownAnchor(R.id.progressWrapper); getUrl.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) { try { String url; url = ((TextView) arg1.findViewById(R.id.url)).getText() .toString(); if (url.startsWith(mContext.getString(R.string.suggestion))) { url = ((TextView) arg1.findViewById(R.id.title)) .getText().toString(); } else { getUrl.setText(url); } searchTheWeb(url); url = null; InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(getUrl.getWindowToken(), 0); if (mCurrentView != null) { mCurrentView.requestFocus(); } } catch (NullPointerException e) { Log.e("Browser Error: ", "NullPointerException on item click"); } } }); getUrl.setSelectAllOnFocus(true); mSearchAdapter = new SearchAdapter(mContext, isIncognito()); getUrl.setAdapter(mSearchAdapter); } public boolean isIncognito() { return false; } // Damn it, I regret not using SQLite in the first place for this private List getBookmarks() { List bookmarks = new ArrayList(); File bookUrl = new File(getApplicationContext().getFilesDir(), "bookurl"); File book = new File(getApplicationContext().getFilesDir(), "bookmarks"); try { BufferedReader readUrl = new BufferedReader(new FileReader(bookUrl)); BufferedReader readBook = new BufferedReader(new FileReader(book)); String u, t; while ((u = readUrl.readLine()) != null && (t = readBook.readLine()) != null) { HistoryItem map = new HistoryItem(u, t); bookmarks.add(map); } readBook.close(); readUrl.close(); } catch (FileNotFoundException ignored) { } catch (IOException ignored) { } Collections.sort(bookmarks, new SortIgnoreCase()); return bookmarks; } /** * returns a list of HistoryItems * * @return */ private List getLatestHistory() { DatabaseHandler historyHandler = new DatabaseHandler(mContext); return historyHandler.getLastHundredItems(); } /** * function that opens the HTML history page in the browser */ private void openHistory() { Thread history = new Thread(new Runnable() { @Override public void run() { String historyHtml = HistoryPageVariables.Heading; List historyList = getLatestHistory(); Iterator it = historyList.iterator(); HistoryItem helper; while (it.hasNext()) { helper = it.next(); historyHtml += HistoryPageVariables.Part1 + helper.getUrl() + HistoryPageVariables.Part2 + helper.getTitle() + HistoryPageVariables.Part3 + helper.getUrl() + HistoryPageVariables.Part4; } historyHtml += HistoryPageVariables.End; File historyWebPage = new File(getFilesDir(), "history.html"); try { FileWriter hWriter = new FileWriter(historyWebPage, false); hWriter.write(historyHtml); hWriter.close(); } catch (IOException e) { e.printStackTrace(); } mCurrentView.loadUrl(Constants.FILE + historyWebPage); mSearch.setText(""); } }); history.run(); } /** * helper function that opens the bookmark drawer */ private void openBookmarks() { if (mDrawerLayout.isDrawerOpen(mDrawer)) { mDrawerLayout.closeDrawers(); } mDrawerToggle.syncState(); mDrawerLayout.openDrawer(mDrawerRight); } public void closeDrawers() { mDrawerLayout.closeDrawers(); } @Override /** * open the HTML bookmarks page, parameter view is the WebView that should show the page */ public void openBookmarkPage(WebView view) { String bookmarkHtml = BookmarkPageVariables.Heading; Iterator iter = mBookmarkList.iterator(); HistoryItem helper; while (iter.hasNext()) { helper = iter.next(); bookmarkHtml += (BookmarkPageVariables.Part1 + helper.getUrl() + BookmarkPageVariables.Part2 + helper.getUrl() + BookmarkPageVariables.Part3 + helper.getTitle() + BookmarkPageVariables.Part4); } bookmarkHtml += BookmarkPageVariables.End; File bookmarkWebPage = new File(mContext.getCacheDir(), "bookmarks.html"); try { FileWriter bookWriter = new FileWriter(bookmarkWebPage, false); bookWriter.write(bookmarkHtml); bookWriter.close(); } catch (IOException e) { e.printStackTrace(); } view.loadUrl(Constants.FILE + bookmarkWebPage); } /** * adds a bookmark with a title and url. Simple. */ public void addBookmark(Context context, String title, String url) { File book = new File(context.getFilesDir(), "bookmarks"); File bookUrl = new File(context.getFilesDir(), "bookurl"); HistoryItem bookmark = new HistoryItem(url, title); try { BufferedReader readUrlRead = new BufferedReader(new FileReader( bookUrl)); String u; while ((u = readUrlRead.readLine()) != null) { if (u.contentEquals(url)) { readUrlRead.close(); return; } } readUrlRead.close(); } catch (FileNotFoundException ignored) { } catch (IOException ignored) { } catch (NullPointerException ignored) { } try { BufferedWriter bookWriter = new BufferedWriter(new FileWriter(book, true)); BufferedWriter urlWriter = new BufferedWriter(new FileWriter( bookUrl, true)); bookWriter.write(title); urlWriter.write(url); bookWriter.newLine(); urlWriter.newLine(); bookWriter.close(); urlWriter.close(); mBookmarkList.add(bookmark); Collections.sort(mBookmarkList, new SortIgnoreCase()); notifyBookmarkDataSetChanged(); } catch (FileNotFoundException ignored) { } catch (IOException ignored) { } catch (NullPointerException ignored) { } mSearchAdapter.refreshBookmarks(); } @Override public void update() { mTitleAdapter.notifyDataSetChanged(); } @Override /** * opens a file chooser * param ValueCallback is the message from the WebView indicating a file chooser * should be opened */ public void openFileChooser(ValueCallback uploadMsg) { mUploadMessage = uploadMsg; Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("*/*"); startActivityForResult(Intent.createChooser(i, "File Chooser"), 1); } @Override /** * used to allow uploading into the browser, doesn't get called in KitKat :( */ protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == 1) { if (null == mUploadMessage) return; Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData(); mUploadMessage.onReceiveValue(result); mUploadMessage = null; } } @Override /** * handles long presses for the browser, tries to get the * url of the item that was clicked and sends it (it can be null) * to the click handler that does cool stuff with it */ public void onLongPress() { if (mClickHandler == null) { mClickHandler = new ClickHandler(mContext); } Message click = mClickHandler.obtainMessage(); if (click != null) { click.setTarget(mClickHandler); } mCurrentView.getWebView().requestFocusNodeHref(click); } @Override public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) { if (view == null) { return; } if (mCustomView != null && callback != null) { callback.onCustomViewHidden(); return; } view.setKeepScreenOn(true); mOriginalOrientation = getRequestedOrientation(); FrameLayout decor = (FrameLayout) getWindow().getDecorView(); mFullscreenContainer = new FullscreenHolder(this); mCustomView = view; mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS); decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); setFullscreen(true); mCurrentView.setVisibility(View.GONE); if (view instanceof FrameLayout) { if (((FrameLayout) view).getFocusedChild() instanceof VideoView) { mVideoView = (VideoView) ((FrameLayout) view).getFocusedChild(); mVideoView.setOnErrorListener(new VideoCompletionListener()); mVideoView .setOnCompletionListener(new VideoCompletionListener()); } } mCustomViewCallback = callback; } @Override public void onHideCustomView() { if (mCustomView == null || mCustomViewCallback == null || mCurrentView == null) return; Log.i(Constants.LOGTAG, "onHideCustomView"); mCurrentView.setVisibility(View.VISIBLE); mCustomView.setKeepScreenOn(false); setFullscreen(mPreferences.getBoolean( PreferenceConstants.HIDE_STATUS_BAR, false)); FrameLayout decor = (FrameLayout) getWindow().getDecorView(); if (decor != null) { decor.removeView(mFullscreenContainer); } if (API < 19) { try { mCustomViewCallback.onCustomViewHidden(); } catch (Throwable ignored) { } } mFullscreenContainer = null; mCustomView = null; if (mVideoView != null) { mVideoView.setOnErrorListener(null); mVideoView.setOnCompletionListener(null); mVideoView = null; } setRequestedOrientation(mOriginalOrientation); } private class VideoCompletionListener implements MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener { @Override public boolean onError(MediaPlayer mp, int what, int extra) { return false; } @Override public void onCompletion(MediaPlayer mp) { onHideCustomView(); } } /** * turns on fullscreen mode in the app * * @param enabled * whether to enable fullscreen or not */ public void setFullscreen(boolean enabled) { Window win = getWindow(); WindowManager.LayoutParams winParams = win.getAttributes(); final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN; if (enabled) { winParams.flags |= bits; } else { winParams.flags &= ~bits; if (mCustomView != null) { mCustomView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); } else { mBrowserFrame .setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); } } win.setAttributes(winParams); } /** * a class extending FramLayout used to display fullscreen videos */ static class FullscreenHolder extends FrameLayout { public FullscreenHolder(Context ctx) { super(ctx); setBackgroundColor(ctx.getResources().getColor( android.R.color.black)); } @Override public boolean onTouchEvent(MotionEvent evt) { return true; } } @Override /** * a stupid method that returns the bitmap image to display in place of * a loading video */ public Bitmap getDefaultVideoPoster() { if (mDefaultVideoPoster == null) { mDefaultVideoPoster = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_media_play); } return mDefaultVideoPoster; } @Override /** * dumb method that returns the loading progress for a video */ public View getVideoLoadingProgressView() { if (mVideoProgressView == null) { LayoutInflater inflater = LayoutInflater.from(this); mVideoProgressView = inflater.inflate( R.layout.video_loading_progress, null); } return mVideoProgressView; } @Override /** * handles javascript requests to create a new window in the browser */ public void onCreateWindow(boolean isUserGesture, Message resultMsg) { if (resultMsg == null) { return; } newTab("", true); WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; transport.setWebView(mCurrentView.getWebView()); resultMsg.sendToTarget(); } @Override /** * returns the Activity instance for this activity, * very helpful when creating things in other classes... I think */ public Activity getActivity() { return mActivity; } /** * it hides the action bar, seriously what else were you expecting */ @Override public void hideActionBar() { if (mActionBar.isShowing() && mFullScreen) { mActionBar.hide(); } } @Override /** * obviously it shows the action bar if it's hidden */ public void showActionBar() { if (!mActionBar.isShowing() && mFullScreen) { mActionBar.show(); } } @Override /** * handles a long click on the page, parameter String url * is the url that should have been obtained from the WebView touch node * thingy, if it is null, this method tries to deal with it and find a workaround */ public void longClickPage(final String url) { HitTestResult result = null; if (mCurrentView.getWebView() != null) { result = mCurrentView.getWebView().getHitTestResult(); } if (url != null) { if (result != null) { if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == HitTestResult.IMAGE_TYPE) { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: { newTab(url, false); break; } case DialogInterface.BUTTON_NEGATIVE: { mCurrentView.loadUrl(url); break; } case DialogInterface.BUTTON_NEUTRAL: { if (API > 8) { Utils.downloadFile(mActivity, url, mCurrentView.getUserAgent(), "attachment", false); } break; } } } }; AlertDialog.Builder builder = new AlertDialog.Builder( mActivity); // dialog builder.setTitle(url.replace(Constants.HTTP, "")) .setMessage( getResources().getString( R.string.dialog_image)) .setPositiveButton( getResources().getString( R.string.action_new_tab), dialogClickListener) .setNegativeButton( getResources().getString( R.string.action_open), dialogClickListener) .setNeutralButton( getResources().getString( R.string.action_download), dialogClickListener).show(); } else { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: { newTab(url, false); break; } case DialogInterface.BUTTON_NEGATIVE: { mCurrentView.loadUrl(url); break; } case DialogInterface.BUTTON_NEUTRAL: { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("label", url); clipboard.setPrimaryClip(clip); break; } } } }; AlertDialog.Builder builder = new AlertDialog.Builder( mActivity); // dialog builder.setTitle(url) .setMessage( getResources().getString( R.string.dialog_link)) .setPositiveButton( getResources().getString( R.string.action_new_tab), dialogClickListener) .setNegativeButton( getResources().getString( R.string.action_open), dialogClickListener) .setNeutralButton( getResources().getString( R.string.action_copy), dialogClickListener).show(); } } else { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: { newTab(url, false); break; } case DialogInterface.BUTTON_NEGATIVE: { mCurrentView.loadUrl(url); break; } case DialogInterface.BUTTON_NEUTRAL: { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("label", url); clipboard.setPrimaryClip(clip); break; } } } }; AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog builder.setTitle(url) .setMessage( getResources().getString(R.string.dialog_link)) .setPositiveButton( getResources().getString( R.string.action_new_tab), dialogClickListener) .setNegativeButton( getResources().getString(R.string.action_open), dialogClickListener) .setNeutralButton( getResources().getString(R.string.action_copy), dialogClickListener).show(); } } else if (result != null) { if (result.getExtra() != null) { final String newUrl = result.getExtra(); if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == HitTestResult.IMAGE_TYPE) { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: { newTab(newUrl, false); break; } case DialogInterface.BUTTON_NEGATIVE: { mCurrentView.loadUrl(newUrl); break; } case DialogInterface.BUTTON_NEUTRAL: { if (API > 8) { Utils.downloadFile(mActivity, newUrl, mCurrentView.getUserAgent(), "attachment", false); } break; } } } }; AlertDialog.Builder builder = new AlertDialog.Builder( mActivity); // dialog builder.setTitle(newUrl.replace(Constants.HTTP, "")) .setMessage( getResources().getString( R.string.dialog_image)) .setPositiveButton( getResources().getString( R.string.action_new_tab), dialogClickListener) .setNegativeButton( getResources().getString( R.string.action_open), dialogClickListener) .setNeutralButton( getResources().getString( R.string.action_download), dialogClickListener).show(); } else { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: { newTab(newUrl, false); break; } case DialogInterface.BUTTON_NEGATIVE: { mCurrentView.loadUrl(newUrl); break; } case DialogInterface.BUTTON_NEUTRAL: { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("label", newUrl); clipboard.setPrimaryClip(clip); break; } } } }; AlertDialog.Builder builder = new AlertDialog.Builder( mActivity); // dialog builder.setTitle(newUrl) .setMessage( getResources().getString( R.string.dialog_link)) .setPositiveButton( getResources().getString( R.string.action_new_tab), dialogClickListener) .setNegativeButton( getResources().getString( R.string.action_open), dialogClickListener) .setNeutralButton( getResources().getString( R.string.action_copy), dialogClickListener).show(); } } } } /** * This method lets the search bar know that the page is currently loading * and that it should display the stop icon to indicate to the user that * pressing it stops the page from loading */ public void setIsLoading() { if (!mSearch.hasFocus()) { mIcon = mDeleteIcon; mSearch.setCompoundDrawables(null, null, mDeleteIcon, null); } } /** * This tells the search bar that the page is finished loading and it should * display the refresh icon */ public void setIsFinishedLoading() { if (!mSearch.hasFocus()) { mIcon = mRefreshIcon; mSearch.setCompoundDrawables(null, null, mRefreshIcon, null); } } /** * handle presses on the refresh icon in the search bar, if the page is * loading, stop the page, if it is done loading refresh the page. * * See setIsFinishedLoading and setIsLoading for displaying the correct icon */ public void refreshOrStop() { if (mCurrentView != null) { if (mCurrentView.getProgress() < 100) { mCurrentView.stopLoading(); } else { mCurrentView.reload(); } } } @Override public boolean isActionBarShowing() { if (mActionBar != null) { return mActionBar.isShowing(); } else { return false; } } // Override this, use finish() for Incognito, moveTaskToBack for Main public void closeActivity() { finish(); } public class SortIgnoreCase implements Comparator { public int compare(HistoryItem o1, HistoryItem o2) { return o1.getTitle().toLowerCase(Locale.getDefault()) .compareTo(o2.getTitle().toLowerCase(Locale.getDefault())); } } }