private void bindBrowser(WebView appView) { gap = new Device(appView, this); accel = new AccelBroker(appView, this); launcher = new CameraLauncher(appView, this); mContacts = new ContactManager(appView, this); fs = new FileUtils(appView); netMan = new NetworkManager(appView, this); mCompass = new CompassListener(appView, this); crypto = new CryptoHandler(appView); mKey = new BrowserKey(appView, this); audio = new AudioHandler(appView, this); uiControls = new UIControls(appView, this); // This creates the new javascript interfaces for PhoneGap appView.addJavascriptInterface(gap, "DroidGap"); appView.addJavascriptInterface(accel, "Accel"); appView.addJavascriptInterface(launcher, "GapCam"); appView.addJavascriptInterface(mContacts, "ContactHook"); appView.addJavascriptInterface(fs, "FileUtil"); appView.addJavascriptInterface(netMan, "NetworkManager"); appView.addJavascriptInterface(mCompass, "CompassHook"); appView.addJavascriptInterface(crypto, "GapCrypto"); appView.addJavascriptInterface(mKey, "BackButton"); appView.addJavascriptInterface(audio, "GapAudio"); appView.addJavascriptInterface(uiControls, "UIControlsHook"); if (android.os.Build.VERSION.RELEASE.startsWith("1.")) { cupcakeStorage = new Storage(appView); geo = new GeoBroker(appView, this); appView.addJavascriptInterface(cupcakeStorage, "droidStorage"); appView.addJavascriptInterface(geo, "Geo"); } }
/** @author Andreas Schildbach */ public final class Constants { public static final boolean TEST = R.class.getPackage().getName().contains("_test"); /** Network this wallet is on (e.g. testnet or mainnet). */ public static final NetworkParameters NETWORK_PARAMETERS = TEST ? TestNet3Params.get() : MainNetParams.get(); public static final class Files { private static final String FILENAME_NETWORK_SUFFIX = NETWORK_PARAMETERS.getId().equals(NetworkParameters.ID_MAINNET) ? "" : "-testnet"; /** Filename of the wallet. */ public static final String WALLET_FILENAME_PROTOBUF = "wallet-protobuf" + FILENAME_NETWORK_SUFFIX; /** Filename of the automatic key backup (old format, can only be read). */ public static final String WALLET_KEY_BACKUP_BASE58 = "key-backup-base58" + FILENAME_NETWORK_SUFFIX; /** Filename of the automatic wallet backup. */ public static final String WALLET_KEY_BACKUP_PROTOBUF = "key-backup-protobuf" + FILENAME_NETWORK_SUFFIX; /** Path to external storage */ public static final File EXTERNAL_STORAGE_DIR = Environment.getExternalStorageDirectory(); /** Manual backups go here. */ public static final File EXTERNAL_WALLET_BACKUP_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); /** Filename of the manual key backup (old format, can only be read). */ public static final String EXTERNAL_WALLET_KEY_BACKUP = "bitcoin-wallet-keys" + FILENAME_NETWORK_SUFFIX; /** Filename of the manual wallet backup. */ public static final String EXTERNAL_WALLET_BACKUP = "bitcoin-wallet-backup" + FILENAME_NETWORK_SUFFIX; /** Filename of the block store for storing the chain. */ public static final String BLOCKCHAIN_FILENAME = "blockchain" + FILENAME_NETWORK_SUFFIX; /** Filename of the block checkpoints file. */ public static final String CHECKPOINTS_FILENAME = "checkpoints" + FILENAME_NETWORK_SUFFIX + ".txt"; } /** Maximum size of backups. Files larger will be rejected. */ public static final long BACKUP_MAX_CHARS = 10000000; private static final String BITEASY_API_URL_PROD = "https://api.biteasy.com/blockchain/v1/"; private static final String BITEASY_API_URL_TEST = "https://api.biteasy.com/testnet/v1/"; /** Base URL for blockchain API. */ public static final String BITEASY_API_URL = NETWORK_PARAMETERS.getId().equals(NetworkParameters.ID_MAINNET) ? BITEASY_API_URL_PROD : BITEASY_API_URL_TEST; /** URL to fetch version alerts from. */ public static final String VERSION_URL = "https://wallet.schildbach.de/version"; /** MIME type used for transmitting single transactions. */ public static final String MIMETYPE_TRANSACTION = "application/x-btctx"; /** MIME type used for transmitting wallet backups. */ public static final String MIMETYPE_WALLET_BACKUP = "application/x-bitcoin-wallet-backup"; /** Number of confirmations until a transaction is fully confirmed. */ public static final int MAX_NUM_CONFIRMATIONS = 7; /** User-agent to use for network access. */ public static final String USER_AGENT = "Bitcoin Wallet"; /** Default currency to use if all default mechanisms fail. */ public static final String DEFAULT_EXCHANGE_CURRENCY = "USD"; /** Donation address for tip/donate action. */ public static final String DONATION_ADDRESS = "18CK5k1gajRKKSC7yVSTXT9LUzbheh1XY4"; /** Recipient e-mail address for reports. */ public static final String REPORT_EMAIL = "*****@*****.**"; /** Subject line for manually reported issues. */ public static final String REPORT_SUBJECT_ISSUE = "Reported issue"; /** Subject line for crash reports. */ public static final String REPORT_SUBJECT_CRASH = "Crash report"; public static final char CHAR_HAIR_SPACE = '\u200a'; public static final char CHAR_THIN_SPACE = '\u2009'; public static final char CHAR_ALMOST_EQUAL_TO = '\u2248'; public static final char CHAR_CHECKMARK = '\u2713'; public static final char CURRENCY_PLUS_SIGN = '\uff0b'; public static final char CURRENCY_MINUS_SIGN = '\uff0d'; public static final String PREFIX_ALMOST_EQUAL_TO = Character.toString(CHAR_ALMOST_EQUAL_TO) + CHAR_THIN_SPACE; public static final int ADDRESS_FORMAT_GROUP_SIZE = 4; public static final int ADDRESS_FORMAT_LINE_SIZE = 12; public static final MonetaryFormat LOCAL_FORMAT = new MonetaryFormat().noCode().minDecimals(2).optionalDecimals(); public static final BaseEncoding HEX = BaseEncoding.base16().lowerCase(); public static final String SOURCE_URL = "https://github.com/schildbach/bitcoin-wallet"; public static final String BINARY_URL = "https://github.com/schildbach/bitcoin-wallet/releases"; public static final String MARKET_APP_URL = "market://details?id=%s"; public static final String WEBMARKET_APP_URL = "https://play.google.com/store/apps/details?id=%s"; public static final int HTTP_TIMEOUT_MS = 15 * (int) DateUtils.SECOND_IN_MILLIS; public static final int PEER_TIMEOUT_MS = 15 * (int) DateUtils.SECOND_IN_MILLIS; public static final long LAST_USAGE_THRESHOLD_JUST_MS = DateUtils.HOUR_IN_MILLIS; public static final long LAST_USAGE_THRESHOLD_RECENTLY_MS = 2 * DateUtils.DAY_IN_MILLIS; public static final int SDK_JELLY_BEAN = 16; public static final int SDK_JELLY_BEAN_MR2 = 18; public static final int SDK_LOLLIPOP = 21; public static final int SDK_DEPRECATED_BELOW = Build.VERSION_CODES.ICE_CREAM_SANDWICH; public static final boolean BUG_OPENSSL_HEARTBLEED = Build.VERSION.SDK_INT == Constants.SDK_JELLY_BEAN && Build.VERSION.RELEASE.startsWith("4.1.1"); public static final int MEMORY_CLASS_LOWEND = 48; }
public class MainTabActivity extends TabActivity { private static final int DIALOG_LICENSEAGREEMENT = 0; private boolean tabsAdded; private static final String TAG_NORMAL = "normal"; private static final String TAG_ALL = "all"; private static final String TAG_FAVORITE = "favorite"; public static MainTabActivity INSTANCE; public static final boolean POSTGINGERBREAD = !Build.VERSION.RELEASE.startsWith("1") && !Build.VERSION.RELEASE.startsWith("2"); // this way around is future save private static Boolean LIGHTTHEME; public static boolean isLightTheme(Context context) { if (LIGHTTHEME == null) { LIGHTTHEME = PreferenceManager.getDefaultSharedPreferences(context) .getBoolean(Strings.SETTINGS_LIGHTTHEME, false); } return LIGHTTHEME; } private Menu menu; private BroadcastReceiver refreshReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { internalSetProgressBarIndeterminateVisibility(true); } }; private boolean hasContent; private boolean progressBarVisible; private Vector<String> visitedTabs; public void onCreate(Bundle savedInstanceState) { if (isLightTheme(this)) { setTheme(R.style.Theme_Light); } super.onCreate(savedInstanceState); // We need to display progress information requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.tabs); INSTANCE = this; hasContent = false; visitedTabs = new Vector<String>(3); if (getPreferences(MODE_PRIVATE).getBoolean(Strings.PREFERENCE_LICENSEACCEPTED, false)) { setContent(); } else { /* Workaround for android issue 4499 on 1.5 devices */ getTabHost() .addTab( getTabHost() .newTabSpec(Strings.EMPTY) .setIndicator(Strings.EMPTY) .setContent(new Intent(this, EmptyActivity.class))); showDialog(DIALOG_LICENSEAGREEMENT); } } @Override protected void onResume() { super.onResume(); internalSetProgressBarIndeterminateVisibility(isCurrentlyRefreshing()); registerReceiver(refreshReceiver, new IntentFilter("de.shandschuh.sparserss.REFRESH")); } @Override protected void onPause() { unregisterReceiver(refreshReceiver); super.onPause(); } @Override protected Dialog onCreateDialog(int id) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setIcon(android.R.drawable.ic_dialog_alert); builder.setTitle(R.string.dialog_licenseagreement); builder.setNegativeButton( R.string.button_decline, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); finish(); } }); builder.setPositiveButton( R.string.button_accept, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); Editor editor = getPreferences(MODE_PRIVATE).edit(); editor.putBoolean(Strings.PREFERENCE_LICENSEACCEPTED, true); editor.commit(); /* Part of workaround for android issue 4499 on 1.5 devices */ getTabHost().clearAllTabs(); /* we only want to invoke actions if the license is accepted */ setContent(); } }); setupLicenseText(builder); builder.setOnKeyListener( new OnKeyListener() { public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { dialog.cancel(); finish(); } return true; } }); return builder.create(); } @Override public boolean onCreateOptionsMenu(Menu menu) { this.menu = menu; Activity activity = getCurrentActivity(); if (hasContent && activity != null) { return activity.onCreateOptionsMenu(menu); } else { menu.add(Strings.EMPTY); // to let the menu be available return true; } } @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { Activity activity = getCurrentActivity(); if (hasContent && activity != null) { return activity.onMenuItemSelected(featureId, item); } else { return super.onMenuItemSelected(featureId, item); } } @Override public boolean onPrepareOptionsMenu(Menu menu) { Activity activity = getCurrentActivity(); if (hasContent && activity != null) { return activity.onPrepareOptionsMenu(menu); } else { return super.onPrepareOptionsMenu(menu); } } private void setContent() { TabHost tabHost = getTabHost(); tabHost.addTab( tabHost .newTabSpec(TAG_NORMAL) .setIndicator(getString(R.string.overview)) .setContent(new Intent().setClass(this, RSSOverview.class))); hasContent = true; if (PreferenceManager.getDefaultSharedPreferences(this) .getBoolean(Strings.SETTINGS_SHOWTABS, false)) { setTabWidgetVisible(true); } final MainTabActivity mainTabActivity = this; if (POSTGINGERBREAD) { /* Change the menu also on ICS when tab is changed */ tabHost.setOnTabChangedListener( new OnTabChangeListener() { public void onTabChanged(String tabId) { if (menu != null) { menu.clear(); onCreateOptionsMenu(menu); } SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(mainTabActivity).edit(); editor.putString(Strings.PREFERENCE_LASTTAB, tabId); editor.commit(); setCurrentTab(tabId); } }); if (menu != null) { menu.clear(); onCreateOptionsMenu(menu); } } else { tabHost.setOnTabChangedListener( new OnTabChangeListener() { @Override public void onTabChanged(String tabId) { setCurrentTab(tabId); } }); } } private void setCurrentTab(String currentTab) { if (visitedTabs.contains(currentTab)) { // requery the tab but only if it has been shown already Activity activity = getCurrentActivity(); if (hasContent && activity != null) { ((Requeryable) activity).requery(); } } else { visitedTabs.add(currentTab); } } public void setTabWidgetVisible(boolean visible) { if (visible) { TabHost tabHost = getTabHost(); if (!tabsAdded) { tabHost.addTab( tabHost .newTabSpec(TAG_ALL) .setIndicator(getString(R.string.all)) .setContent( new Intent(Intent.ACTION_VIEW, FeedData.EntryColumns.CONTENT_URI) .putExtra(EntriesListActivity.EXTRA_SHOWFEEDINFO, true))); tabHost.addTab( tabHost .newTabSpec(TAG_FAVORITE) .setIndicator( getString(R.string.favorites), getResources().getDrawable(android.R.drawable.star_big_on)) .setContent( new Intent(Intent.ACTION_VIEW, FeedData.EntryColumns.FAVORITES_CONTENT_URI) .putExtra(EntriesListActivity.EXTRA_SHOWFEEDINFO, true) .putExtra(EntriesListActivity.EXTRA_AUTORELOAD, true))); tabsAdded = true; } getTabWidget().setVisibility(View.VISIBLE); String lastTab = PreferenceManager.getDefaultSharedPreferences(this) .getString(Strings.PREFERENCE_LASTTAB, TAG_NORMAL); boolean tabFound = false; for (int i = 0; i < tabHost.getTabWidget().getChildCount(); ++i) { tabHost.setCurrentTab(i); String currentTab = tabHost.getCurrentTabTag(); if (lastTab.equals(currentTab)) { tabFound = true; break; } } if (!tabFound) { tabHost.setCurrentTab(0); } } else { getTabWidget().setVisibility(View.GONE); } } void setupLicenseText(AlertDialog.Builder builder) { View view = getLayoutInflater().inflate(R.layout.license, null); final TextView textView = (TextView) view.findViewById(R.id.license_text); textView.setTextColor( textView.getTextColors().getDefaultColor()); // disables color change on selection textView.setText( new StringBuilder(getString(R.string.license_intro)) .append(Strings.THREENEWLINES) .append(getString(R.string.license))); final TextView contributorsTextView = (TextView) view.findViewById(R.id.contributors_togglebutton); contributorsTextView.setOnClickListener( new OnClickListener() { boolean showingLicense = true; @Override public void onClick(View view) { if (showingLicense) { textView.setText(R.string.contributors_list); contributorsTextView.setText(R.string.license_word); } else { textView.setText( new StringBuilder(getString(R.string.license_intro)) .append(Strings.THREENEWLINES) .append(getString(R.string.license))); contributorsTextView.setText(R.string.contributors); } showingLicense = !showingLicense; } }); builder.setView(view); } private boolean isCurrentlyRefreshing() { ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (FetcherService.class.getName().equals(service.service.getClassName())) { return true; } } return false; } public void internalSetProgressBarIndeterminateVisibility(boolean progressBarVisible) { setProgressBarIndeterminateVisibility(progressBarVisible); this.progressBarVisible = progressBarVisible; Activity activity = getCurrentActivity(); if (activity != null) { activity.onPrepareOptionsMenu(null); } } public boolean isProgressBarVisible() { return progressBarVisible; } }
/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_NO_TITLE); getWindow() .setFlags( WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); // This builds the view. We could probably get away with NOT having a LinearLayout, but I like // having a bucket! LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0F); LinearLayout.LayoutParams webviewParams = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 1.0F); root = new LinearLayout(this); root.setOrientation(LinearLayout.VERTICAL); root.setBackgroundColor(Color.BLACK); root.setLayoutParams(containerParams); appView = new WebView(this); appView.setLayoutParams(webviewParams); WebViewReflect.checkCompatibility(); if (android.os.Build.VERSION.RELEASE.startsWith("2.")) appView.setWebChromeClient(new EclairClient(this)); else { appView.setWebChromeClient(new GapClient(this)); } appView.setInitialScale(100); appView.setVerticalScrollBarEnabled(false); WebSettings settings = appView.getSettings(); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); Package pack = this.getClass().getPackage(); String appPackage = pack.getName(); WebViewReflect.setStorage(settings, true, "/data/data/" + appPackage + "/app_database/"); // Disable cookies CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(false); // Turn on DOM storage! WebViewReflect.setDomStorage(settings); // Turn off native geolocation object in browser - we use our own :) WebViewReflect.setGeolocationEnabled(settings, true); /* Bind the appView object to the gap class methods */ bindBrowser(appView); if (cupcakeStorage != null) cupcakeStorage.setStorage(appPackage); root.addView(appView); setContentView(root); }