Merge pull request #18 from premnirmal/gfinance

re-enable google finance
This commit is contained in:
Prem
2016-03-28 16:52:44 -04:00
20 changed files with 223 additions and 181 deletions
+1 -6
View File
@@ -12,18 +12,13 @@ The main purpose of this app is to demonstrate usage of Dagger and RxJava
- Based off the [ministocks widget](https://github.com/niteshpatel/ministocks) but allowing unlimited tickers in a grid so you don't have to worry about sizing.
- Stocks can be sorted by dragging and dropping the list.
- Only performs automatic fetching of stocks during trading hours and weekdays.
- **Now has Graphs!** Using [android MPAndroidChart] (https://github.com/PhilJay/MPAndroidChart)
- **Now has Graphs!** Using [MPAndroidChart] (https://github.com/PhilJay/MPAndroidChart)
## Importing and exporting
- You can import a list of tickers by selecting **import tickers** from the settings menu. All you need is a textfile with your tickers in *comma-separated* format.
- You can also export your tickers to a file by selecting **export tickers**.
- You can set the font size in Settings.
## Future versions
*Future versions will include:*
- Ability to pick indices such as **^DJI**.
- Ability to change the app theme and colors
## License
MIT
@@ -7,7 +7,8 @@ class CrashLogger {
companion object {
@JvmStatic fun logException(throwable: Throwable) {
val exception: Exception = java.lang.Exception(throwable)
exception.printStackTrace()
}
}
}
@@ -81,4 +81,8 @@ public class Stock implements Comparable<Stock>, Serializable {
public String toString() {
return symbol;
}
public boolean isIndex() {
return symbol != null && symbol.startsWith("^");
}
}
@@ -1,54 +0,0 @@
package com.github.premnirmal.ticker.network;
import java.util.ArrayList;
import java.util.List;
/**
* Created by premnirmal on 3/17/15.
*/
class StockConverter {
static Stock convert(GStock gStock) {
final Stock stock = new Stock();
stock.symbol = gStock.t;
stock.Name = gStock.e; // TODO where to get name from?
stock.LastTradePriceOnly = gStock.lCur != null ? Float.parseFloat(gStock.lCur.replace(",","")) : 0f;
final double changePercent = Double.parseDouble(gStock.cp);
if(changePercent > 0) {
stock.ChangeinPercent = "+" + changePercent + "%";
} else {
stock.ChangeinPercent = gStock.cp + "%";
}
stock.Change = gStock.c;
stock.StockExchange = gStock.e != null ? gStock.e.replace("INDEX","") : "";
stock.AverageDailyVolume = "0";
stock.YearLow = 0.0f;
stock.YearHigh = 0.0f;
return stock;
}
static List<Stock> convertResponseQuotes(List<Stock> quotes) {
for (Stock quote : quotes) {
final String newSymbol = quote.symbol
.replace(".DJI", "^DJI")
.replace(".IXIC", "^IXIC");
quote.symbol = newSymbol;
}
return quotes;
}
static List<String> convertRequestSymbols(List<String> symbols) {
final List<String> newSymbols = new ArrayList<>();
for (String symbol : symbols) {
newSymbols.add(symbol
// .replace("^DJI", ".DJI")
// .replace("^IXIC", ".IXIC")
.replace("^","") // for symbols like ^SPY for yahoo
);
}
return newSymbols;
}
}
@@ -28,7 +28,7 @@ abstract class BaseActivity : AppCompatActivity() {
* Using this to automatically unsubscribe from observables on lifecycle events
*/
protected fun <T> bind(observable: Observable<T>): Observable<T> {
return observable.compose(RxLifecycle.bindActivity<T>(lifecycle()));
return observable.compose(RxLifecycle.bindActivity<T>(lifecycle()))
}
override fun attachBaseContext(newBase: Context) {
@@ -36,6 +36,8 @@ class Tools private constructor(private val sharedPreferences: SharedPreferences
@JvmField
val SETTING_AUTOSORT = "SETTING_AUTOSORT"
@JvmField
val ENABLE_GOOGLE_FINANCE = "ENABLE_GOOGLE_FINANCE"
@JvmField
val WIDGET_BG = "WIDGET_BG"
@JvmField
val TEXT_COLOR = "TEXT_COLOR"
@@ -256,5 +258,9 @@ class Tools private constructor(private val sharedPreferences: SharedPreferences
// if the user hasn't rated, try again on occasions
return (random.nextInt() % 2 == 0) && !hasUserAlreadyRated()
}
@JvmStatic fun googleFinanceEnabled(): Boolean {
return instance.sharedPreferences.getBoolean(Tools.ENABLE_GOOGLE_FINANCE, true)
}
}
}
@@ -32,7 +32,7 @@ import javax.inject.Singleton
class StocksProvider(private val api: StocksApi, private val bus: RxBus, private val context: Context, private val preferences: SharedPreferences) : IStocksProvider {
private val tickerList: MutableList<String>
private val stockList: MutableList<Stock> = ArrayList<Stock>()
private val stockList: MutableList<Stock> = ArrayList()
private val positionList: MutableList<Stock>
private var lastFetched: String? = null
private val storage: StocksStorage
@@ -45,7 +45,7 @@ class StocksProvider(private val api: StocksApi, private val bus: RxBus, private
for (ticker in tickerList) {
newTickerList.add(ticker.replace(".".toRegex(), ""))
}
tickerList.removeAll(_GOOGLE_SYMBOLS) // removed google finance because it's causing lots of problems, returning 400s
// tickerList.removeAll(_GOOGLE_SYMBOLS) // removed google finance because it's causing lots of problems, returning 400s
if (preferences.contains(STOCK_LIST)) {
// for users using older versions
val deprecatedTickerSet = preferences.getStringSet(STOCK_LIST, DEFAULT_SET)
@@ -75,30 +75,30 @@ class StocksProvider(private val api: StocksApi, private val bus: RxBus, private
if (!stockList.isEmpty()) {
sortStockList()
sendBroadcast()
removeGoogleStocks()
// removeGoogleStocks()
} else {
fetch()
}
}
private fun removeGoogleStocks() {
val dummy1 = Stock()
dummy1.symbol = "^DJI"
val dummy2 = Stock()
dummy2.symbol = "^IXIC"
val dummy3 = Stock()
dummy3.symbol = ".DJI"
val dummy4 = Stock()
dummy4.symbol = ".IXIC"
stockList.remove(dummy1)
stockList.remove(dummy2)
stockList.remove(dummy3)
stockList.remove(dummy4)
}
// private fun removeGoogleStocks() {
// val dummy1 = Stock()
// dummy1.symbol = "^DJI"
// val dummy2 = Stock()
// dummy2.symbol = "^IXIC"
// val dummy3 = Stock()
// dummy3.symbol = ".DJI"
// val dummy4 = Stock()
// dummy4.symbol = ".IXIC"
// stockList.remove(dummy1)
// stockList.remove(dummy2)
// stockList.remove(dummy3)
// stockList.remove(dummy4)
// }
private fun save() {
preferences.edit().remove(STOCK_LIST).putString(POSITION_LIST, Tools.positionsToString(positionList)).putString(SORTED_STOCK_LIST, Tools.toCommaSeparatedString(tickerList)).putString(LAST_FETCHED, lastFetched).apply()
removeGoogleStocks()
// removeGoogleStocks()
storage.save(stockList).subscribe(object : Subscriber<Boolean>() {
override fun onCompleted() {
@@ -7,6 +7,7 @@ import com.github.premnirmal.ticker.model.HistoryProvider
import com.github.premnirmal.ticker.model.IHistoryProvider
import com.github.premnirmal.ticker.model.IStocksProvider
import com.github.premnirmal.ticker.model.StocksProvider
import com.github.premnirmal.tickerwidget.BuildConfig
import com.github.premnirmal.tickerwidget.R
import com.squareup.okhttp.OkHttpClient
import dagger.Module
@@ -42,9 +43,9 @@ class ApiModule {
}
@Provides @Singleton
internal fun provideStocksApi(yahooFinance: YahooFinance): StocksApi {
internal fun provideStocksApi(yahooFinance: YahooFinance, googleFinance: GoogleFinance): StocksApi {
if (stocksApi == null) {
stocksApi = StocksApi(yahooFinance)
stocksApi = StocksApi(yahooFinance, googleFinance)
}
return stocksApi as StocksApi
}
@@ -54,21 +55,23 @@ class ApiModule {
val restAdapter = RestAdapter.Builder()
.setClient(okHttpClient)
.setEndpoint(context.getString(R.string.yahoo_endpoint))
.setLogLevel(if (BuildConfig.DEBUG) RestAdapter.LogLevel.FULL else RestAdapter.LogLevel.NONE)
.build()
val yahooFinance = restAdapter.create(YahooFinance::class.java)
return yahooFinance
}
// @Provides
// @Singleton
// GoogleFinance provideGoogleFinance(Context context) {
// final RestAdapter restAdapter = new RestAdapter.Builder()
// .setEndpoint(context.getString(R.string.google_endpoint))
// .setConverter(new GStockConverter())
// .build();
// final GoogleFinance googleFinance = restAdapter.create(GoogleFinance.class);
// return googleFinance;
// }
@Provides @Singleton
internal fun provideGoogleFinance(context: Context, okHttpClient: OkClient): GoogleFinance {
val restAdapter: RestAdapter = RestAdapter.Builder()
.setClient(okHttpClient)
.setEndpoint(context.getString(R.string.google_endpoint))
.setLogLevel(if (BuildConfig.DEBUG) RestAdapter.LogLevel.FULL else RestAdapter.LogLevel.NONE)
.setConverter(GStockConverter())
.build()
val googleFinance: GoogleFinance = restAdapter.create(GoogleFinance::class.java)
return googleFinance
}
@Provides @Singleton
internal fun provideSuggestionsApi(context: Context, okHttpClient: OkClient): SuggestionApi {
@@ -76,6 +79,7 @@ class ApiModule {
val restAdapter = RestAdapter.Builder()
.setClient(okHttpClient)
.setEndpoint(context.getString(R.string.suggestions_endpoint))
.setLogLevel(if (BuildConfig.DEBUG) RestAdapter.LogLevel.FULL else RestAdapter.LogLevel.NONE)
.setConverter(StupidYahooWrapConverter())
.build()
suggestionApi = restAdapter.create(SuggestionApi::class.java)
@@ -1,11 +1,9 @@
package com.github.premnirmal.ticker.network
import com.google.gson.reflect.TypeToken
import retrofit.converter.ConversionException
import retrofit.mime.TypedInput
import java.io.IOException
import java.lang.reflect.Type
import java.util.*
/**
* Created on 3/3/16.
@@ -18,18 +16,15 @@ internal class GStockConverter : BaseConverter() {
val bodyString = getString(body.`in`()).replace("\n".toRegex(), "")
val responseString: String
if (bodyString.startsWith("//")) {
responseString = bodyString.substring(2, bodyString.length)
responseString = bodyString.substring(bodyString.indexOf('['), bodyString.lastIndexOf(']') + 1)
} else {
responseString = bodyString
}
val collectionType = object : TypeToken<List<GStock>>() {
}.type
val stocks = gson.fromJson<List<GStock>>(responseString, collectionType)
val stocks = gson.fromJson<List<GStock>>(responseString, type)
return stocks
} catch (e: IOException) {
e.printStackTrace()
return ArrayList<GStock>()
throw ConversionException(e)
}
}
@@ -0,0 +1,57 @@
package com.github.premnirmal.ticker.network
import java.util.*
/**
* Created by premnirmal on 3/21/16.
*/
internal object StockConverter {
fun convert(gStock: GStock): Stock {
val stock = Stock()
val name = if (gStock.e != null) gStock.e.replace("INDEX", "") else ""
stock.symbol = gStock.t
stock.Name = name
stock.LastTradePriceOnly =
if (gStock.lCur != null)
(gStock.lCur.replace(",", "")).toFloat()
else
0f
val changePercent = gStock.cp.toDouble()
if (changePercent > 0) {
stock.ChangeinPercent = "+$changePercent%"
} else {
stock.ChangeinPercent = "${gStock.cp}%"
}
stock.Change = gStock.c
stock.StockExchange = name
stock.AverageDailyVolume = "0"
stock.YearLow = 0.0f
stock.YearHigh = 0.0f
return stock
}
fun convertResponseQuotes(quotes: List<Stock>): List<Stock> {
for (quote in quotes) {
val newSymbol = quote.symbol.replace(".", "^")
quote.symbol = newSymbol
}
return quotes
}
fun convertRequestSymbols(symbols: List<String>): ArrayList<String> {
val newSymbols = ArrayList<String>()
for (symbol in symbols) {
newSymbols.add(symbol
.replace("^DJI", ".DJI")
.replace("^IXIC", ".IXIC")
.replace("^SPY", "SPY") // for symbols like ^SPY for yahoo
.replace("^", ".")
)
}
return newSymbols
}
}
@@ -1,58 +1,22 @@
package com.github.premnirmal.ticker.network
import com.github.premnirmal.ticker.model.StocksProvider
import com.github.premnirmal.ticker.CrashLogger
import com.github.premnirmal.ticker.Tools
import com.github.premnirmal.ticker.network.historicaldata.HistoricalData
import rx.Observable
import rx.functions.Func2
import java.util.*
/**
* Created on 3/3/16.
*/
class StocksApi(internal val yahooApi: YahooFinance) {
// this.googleApi = googleApi;
// final GoogleFinance googleApi;
class StocksApi(internal val yahooApi: YahooFinance, internal val googleApi: GoogleFinance) {
var lastFetched: String? = null
fun getYahooFinanceStocks(query: String): Observable<StockQuery> {
return yahooApi.getStocks(query)
}
// public Observable<List<Stock>> getGoogleFinanceStocks(String query) {
// return googleApi.getStock(query)
// .map(new Func1<List<GStock>, List<Stock>>() {
// @Override
// public List<Stock> call(List<GStock> gStocks) {
// final List<Stock> stocks = new ArrayList<Stock>();
// for (GStock gStock : gStocks) {
// stocks.add(StockConverter.convert(gStock));
// }
// final List<Stock> updatedStocks = StockConverter.convertResponseQuotes(stocks);
// return updatedStocks;
// }
// }).onErrorResumeNext(new Func1<Throwable, Observable<? extends List<Stock>>>() {
// @Override
// public Observable<? extends List<Stock>> call(Throwable throwable) {
// Crashlytics.logException(new RuntimeException("Encountered onErrorResumeNext", throwable));
// return Observable.empty();
// }
// });
// }
fun getHistory(query: String): Observable<HistoricalData> {
return yahooApi.getHistory(query)
}
fun getStocks(tickerList: List<String>): Observable<List<Stock>> {
val symbols = StockConverter.convertRequestSymbols(tickerList)
val yahooSymbols = ArrayList(symbols)
// final List<String> googleSymbols = new ArrayList<>(symbols);
yahooSymbols.removeAll(StocksProvider.GOOGLE_SYMBOLS)
yahooSymbols.removeAll(StocksProvider._GOOGLE_SYMBOLS)
// googleSymbols.retainAll(StocksProvider.GOOGLE_SYMBOLS);
// final Observable<List<Stock>> googleObservable = getGoogleFinanceStocks(QueryCreator.googleStocksQuery(googleSymbols.toArray()));
val yahooObservable = getYahooFinanceStocks(QueryCreator.buildStocksQuery(yahooSymbols.toArray())).map { stockQuery ->
fun getYahooFinanceStocks(tickers: Array<Any>): Observable<List<Stock>> {
val query = QueryCreator.buildStocksQuery(tickers)
return yahooApi.getStocks(query).map({ stockQuery ->
if (stockQuery == null) {
ArrayList()
} else {
@@ -60,21 +24,57 @@ class StocksApi(internal val yahooApi: YahooFinance) {
lastFetched = query.created
query.results.quote
}
})
}
fun getGoogleFinanceStocks(tickers: Array<Any>): Observable<List<Stock>> {
val query = QueryCreator.googleStocksQuery(tickers)
return googleApi.getStock(query).map({ gStocks ->
val stocks = ArrayList<Stock>()
for (gStock in gStocks) {
stocks.add(StockConverter.convert(gStock))
}
val updatedStocks = StockConverter.convertResponseQuotes(stocks)
updatedStocks
}).onErrorReturn({ throwable ->
CrashLogger.logException(throwable)
ArrayList<Stock>()
})
}
fun getHistory(query: String): Observable<HistoricalData> {
return yahooApi.getHistory(query)
}
fun getStocks(tickerList: List<String>): Observable<List<Stock>> {
val symbols = StockConverter.convertRequestSymbols(tickerList)
if (!Tools.googleFinanceEnabled()) {
val yahooObservable = getYahooFinanceStocks(symbols.toArray())
return yahooObservable
} else {
val yahooSymbols = ArrayList<String>()
val googleSymbols = ArrayList<String>()
for (symbol: String in symbols) {
if (symbol.startsWith("^") || symbol.startsWith(".")) {
googleSymbols.add(symbol.replace("^", "."))
} else {
yahooSymbols.add(symbol)
}
}
val yahooObservable = getYahooFinanceStocks(yahooSymbols.toArray())
if (googleSymbols.isEmpty()) {
return yahooObservable
} else {
val googleObservable = getGoogleFinanceStocks(googleSymbols.toArray())
val allStocks = Observable.zip(yahooObservable, googleObservable, { stocks, stocks2 ->
val zipped: MutableList<Stock> = ArrayList()
zipped.addAll(stocks2)
zipped.addAll(stocks)
zipped as List<Stock>
})
return allStocks
}
}
// .onErrorResumeNext { throwable ->
// CrashLogger.logException(RuntimeException("Encountered onErrorResumeNext for yahooFinance", throwable))
// Observable.empty<List<Stock>>()
// }
// final Observable<List<Stock>> allStocks = yahooObservable.zipWith(googleObservable, new Func2<List<Stock>, List<Stock>, List<Stock>>() {
// @Override
// public List<Stock> call(List<Stock> stocks, List<Stock> stocks2) {
// stocks.addAll(stocks2);
// return stocks;
// }
// });
return yahooObservable
}
}
@@ -20,7 +20,7 @@ internal class StupidYahooWrapConverter : BaseConverter() {
val suggestions = gson.fromJson(m.group(1), Suggestions::class.java)
return suggestions
}
throw RuntimeException("Invalid response")
throw ConversionException("Invalid response")
} catch (e: IOException) {
e.printStackTrace()
return null
@@ -20,7 +20,7 @@ internal class StockVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
val position = adapterPosition
itemView.findViewById(R.id.stockContainer).setOnClickListener { listener.onClick(stock) }
itemView.findViewById(R.id.stockContainer).setOnClickListener { if (!stock.isIndex) listener.onClick(stock) }
itemView.findViewById(R.id.trash).setOnClickListener { v -> listener.onRemoveClick(v, stock, position) }
val swipeLayout = itemView as SwipeLayout
@@ -52,7 +52,8 @@ internal class StockVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
changeInPercent.setText(Tools.DECIMAL_FORMAT.format(changePercentVal))
val changeValue = itemView.findViewById(R.id.changeValue) as StockFieldView
changeValue.setText(Tools.DECIMAL_FORMAT.format(changeVal))
setStockFieldText(itemView, R.id.totalValue, Tools.DECIMAL_FORMAT.format(stock.LastTradePriceOnly))
val totalValueText = itemView.findViewById(R.id.totalValue) as TextView
totalValueText.text = Tools.DECIMAL_FORMAT.format(stock.LastTradePriceOnly)
val color: Int
if (change >= 0) {
@@ -78,13 +79,20 @@ internal class StockVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
setStockFieldText(itemView, R.id.yearLow, Tools.DECIMAL_FORMAT.format(stock.LastTradePriceOnly - stock.PositionPrice))
} else {
setStockFieldLabel(itemView, R.id.averageDailyVolume, "Daily Volume")
setStockFieldText(itemView, R.id.averageDailyVolume, "${stock.AverageDailyVolume}")
setStockFieldLabel(itemView, R.id.exchange, "Exchange")
setStockFieldText(itemView, R.id.exchange, "${stock.StockExchange}")
setStockFieldLabel(itemView, R.id.yearHigh, "Year High")
setStockFieldText(itemView, R.id.yearHigh, Tools.DECIMAL_FORMAT.format(stock.YearHigh))
setStockFieldLabel(itemView, R.id.yearLow, "Year Low")
setStockFieldText(itemView, R.id.yearLow, Tools.DECIMAL_FORMAT.format(stock.YearLow))
setStockFieldText(itemView, R.id.exchange, "${stock.StockExchange}")
val isIndex = stock.symbol.startsWith("^")
if (isIndex) {
setStockFieldText(itemView, R.id.averageDailyVolume, "NA")
setStockFieldText(itemView, R.id.yearHigh, "NA")
setStockFieldText(itemView, R.id.yearLow, "NA")
} else {
setStockFieldText(itemView, R.id.averageDailyVolume, "${stock.AverageDailyVolume}")
setStockFieldText(itemView, R.id.yearHigh, Tools.DECIMAL_FORMAT.format(stock.YearHigh))
setStockFieldText(itemView, R.id.yearLow, Tools.DECIMAL_FORMAT.format(stock.YearLow))
}
}
}
@@ -87,13 +87,15 @@ class TickerSelectorActivity : BaseActivity() {
if (!stocksProvider.getTickers().contains(ticker)) {
stocksProvider.addStock(ticker)
InAppMessage.showMessage(this@TickerSelectorActivity, ticker + " added to list")
showDialog("Do you want to add positions for $ticker?",
DialogInterface.OnClickListener { dialog, which ->
val intent = Intent(this@TickerSelectorActivity, AddPositionActivity::class.java)
intent.putExtra(EditPositionActivity.TICKER, ticker)
startActivity(intent)
},
DialogInterface.OnClickListener { dialog, which -> dialog.dismiss() })
if (!ticker.startsWith("^")) { // don't allow positions for indices
showDialog("Do you want to add positions for $ticker?",
DialogInterface.OnClickListener { dialog, which ->
val intent = Intent(this@TickerSelectorActivity, AddPositionActivity::class.java)
intent.putExtra(EditPositionActivity.TICKER, ticker)
startActivity(intent)
},
DialogInterface.OnClickListener { dialog, which -> dialog.dismiss() })
}
} else {
showDialog("${ticker} is already in your portfolio")
}
@@ -124,6 +124,21 @@ class SettingsActivity : PreferenceActivity(), ActivityCompat.OnRequestPermissio
// Add 'general' preferences.
addPreferencesFromResource(R.xml.prefs)
run({
val gStockPreference = findPreference(Tools.ENABLE_GOOGLE_FINANCE) as CheckBoxPreference
val enable = Tools.googleFinanceEnabled()
gStockPreference.isChecked = enable
gStockPreference.onPreferenceChangeListener = object : DefaultPreferenceChangeListener() {
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
super.onPreferenceChange(preference, newValue)
val checked = newValue as Boolean
preferences.edit().putBoolean(Tools.ENABLE_GOOGLE_FINANCE, checked).apply()
return true
}
}
})
run({
val exportPref = findPreference("EXPORT")
exportPref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
@@ -88,14 +88,16 @@
android:layout_weight="0.6"
android:layout_height="wrap_content">
<com.github.premnirmal.ticker.ui.StockFieldView
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:textColor="@android:color/white"
tools:text="555.02"
style="@style/BoldTextView"
android:layout_marginBottom="5dp"
android:singleLine="true"
android:textSize="@dimen/large_text"
sfw:name="Price"
sfw:size="@dimen/large_text"
android:id="@+id/totalValue" />
<com.github.premnirmal.ticker.ui.StockFieldView
+4 -2
View File
@@ -15,8 +15,8 @@
<string name="action_rearrage">Rearrange Stocks</string>
<string name="action_update">Refresh Now</string>
<string name="yahoo_endpoint">http://query.yahooapis.com/v1/public</string>
<string name="google_endpoint">http://finance.google.com/finance</string>
<string name="yahoo_endpoint">https://query.yahooapis.com/v1/public</string>
<string name="google_endpoint">https://finance.google.com/finance</string>
<string name="suggestions_endpoint">https://s.yimg.com/aq</string>
<string name="add_ticker">Add Ticker</string>
<string name="dotdotdot"></string>
@@ -71,6 +71,8 @@
<string name="please_rate">Please rate us on the PlayStore, we would love to hear what you have to say!</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="enable_google_finance">Enable Google Finance?</string>
<string name="enable_google_finance_desc">Enabling the Google Finance api allows you to get quotes for tickers indices as ^DJI, but isn\'t robust</string>
<string-array name="graph_or_positions">
<item>View graph</item>
+2 -3
View File
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="whats_new">
<item>UI tweaks</item>
<item>Fixed some widget bugs</item>
<item>Please rate the app in the PlayStore!</item>
<item>Now you can add indices such as ^DJI</item>
<item>Please rate StockTicker in the PlayStore!</item>
</string-array>
</resources>
+6
View File
@@ -67,6 +67,12 @@
android:summary="@string/bold_change_desc"
android:defaultValue="false" />
<CheckBoxPreference
android:key="ENABLE_GOOGLE_FINANCE"
android:title="@string/enable_google_finance"
android:summary="@string/enable_google_finance_desc"
android:defaultValue="true" />
<Preference
android:key="SHARE"
android:title="@string/action_share"
+2 -2
View File
@@ -1,2 +1,2 @@
versionName=1.20.36
versionCode=96
versionName=1.30.01
versionCode=98