diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6ad9481 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jeremy Shore + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..55ea432 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# MarketBot +This is an open source Android application that allows the user to search the market, along with buy and sell orders. + +It is quick and responsive, so you don't have to worry about that slow SDE bloating the APK. + +I used Android Material design standards and made sure it uses as many transitions as possible to make it a smooth and enjoyable experience. + +Make sure to tap on the market orders, because they open to give you more info, like range and location. + +# Future +I have also been working on adding it a "bot" feature that tracks specific items in specific regions of space and notify you when the price of buy or sell orders pass your threshold. Along with that I added network caching so you can still use quite a bit of it in offline mode, and working on building it into the database so it if quicker and requires internet only for specific calls to update info or ones not stored. diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f24c076 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,48 @@ +apply plugin: 'com.android.application' +apply plugin: 'com.neenbedankt.android-apt' + +android { + compileSdkVersion 'android-N' + buildToolsVersion "23.0.2" + + defaultConfig { + applicationId "com.w9jds.marketbot" + minSdkVersion 21 + minSdkVersion 'N' + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + + debugCompile 'com.facebook.stetho:stetho:1.1.1' + debugCompile 'com.facebook.stetho:stetho-okhttp:1.1.1' + debugCompile 'com.facebook.stetho:stetho-urlconnection:1.1.1' + + compile 'com.android.support:support-v4:23.2.0' + compile 'com.android.support:palette-v7:23.2.0' + compile 'com.android.support:appcompat-v7:23.2.0' + compile 'com.android.support:cardview-v7:23.2.0' + compile 'com.android.support:multidex:1.0.1' + compile 'com.android.support:recyclerview-v7:23.2.0' + compile 'com.android.support:design:23.2.0' + + apt 'com.google.dagger:dagger-compiler:2.0.2' + compile 'com.google.dagger:dagger:2.0.2' + provided 'org.glassfish:javax.annotation:10.0-b28' + + compile 'org.mod4j.org.apache.commons:lang:2.1.0' + compile 'com.jakewharton:butterknife:7.0.1' + compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' + compile 'com.github.bumptech.glide:glide:3.7.0' + compile project(path: ':eveapi') +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..32cdb4a --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/JeremyShore/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/com/w9jds/marketbot/ApplicationTest.java b/app/src/androidTest/java/com/w9jds/marketbot/ApplicationTest.java new file mode 100644 index 0000000..c39f197 --- /dev/null +++ b/app/src/androidTest/java/com/w9jds/marketbot/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.w9jds.marketbot; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..34d69ff --- /dev/null +++ b/app/src/debug/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/java/com/w9jds/marketbot/DebugMarketBot.java b/app/src/debug/java/com/w9jds/marketbot/DebugMarketBot.java new file mode 100644 index 0000000..f0bdccc --- /dev/null +++ b/app/src/debug/java/com/w9jds/marketbot/DebugMarketBot.java @@ -0,0 +1,23 @@ +package com.w9jds.marketbot; + +import android.app.Application; + +import com.facebook.stetho.Stetho; + +/** + * Created by w9jds on 3/13/2016. + */ +public class DebugMarketBot extends Application { + + @Override + public void onCreate() { + super.onCreate(); + + Stetho.initialize( + Stetho.newInitializerBuilder(this) + .enableDumpapp(Stetho.defaultDumperPluginsProvider(this)) + .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(this)) + .build()); + + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ba571ce --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/w9jds/marketbot/activities/ItemActivity.java b/app/src/main/java/com/w9jds/marketbot/activities/ItemActivity.java new file mode 100644 index 0000000..0028c5a --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/activities/ItemActivity.java @@ -0,0 +1,155 @@ +package com.w9jds.marketbot.activities; + +import android.os.Bundle; +import android.support.design.widget.TabLayout; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Spinner; + +import com.w9jds.eveapi.Models.MarketItemBase; +import com.w9jds.eveapi.Models.Region; +import com.w9jds.eveapi.Models.Type; +import com.w9jds.marketbot.R; +import com.w9jds.marketbot.adapters.OrdersTabAdapter; +import com.w9jds.marketbot.adapters.RegionAdapter; +import com.w9jds.marketbot.data.BaseDataManager; +import com.w9jds.marketbot.data.DataManager; +import com.w9jds.marketbot.ui.fragments.OrdersTab; + +import java.util.HashMap; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jeremy Shore on 2/22/16. + */ +public class ItemActivity extends AppCompatActivity implements BaseDataManager.DataLoadingCallbacks, AdapterView.OnItemSelectedListener { + + @Bind(R.id.region_spinner) + Spinner regionSpinner; + + @Bind(R.id.content_pager) + ViewPager pager; + + @Bind(R.id.sliding_tabs) + TabLayout tabLayout; + + private ActionBar actionBar; + private DataManager dataManager; + private Type currentType; + private RegionAdapter regionAdapter; + private OrdersTabAdapter tabAdapter; + + private HashMap orderTabs = new HashMap<>(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_market_item); + ButterKnife.bind(this); + + Toolbar toolbar = ButterKnife.findById(this, R.id.main_toolbar); + + setSupportActionBar(toolbar); + actionBar = getSupportActionBar(); + + if (actionBar != null) { + actionBar.setDisplayShowTitleEnabled(false); + actionBar.setDisplayShowHomeEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + } + + currentType = getIntent().getParcelableExtra("currentType"); + regionAdapter = new RegionAdapter(this); +// dataManager = new DataManager(this, getApplication()) { +// @Override +// public void onDataLoaded(List data) { +// if (data.size() > 0) { +// if (data.get(0) instanceof Region) { +// regionAdapter.addAllItems(data); +// +// regionSpinner.setSelection(regionAdapter.getPositionfromId(10000002), true); +// } +// } +// } +// +// @Override +// public void onDataLoaded(Object data) { +// // never fired +// } +// }; + + regionSpinner.setAdapter(regionAdapter); + regionSpinner.setOnItemSelectedListener(this); + + tabAdapter = new OrdersTabAdapter(getSupportFragmentManager(), this, currentType.getId()); + pager.setOffscreenPageLimit(4); + pager.setAdapter(tabAdapter); + tabLayout.setupWithViewPager(pager); + + tabLayout.setTabsFromPagerAdapter(tabAdapter); + + dataManager.loadRegions(); + } + + public String getCurrentTypeIcon() { + return currentType.getIconLink(); + } + + public String getCurrentTypeName() { + return currentType.getName(); + } + + @Override + public boolean onNavigateUp() { + finishAfterTransition(); + return true; + } + + @Override + public void dataStartedLoading() { + + } + + @Override + public void dataFinishedLoading() { + + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + Region region = (Region) regionAdapter.getItem(position); + + for (OrdersTab tab : orderTabs.values()) { + tab.updateOrdersList(region, currentType); + } + } + + public void addOrdersFragment(int position, OrdersTab tab) { + orderTabs.put(position, tab); + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/activities/MainActivity.java b/app/src/main/java/com/w9jds/marketbot/activities/MainActivity.java new file mode 100644 index 0000000..2a35530 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/activities/MainActivity.java @@ -0,0 +1,252 @@ +package com.w9jds.marketbot.activities; + +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.w9jds.eveapi.Models.MarketGroup; +import com.w9jds.eveapi.Models.MarketItemBase; +import com.w9jds.marketbot.R; +import com.w9jds.marketbot.adapters.MarketGroupsAdapter; +import com.w9jds.marketbot.classes.MarketBot; +import com.w9jds.marketbot.data.BaseDataManager; +import com.w9jds.marketbot.data.DataManager; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class MainActivity extends AppCompatActivity implements MarketGroupsAdapter.onMarketGroupChanged, + BaseDataManager.DataLoadingCallbacks{ + + @Bind(R.id.market_groups) + RecyclerView recyclerView; + + @Bind(R.id.dataloading_progress) + ProgressBar progressBar; + + @Bind(R.id.toolbar) + Toolbar toolbar; + + private ActionBar actionBar; + private DataManager dataManager; + private MarketGroupsAdapter adapter; + private LinearLayoutManager layoutManager; + + private ArrayList marketGroupList; + private MarketGroup currentParent; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + + ((MarketBot)getApplication()).getStorageComponent().inject(this); + + + setSupportActionBar(toolbar); + actionBar = getSupportActionBar(); + + if (actionBar != null) { + // needs to be space so toolbar doesn't remove it from the view hierarchy + actionBar.setTitle(" "); + } + + layoutManager = new LinearLayoutManager(this); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setItemAnimator(new DefaultItemAnimator()); + +// dataManager = new DataManager(this, getApplication()) { +// @Override +// public void onDataLoaded(List data) { +// if (marketGroupList == null) { +// marketGroupList = new ArrayList<>(data); +// } +// +// adapter.addAndResort(data); +// } +// +// @Override +// public void onDataLoaded(Object data) { +// // never fired +// } +// }; + + dataManager.registerCallback(this); + + adapter = new MarketGroupsAdapter(this, dataManager, this); + recyclerView.setAdapter(adapter); + + dataManager.loadMarketGroups(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void updateSelectedParentGroup(MarketGroup group) { + currentParent = group; + + if (group.items.size() > 0) { + adapter.updateCollection(group.items.values()); + } + + if (currentParent != null) { + if (group.children.size() > 0) { + if (group.items.size() > 0) { + adapter.addAndResort(group.children.values()); + } + else { + adapter.clear(); + adapter.updateCollection(group.children.values()); + } + } else { + if (group.items.size() < 1) { + adapter.clear(); + } + + dataManager.loadGroupTypes(currentParent.getTypesLocation()); + } + } + + + animateTitleChange(); +// showToolbar(); + recyclerView.smoothScrollToPosition(0); + } + + /** + * Since we don't have access ot the views inside the toolbar we need to find the title view + * @return Toolbar Title + */ + private View getToolbarTitle() { + + for (int i = 0; i < toolbar.getChildCount(); i++) { + View child = toolbar.getChildAt(i); + if (child instanceof TextView) { + return child; + } + } + + return new View(this); + } + + private void animateTitleChange() { + final View view = getToolbarTitle(); + + if (view instanceof TextView) { + AlphaAnimation fadeOut = new AlphaAnimation(1f, 0f); + fadeOut.setDuration(250); + fadeOut.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + + } + + @Override + public void onAnimationEnd(Animation animation) { + if (currentParent != null) { + actionBar.setTitle(currentParent.getName()); + actionBar.setDisplayShowHomeEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + } + else { + actionBar.setTitle(" "); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(false); + } + + AlphaAnimation fadeIn = new AlphaAnimation(0f, 1f); + fadeIn.setDuration(250); + view.startAnimation(fadeIn); + } + + @Override + public void onAnimationRepeat(Animation animation) { + + } + }); + + view.startAnimation(fadeOut); + } + } + + @Override + public void onBackPressed() { + if (!dataManager.isDataLoading()) { + if (currentParent != null && currentParent.hasParent()) { + bfsForParent(currentParent.getParentGroupId()); + adapter.updateCollection(currentParent.children.values()); + } else if (currentParent != null) { + adapter.updateCollection(marketGroupList); + currentParent = null; + } else { + super.onBackPressed(); + } + + animateTitleChange(); + } + } + + @Override + public void dataStartedLoading() { + if (adapter.getAdapterSize() < 1) { + progressBar.setVisibility(View.VISIBLE); + } + } + + @Override + public void dataFinishedLoading() { + progressBar.setVisibility(View.GONE); + } + + private void bfsForParent(long parentId) { + boolean parentFound = false; + Queue queue = new LinkedList(); + + for (MarketItemBase group : marketGroupList) { + if (parentFound) { + break; + } + + queue.add(group); + + while(!queue.isEmpty()) { + MarketGroup node = (MarketGroup)queue.remove(); + if (node.getId() == parentId) { + currentParent = node; + parentFound = true; + queue.clear(); + } + else { + for (MarketGroup child : node.children.values()) { + queue.add(child); + } + } + } + } + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/adapters/MarketGroupsAdapter.java b/app/src/main/java/com/w9jds/marketbot/adapters/MarketGroupsAdapter.java new file mode 100644 index 0000000..01cd29d --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/adapters/MarketGroupsAdapter.java @@ -0,0 +1,257 @@ +package com.w9jds.marketbot.adapters; + +import android.app.Activity; +import android.app.ActivityOptions; +import android.content.Intent; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v7.widget.RecyclerView; +import android.util.Pair; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.w9jds.eveapi.Models.MarketGroup; +import com.w9jds.eveapi.Models.MarketItemBase; +import com.w9jds.eveapi.Models.Type; +import com.w9jds.marketbot.R; +import com.w9jds.marketbot.activities.ItemActivity; +import com.w9jds.marketbot.data.DataLoadingSubject; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jeremy on 2/18/2016. + */ +public final class MarketGroupsAdapter extends RecyclerView.Adapter { + + private final int MARKET_GROUP_VIEW = 0; + private final int MARKET_TYPE_VIEW = 1; + + + private final Activity host; + private final LayoutInflater layoutInflater; + private final @Nullable DataLoadingSubject dataLoading; + + private onMarketGroupChanged groupChangedListener; + private List items = new ArrayList<>(); + + public interface onMarketGroupChanged { + void updateSelectedParentGroup(MarketGroup group); + } + + public MarketGroupsAdapter(Activity host, DataLoadingSubject dataLoading, onMarketGroupChanged changed) { + this.groupChangedListener = changed; + this.host = host; + this.layoutInflater = LayoutInflater.from(host); + this.dataLoading = dataLoading; + this.items = new ArrayList<>(); + } + + public static class MarketGroupHolder extends RecyclerView.ViewHolder { + + @Bind(R.id.title) TextView title; + @Bind(R.id.subtitle) TextView subtitle; + + public MarketGroupHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + + } + } + + public static class MarketTypeHolder extends RecyclerView.ViewHolder { + + @Bind(R.id.item_image) + ImageView image; + @Bind(R.id.item_title) + TextView title; +// @Bind(R.id.item_subtitle) +// TextView subtitle; + + public MarketTypeHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch(viewType) { + case MARKET_GROUP_VIEW: + return createMarketGroupHolder(parent); + case MARKET_TYPE_VIEW: + return createMarketTypeHolder(parent); + } + + return null; + } + + private MarketGroupHolder createMarketGroupHolder(ViewGroup parent) { + final MarketGroupHolder holder = new MarketGroupHolder(layoutInflater.inflate( + R.layout.threeline_item_layout, parent, false)); + + holder.itemView.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + MarketGroup item = (MarketGroup) getItem(holder.getAdapterPosition()); + groupChangedListener.updateSelectedParentGroup(item); + } + } + ); + + return holder; + } + + private MarketTypeHolder createMarketTypeHolder(ViewGroup parent) { + final MarketTypeHolder holder = new MarketTypeHolder(layoutInflater.inflate( + R.layout.type_item_layout, parent, false)); + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Type type = (Type) getItem(holder.getAdapterPosition()); + + final Intent intent = new Intent(); + intent.setClass(host, ItemActivity.class); + intent.putExtra("currentType", type); + + final ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(host, + new Pair(host.findViewById(R.id.app_bar), host.getString(R.string.toolbar_transition_name)), + new Pair(holder.image, host.getString(R.string.type_icon_transition)), + new Pair(holder.title, host.getString(R.string.type_name_transition))); + + ActivityCompat.startActivity(host, intent, options.toBundle()); +// host.startActivity(intent); + } + }); + + return holder; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + switch(getItemViewType(position)) { + case MARKET_GROUP_VIEW: + bindMarketGroup((MarketGroup) getItem(position), (MarketGroupHolder) holder); + break; + case MARKET_TYPE_VIEW: + bindMarketType((Type) getItem(position), (MarketTypeHolder) holder); + break; + } + + } + + @Override + public int getItemViewType(int position) { + if (getItem(position) instanceof MarketGroup) { + return MARKET_GROUP_VIEW; + } + else { + return MARKET_TYPE_VIEW; + } + } + + @Override + public int getItemCount() { + return items.size(); + } + + public Object getItem(int position) { + return items.get(position); + } + + public int getAdapterSize() { + return items.size(); + } + + private void bindMarketGroup(final MarketGroup group, MarketGroupHolder holder) { + holder.title.setText(group.getName()); + + holder.subtitle.setVisibility(View.VISIBLE); + if (group.getDescription().equals("")) { + holder.subtitle.setVisibility(View.GONE); + } + else { + holder.subtitle.setText(group.getDescription()); + } + } + + private void bindMarketType(final Type type, MarketTypeHolder holder) { + holder.title.setText(type.getName()); + + Glide.with(host) + .load(type.getIconLink()) +// .placeholder(R.drawable.ic_profile_none) + .into(holder.image); + } + + public void addAndResort(Collection newItems) { + ArrayList groups = new ArrayList<>(newItems); + groups.addAll(items); + int oldSize = items.size(); + Collections.sort(groups, new Comparitor()); + + items = groups; + + if (items.size() > 0) { + notifyItemRangeChanged(0, oldSize); + } + + notifyItemRangeInserted(oldSize, items.size() - oldSize); + } + + public void updateCollection(Collection newChildren) { + ArrayList groups = new ArrayList<>(newChildren); + Collections.sort(groups, new Comparitor()); + + int newSize = groups.size(); + int oldSize = items.size(); + + items = groups; + + if (newSize < oldSize) { + notifyItemRangeRemoved(newSize, oldSize - newSize); + notifyItemRangeChanged(0, newSize); + } + if (newSize == oldSize) { + notifyItemRangeChanged(0, newSize); + } + if (newSize > oldSize) { + notifyItemRangeChanged(0, oldSize); + notifyItemRangeInserted(oldSize, newSize - oldSize); + } + } + + public void clear() { + int oldSize = items.size(); + + items.clear(); + notifyItemRangeRemoved(0, oldSize); + } + + private class Comparitor implements Comparator { + @Override + public int compare(MarketItemBase lhs, MarketItemBase rhs) { + if (lhs instanceof MarketGroup && rhs instanceof Type) { + return -1; + } + if (lhs instanceof Type && rhs instanceof MarketGroup) { + return 1; + } + + return lhs.getName().compareTo(rhs.getName()); + } + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/adapters/OrdersAdapter.java b/app/src/main/java/com/w9jds/marketbot/adapters/OrdersAdapter.java new file mode 100644 index 0000000..bfb0f35 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/adapters/OrdersAdapter.java @@ -0,0 +1,280 @@ +package com.w9jds.marketbot.adapters; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; +import android.widget.TextView; + +import com.w9jds.eveapi.Models.MarketGroup; +import com.w9jds.eveapi.Models.MarketItemBase; +import com.w9jds.eveapi.Models.MarketOrder; +import com.w9jds.marketbot.R; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jeremy on 3/1/2016. + */ +public final class OrdersAdapter extends RecyclerView.Adapter { + + Context context; + ArrayList orders = new ArrayList<>(); + + public OrdersAdapter(Context context) { + this.context = context; + } + + static class OrderViewHolder extends RecyclerView.ViewHolder { + + @Bind(R.id.inventory) + TextView volume; + @Bind(R.id.price) + TextView price; + @Bind(R.id.location) + TextView location; + @Bind(R.id.range) + TextView range; + @Bind(R.id.details_pane) + View details; +// @Bind(R.id.sales_progress) +// View salesProgress; + @Bind(R.id.range_container) + View rangeContainer; + + boolean isOpen; + boolean animationRunning; + + public OrderViewHolder(View view) { + super(view); + ButterKnife.bind(this, view); + } + } + +// private void animateSalesProgressBar(final OrderViewHolder viewHolder, MarketOrder order) { +// ValueAnimator slideAnimation = ValueAnimator +// .ofInt(0, calculateSalesProgress(viewHolder, order)) +// .setDuration(250); +// +// slideAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { +// @Override +// public void onAnimationUpdate(ValueAnimator animation) { +// viewHolder.salesProgress.getLayoutParams().width = (int)animation.getAnimatedValue(); +// viewHolder.salesProgress.requestLayout(); +// } +// }); +// +// AnimatorSet set = new AnimatorSet(); +// set.play(slideAnimation); +// set.setInterpolator(new AccelerateInterpolator()); +// set.start(); +// } + + private void animateDetailsPane(final OrderViewHolder holder) { + ValueAnimator slideOpen; + + if (!holder.isOpen) { + slideOpen = ValueAnimator + .ofInt(0, calculateDetailsPaneHeight(holder)) + .setDuration(250); + } + else { + slideOpen = ValueAnimator + .ofInt(calculateDetailsPaneHeight(holder), 0) + .setDuration(250); + } + + slideOpen.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + holder.details.getLayoutParams().height = (int) animation.getAnimatedValue(); + holder.details.requestLayout(); + } + }); + + slideOpen.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + holder.animationRunning = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + holder.animationRunning = false; + holder.isOpen = !holder.isOpen; + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + + AnimatorSet set = new AnimatorSet(); + set.play(slideOpen); + set.setInterpolator(new AccelerateDecelerateInterpolator()); + set.start(); + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + final OrderViewHolder holder = new OrderViewHolder(LayoutInflater.from(context).inflate( + R.layout.market_order_item, parent, false)); + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!holder.animationRunning) { + animateDetailsPane(holder); + } + } + }); + + return holder; + } + + private int calculateDetailsPaneHeight(OrderViewHolder holder) { + int widthSpec = View.MeasureSpec.makeMeasureSpec(holder.itemView.getWidth(), View.MeasureSpec.EXACTLY); + int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + + holder.details.measure(widthSpec, heightSpec); + return holder.details.getMeasuredHeight(); + } + + private int calculateSalesProgress(OrderViewHolder holder, MarketOrder order) { + double percentage = (double)order.getVolume() / (double)order.getVolumeStart(); + + return (int)(holder.itemView.getWidth() * percentage); + } + + @Override + public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { + final OrderViewHolder viewHolder = (OrderViewHolder) holder; + final MarketOrder order = (MarketOrder) getItem(position); + + DecimalFormat formatter = new DecimalFormat("#,###.00"); + String price = formatter.format(order.getPrice()) + " ISK"; + + formatter = new DecimalFormat("#,###"); + String volume = formatter.format(order.getVolume()); + + viewHolder.location.setText(order.getLocation().getName()); + viewHolder.price.setText(price); + viewHolder.volume.setText(volume); + + if (order.isBuyOrder()) { + String range = order.getRange(); + viewHolder.rangeContainer.setVisibility(View.VISIBLE); + + if (StringUtils.isNumeric(range)) { + range = range + " Jumps"; + } + range = WordUtils.capitalizeFully(range); + viewHolder.range.setText(range); + } + else { + viewHolder.rangeContainer.setVisibility(View.GONE); + } + + viewHolder.details.getLayoutParams().height = 0; + viewHolder.details.requestLayout(); + + holder.itemView.post(new Runnable() { + @Override + public void run() { +// animateSalesProgressBar(viewHolder, order); + + if (viewHolder.isOpen) { + animateDetailsPane(viewHolder); + } + } + }); + } + + @Override + public int getItemCount() { + return orders.size(); + } + + private class Comparitor implements Comparator { + @Override + public int compare(MarketOrder lhs, MarketOrder rhs) { + if (lhs.isBuyOrder()) { + if (lhs.getPrice() < rhs.getPrice()) { + return 1; + } + else if (lhs.getPrice() > rhs.getPrice()) { + return -1; + } + else { + return 0; + } + } else { + if (lhs.getPrice() < rhs.getPrice()) { + return -1; + } + else if (lhs.getPrice() > rhs.getPrice()) { + return 1; + } + else { + return 0; + } + } + } + } + + public Object getItem(int position) { + return orders.get(position); + } + + public void updateCollection(Collection newChildren) { + ArrayList newOrders = new ArrayList<>(newChildren); + Collections.sort(newOrders, new Comparitor()); + + int newSize = newOrders.size(); + int oldSize = orders.size(); + + orders = newOrders; + + if (newSize < oldSize) { + notifyItemRangeChanged(0, newSize); + notifyItemRangeRemoved(newSize, oldSize - newSize); + } + if (newSize == oldSize) { + notifyItemRangeChanged(0, newSize); + } + if (newSize > oldSize) { + notifyItemRangeChanged(0, oldSize); + notifyItemRangeInserted(oldSize, newSize - oldSize); + } + } + + public void clear() { + int oldSize = orders.size(); + + orders.clear(); + notifyItemRangeRemoved(0, oldSize); + } + +} diff --git a/app/src/main/java/com/w9jds/marketbot/adapters/OrdersTabAdapter.java b/app/src/main/java/com/w9jds/marketbot/adapters/OrdersTabAdapter.java new file mode 100644 index 0000000..5308781 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/adapters/OrdersTabAdapter.java @@ -0,0 +1,62 @@ +package com.w9jds.marketbot.adapters; + +import android.content.Context; +import android.content.res.Resources; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentStatePagerAdapter; + +import com.w9jds.eveapi.Models.Region; +import com.w9jds.eveapi.Models.Type; +import com.w9jds.marketbot.R; +import com.w9jds.marketbot.ui.fragments.OrdersTab; +import com.w9jds.marketbot.ui.fragments.TypeInfoTab; + +/** + * Created by Jeremy on 3/1/2016. + */ +public final class OrdersTabAdapter extends FragmentStatePagerAdapter { + + private Resources resources; + private long typeId; + + final int PAGE_COUNT = 3; + + public OrdersTabAdapter(FragmentManager fragmentManager, Context context, long typeId) { + super(fragmentManager); + resources = context.getResources(); + this.typeId = typeId; + } + + @Override + public int getCount() { + return PAGE_COUNT; + } + + @Override + public CharSequence getPageTitle(int position) { + + switch(position) { + case 0: + return resources.getString(R.string.type_info_label); + case 1: + return resources.getString(R.string.sell_orders); + case 2: + return resources.getString(R.string.buy_orders); + default: + return ""; + } + } + + @Override + public Fragment getItem(int position) { + if (position == 0) { + return TypeInfoTab.create(position, typeId); + } + else { + return OrdersTab.create(position); + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/adapters/RegionAdapter.java b/app/src/main/java/com/w9jds/marketbot/adapters/RegionAdapter.java new file mode 100644 index 0000000..7b5c5c9 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/adapters/RegionAdapter.java @@ -0,0 +1,120 @@ +package com.w9jds.marketbot.adapters; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.SpinnerAdapter; +import android.widget.TextView; + +import com.w9jds.eveapi.Models.MarketItemBase; +import com.w9jds.eveapi.Models.Region; +import com.w9jds.marketbot.R; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import butterknife.ButterKnife; + +/** + * Created by Jeremy on 3/1/2016. + */ +public final class RegionAdapter extends BaseAdapter implements SpinnerAdapter { + + private Context context; + private List regions = new ArrayList<>(); + + public RegionAdapter(Context context) { + this.context = context; + } + + public int getPositionfromId(int id) { + int size = regions.size(); + for (int i = 0; i < size; i++) { + Region region = regions.get(i); + if (region.getId() == id) { + return i; + } + } + + return -1; + } + + @Override + public int getCount() { + return regions.size(); + } + + @Override + public Object getItem(int position) { + return regions.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + static class RegionViewHolder { + + TextView name; + + public RegionViewHolder(View view) { + name = ButterKnife.findById(view, R.id.region_name); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + RegionViewHolder holder; + final Region region = (Region) getItem(position); + + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.toolbar_spinner_item_actionbar, parent, false); + + holder = new RegionViewHolder(convertView); + convertView.setTag(holder); + } + else { + holder = (RegionViewHolder) convertView.getTag(); + } + + holder.name.setText(region.getName()); + + return convertView; + } + + public void addAllItems(Collection regions) { + for (MarketItemBase item : regions) { + Region region = (Region)item; + if (!region.getName().matches("[A-Z]-R\\w+")) { + this.regions.add(region); + } + } + + notifyDataSetChanged(); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + RegionViewHolder holder; + final Region region = (Region) getItem(position); + + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.toolbar_spinner_item_dropdown, parent, false); + + holder = new RegionViewHolder(convertView); + convertView.setTag(holder); + } + else { + holder = (RegionViewHolder) convertView.getTag(); + } + + holder.name.setText(region.getName()); + + return convertView; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/classes/MarketBot.java b/app/src/main/java/com/w9jds/marketbot/classes/MarketBot.java new file mode 100644 index 0000000..f4d582c --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/MarketBot.java @@ -0,0 +1,44 @@ +package com.w9jds.marketbot.classes; + +import android.app.Application; + +import com.w9jds.eveapi.Client.Crest; +import com.w9jds.marketbot.classes.components.DaggerNetComponent; +import com.w9jds.marketbot.classes.components.DaggerStorageComponent; +import com.w9jds.marketbot.classes.components.NetComponent; +import com.w9jds.marketbot.classes.components.StorageComponent; +import com.w9jds.marketbot.classes.modules.ApplicationModule; +import com.w9jds.marketbot.classes.modules.StorageModule; +import com.w9jds.marketbot.classes.modules.NetModule; + +/** + * Created by Jeremy Shore on 3/9/16. + */ +public final class MarketBot extends Application { + + private StorageComponent storageComponent; + private NetComponent netComponent; + + @Override + public void onCreate() { + super.onCreate(); + + netComponent = DaggerNetComponent.builder() + .applicationModule(new ApplicationModule(this)) + .netModule(new NetModule(Crest.PUBLIC_TRANQUILITY)) + .build(); + + storageComponent = DaggerStorageComponent.builder() + .storageModule(new StorageModule(this)) + .build(); + + } + + public NetComponent getNetComponent() { + return netComponent; + } + + public StorageComponent getStorageComponent() { + return storageComponent; + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/classes/StorageColumn.java b/app/src/main/java/com/w9jds/marketbot/classes/StorageColumn.java new file mode 100644 index 0000000..6e57a8a --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/StorageColumn.java @@ -0,0 +1,12 @@ +package com.w9jds.marketbot.classes; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Created by Jeremy on 3/5/2016. + */ +@Target(ElementType.FIELD) +public @interface StorageColumn { + String value(); +} diff --git a/app/src/main/java/com/w9jds/marketbot/classes/Triplet.java b/app/src/main/java/com/w9jds/marketbot/classes/Triplet.java new file mode 100644 index 0000000..d24ba53 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/Triplet.java @@ -0,0 +1,25 @@ +package com.w9jds.marketbot.classes; + +public final class Triplet { + T a; + U b; + V c; + + public Triplet(T a, U b, V c) { + this.a = a; + this.b = b; + this.c = c; + } + + public T getA() { + return a; + } + + public U getB() { + return b; + } + + public V getC() { + return c; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/classes/ViewOffsetHelper.java b/app/src/main/java/com/w9jds/marketbot/classes/ViewOffsetHelper.java new file mode 100644 index 0000000..0545173 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/ViewOffsetHelper.java @@ -0,0 +1,114 @@ +package com.w9jds.marketbot.classes; + +import android.os.Build; +import android.support.v4.view.ViewCompat; +import android.util.Property; +import android.view.View; +import android.view.ViewParent; + +import com.w9jds.marketbot.classes.utils.AnimUtils; + +/** + * Borrowed from the design lib. + * + * Utility helper for moving a {@link android.view.View} around using + * {@link android.view.View#offsetLeftAndRight(int)} and + * {@link android.view.View#offsetTopAndBottom(int)}. + *

+ * Also the setting of absolute offsets (similar to translationX/Y), rather than additive + * offsets. + */ +public final class ViewOffsetHelper { + + private final View mView; + + private int mLayoutTop; + private int mLayoutLeft; + private int mOffsetTop; + private int mOffsetLeft; + + public ViewOffsetHelper(View view) { + mView = view; + } + + public void onViewLayout() { + // Now grab the intended top + mLayoutTop = mView.getTop(); + mLayoutLeft = mView.getLeft(); + + // And offset it as needed + updateOffsets(); + } + + private void updateOffsets() { + ViewCompat.offsetTopAndBottom(mView, mOffsetTop - (mView.getTop() - mLayoutTop)); + ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - (mView.getLeft() - mLayoutLeft)); + + // Manually invalidate the view and parent to make sure we get drawn pre-M + if (Build.VERSION.SDK_INT < 23) { + tickleInvalidationFlag(mView); + final ViewParent vp = mView.getParent(); + if (vp instanceof View) { + tickleInvalidationFlag((View) vp); + } + } + } + + private static void tickleInvalidationFlag(View view) { + final float x = ViewCompat.getTranslationX(view); + ViewCompat.setTranslationY(view, x + 1); + ViewCompat.setTranslationY(view, x); + } + + /** + * Set the top and bottom offset for this {@link ViewOffsetHelper}'s view. + * + * @param offset the offset in px. + * @return true if the offset has changed + */ + public boolean setTopAndBottomOffset(int offset) { + if (mOffsetTop != offset) { + mOffsetTop = offset; + updateOffsets(); + return true; + } + return false; + } + + /** + * Set the left and right offset for this {@link ViewOffsetHelper}'s view. + * + * @param offset the offset in px. + * @return true if the offset has changed + */ + public boolean setLeftAndRightOffset(int offset) { + if (mOffsetLeft != offset) { + mOffsetLeft = offset; + updateOffsets(); + return true; + } + return false; + } + + public int getTopAndBottomOffset() { + return mOffsetTop; + } + + public int getLeftAndRightOffset() { + return mOffsetLeft; + } + + public static final Property OFFSET_Y = new AnimUtils + .IntProperty("topAndBottomOffset") { + + @Override + public void setValue(ViewOffsetHelper viewOffsetHelper, int offset) { + viewOffsetHelper.setTopAndBottomOffset(offset); + } + + @Override + public Integer get(ViewOffsetHelper viewOffsetHelper) { + return viewOffsetHelper.getTopAndBottomOffset(); + } + }; +} diff --git a/app/src/main/java/com/w9jds/marketbot/classes/components/NetComponent.java b/app/src/main/java/com/w9jds/marketbot/classes/components/NetComponent.java new file mode 100644 index 0000000..0295d2c --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/components/NetComponent.java @@ -0,0 +1,23 @@ +package com.w9jds.marketbot.classes.components; + +import com.w9jds.marketbot.activities.ItemActivity; +import com.w9jds.marketbot.activities.MainActivity; +import com.w9jds.marketbot.classes.modules.ApplicationModule; +import com.w9jds.marketbot.classes.modules.NetModule; +import com.w9jds.marketbot.data.DataManager; + +import java.lang.annotation.Retention; + +import javax.inject.Scope; +import javax.inject.Singleton; +import dagger.Component; + +/** + * Created by Jeremy Shore on 3/9/16. + */ + +@Component(modules={ApplicationModule.class, NetModule.class}) +public interface NetComponent { + void inject(MainActivity activity); + void inject(ItemActivity activity); +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/classes/components/StorageComponent.java b/app/src/main/java/com/w9jds/marketbot/classes/components/StorageComponent.java new file mode 100644 index 0000000..658eece --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/components/StorageComponent.java @@ -0,0 +1,22 @@ +package com.w9jds.marketbot.classes.components; + +import com.w9jds.eveapi.Client.Crest; +import com.w9jds.marketbot.activities.MainActivity; +import com.w9jds.marketbot.classes.modules.ApplicationModule; +import com.w9jds.marketbot.classes.modules.StorageModule; +import com.w9jds.marketbot.data.DataManager; + +import javax.inject.Singleton; + +import dagger.Component; + +/** + * Created by Jeremy Shore on 3/10/16. + */ +@Singleton +@Component(modules={StorageModule.class}) +public interface StorageComponent { + + void inject(DataManager manager); + void inject(Crest crest); +} diff --git a/app/src/main/java/com/w9jds/marketbot/classes/modules/ApplicationModule.java b/app/src/main/java/com/w9jds/marketbot/classes/modules/ApplicationModule.java new file mode 100644 index 0000000..925b751 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/modules/ApplicationModule.java @@ -0,0 +1,24 @@ +package com.w9jds.marketbot.classes.modules; + +import android.app.Application; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +@Module +public class ApplicationModule { + + private Application appModule; + + public ApplicationModule(Application application) { + appModule = application; + } + + @Provides + @Singleton + Application privideApplication() { + return appModule; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/classes/modules/NetModule.java b/app/src/main/java/com/w9jds/marketbot/classes/modules/NetModule.java new file mode 100644 index 0000000..6c5a973 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/modules/NetModule.java @@ -0,0 +1,98 @@ +package com.w9jds.marketbot.classes.modules; + +import android.app.Application; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import com.google.gson.Gson; + +import java.io.IOException; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; +import okhttp3.Cache; +import okhttp3.CacheControl; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +@Module +public class NetModule { + + String mBaseUrl; + + public NetModule(String baseUrl) { + this.mBaseUrl = baseUrl; + } + +// private Interceptor FORCE_CACHE_INTERCEPTOR = new Interceptor() { +// @Override +// public okhttp3.Response intercept(Chain chain) throws IOException { +// Request request = chain.request(); +// +// request.newBuilder() +// +// .cacheControl(CacheControl.FORCE_CACHE) +// .build(); +// +// return chain.proceed(request); +// } +// }; +// +// private Interceptor CACHE_2MONTHS_INTERCEPTOR = new Interceptor() { +// @Override +// public okhttp3.Response intercept(Chain chain) throws IOException { +// okhttp3.Response response = chain.proceed(chain.request()); +// +// return response.newBuilder() +// .header("Cache-Control", "private, max-age=5184000") +// .build(); +// } +// }; + + @Provides + @Singleton + Interceptor provideInterceptor() { + return new Interceptor() { + @Override + public okhttp3.Response intercept(Chain chain) throws IOException { + okhttp3.Response response = chain.proceed(chain.request()); + + return response.newBuilder() + .addHeader("User-Agent", "MarketBot by Jeremy Shore") +// .header("Cache-Control", "private, max-age=5184000") + .build(); + } + }; + } + + @Provides + @Singleton + Cache provideOkHttpCache(Application application) { + int cacheSize = 10 * 1024 * 1024; + return new Cache(application.getCacheDir(), cacheSize); + } + + @Provides + @Singleton + OkHttpClient provideOkHttpClient(Cache cache, Interceptor interceptor) { + return new OkHttpClient.Builder() + .cache(cache) + .addNetworkInterceptor(interceptor) + .build(); + } + + @Provides + @Singleton + Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) { + return new Retrofit.Builder() + .baseUrl(mBaseUrl) + .addConverterFactory(GsonConverterFactory.create()) + .client(okHttpClient) + .build(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/classes/modules/StorageModule.java b/app/src/main/java/com/w9jds/marketbot/classes/modules/StorageModule.java new file mode 100644 index 0000000..503739f --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/modules/StorageModule.java @@ -0,0 +1,53 @@ +package com.w9jds.marketbot.classes.modules; + +import android.content.Context; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.preference.PreferenceManager; + +import com.w9jds.marketbot.data.storage.Database; + +import javax.inject.Named; +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +/** + * Created by Jeremy Shore on 3/10/16. + */ +@Module +public class StorageModule { + + private Context context; + private SharedPreferences sharedPreferences; + + public StorageModule(Context context) { + this.context = context; + } + + @Provides + @Singleton + SharedPreferences provideSharedPreferences() { + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + return sharedPreferences; + } + + @Provides + @Singleton + String provideServerVersion() { + return sharedPreferences.getString("serverVersion", ""); + } + + @Provides @Named("write") + @Singleton + public SQLiteDatabase provideWritableDatabase() { + return new Database(context).getWritableDatabaseHelper(); + } + + @Provides @Named("read") + @Singleton + public SQLiteDatabase provideReadableDatabase() { + return new Database(context).getReadableDatabaseHelper(); + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/classes/utils/AnimUtils.java b/app/src/main/java/com/w9jds/marketbot/classes/utils/AnimUtils.java new file mode 100644 index 0000000..14b0dc1 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/utils/AnimUtils.java @@ -0,0 +1,319 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.w9jds.marketbot.classes.utils; + +import android.animation.Animator; +import android.animation.TimeInterpolator; +import android.content.Context; +import android.transition.Transition; +import android.util.ArrayMap; +import android.util.Property; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +import java.util.ArrayList; + +/** + * Utility methods for working with animations. + */ +public final class AnimUtils { + + private AnimUtils() { } + + private static Interpolator fastOutSlowIn; + private static Interpolator fastOutLinearIn; + private static Interpolator linearOutSlowIn; + + public static Interpolator getFastOutSlowInInterpolator(Context context) { + if (fastOutSlowIn == null) { + fastOutSlowIn = AnimationUtils.loadInterpolator(context, + android.R.interpolator.fast_out_slow_in); + } + return fastOutSlowIn; + } + + public static Interpolator getFastOutLinearInInterpolator(Context context) { + if (fastOutLinearIn == null) { + fastOutLinearIn = AnimationUtils.loadInterpolator(context, + android.R.interpolator.fast_out_linear_in); + } + return fastOutLinearIn; + } + + public static Interpolator getLinearOutSlowInInterpolator(Context context) { + if (linearOutSlowIn == null) { + linearOutSlowIn = AnimationUtils.loadInterpolator(context, + android.R.interpolator.linear_out_slow_in); + } + return linearOutSlowIn; + } + + /** + * Linear interpolate between a and b with parameter t. + */ + public static float lerp(float a, float b, float t) { + return a + (b - a) * t; + } + + + /** + * An implementation of {@link android.util.Property} to be used specifically with fields of + * type + * float. This type-specific subclass enables performance benefit by allowing + * calls to a {@link #set(Object, Float) set()} function that takes the primitive + * float type and avoids autoboxing and other overhead associated with the + * Float class. + * + * @param The class on which the Property is declared. + **/ + public static abstract class FloatProperty extends Property { + public FloatProperty(String name) { + super(Float.class, name); + } + + /** + * A type-specific override of the {@link #set(Object, Float)} that is faster when dealing + * with fields of type float. + */ + public abstract void setValue(T object, float value); + + @Override + final public void set(T object, Float value) { + setValue(object, value); + } + } + + /** + * An implementation of {@link android.util.Property} to be used specifically with fields of + * type + * int. This type-specific subclass enables performance benefit by allowing + * calls to a {@link #set(Object, Integer) set()} function that takes the primitive + * int type and avoids autoboxing and other overhead associated with the + * Integer class. + * + * @param The class on which the Property is declared. + */ + public static abstract class IntProperty extends Property { + + public IntProperty(String name) { + super(Integer.class, name); + } + + /** + * A type-specific override of the {@link #set(Object, Integer)} that is faster when dealing + * with fields of type int. + */ + public abstract void setValue(T object, int value); + + @Override + final public void set(T object, Integer value) { + setValue(object, value.intValue()); + } + + } + + /** + * https://halfthought.wordpress.com/2014/11/07/reveal-transition/ + *

+ * Interrupting Activity transitions can yield an OperationNotSupportedException when the + * transition tries to pause the animator. Yikes! We can fix this by wrapping the Animator: + */ + public static class NoPauseAnimator extends Animator { + private final Animator mAnimator; + private final ArrayMap mListeners = + new ArrayMap(); + + public NoPauseAnimator(Animator animator) { + mAnimator = animator; + } + + @Override + public void addListener(AnimatorListener listener) { + AnimatorListener wrapper = new AnimatorListenerWrapper(this, listener); + if (!mListeners.containsKey(listener)) { + mListeners.put(listener, wrapper); + mAnimator.addListener(wrapper); + } + } + + @Override + public void cancel() { + mAnimator.cancel(); + } + + @Override + public void end() { + mAnimator.end(); + } + + @Override + public long getDuration() { + return mAnimator.getDuration(); + } + + @Override + public TimeInterpolator getInterpolator() { + return mAnimator.getInterpolator(); + } + + @Override + public void setInterpolator(TimeInterpolator timeInterpolator) { + mAnimator.setInterpolator(timeInterpolator); + } + + @Override + public ArrayList getListeners() { + return new ArrayList(mListeners.keySet()); + } + + @Override + public long getStartDelay() { + return mAnimator.getStartDelay(); + } + + @Override + public void setStartDelay(long delayMS) { + mAnimator.setStartDelay(delayMS); + } + + @Override + public boolean isPaused() { + return mAnimator.isPaused(); + } + + @Override + public boolean isRunning() { + return mAnimator.isRunning(); + } + + @Override + public boolean isStarted() { + return mAnimator.isStarted(); + } + + /* We don't want to override pause or resume methods because we don't want them + * to affect mAnimator. + public void pause(); + + public void resume(); + + public void addPauseListener(AnimatorPauseListener listener); + + public void removePauseListener(AnimatorPauseListener listener); + */ + + @Override + public void removeAllListeners() { + mListeners.clear(); + mAnimator.removeAllListeners(); + } + + @Override + public void removeListener(AnimatorListener listener) { + AnimatorListener wrapper = mListeners.get(listener); + if (wrapper != null) { + mListeners.remove(listener); + mAnimator.removeListener(wrapper); + } + } + + @Override + public Animator setDuration(long durationMS) { + mAnimator.setDuration(durationMS); + return this; + } + + @Override + public void setTarget(Object target) { + mAnimator.setTarget(target); + } + + @Override + public void setupEndValues() { + mAnimator.setupEndValues(); + } + + @Override + public void setupStartValues() { + mAnimator.setupStartValues(); + } + + @Override + public void start() { + mAnimator.start(); + } + } + + static class AnimatorListenerWrapper implements Animator.AnimatorListener { + private final Animator mAnimator; + private final Animator.AnimatorListener mListener; + + public AnimatorListenerWrapper(Animator animator, Animator.AnimatorListener listener) { + mAnimator = animator; + mListener = listener; + } + + @Override + public void onAnimationStart(Animator animator) { + mListener.onAnimationStart(mAnimator); + } + + @Override + public void onAnimationEnd(Animator animator) { + mListener.onAnimationEnd(mAnimator); + } + + @Override + public void onAnimationCancel(Animator animator) { + mListener.onAnimationCancel(mAnimator); + } + + @Override + public void onAnimationRepeat(Animator animator) { + mListener.onAnimationRepeat(mAnimator); + } + } + + public static class TransitionListenerAdapter implements Transition.TransitionListener { + + @Override + public void onTransitionStart(Transition transition) { + + } + + @Override + public void onTransitionEnd(Transition transition) { + + } + + @Override + public void onTransitionCancel(Transition transition) { + + } + + @Override + public void onTransitionPause(Transition transition) { + + } + + @Override + public void onTransitionResume(Transition transition) { + + } + } + +} diff --git a/app/src/main/java/com/w9jds/marketbot/classes/utils/ColorUtils.java b/app/src/main/java/com/w9jds/marketbot/classes/utils/ColorUtils.java new file mode 100644 index 0000000..188fb9b --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/utils/ColorUtils.java @@ -0,0 +1,176 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.w9jds.marketbot.classes.utils; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.support.annotation.ColorInt; +import android.support.annotation.FloatRange; +import android.support.annotation.IntDef; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.graphics.Palette; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Utility methods for working with colors. + */ +public final class ColorUtils { + + private ColorUtils() { } + + public static final int IS_LIGHT = 0; + public static final int IS_DARK = 1; + public static final int LIGHTNESS_UNKNOWN = 2; + + /** + * Set the alpha component of {@code color} to be {@code alpha}. + */ + public static int modifyAlpha(@ColorInt int color, @IntRange(from = 0, to = 255) int alpha) { + return (color & 0x00ffffff) | (alpha << 24); + } + + /** + * Set the alpha component of {@code color} to be {@code alpha}. + */ + public static int modifyAlpha(@ColorInt int color, + @FloatRange(from = 0f, to = 1f) float alpha) { + return modifyAlpha(color, (int) (255f * alpha)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 0.0 will return {@code color1}, 0.5 will give an even blend, + * 1.0 will return {@code color2}. + */ + public static @ColorInt int blendColors(@ColorInt int color1, + @ColorInt int color2, + @FloatRange(from = 0f, to = 1f) float ratio) { + final float inverseRatio = 1f - ratio; + float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio); + float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio); + float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio); + float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio); + return Color.argb((int) a, (int) r, (int) g, (int) b); + } + + /** + * Checks if the most populous color in the given palette is dark + *

+ * Annoyingly we have to return this Lightness 'enum' rather than a boolean as palette isn't + * guaranteed to find the most populous color. + */ + public static @Lightness int isDark(Palette palette) { + Palette.Swatch mostPopulous = getMostPopulousSwatch(palette); + if (mostPopulous == null) return LIGHTNESS_UNKNOWN; + return isDark(mostPopulous.getHsl()) ? IS_DARK : IS_LIGHT; + } + + public static @Nullable Palette.Swatch getMostPopulousSwatch(Palette palette) { + Palette.Swatch mostPopulous = null; + if (palette != null) { + for (Palette.Swatch swatch : palette.getSwatches()) { + if (mostPopulous == null || swatch.getPopulation() > mostPopulous.getPopulation()) { + mostPopulous = swatch; + } + } + } + return mostPopulous; + } + + /** + * Determines if a given bitmap is dark. This extracts a palette inline so should not be called + * with a large image!! + *

+ * Note: If palette fails then check the color of the central pixel + */ + public static boolean isDark(@NonNull Bitmap bitmap) { + return isDark(bitmap, bitmap.getWidth() / 2, bitmap.getHeight() / 2); + } + + /** + * Determines if a given bitmap is dark. This extracts a palette inline so should not be called + * with a large image!! If palette fails then check the color of the specified pixel + */ + public static boolean isDark(@NonNull Bitmap bitmap, int backupPixelX, int backupPixelY) { + // first try palette with a small color quant size + Palette palette = Palette.from(bitmap).maximumColorCount(3).generate(); + if (palette != null && palette.getSwatches().size() > 0) { + return isDark(palette) == IS_DARK; + } else { + // if palette failed, then check the color of the specified pixel + return isDark(bitmap.getPixel(backupPixelX, backupPixelY)); + } + } + + /** + * Check that the lightness value (0–1) + */ + public static boolean isDark(float[] hsl) { // @Size(3) + return hsl[2] < 0.5f; + } + + /** + * Convert to HSL & check that the lightness value + */ + public static boolean isDark(@ColorInt int color) { + float[] hsl = new float[3]; + android.support.v4.graphics.ColorUtils.colorToHSL(color, hsl); + return isDark(hsl); + } + + /** + * Calculate a variant of the color to make it more suitable for overlaying information. Light + * colors will be lightened and dark colors will be darkened + * + * @param color the color to adjust + * @param isDark whether {@code color} is light or dark + * @param lightnessMultiplier the amount to modify the color e.g. 0.1f will alter it by 10% + * @return the adjusted color + */ + public static @ColorInt int scrimify(@ColorInt int color, + boolean isDark, + @FloatRange(from = 0f, to = 1f) float lightnessMultiplier) { + float[] hsl = new float[3]; + android.support.v4.graphics.ColorUtils.colorToHSL(color, hsl); + + if (!isDark) { + lightnessMultiplier += 1f; + } else { + lightnessMultiplier = 1f - lightnessMultiplier; + } + + + hsl[2] = MathUtils.constrain(0f, 1f, hsl[2] * lightnessMultiplier); + return android.support.v4.graphics.ColorUtils.HSLToColor(hsl); + } + + public static @ColorInt int scrimify(@ColorInt int color, + @FloatRange(from = 0f, to = 1f) float lightnessMultiplier) { + return scrimify(color, isDark(color), lightnessMultiplier); + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({IS_LIGHT, IS_DARK, LIGHTNESS_UNKNOWN}) + public @interface Lightness { + } + +} diff --git a/app/src/main/java/com/w9jds/marketbot/classes/utils/MathUtils.java b/app/src/main/java/com/w9jds/marketbot/classes/utils/MathUtils.java new file mode 100644 index 0000000..d55a74b --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/utils/MathUtils.java @@ -0,0 +1,10 @@ +package com.w9jds.marketbot.classes.utils; + +public final class MathUtils { + + private MathUtils() { } + + public static float constrain(float min, float max, float v) { + return Math.max(min, Math.min(max, v)); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/classes/utils/StorageUtils.java b/app/src/main/java/com/w9jds/marketbot/classes/utils/StorageUtils.java new file mode 100644 index 0000000..1bb078f --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/utils/StorageUtils.java @@ -0,0 +1,57 @@ +package com.w9jds.marketbot.classes.utils; + +import android.database.Cursor; + +import com.w9jds.marketbot.classes.StorageColumn; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; + +/** + * Created by Jeremy on 3/5/2016. + */ +public final class StorageUtils { + + public static T buildClass(T object, Cursor cursor) { + try { + Class type = object.getClass(); + + ArrayList fields = new ArrayList<>(); + fields.addAll(Arrays.asList(type.getDeclaredFields())); + if (type.getSuperclass() != Object.class) { + fields.addAll(Arrays.asList(type.getSuperclass().getDeclaredFields())); + } + + for (Field field : fields) { + if (field.isAnnotationPresent(StorageColumn.class)) { + String columnName = field.getAnnotation(StorageColumn.class).value(); + int columnIndex = cursor.getColumnIndex(columnName); + + if (columnIndex > -1) { + if (field.getType() == String.class) { + field.set(object, cursor.getString(columnIndex)); + } + if (field.getType() == long.class) { + field.set(object, cursor.getLong(columnIndex)); + } + if (field.getType() == double.class) { + field.set(object, cursor.getDouble(columnIndex)); + } + if (field.getType() == Date.class) { + long date = cursor.getLong(columnIndex); + field.set(object, new Date(date)); + } + } + } + } + } + catch(Exception ex) { + ex.printStackTrace(); + } + + return object; + } + +} diff --git a/app/src/main/java/com/w9jds/marketbot/classes/utils/ViewUtils.java b/app/src/main/java/com/w9jds/marketbot/classes/utils/ViewUtils.java new file mode 100644 index 0000000..e7bfcd4 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/classes/utils/ViewUtils.java @@ -0,0 +1,252 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.w9jds.marketbot.classes.utils; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.Outline; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.FloatRange; +import android.support.annotation.NonNull; +import android.support.v7.graphics.Palette; +import android.text.TextPaint; +import android.util.DisplayMetrics; +import android.util.Property; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.widget.ImageView; + +import com.w9jds.marketbot.classes.utils.AnimUtils; +import com.w9jds.marketbot.classes.utils.ColorUtils; + +/** + * Utility methods for working with Views. + */ +public final class ViewUtils { + + private ViewUtils() { } + + private static int actionBarSize = -1; + + public static int getActionBarSize(Context context) { + if (actionBarSize < 0) { + TypedValue value = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.actionBarSize, value, true); + actionBarSize = TypedValue.complexToDimensionPixelSize(value.data, context + .getResources().getDisplayMetrics()); + } + return actionBarSize; + } + + /** + * Determine if the navigation bar will be on the bottom of the screen, based on logic in + * PhoneWindowManager. + */ + public static boolean isNavBarOnBottom(@NonNull Context context) { + final Resources res= context.getResources(); + final Configuration cfg = context.getResources().getConfiguration(); + final DisplayMetrics dm =res.getDisplayMetrics(); + boolean canMove = (dm.widthPixels != dm.heightPixels && + cfg.smallestScreenWidthDp < 600); + return(!canMove || dm.widthPixels < dm.heightPixels); + } + + public static RippleDrawable createRipple(@ColorInt int color, + @FloatRange(from = 0f, to = 1f) float alpha, + boolean bounded) { + color = ColorUtils.modifyAlpha(color, alpha); + return new RippleDrawable(ColorStateList.valueOf(color), null, + bounded ? new ColorDrawable(Color.WHITE) : null); + } + + public static RippleDrawable createRipple(@NonNull Palette palette, + @FloatRange(from = 0f, to = 1f) float darkAlpha, + @FloatRange(from = 0f, to = 1f) float lightAlpha, + @ColorInt int fallbackColor, + boolean bounded) { + int rippleColor = fallbackColor; + if (palette != null) { + // try the named swatches in preference order + if (palette.getVibrantSwatch() != null) { + rippleColor = + ColorUtils.modifyAlpha(palette.getVibrantSwatch().getRgb(), darkAlpha); + + } else if (palette.getLightVibrantSwatch() != null) { + rippleColor = ColorUtils.modifyAlpha(palette.getLightVibrantSwatch().getRgb(), + lightAlpha); + } else if (palette.getDarkVibrantSwatch() != null) { + rippleColor = ColorUtils.modifyAlpha(palette.getDarkVibrantSwatch().getRgb(), + darkAlpha); + } else if (palette.getMutedSwatch() != null) { + rippleColor = ColorUtils.modifyAlpha(palette.getMutedSwatch().getRgb(), darkAlpha); + } else if (palette.getLightMutedSwatch() != null) { + rippleColor = ColorUtils.modifyAlpha(palette.getLightMutedSwatch().getRgb(), + lightAlpha); + } else if (palette.getDarkMutedSwatch() != null) { + rippleColor = + ColorUtils.modifyAlpha(palette.getDarkMutedSwatch().getRgb(), darkAlpha); + } + } + return new RippleDrawable(ColorStateList.valueOf(rippleColor), null, + bounded ? new ColorDrawable(Color.WHITE) : null); + } + + public static void setLightStatusBar(@NonNull View view) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + int flags = view.getSystemUiVisibility(); + flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + view.setSystemUiVisibility(flags); + } + } + + public static void clearLightStatusBar(@NonNull View view) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + int flags = view.getSystemUiVisibility(); + flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + view.setSystemUiVisibility(flags); + } + } + + /** + * Recursive binary search to find the best size for the text. + * + * Adapted from https://github.com/grantland/android-autofittextview + */ + public static float getSingleLineTextSize(String text, + TextPaint paint, + float targetWidth, + float low, + float high, + float precision, + DisplayMetrics metrics) { + final float mid = (low + high) / 2.0f; + + paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid, metrics)); + final float maxLineWidth = paint.measureText(text); + + if ((high - low) < precision) { + return low; + } else if (maxLineWidth > targetWidth) { + return getSingleLineTextSize(text, paint, targetWidth, low, mid, precision, metrics); + } else if (maxLineWidth < targetWidth) { + return getSingleLineTextSize(text, paint, targetWidth, mid, high, precision, metrics); + } else { + return mid; + } + } + + public static final Property BACKGROUND_COLOR + = new AnimUtils.IntProperty("backgroundColor") { + + @Override + public void setValue(View view, int value) { + view.setBackgroundColor(value); + } + + @Override + public Integer get(View view) { + Drawable d = view.getBackground(); + if (d instanceof ColorDrawable) { + return ((ColorDrawable) d).getColor(); + } + return Color.TRANSPARENT; + } + }; + + public static final Property IMAGE_ALPHA + = new AnimUtils.IntProperty("imageAlpha") { + + @Override + public void setValue(ImageView imageView, int value) { + imageView.setImageAlpha(value); + } + + @Override + public Integer get(ImageView imageView) { + return imageView.getImageAlpha(); + } + }; + + public static final ViewOutlineProvider CIRCULAR_OUTLINE = new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(view.getPaddingLeft(), + view.getPaddingTop(), + view.getWidth() - view.getPaddingRight(), + view.getHeight() - view.getPaddingBottom()); + } + }; + + /** + * Determines if two views intersect in the window. + */ + public static boolean viewsIntersect(View view1, View view2) { + if (view1 == null || view2 == null) return false; + + final int[] view1Loc = new int[2]; + view1.getLocationOnScreen(view1Loc); + final Rect view1Rect = new Rect(view1Loc[0], + view1Loc[1], + view1Loc[0] + view1.getWidth(), + view1Loc[1] + view1.getHeight()); + int[] view2Loc = new int[2]; + view2.getLocationOnScreen(view2Loc); + final Rect view2Rect = new Rect(view2Loc[0], + view2Loc[1], + view2Loc[0] + view2.getWidth(), + view2Loc[1] + view2.getHeight()); + return view1Rect.intersect(view2Rect); + } + + public static void setPaddingStart(View view, int paddingStart) { + view.setPaddingRelative(paddingStart, + view.getPaddingTop(), + view.getPaddingEnd(), + view.getPaddingBottom()); + } + + public static void setPaddingTop(View view, int paddingTop) { + view.setPaddingRelative(view.getPaddingStart(), + paddingTop, + view.getPaddingEnd(), + view.getPaddingBottom()); + } + + public static void setPaddingEnd(View view, int paddingEnd) { + view.setPaddingRelative(view.getPaddingStart(), + view.getPaddingTop(), + paddingEnd, + view.getPaddingBottom()); + } + + public static void setPaddingBottom(View view, int paddingBottom) { + view.setPaddingRelative(view.getPaddingStart(), + view.getPaddingTop(), + view.getPaddingEnd(), + paddingBottom); + } + +} diff --git a/app/src/main/java/com/w9jds/marketbot/data/BaseDataManager.java b/app/src/main/java/com/w9jds/marketbot/data/BaseDataManager.java new file mode 100644 index 0000000..05a6db4 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/data/BaseDataManager.java @@ -0,0 +1,73 @@ +package com.w9jds.marketbot.data; + +import com.w9jds.eveapi.Client.Crest; +import com.w9jds.eveapi.Models.MarketItemBase; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by Jeremy on 2/19/2016. + */ +public abstract class BaseDataManager implements DataLoadingSubject { + + private AtomicInteger loadingCount; + private List loadingCallbacks; + + public BaseDataManager() { + // setup the API access objects + loadingCount = new AtomicInteger(0); + } + + public abstract void onDataLoaded(List data); + public abstract void onDataLoaded(Object data); + + @Override + public boolean isDataLoading() { + return loadingCount.get() > 0; + } + + protected void loadStarted() { + dispatchLoadingStartedCallbacks(); + } + + protected void loadFinished() { + dispatchLoadingFinishedCallbacks(); + } + + protected void resetLoadingCount() { + loadingCount.set(0); + } + + @Override + public void registerCallback(DataLoadingSubject.DataLoadingCallbacks callback) { + if (loadingCallbacks == null) { + loadingCallbacks = new ArrayList<>(1); + } + loadingCallbacks.add(callback); + } + + @Override + public void unregisterCallback(DataLoadingSubject.DataLoadingCallbacks callback) { + if (loadingCallbacks.contains(callback)) { + loadingCallbacks.remove(callback); + } + } + + protected void dispatchLoadingStartedCallbacks() { + if (loadingCallbacks != null && !loadingCallbacks.isEmpty()) { + for (DataLoadingCallbacks loadingCallback : loadingCallbacks) { + loadingCallback.dataStartedLoading(); + } + } + } + + protected void dispatchLoadingFinishedCallbacks() { + if (loadingCallbacks != null && !loadingCallbacks.isEmpty()) { + for (DataLoadingCallbacks loadingCallback : loadingCallbacks) { + loadingCallback.dataFinishedLoading(); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/data/DataLoadingSubject.java b/app/src/main/java/com/w9jds/marketbot/data/DataLoadingSubject.java new file mode 100644 index 0000000..c55845e --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/data/DataLoadingSubject.java @@ -0,0 +1,15 @@ +package com.w9jds.marketbot.data; + +/** + * Created by Jeremy on 2/19/2016. + */ +public interface DataLoadingSubject { + boolean isDataLoading(); + void registerCallback(DataLoadingCallbacks callbacks); + void unregisterCallback(DataLoadingCallbacks callbacks); + + interface DataLoadingCallbacks { + void dataStartedLoading(); + void dataFinishedLoading(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/data/DataManager.java b/app/src/main/java/com/w9jds/marketbot/data/DataManager.java new file mode 100644 index 0000000..c30dc3c --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/data/DataManager.java @@ -0,0 +1,211 @@ +package com.w9jds.marketbot.data; + +import android.app.Application; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +import com.w9jds.eveapi.Callback; +import com.w9jds.eveapi.Client.Crest; +import com.w9jds.eveapi.Models.MarketGroup; +import com.w9jds.eveapi.Models.OrderType; +import com.w9jds.eveapi.Models.Region; +import com.w9jds.eveapi.Models.ServerInfo; +import com.w9jds.eveapi.Models.Type; +import com.w9jds.eveapi.Models.TypeInfo; +import com.w9jds.eveapi.Models.containers.MarketOrders; +import com.w9jds.eveapi.Models.containers.Types; +import com.w9jds.marketbot.classes.MarketBot; +import com.w9jds.marketbot.data.storage.DataContracts; + +import java.util.ArrayList; +import java.util.Hashtable; + +import javax.inject.Inject; +import javax.inject.Named; + +import retrofit2.Retrofit; + +/** + * Created by Jeremy Shore on 2/19/16. + */ +public abstract class DataManager extends BaseDataManager { + + @Inject + Retrofit retrofit; + @Inject @Named("read") + SQLiteDatabase readDatabase; + @Inject @Named("write") + SQLiteDatabase writeDatabase; + @Inject + SharedPreferences sharedPreferences; + @Inject + String serverVersion; + + Context context; + Crest crest; + + public DataManager(Application application) { + super(); + ((MarketBot)application).getStorageComponent().inject(this); + + this.context = application; + crest = new Crest(retrofit); + } + + private boolean isConnected() { + ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + + NetworkInfo activeNetwork = manager.getActiveNetworkInfo(); + return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); + } + + public void loadMarketGroups() { + if (isConnected()) { + crest.getServerVersion(new Callback() { + @Override + public void success(ServerInfo serverInfo) { + if (serverInfo.getServerVersion().equals(serverVersion)) { + loadMarketGroups(null); + } + else { + sharedPreferences.edit() + .putString("serverVersion", serverInfo.getServerVersion()) + .apply(); + + updateMarketGroups(); + } + } + + @Override + public void failure(String error) { + + } + }); + } else { + loadMarketGroups(null); + } + } + + public void loadMarketGroups(Long parentId) { +// onDataLoaded(); + + loadFinished(); + } + + private void updateMarketGroups() { + loadStarted(); + crest.getMarketGroups(new Callback>() { + + @Override + public void success(Hashtable groups) { + DataContracts.MarketGroupEntry.createNewMarketGroups(writeDatabase, groups.values()); + + loadMarketGroups(null); + } + + @Override + public void failure(String error) { + // failed to update marketgroups + loadFinished(); + } + }); + } + + public void loadGroupTypes(String targetLocation) { + loadStarted(); + crest.getMarketTypes(targetLocation, new Callback() { + @Override + public void success(Types types) { + if (types != null) { + onDataLoaded(types.items); + } + + loadFinished(); + } + + @Override + public void failure(String error) { + loadFinished(); + } + }); + } + + public void loadTypeInfo(long typeId) { + loadStarted(); + crest.getTypeInfo(typeId, new Callback() { + @Override + public void success(TypeInfo typeInfo) { + if (typeInfo != null) { + onDataLoaded(typeInfo); + } + + loadFinished(); + } + + @Override + public void failure(String error) { + loadFinished(); + } + }); + } + + public void loadRegions() { + loadStarted(); + crest.getRegions(new Callback>() { + @Override + public void success(ArrayList regions) { + if (regions != null) { + onDataLoaded(regions); + } + + loadFinished(); + } + + @Override + public void failure(String error) { + loadFinished(); + } + }); + } + + public void loadSellOrders(Region region, Type type) { + loadStarted(); + crest.getOrders(region.getId(), type.getHref(), OrderType.sell, new Callback() { + @Override + public void success(MarketOrders marketOrders) { + if (marketOrders != null) { + onDataLoaded(marketOrders.orders); + } + + loadFinished(); + } + + @Override + public void failure(String error) { + loadFinished(); + } + }); + } + + public void loadBuyOrders(Region region, Type type) { + loadStarted(); + crest.getOrders(region.getId(), type.getHref(), OrderType.buy, new Callback() { + @Override + public void success(MarketOrders marketOrders) { + if (marketOrders != null) { + onDataLoaded(marketOrders.orders); + } + + loadFinished(); + } + + @Override + public void failure(String error) { + loadFinished(); + } + }); + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/data/models/Bot.java b/app/src/main/java/com/w9jds/marketbot/data/models/Bot.java new file mode 100644 index 0000000..623a2f5 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/data/models/Bot.java @@ -0,0 +1,89 @@ +package com.w9jds.marketbot.data.models; + +import android.os.PersistableBundle; + +import com.w9jds.marketbot.classes.StorageColumn; +import com.w9jds.marketbot.data.storage.DataContracts.BotEntry; + +/** + * Created by Jeremy on 3/5/2016. + */ +public final class Bot { + + @StorageColumn(BotEntry.COLUMN_INTERVAL) + long interval; + + @StorageColumn(BotEntry.COLUMN_TYPE_ID) + long typeId; + + @StorageColumn(BotEntry.COLUMN_REGION_ID) + long regionId; + + @StorageColumn(BotEntry.COLUMN_THRESHOLD) + double threshold; + + @StorageColumn(BotEntry.COLUMN_TYPE_HREF) + String href; + + @StorageColumn(BotEntry.COLUMN_IS_ABOVE) + boolean isAbove; + + @StorageColumn(BotEntry.COLUMN_IS_BUY) + boolean isBuy; + + public boolean isBuyOrder() { + return isBuy; + } + + public boolean isCheckAboveThreshold() { + return isAbove; + } + + public String getTypeHref() { + return href; + } + + public long getTypeId() { + return typeId; + } + + public long getRegionId() { + return regionId; + } + + public long getInterval() { + return interval; + } + + public double getThreshold() { + return threshold; + } + + public PersistableBundle toPersistableBundle() { + PersistableBundle bundle = new PersistableBundle(); + + bundle.putLong(BotEntry.COLUMN_INTERVAL, this.interval); + bundle.putLong(BotEntry.COLUMN_TYPE_ID, this.typeId); + bundle.putLong(BotEntry.COLUMN_REGION_ID, this.regionId); + bundle.putDouble(BotEntry.COLUMN_THRESHOLD, this.threshold); + bundle.putString(BotEntry.COLUMN_TYPE_HREF, this.href); + bundle.putInt(BotEntry.COLUMN_IS_ABOVE, this.isAbove ? 0 : 1); + bundle.putInt(BotEntry.COLUMN_IS_BUY, this.isBuy ? 0 : 1); + + return bundle; + } + + public static Bot fromPersistableBundle(PersistableBundle bundle) { + Bot bot = new Bot(); + + bot.interval = bundle.getLong(BotEntry.COLUMN_INTERVAL); + bot.typeId = bundle.getLong(BotEntry.COLUMN_TYPE_ID); + bot.regionId = bundle.getLong(BotEntry.COLUMN_REGION_ID); + bot.threshold = bundle.getDouble(BotEntry.COLUMN_THRESHOLD); + bot.href = bundle.getString(BotEntry.COLUMN_TYPE_HREF); + bot.isAbove = bundle.getInt(BotEntry.COLUMN_IS_ABOVE) == 0; + bot.isBuy = bundle.getInt(BotEntry.COLUMN_IS_BUY) == 0; + + return bot; + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/data/storage/DataContracts.java b/app/src/main/java/com/w9jds/marketbot/data/storage/DataContracts.java new file mode 100644 index 0000000..ed91091 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/data/storage/DataContracts.java @@ -0,0 +1,167 @@ +package com.w9jds.marketbot.data.storage; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.provider.BaseColumns; + +import com.w9jds.eveapi.Models.MarketGroup; +import com.w9jds.marketbot.classes.utils.StorageUtils; +import com.w9jds.marketbot.data.models.Bot; + +import java.util.ArrayList; +import java.util.Collection; + +public final class DataContracts { + + public static final class BotEntry implements BaseColumns { + public static final String COLUMN_INTERVAL = "interval"; + public static final String COLUMN_TYPE_ID = "typeId"; + public static final String COLUMN_REGION_ID = "regionId"; + public static final String COLUMN_TYPE_HREF = "typeHref"; + public static final String COLUMN_THRESHOLD = "threshold"; + public static final String COLUMN_IS_BUY = "isBuy"; + public static final String COLUMN_IS_ABOVE = "isAbove"; + + // Table name + public static final String TABLE_NAME = "Bots"; + + // Create Command + public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(" + + _ID + " INTEGER PRIMARY KEY," + + COLUMN_INTERVAL + " INTEGER," + + COLUMN_TYPE_ID + " INTEGER," + + COLUMN_REGION_ID + " INTEGER," + + COLUMN_THRESHOLD + " REAL," + + COLUMN_TYPE_HREF + " TEXT," + + COLUMN_IS_BUY + " BOOLEAN," + + COLUMN_IS_ABOVE + " BOOLEAN," + + " UNIQUE (" + _ID + ") ON CONFLICT REPLACE);"; + + public static long createNewBot(Context context, Bot bot) { + SQLiteDatabase database = new Database(context).getWritableDatabaseHelper(); + long newId; + + database.beginTransaction(); + + ContentValues thisItem = new ContentValues(); + thisItem.put(COLUMN_INTERVAL, bot.getInterval()); + thisItem.put(COLUMN_TYPE_ID, bot.getTypeId()); + thisItem.put(COLUMN_REGION_ID, bot.getRegionId()); + thisItem.put(COLUMN_THRESHOLD, bot.getThreshold()); + thisItem.put(COLUMN_IS_ABOVE, bot.isCheckAboveThreshold()); + thisItem.put(COLUMN_IS_BUY, bot.isBuyOrder()); + thisItem.put(COLUMN_TYPE_HREF, bot.getTypeHref()); + + newId = database.insertWithOnConflict(TABLE_NAME, null, thisItem, SQLiteDatabase.CONFLICT_IGNORE); + + database.setTransactionSuccessful(); + database.endTransaction(); + database.close(); + return newId; + } + + public static Bot getBot(Context context, long id) { + SQLiteDatabase database = new Database(context).getWritableDatabaseHelper(); + + database.beginTransaction(); + + ArrayList bots = new ArrayList<>(); + + Cursor cursor = database.rawQuery("SELECT * FROM " + TABLE_NAME + + " WHERE " + _ID + "='" + id + "'", null); + + if (cursor.moveToFirst()) { + while (!cursor.isAfterLast()) { + bots.add(StorageUtils.buildClass(new Bot(), cursor)); + cursor.moveToNext(); + } + } + + database.setTransactionSuccessful(); + database.endTransaction(); + database.close(); + + if (bots.size() > 0) { + return bots.get(0); + } + else { + return null; + } + } + } + + public static final class MarketGroupEntry implements BaseColumns { + public static final String COLUMN_NAME = "name"; + public static final String COLUMN_HREF = "href"; + public static final String COLUMN_DESCRIPTION = "description"; + public static final String COLUMN_PARENT_ID = "parentId"; + public static final String COLUMN_TYPES_LOCATION = "typesHref"; + + // Table name + public static final String TABLE_NAME = "MarketGroups"; + + // Create Command + public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(" + + _ID + " INTEGER PRIMARY KEY," + + COLUMN_NAME + " TEXT," + + COLUMN_HREF + " TEXT," + + COLUMN_DESCRIPTION + " TEXT," + + COLUMN_PARENT_ID + " INTEGER," + + COLUMN_TYPES_LOCATION + " TEXT," + + " UNIQUE (" + _ID + ") ON CONFLICT REPLACE);"; + + public static long createNewMarketGroup(SQLiteDatabase database, MarketGroup group) { + long newId; + + database.beginTransaction(); + + ContentValues thisItem = new ContentValues(); + thisItem.put(_ID, group.getId()); + thisItem.put(COLUMN_NAME, group.getName()); + thisItem.put(COLUMN_HREF, group.getHref()); + thisItem.put(COLUMN_DESCRIPTION, group.getDescription()); + thisItem.put(COLUMN_PARENT_ID, group.getParentGroupId()); + thisItem.put(COLUMN_TYPES_LOCATION, group.getTypesLocation()); + + newId = database.insertWithOnConflict(TABLE_NAME, null, thisItem, SQLiteDatabase.CONFLICT_IGNORE); + + database.setTransactionSuccessful(); + database.endTransaction(); + database.close(); + return newId; + } + + public static void createNewMarketGroups(SQLiteDatabase database, Collection groups) { + long newId; + database.beginTransaction(); + + for (MarketGroup group : groups) { + ContentValues thisItem = new ContentValues(); + thisItem.put(_ID, group.getId()); + thisItem.put(COLUMN_NAME, group.getName()); + thisItem.put(COLUMN_HREF, group.getHref()); + thisItem.put(COLUMN_DESCRIPTION, group.getDescription()); + thisItem.put(COLUMN_PARENT_ID, group.getParentGroupId()); + thisItem.put(COLUMN_TYPES_LOCATION, group.getTypesLocation()); + + database.insertWithOnConflict(TABLE_NAME, null, thisItem, SQLiteDatabase.CONFLICT_REPLACE); + } + + database.setTransactionSuccessful(); + database.endTransaction(); + database.close(); + } + + public static void getMarketGroupsforParent(SQLiteDatabase database, Long parentId) { + + String query = "SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_PARENT_ID + "='" + + parentId + "'"; + + + + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/data/storage/Database.java b/app/src/main/java/com/w9jds/marketbot/data/storage/Database.java new file mode 100644 index 0000000..4ef5b51 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/data/storage/Database.java @@ -0,0 +1,56 @@ +package com.w9jds.marketbot.data.storage; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +public final class Database { + + private static final int DATABASE_VERSION = 1; + private static final String DATABASE_NAME = "MarketBotDb"; + + private final Helper mDatabaseOpenHelper; + + public Database(Context context) { + mDatabaseOpenHelper = new Helper(context); + } + + public SQLiteDatabase getWritableDatabaseHelper() { + return mDatabaseOpenHelper.getWritableDatabase(); + } + + public SQLiteDatabase getReadableDatabaseHelper() { + return mDatabaseOpenHelper.getReadableDatabase(); + } + + private static class Helper extends SQLiteOpenHelper { + + public Helper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase database) { + database.beginTransaction(); + + database.execSQL(DataContracts.BotEntry.CREATE_TABLE); + + database.setTransactionSuccessful(); + database.endTransaction(); + } + + @Override + public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion) { + database.beginTransaction(); + + database.execSQL("DROP TABLE IF EXISTS " + DataContracts.BotEntry.TABLE_NAME); + + database.setTransactionSuccessful(); + database.endTransaction(); + + onCreate(database); + + database.close(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/services/BotJobService.java b/app/src/main/java/com/w9jds/marketbot/services/BotJobService.java new file mode 100644 index 0000000..ffba418 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/services/BotJobService.java @@ -0,0 +1,289 @@ +package com.w9jds.marketbot.services; + +import android.annotation.TargetApi; +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.content.Context; + +import android.os.AsyncTask; +import android.os.Build; + +import com.w9jds.eveapi.Client.Crest; + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public final class BotJobService extends JobService { + + private static final String TAG = "MailService"; + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public boolean onStartJob(JobParameters params) { + new CheckMarketOrders(this) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); + return true; + } + + @Override + public boolean onStopJob(JobParameters params) { + + + return false; + } + + private class CheckMarketOrders extends AsyncTask { + + private Context context; + private JobParameters params; + private Crest publicCrestApi; + + public CheckMarketOrders(Context context) { + this.context = context; + + publicCrestApi = new Crest.Builder() + .setPublicTranquilityEndpoint() + .build(context); + } + + @Override + protected JobParameters[] doInBackground(JobParameters... params) { + + this.params = params[0]; + + +// mCurrentCharacter = new Character(); +// PersistableBundle bundle = mParams.getExtras(); +// +// mCurrentCharacter.setCharacterId(bundle.getLong(Constants.CHARACTER_ID)); +// mCurrentCharacter.setCharacterName(bundle.getString(Constants.CHARACTER_NAME)); +// mCurrentCharacter.setKeyId(bundle.getString(Constants.KEYID)); +// mCurrentCharacter.setvCode(bundle.getString(Constants.VCODE)); +// +// mApiService = new Api.Builder() +// .setTranquilityEndpoint() +// .build(); +// +// mApiService.getMailHeaders(mCurrentCharacter, this); + + // Do updating and stopping logical here. + return params; + } + +// @Override +// public void success(Response> mails) { +// scheduleNextUpdate(mails.getCurrentTime(), mails.getCachedUntil()); +// ArrayList ids = new ArrayList<>(); +// +// for (MailHeader header : mails.getResult()) { +// if (header.getSenderId() != mCurrentCharacter.getCharacterId()) { +// long newId = MailHeaderEntry.addNewMailHeader(mContext, header); +// ids.add(header.getMessageId()); +// +// if (newId > 0) { +// mMails.add(header); +// } +// } +// } +// +// if (ids.size() > 0) { +// mApiService.getMailBodies(mCurrentCharacter, ids, mMailBodyCallback); +// } +// +// cleanOutMailbox(mails.getResult()); +// } + +// private void cleanOutMailbox(ArrayList mails) { +// HashMap headers = new HashMap<>(); +// ArrayList onDevice = MailHeaderEntry.getInbox(mContext, mCurrentCharacter); +// ArrayList removeItems = new ArrayList<>(); +// +// for (MailHeader header : mails) { +// headers.put(header.getMessageId(), header); +// } +// +// for (MailHeader header : onDevice) { +// if (!headers.containsKey(header.getMessageId())) { +// removeItems.add(header.getMessageId()); +// Log.d(TAG, "removing message " + header.getMessageId()); +// } +// } +// +// MailHeaderEntry.removeMails(mContext, removeItems); +// } + +// private void buildSummary(ArrayList headers) { +// NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); +// inboxStyle.setBigContentTitle(headers.size() + " new messages"); +// inboxStyle.setSummaryText(mCurrentCharacter.getCharacterName()); +// +// Intent mailIntent = new Intent(mContext, MainActivity.class); +// Bundle bundle = new Bundle(); +// bundle.putString(Constants.CURRENT_ACCOUNT, mCurrentCharacter.getAccountName()); +// bundle.putLong(Constants.CHARACTER_ID, mCurrentCharacter.getCharacterId()); +// bundle.putString(SyncStateContract.Constants.CURRENT_FRAGMENT, "Mail"); +// mailIntent.putExtra(Constants.NOTIFICATION_BUNDLE, bundle); +// +// PendingIntent intent = PendingIntent.getActivity(mContext, (int)mCurrentCharacter.getCharacterId(), mailIntent, +// PendingIntent.FLAG_UPDATE_CURRENT); +// +// for (MailHeader header : headers) { +// int senderLength = header.getSenderName().length(); +// int titleLength = header.getTitle().length(); +// +// Spannable spannable = new SpannableString(header.getSenderName() + " " + header.getTitle()); +// spannable.setSpan(new StyleSpan(Typeface.BOLD), 0, +// senderLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); +// spannable.setSpan(new StyleSpan(Typeface.NORMAL), senderLength, senderLength + titleLength, +// Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); +// inboxStyle.addLine(spannable); +// +// buildNotification(header, false); +// } +// +// NotificationManager manager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); +// +// Notification summaryNotification = new NotificationCompat.Builder(mContext) +// .setContentTitle(headers.size() + " new messages") +// .setContentText(mCurrentCharacter.getCharacterName()) +// .setAutoCancel(true) +// .setSmallIcon(R.drawable.ic_mail_white_24dp) +// .setTicker(String.valueOf(headers.size())) +// .setNumber(headers.size()) +// .setColor(Color.parseColor("#2980b9")) +// .setStyle(inboxStyle) +// .setContentIntent(intent) +// .setGroup(String.valueOf(mCurrentCharacter.getCharacterId())) +// .setGroupSummary(true) +// .setDefaults(Notification.DEFAULT_ALL) +// .build(); +// +// manager.notify((int) mCurrentCharacter.getCharacterId(), summaryNotification); +// } + +// private void buildNotification(final MailHeader header, final boolean soundOn) { +// Intent mailIntent = new Intent(mContext, MessageActivity.class); +// Bundle bundle = new Bundle(); +// bundle.putLong(Constants.CHARACTER_ID, mCurrentCharacter.getCharacterId()); +// bundle.putLong(Constants.CURRENT_MESSAGE, header.getMessageId()); +// mailIntent.putExtra(Constants.NOTIFICATION_BUNDLE, bundle); +// +// final PendingIntent intent = PendingIntent.getActivity(mContext, 0, mailIntent, +// PendingIntent.FLAG_UPDATE_CURRENT); +// +// DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); +// float imageSize = 64 * (metrics.density / 3f); +// +// Glide.with(mContext) +// .load(String.format(Constants.PORTRAIT_FORMAT, ImagePath.CHARACTER, +// header.getSenderId(), "200", ImageType.JPG)) +// .asBitmap() +// .into(new SimpleTarget((int) imageSize, (int) imageSize) { +// @Override +// public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { +// +// NotificationManager manager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); +// NotificationCompat.Builder mailBuilder = new NotificationCompat.Builder(mContext) +// .setSmallIcon(R.drawable.ic_mail_white_24dp) +// .setLargeIcon(resource) +// .setColor(Color.parseColor("#2980b9")) +// .setAutoCancel(true) +// .setContentTitle(header.getSenderName()) +// .setContentText(header.getTitle()) +// .setContentIntent(intent) +// .setGroup(String.valueOf(mCurrentCharacter.getCharacterId())); +// +// if (header.getMessageBody() != null && !header.getMessageBody().equals("")) { +// String messageBody = Html.fromHtml(header.getMessageBody()).toString(); +// messageBody = messageBody.replaceAll("\\n\\n","\n") +// .replaceAll("\\s\\s+", " "); +// +// mailBuilder.setStyle(new NotificationCompat.BigTextStyle() +// .setBigContentTitle(header.getTitle()) +// .bigText(messageBody) +// .setSummaryText(mCurrentCharacter.getCharacterName())); +// } +// +// if (soundOn) { +// mailBuilder.setDefaults(Notification.DEFAULT_ALL); +// } +// +// manager.notify((int) header.getMessageId(), mailBuilder.build()); +// } +// }); +// } + +// Callback> mMailBodyCallback = new Callback>() { +// @Override +// public void success(ArrayList mailBodies) { +// MailBodyEntry.addMailBodies(mContext, mailBodies); +// HashMap bodies = new HashMap<>(); +// +// for (MailBody body : mailBodies) { +// bodies.put(body.getMessageId(), body.getMessageBody()); +// } +// +// for (MailHeader header : mMails) { +// header.setMessageBody(bodies.get(header.getMessageId())); +// } +// +// if (mMails.size() > 1) { +// buildSummary(mMails); +// } +// else if (mMails.size() == 1) { +// buildNotification(mMails.get(0), true); +// } +// +// jobFinished(mParams, false); +// } +// +// @Override +// public void failure(String error) { +// jobFinished(mParams, true); +// } +// }; + +// @Override +// public void failure(String error) { +// jobFinished(mParams, true); +// } + +// private void scheduleNextUpdate(Date current, Date cached) { +// long diff = cached.getTime() - current.getTime(); +// +// PersistableBundle info = new PersistableBundle(); +// info.putLong(Constants.CHARACTER_ID, mCurrentCharacter.getCharacterId()); +// info.putString(Constants.CHARACTER_NAME, mCurrentCharacter.getCharacterName()); +// info.putString(Constants.KEYID, mCurrentCharacter.getKeyId()); +// info.putString(Constants.VCODE, mCurrentCharacter.getvCode()); +// +// JobScheduler scheduler = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE); +// ComponentName serviceName = new ComponentName(mContext, MailJobService.class); +// JobInfo jobInfo = new JobInfo.Builder((int)mCurrentCharacter.getCharacterId() + 1, serviceName) +// .setExtras(info) +// .setPersisted(true) +// .setRequiresCharging(false) +// .setRequiresDeviceIdle(false) +// .setMinimumLatency(diff + 60000) +// .build(); +// +// scheduler.schedule(jobInfo); +// } + + @Override + protected void onPostExecute(JobParameters[] result) { +// for (JobParameters params : result) { +// if (!hasJobBeenStopped(params)) { +// jobFinished(params, false); +// } +// } + } + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/ui/animations/GridItemAnimator.java b/app/src/main/java/com/w9jds/marketbot/ui/animations/GridItemAnimator.java new file mode 100644 index 0000000..efcd5bd --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/animations/GridItemAnimator.java @@ -0,0 +1,52 @@ +package com.w9jds.marketbot.ui.animations; + +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Animation; +import android.view.animation.TranslateAnimation; + +import com.w9jds.marketbot.adapters.MarketGroupsAdapter; + +public class GridItemAnimator extends DefaultItemAnimator { + + @Override + public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) { + return true; + } + + @Override + public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, + ItemHolderInfo preInfo, ItemHolderInfo postInfo) { + + final MarketGroupsAdapter.MarketGroupHolder holder = (MarketGroupsAdapter.MarketGroupHolder)newHolder; + + TranslateAnimation exitAnimation = new TranslateAnimation(0, -holder.itemView.getWidth(), 0, 0); + exitAnimation.setDuration(500); + exitAnimation.setFillAfter(false); + + + TranslateAnimation enterAnimation = new TranslateAnimation(holder.itemView.getWidth(), 0, 0, 0); + enterAnimation.setDuration(500); + enterAnimation.setFillAfter(false); + + exitAnimation.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + dispatchAnimationStarted(holder); + } + + @Override + public void onAnimationEnd(Animation animation) { + + dispatchAnimationFinished(holder); + } + + @Override + public void onAnimationRepeat(Animation animation) { + + } + }); + + return super.animateChange(oldHolder, newHolder, preInfo, postInfo); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/ui/drawable/MorphDrawable.java b/app/src/main/java/com/w9jds/marketbot/ui/drawable/MorphDrawable.java new file mode 100644 index 0000000..820e3cb --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/drawable/MorphDrawable.java @@ -0,0 +1,103 @@ +package com.w9jds.marketbot.ui.drawable; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorInt; +import android.util.Property; + +import com.w9jds.marketbot.classes.utils.AnimUtils; + +/** + * A drawable that can morph size, shape (via it's corner radius) and color. Specifically this is + * useful for animating between a FAB and a dialog. + */ +public class MorphDrawable extends Drawable { + + private float cornerRadius; + private Paint paint; + + public static final Property CORNER_RADIUS = new AnimUtils + .FloatProperty("cornerRadius") { + + @Override + public void setValue(MorphDrawable morphDrawable, float value) { + morphDrawable.setCornerRadius(value); + } + + @Override + public Float get(MorphDrawable morphDrawable) { + return morphDrawable.getCornerRadius(); + } + }; + + + public static final Property COLOR = new AnimUtils + .IntProperty("color") { + + @Override + public void setValue(MorphDrawable morphDrawable, int value) { + morphDrawable.setColor(value); + } + + @Override + public Integer get(MorphDrawable morphDrawable) { + return morphDrawable.getColor(); + } + }; + + public MorphDrawable(@ColorInt int color, float cornerRadius) { + this.cornerRadius = cornerRadius; + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(color); + } + + public float getCornerRadius() { + return cornerRadius; + } + + public void setCornerRadius(float cornerRadius) { + this.cornerRadius = cornerRadius; + invalidateSelf(); + } + + public int getColor() { + return paint.getColor(); + } + + public void setColor(int color) { + paint.setColor(color); + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right, getBounds() + .bottom, cornerRadius, cornerRadius, paint); + } + + @Override + public void getOutline(Outline outline) { + outline.setRoundRect(getBounds(), cornerRadius); + } + + @Override + public void setAlpha(int alpha) { + paint.setAlpha(alpha); + invalidateSelf(); + } + + @Override + public void setColorFilter(ColorFilter cf) { + paint.setColorFilter(cf); + invalidateSelf(); + } + + @Override + public int getOpacity() { + return paint.getAlpha(); + } + +} diff --git a/app/src/main/java/com/w9jds/marketbot/ui/fragments/OrdersTab.java b/app/src/main/java/com/w9jds/marketbot/ui/fragments/OrdersTab.java new file mode 100644 index 0000000..f099558 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/fragments/OrdersTab.java @@ -0,0 +1,157 @@ +package com.w9jds.marketbot.ui.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.w9jds.eveapi.Models.MarketItemBase; +import com.w9jds.eveapi.Models.MarketOrder; +import com.w9jds.eveapi.Models.Region; +import com.w9jds.eveapi.Models.Type; +import com.w9jds.marketbot.R; +import com.w9jds.marketbot.activities.ItemActivity; +import com.w9jds.marketbot.adapters.OrdersAdapter; +import com.w9jds.marketbot.classes.Triplet; +import com.w9jds.marketbot.data.BaseDataManager; +import com.w9jds.marketbot.data.DataManager; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jeremy on 3/1/2016. + */ +public final class OrdersTab extends Fragment implements BaseDataManager.DataLoadingCallbacks { + + static final String ARG_PAGE = "ARG_PAGE"; + + @Bind(R.id.swipe_refresh) + SwipeRefreshLayout refreshLayout; + + @Bind(R.id.orders_list) + RecyclerView orders; + + private int position; + + private OrdersAdapter adapter; + private DataManager dataManager; + + private Region currentRegion; + private Type currentType; + + public static OrdersTab create(int page) { + Bundle args = new Bundle(); + args.putInt(ARG_PAGE, page); + OrdersTab fragment = new OrdersTab(); + fragment.setArguments(args); + return fragment; + } + + public OrdersTab() { + + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle args = getArguments(); + + position = args.getInt(ARG_PAGE); + + adapter = new OrdersAdapter(getContext()); + dataManager = new DataManager(getContext()) { + @Override + public void onDataLoaded(List data) { + if (data.size() > 0) { + if (data.get(0) instanceof MarketOrder) { + ArrayList orders = new ArrayList<>(); + for (MarketItemBase base : data) { + orders.add((MarketOrder) base); + } + + adapter.updateCollection(orders); + } + } + } + + @Override + public void onDataLoaded(Object data) { + // never fired + } + }; + + dataManager.registerCallback(this); + ((ItemActivity)getActivity()).addOrdersFragment(position, this); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_orders_list, container, false); + ButterKnife.bind(this, view); + + orders.setLayoutManager(new LinearLayoutManager(getContext())); + orders.setItemAnimator(new DefaultItemAnimator()); + orders.setAdapter(adapter); + + refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + updateOrdersList(currentRegion, currentType); + } + }); + + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.unbind(this); + } + + public void updateOrdersList(Region region, Type type) { + currentRegion = region; + currentType = type; + + adapter.clear(); + switch(position) { + case 1: + dataManager.loadSellOrders(region, type); + break; + case 2: + dataManager.loadBuyOrders(region, type); + break; + } + } + + @Override + public void dataStartedLoading() { + if (refreshLayout != null) { + refreshLayout.setRefreshing(true); + } + } + + @Override + public void dataFinishedLoading() { + if (refreshLayout != null) { + refreshLayout.setRefreshing(false); + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/ui/fragments/TypeInfoTab.java b/app/src/main/java/com/w9jds/marketbot/ui/fragments/TypeInfoTab.java new file mode 100644 index 0000000..3dd34c6 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/fragments/TypeInfoTab.java @@ -0,0 +1,153 @@ +package com.w9jds.marketbot.ui.fragments; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.w9jds.eveapi.Models.MarketItemBase; +import com.w9jds.eveapi.Models.TypeInfo; +import com.w9jds.marketbot.R; +import com.w9jds.marketbot.activities.ItemActivity; +import com.w9jds.marketbot.data.BaseDataManager; +import com.w9jds.marketbot.data.DataManager; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.List; +import java.util.Locale; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jeremy Shore on 3/3/16. + */ +public final class TypeInfoTab extends Fragment implements BaseDataManager.DataLoadingCallbacks { + + static final String ARG_PAGE = "ARG_PAGE"; + static final String ARG_TYPEID = "ARG_TYPEID"; + + @Bind(R.id.dataloading_progress) + ProgressBar loading; + + @Bind(R.id.type_name) + TextView name; + @Bind(R.id.type_description) + TextView description; + @Bind(R.id.mass_value) + TextView mass; + @Bind(R.id.capacity_value) + TextView capacity; + @Bind(R.id.volume_value) + TextView volume; + @Bind(R.id.portion_value) + TextView portion; + + @Bind(R.id.item_icon) + ImageView icon; + + private int position; + private long typeId; + private DataManager dataManager; + + /** + * TODO: Possibly show ship 3d view using webresource + */ + public static TypeInfoTab create(int page, long typeId) { + Bundle args = new Bundle(); + args.putInt(ARG_PAGE, page); + args.putLong(ARG_TYPEID, typeId); + TypeInfoTab fragment = new TypeInfoTab(); + fragment.setArguments(args); + return fragment; + } + + private String formatNumberValue(double value, String unit) { + if (value > 1000000) { + return String.format(Locale.ENGLISH, "%.2fm %s", value / 1000000.0, unit); + } + else { + NumberFormat formatter = new DecimalFormat("#,##0.00"); + return formatter.format(value) + " " + unit; + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle args = getArguments(); + + position = args.getInt(ARG_PAGE); + typeId = args.getLong(ARG_TYPEID); + + dataManager = new DataManager(getContext()) { + @Override + public void onDataLoaded(List data) { + + } + + @Override + public void onDataLoaded(Object data) { + if (data instanceof TypeInfo) { + TypeInfo info = (TypeInfo) data; + + NumberFormat formatter = new DecimalFormat("#,###"); + String capacityValue = formatter.format(info.getCapacity()) + " m3"; + + description.setText(Html.fromHtml(info.getDescription())); + mass.setText(formatNumberValue(info.getMass(), "kg")); + capacity.setText(capacityValue); + portion.setText(String.valueOf(info.getPortionSize())); + volume.setText(formatNumberValue(info.getVolume(), "m3")); + } + } + }; + + dataManager.registerCallback(this); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_type_info, container, false); + ButterKnife.bind(this, view); + + ItemActivity host = (ItemActivity) getActivity(); + name.setText(host.getCurrentTypeName()); + Glide.with(host) + .load(host.getCurrentTypeIcon()) + .into(icon); + + dataManager.loadTypeInfo(typeId); + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.unbind(this); + } + + @Override + public void dataStartedLoading() { + mass.setVisibility(View.GONE); + capacity.setVisibility(View.GONE); + + loading.setVisibility(View.VISIBLE); + } + + @Override + public void dataFinishedLoading() { + loading.setVisibility(View.GONE); + + mass.setVisibility(View.VISIBLE); + capacity.setVisibility(View.VISIBLE); + } + +} diff --git a/app/src/main/java/com/w9jds/marketbot/ui/fragments/onRegionChanged.java b/app/src/main/java/com/w9jds/marketbot/ui/fragments/onRegionChanged.java new file mode 100644 index 0000000..56806fd --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/fragments/onRegionChanged.java @@ -0,0 +1,13 @@ +package com.w9jds.marketbot.ui.fragments; + +import com.w9jds.eveapi.Models.Region; +import com.w9jds.eveapi.Models.Type; + +/** + * Created by Jeremy on 3/1/2016. + */ +public interface onRegionChanged { + + void updateOrdersList(Region region, Type type, int position); + +} diff --git a/app/src/main/java/com/w9jds/marketbot/ui/tansitions/FabDialogMorphSetup.java b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/FabDialogMorphSetup.java new file mode 100644 index 0000000..a6f2530 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/FabDialogMorphSetup.java @@ -0,0 +1,50 @@ +package com.w9jds.marketbot.ui.tansitions; + +/** + * Helper class for setting up Fab <-> Dialog shared element transitions. + */ +public class FabDialogMorphSetup { +// +// public static final String EXTRA_SHARED_ELEMENT_START_COLOR = +// "EXTRA_SHARED_ELEMENT_START_COLOR"; +// public static final String EXTRA_SHARED_ELEMENT_START_CORNER_RADIUS = +// "EXTRA_SHARED_ELEMENT_START_CORNER_RADIUS"; +// +// private FabDialogMorphSetup() { } +// +// /** +// * Configure the shared element transitions for morphin from a fab <-> dialog. We need to do +// * this in code rather than declaratively as we need to supply the color to transition from/to +// * and the dialog corner radius which is dynamically supplied depending upon where this screen +// * is launched from. +// */ +// public static void setupSharedEelementTransitions(@NonNull Activity activity, +// @Nullable View target, +// int dialogCornerRadius) { +// if (!activity.getIntent().hasExtra(EXTRA_SHARED_ELEMENT_START_COLOR)) return; +// +// int startCornerRadius = activity.getIntent().getIntExtra +// (EXTRA_SHARED_ELEMENT_START_CORNER_RADIUS, -1); +// +// ArcMotion arcMotion = new ArcMotion(); +// arcMotion.setMinimumHorizontalAngle(50f); +// arcMotion.setMinimumVerticalAngle(50f); +// int color = activity.getIntent(). +// getIntExtra(EXTRA_SHARED_ELEMENT_START_COLOR, Color.TRANSPARENT); +// Interpolator easeInOut = +// AnimUtils.getFastOutSlowInInterpolator(activity); +// MorphFabToDialog sharedEnter = new MorphFabToDialog(color, dialogCornerRadius, startCornerRadius); +// sharedEnter.setPathMotion(arcMotion); +// sharedEnter.setInterpolator(easeInOut); +// MorphDialogToFab sharedReturn = new MorphDialogToFab(color, startCornerRadius); +// sharedReturn.setPathMotion(arcMotion); +// sharedReturn.setInterpolator(easeInOut); +// if (target != null) { +// sharedEnter.addTarget(target); +// sharedReturn.addTarget(target); +// } +// activity.getWindow().setSharedElementEnterTransition(sharedEnter); +// activity.getWindow().setSharedElementReturnTransition(sharedReturn); +// } + +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/ui/tansitions/GroupSharedEnter.java b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/GroupSharedEnter.java new file mode 100644 index 0000000..41e6f0b --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/GroupSharedEnter.java @@ -0,0 +1,27 @@ +package com.w9jds.marketbot.ui.tansitions; + +import android.content.Context; +import android.graphics.Rect; +import android.transition.ChangeBounds; +import android.transition.TransitionValues; +import android.util.AttributeSet; +import android.view.View; + +public class GroupSharedEnter extends ChangeBounds { + + private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds"; + private static final String PROPNAME_PARENT = "android:changeBounds:parent"; + + public GroupSharedEnter(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureEndValues(transitionValues); + Rect bounds = (Rect) transitionValues.values.get(PROPNAME_BOUNDS); + bounds.right = ((View) transitionValues.values.get(PROPNAME_PARENT)).getWidth(); + bounds.bottom = ((View) transitionValues.values.get(PROPNAME_PARENT)).getHeight(); + transitionValues.values.put(PROPNAME_BOUNDS, bounds); + } +} diff --git a/app/src/main/java/com/w9jds/marketbot/ui/tansitions/MorphDialogToFab.java b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/MorphDialogToFab.java new file mode 100644 index 0000000..c0c23ff --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/MorphDialogToFab.java @@ -0,0 +1,121 @@ +package com.w9jds.marketbot.ui.tansitions; + +import android.transition.ChangeBounds; + +/** + * A transition that morphs a rectangle into a circle, changing it's background color. + */ +public class MorphDialogToFab extends ChangeBounds { +// +// private static final String PROPERTY_COLOR = "io.plaidapp:rectMorph:color"; +// private static final String PROPERTY_CORNER_RADIUS = "plaid:rectMorph:cornerRadius"; +// private static final String[] TRANSITION_PROPERTIES = { +// PROPERTY_COLOR, +// PROPERTY_CORNER_RADIUS +// }; +// +// private @ColorInt +// int endColor = Color.TRANSPARENT; +// private int endCornerRadius = -1; +// +// public MorphDialogToFab(@ColorInt int endColor) { +// super(); +// setEndColor(endColor); +// } +// +// public MorphDialogToFab(@ColorInt int endColor, int endCornerRadius) { +// super(); +// setEndColor(endColor); +// setEndCornerRadius(endCornerRadius); +// } +// +// public MorphDialogToFab(Context context, AttributeSet attrs) { +// super(context, attrs); +// } +// +// public void setEndColor(@ColorInt int endColor) { +// this.endColor = endColor; +// } +// +// public void setEndCornerRadius(int endCornerRadius) { +// this.endCornerRadius = endCornerRadius; +// } +// +// @Override +// public String[] getTransitionProperties() { +// return TRANSITION_PROPERTIES; +// } +// +// @Override +// public void captureStartValues(TransitionValues transitionValues) { +// super.captureStartValues(transitionValues); +// final View view = transitionValues.view; +// if (view.getWidth() <= 0 || view.getHeight() <= 0) { +// return; +// } +// transitionValues.values.put(PROPERTY_COLOR, +// ContextCompat.getColor(view.getContext(), R.color.background_light)); +// transitionValues.values.put(PROPERTY_CORNER_RADIUS, view.getResources() +// .getDimensionPixelSize(R.dimen.dialog_corners)); +// } +// +// @Override +// public void captureEndValues(TransitionValues transitionValues) { +// super.captureEndValues(transitionValues); +// final View view = transitionValues.view; +// if (view.getWidth() <= 0 || view.getHeight() <= 0) { +// return; +// } +// transitionValues.values.put(PROPERTY_COLOR, endColor); +// transitionValues.values.put(PROPERTY_CORNER_RADIUS, +// endCornerRadius >= 0 ? endCornerRadius : view.getHeight() / 2); +// } +// +// @Override +// public Animator createAnimator(final ViewGroup sceneRoot, +// TransitionValues startValues, +// TransitionValues endValues) { +// Animator changeBounds = super.createAnimator(sceneRoot, startValues, endValues); +// if (startValues == null || endValues == null || changeBounds == null) { +// return null; +// } +// +// Integer startColor = (Integer) startValues.values.get(PROPERTY_COLOR); +// Integer startCornerRadius = (Integer) startValues.values.get(PROPERTY_CORNER_RADIUS); +// Integer endColor = (Integer) endValues.values.get(PROPERTY_COLOR); +// Integer endCornerRadius = (Integer) endValues.values.get(PROPERTY_CORNER_RADIUS); +// +// if (startColor == null || startCornerRadius == null || endColor == null || +// endCornerRadius == null) { +// return null; +// } +// +// MorphDrawable background = new MorphDrawable(startColor, startCornerRadius); +// endValues.view.setBackground(background); +// +// Animator color = ObjectAnimator.ofArgb(background, background.COLOR, endColor); +// Animator corners = ObjectAnimator.ofFloat(background, background.CORNER_RADIUS, +// endCornerRadius); +// +// // hide child views (offset down & fade out) +// if (endValues.view instanceof ViewGroup) { +// ViewGroup vg = (ViewGroup) endValues.view; +// for (int i = 0; i < vg.getChildCount(); i++) { +// View v = vg.getChildAt(i); +// v.animate() +// .alpha(0f) +// .translationY(v.getHeight() / 3) +// .setStartDelay(0L) +// .setDuration(50L) +// .setInterpolator(getFastOutLinearInInterpolator(vg.getContext())) +// .start(); +// } +// } +// +// AnimatorSet transition = new AnimatorSet(); +// transition.playTogether(changeBounds, corners, color); +// transition.setDuration(300); +// transition.setInterpolator(getFastOutSlowInInterpolator(sceneRoot.getContext())); +// return transition; +// } +} diff --git a/app/src/main/java/com/w9jds/marketbot/ui/tansitions/MorphFabToDialog.java b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/MorphFabToDialog.java new file mode 100644 index 0000000..6a7e4f3 --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/MorphFabToDialog.java @@ -0,0 +1,128 @@ +package com.w9jds.marketbot.ui.tansitions; + +import android.transition.ChangeBounds; + +/** + * A transition that morphs a circle into a rectangle, changing it's background color. + */ +public class MorphFabToDialog extends ChangeBounds { +// +// private static final String PROPERTY_COLOR = "plaid:circleMorph:color"; +// private static final String PROPERTY_CORNER_RADIUS = "plaid:circleMorph:cornerRadius"; +// private static final String[] TRANSITION_PROPERTIES = { +// PROPERTY_COLOR, +// PROPERTY_CORNER_RADIUS +// }; +// private @ColorInt +// int startColor = Color.TRANSPARENT; +// private int endCornerRadius; +// private int startCornerRadius; +// +// public MorphFabToDialog(@ColorInt int startColor, int endCornerRadius) { +// this(startColor, endCornerRadius, -1); +// } +// +// public MorphFabToDialog(@ColorInt int startColor, int endCornerRadius, int startCornerRadius) { +// super(); +// setStartColor(startColor); +// setEndCornerRadius(endCornerRadius); +// setStartCornerRadius(startCornerRadius); +// } +// +// public MorphFabToDialog(Context context, AttributeSet attrs) { +// super(context, attrs); +// } +// +// public void setStartColor(@ColorInt int startColor) { +// this.startColor = startColor; +// } +// +// public void setEndCornerRadius(int endCornerRadius) { +// this.endCornerRadius = endCornerRadius; +// } +// +// public void setStartCornerRadius(int startCornerRadius) { +// this.startCornerRadius = startCornerRadius; +// } +// +// @Override +// public String[] getTransitionProperties() { +// return TRANSITION_PROPERTIES; +// } +// +// @Override +// public void captureStartValues(TransitionValues transitionValues) { +// super.captureStartValues(transitionValues); +// final View view = transitionValues.view; +// if (view.getWidth() <= 0 || view.getHeight() <= 0) { +// return; +// } +// transitionValues.values.put(PROPERTY_COLOR, startColor); +// transitionValues.values.put(PROPERTY_CORNER_RADIUS, +// startCornerRadius >= 0 ? startCornerRadius : view.getHeight() / 2); +// } +// +// @Override +// public void captureEndValues(TransitionValues transitionValues) { +// super.captureEndValues(transitionValues); +// final View view = transitionValues.view; +// if (view.getWidth() <= 0 || view.getHeight() <= 0) { +// return; +// } +// transitionValues.values.put(PROPERTY_COLOR, +// ContextCompat.getColor(view.getContext(), R.color.background_light)); +// transitionValues.values.put(PROPERTY_CORNER_RADIUS, endCornerRadius); +// } +// +// @Override +// public Animator createAnimator(final ViewGroup sceneRoot, +// TransitionValues startValues, +// final TransitionValues endValues) { +// Animator changeBounds = super.createAnimator(sceneRoot, startValues, endValues); +// if (startValues == null || endValues == null || changeBounds == null) { +// return null; +// } +// +// Integer startColor = (Integer) startValues.values.get(PROPERTY_COLOR); +// Integer startCornerRadius = (Integer) startValues.values.get(PROPERTY_CORNER_RADIUS); +// Integer endColor = (Integer) endValues.values.get(PROPERTY_COLOR); +// Integer endCornerRadius = (Integer) endValues.values.get(PROPERTY_CORNER_RADIUS); +// +// if (startColor == null || startCornerRadius == null || endColor == null || +// endCornerRadius == null) { +// return null; +// } +// +// MorphDrawable background = new MorphDrawable(startColor, startCornerRadius); +// endValues.view.setBackground(background); +// +// Animator color = ObjectAnimator.ofArgb(background, background.COLOR, endColor); +// Animator corners = ObjectAnimator.ofFloat(background, background.CORNER_RADIUS, +// endCornerRadius); +// +// // ease in the dialog's child views (slide up & fade in) +// if (endValues.view instanceof ViewGroup) { +// ViewGroup vg = (ViewGroup) endValues.view; +// float offset = vg.getHeight() / 3; +// for (int i = 0; i < vg.getChildCount(); i++) { +// View v = vg.getChildAt(i); +// v.setTranslationY(offset); +// v.setAlpha(0f); +// v.animate() +// .alpha(1f) +// .translationY(0f) +// .setDuration(150) +// .setStartDelay(150) +// .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(vg.getContext())); +// offset *= 1.8f; +// } +// } +// +// AnimatorSet transition = new AnimatorSet(); +// transition.playTogether(changeBounds, corners, color); +// transition.setDuration(300); +// transition.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(sceneRoot.getContext())); +// return transition; +// } + +} \ No newline at end of file diff --git a/app/src/main/java/com/w9jds/marketbot/ui/tansitions/Pop.java b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/Pop.java new file mode 100644 index 0000000..6c47c5b --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/tansitions/Pop.java @@ -0,0 +1,75 @@ +package com.w9jds.marketbot.ui.tansitions; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.content.Context; +import android.transition.TransitionValues; +import android.transition.Visibility; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.w9jds.marketbot.classes.utils.AnimUtils; + +/** + * Created by Jeremy Shore on 2/18/16. + */ +public class Pop extends Visibility { + + private static final String PROPNAME_ALPHA = "plaid:pop:alpha"; + private static final String PROPNAME_SCALE_X = "plaid:pop:scaleX"; + private static final String PROPNAME_SCALE_Y = "plaid:pop:scaleY"; + + private static final String[] transitionProperties = { + PROPNAME_ALPHA, + PROPNAME_SCALE_X, + PROPNAME_SCALE_Y + }; + + public Pop(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public String[] getTransitionProperties() { + return transitionProperties; + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + transitionValues.values.put(PROPNAME_ALPHA, 0f); + transitionValues.values.put(PROPNAME_SCALE_X, 0f); + transitionValues.values.put(PROPNAME_SCALE_Y, 0f); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureEndValues(transitionValues); + transitionValues.values.put(PROPNAME_ALPHA, 1f); + transitionValues.values.put(PROPNAME_SCALE_X, 1f); + transitionValues.values.put(PROPNAME_SCALE_Y, 1f); + } + + @Override + public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, + TransitionValues endValues) { + return new AnimUtils.NoPauseAnimator(ObjectAnimator.ofPropertyValuesHolder( + endValues.view, + PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f), + PropertyValuesHolder.ofFloat(View.SCALE_X, 0f, 1f), + PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f, 1f))); + } + + @Override + public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, + TransitionValues endValues) { + return new AnimUtils.NoPauseAnimator(ObjectAnimator.ofPropertyValuesHolder( + endValues.view, + PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f), + PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 0f), + PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 0f))); + } + +} diff --git a/app/src/main/java/com/w9jds/marketbot/ui/widgets/ElasticDragDismissFrameLayout.java b/app/src/main/java/com/w9jds/marketbot/ui/widgets/ElasticDragDismissFrameLayout.java new file mode 100644 index 0000000..11ae9af --- /dev/null +++ b/app/src/main/java/com/w9jds/marketbot/ui/widgets/ElasticDragDismissFrameLayout.java @@ -0,0 +1,290 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.w9jds.marketbot.ui.widgets; + +import android.app.Activity; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import com.w9jds.marketbot.R; +import com.w9jds.marketbot.classes.utils.AnimUtils; +import com.w9jds.marketbot.classes.utils.ColorUtils; +import com.w9jds.marketbot.classes.utils.ViewUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * A {@link FrameLayout} which responds to nested scrolls to create drag-dismissable layouts. + * Applies an elasticity factor to reduce movement as you approach the given dismiss distance. + * Optionally also scales down content during drag. + */ +public class ElasticDragDismissFrameLayout extends FrameLayout { + + // configurable attribs + private float dragDismissDistance = Float.MAX_VALUE; + private float dragDismissFraction = -1f; + private float dragDismissScale = 1f; + private boolean shouldScale = false; + private float dragElacticity = 0.8f; + + // state + private float totalDrag; + private boolean draggingDown = false; + private boolean draggingUp = false; + + private List listeners; + + public ElasticDragDismissFrameLayout(Context context) { + this(context, null, 0, 0); + } + + public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0, 0); + } + + public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs, + int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + final TypedArray a = getContext().obtainStyledAttributes( + attrs, R.styleable.ElasticDragDismissFrameLayout, 0, 0); + + if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissDistance)) { + dragDismissDistance = a.getDimensionPixelSize(R.styleable + .ElasticDragDismissFrameLayout_dragDismissDistance, 0); + } else if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissFraction)) { + dragDismissFraction = a.getFloat(R.styleable + .ElasticDragDismissFrameLayout_dragDismissFraction, dragDismissFraction); + } + if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissScale)) { + dragDismissScale = a.getFloat(R.styleable + .ElasticDragDismissFrameLayout_dragDismissScale, dragDismissScale); + shouldScale = dragDismissScale != 1f; + } + if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragElasticity)) { + dragElacticity = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragElasticity, + dragElacticity); + } + a.recycle(); + } + + public interface ElasticDragDismissListener { + + /** + * Called for each drag event. + * + * @param elasticOffset Indicating the drag offset with elasticity applied i.e. may + * exceed 1. + * @param elasticOffsetPixels The elastically scaled drag distance in pixels. + * @param rawOffset Value from [0, 1] indicating the raw drag offset i.e. + * without elasticity applied. A value of 1 indicates that the + * dismiss distance has been reached. + * @param rawOffsetPixels The raw distance the user has dragged + */ + void onDrag(float elasticOffset, float elasticOffsetPixels, + float rawOffset, float rawOffsetPixels); + + /** + * Called when dragging is released and has exceeded the threshold dismiss distance. + */ + void onDragDismissed(); + + } + + @Override + public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { + return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0; + } + + @Override + public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { + // if we're in a drag gesture and the user reverses up the we should take those events + if (draggingDown && dy > 0 || draggingUp && dy < 0) { + dragScale(dy); + consumed[1] = dy; + } + } + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, + int dxUnconsumed, int dyUnconsumed) { + dragScale(dyUnconsumed); + } + + @Override + public void onStopNestedScroll(View child) { + if (Math.abs(totalDrag) >= dragDismissDistance) { + dispatchDismissCallback(); + } else { // settle back to natural position + animate() + .translationY(0f) + .scaleX(1f) + .scaleY(1f) + .setDuration(200L) + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(getContext())) + .setListener(null) + .start(); + totalDrag = 0; + draggingDown = draggingUp = false; + dispatchDragCallback(0f, 0f, 0f, 0f); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if (dragDismissFraction > 0f) { + dragDismissDistance = h * dragDismissFraction; + } + } + + public void addListener(ElasticDragDismissListener listener) { + if (listeners == null) { + listeners = new ArrayList<>(); + } + listeners.add(listener); + } + + public void removeListener(ElasticDragDismissListener listener) { + if (listeners != null && listeners.size() > 0) { + listeners.remove(listener); + } + } + + private void dragScale(int scroll) { + if (scroll == 0) return; + + totalDrag += scroll; + + // track the direction & set the pivot point for scaling + // don't double track i.e. if start dragging down and then reverse, keep tracking as + // dragging down until they reach the 'natural' position + if (scroll < 0 && !draggingUp && !draggingDown) { + draggingDown = true; + if (shouldScale) setPivotY(getHeight()); + } else if (scroll > 0 && !draggingDown && !draggingUp) { + draggingUp = true; + if (shouldScale) setPivotY(0f); + } + // how far have we dragged relative to the distance to perform a dismiss + // (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit + float dragFraction = (float) Math.log10(1 + (Math.abs(totalDrag) / dragDismissDistance)); + + // calculate the desired translation given the drag fraction + float dragTo = dragFraction * dragDismissDistance * dragElacticity; + + if (draggingUp) { + // as we use the absolute magnitude when calculating the drag fraction, need to + // re-apply the drag direction + dragTo *= -1; + } + setTranslationY(dragTo); + + if (shouldScale) { + final float scale = 1 - ((1 - dragDismissScale) * dragFraction); + setScaleX(scale); + setScaleY(scale); + } + + // if we've reversed direction and gone past the settle point then clear the flags to + // allow the list to get the scroll events & reset any transforms + if ((draggingDown && totalDrag >= 0) + || (draggingUp && totalDrag <= 0)) { + totalDrag = dragTo = dragFraction = 0; + draggingDown = draggingUp = false; + setTranslationY(0f); + setScaleX(1f); + setScaleY(1f); + } + dispatchDragCallback(dragFraction, dragTo, + Math.min(1f, Math.abs(totalDrag) / dragDismissDistance), totalDrag); + } + + private void dispatchDragCallback(float elasticOffset, float elasticOffsetPixels, + float rawOffset, float rawOffsetPixels) { + if (listeners != null && listeners.size() > 0) { + for (ElasticDragDismissListener listener : listeners) { + listener.onDrag(elasticOffset, elasticOffsetPixels, + rawOffset, rawOffsetPixels); + } + } + } + + private void dispatchDismissCallback() { + if (listeners != null && listeners.size() > 0) { + for (ElasticDragDismissListener listener : listeners) { + listener.onDragDismissed(); + } + } + } + + /** + * An {@link ElasticDragDismissListener} which fades system chrome (i.e. status bar and + * navigation bar) whilst elastic drags are performed and + * {@link Activity#finishAfterTransition() finishes} the activity when drag dismissed. + */ + public static class SystemChromeFader implements ElasticDragDismissListener { + + private final Activity activity; + private final int statusBarAlpha; + private final int navBarAlpha; + private final boolean fadeNavBar; + + public SystemChromeFader(Activity activity) { + this.activity = activity; + statusBarAlpha = Color.alpha(activity.getWindow().getStatusBarColor()); + navBarAlpha = Color.alpha(activity.getWindow().getNavigationBarColor()); + fadeNavBar = ViewUtils.isNavBarOnBottom(activity); + } + + @Override + public void onDrag(float elasticOffset, float elasticOffsetPixels, + float rawOffset, float rawOffsetPixels) { + if (elasticOffsetPixels > 0) { + // dragging downward, fade the status bar in proportion + activity.getWindow().setStatusBarColor(ColorUtils.modifyAlpha(activity.getWindow() + .getStatusBarColor(), (int) ((1f - rawOffset) * statusBarAlpha))); + } else if (elasticOffsetPixels == 0) { + // reset + activity.getWindow().setStatusBarColor(ColorUtils.modifyAlpha( + activity.getWindow().getStatusBarColor(), statusBarAlpha)); + activity.getWindow().setNavigationBarColor(ColorUtils.modifyAlpha( + activity.getWindow().getNavigationBarColor(), navBarAlpha)); + } else if (fadeNavBar) { + // dragging upward, fade the navigation bar in proportion + activity.getWindow().setNavigationBarColor( + ColorUtils.modifyAlpha(activity.getWindow().getNavigationBarColor(), + (int) ((1f - rawOffset) * navBarAlpha))); + } + } + + public void onDragDismissed() { + activity.finishAfterTransition(); + } + } + +} diff --git a/app/src/main/res/drawable/ic_add_white_24dp.xml b/app/src/main/res/drawable/ic_add_white_24dp.xml new file mode 100644 index 0000000..ffbcf2e --- /dev/null +++ b/app/src/main/res/drawable/ic_add_white_24dp.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..047707e --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_market_item.xml b/app/src/main/res/layout/activity_market_item.xml new file mode 100644 index 0000000..0b762fb --- /dev/null +++ b/app/src/main/res/layout/activity_market_item.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attributes_layout.xml b/app/src/main/res/layout/attributes_layout.xml new file mode 100644 index 0000000..7f6b18f --- /dev/null +++ b/app/src/main/res/layout/attributes_layout.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_orders_list.xml b/app/src/main/res/layout/fragment_orders_list.xml new file mode 100644 index 0000000..bc2c530 --- /dev/null +++ b/app/src/main/res/layout/fragment_orders_list.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_type_info.xml b/app/src/main/res/layout/fragment_type_info.xml new file mode 100644 index 0000000..8086ea2 --- /dev/null +++ b/app/src/main/res/layout/fragment_type_info.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/market_order_item.xml b/app/src/main/res/layout/market_order_item.xml new file mode 100644 index 0000000..f1f7d95 --- /dev/null +++ b/app/src/main/res/layout/market_order_item.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/threeline_item_layout.xml b/app/src/main/res/layout/threeline_item_layout.xml new file mode 100644 index 0000000..8de2b2b --- /dev/null +++ b/app/src/main/res/layout/threeline_item_layout.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/toolbar_spinner_item_actionbar.xml b/app/src/main/res/layout/toolbar_spinner_item_actionbar.xml new file mode 100644 index 0000000..d8b66dd --- /dev/null +++ b/app/src/main/res/layout/toolbar_spinner_item_actionbar.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/toolbar_spinner_item_dropdown.xml b/app/src/main/res/layout/toolbar_spinner_item_dropdown.xml new file mode 100644 index 0000000..37ffbee --- /dev/null +++ b/app/src/main/res/layout/toolbar_spinner_item_dropdown.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/type_item_layout.xml b/app/src/main/res/layout/type_item_layout.xml new file mode 100644 index 0000000..1b57deb --- /dev/null +++ b/app/src/main/res/layout/type_item_layout.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/transition/item_enter.xml b/app/src/main/res/transition/item_enter.xml new file mode 100644 index 0000000..c74d115 --- /dev/null +++ b/app/src/main/res/transition/item_enter.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/transition/type_shared_enter_item.xml b/app/src/main/res/transition/type_shared_enter_item.xml new file mode 100644 index 0000000..1018517 --- /dev/null +++ b/app/src/main/res/transition/type_shared_enter_item.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/app/src/main/res/values/attr_pinnable.xml b/app/src/main/res/values/attr_pinnable.xml new file mode 100644 index 0000000..b807a7a --- /dev/null +++ b/app/src/main/res/values/attr_pinnable.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/values/attrs_drag_dismiss.xml b/app/src/main/res/values/attrs_drag_dismiss.xml new file mode 100644 index 0000000..63c9cc1 --- /dev/null +++ b/app/src/main/res/values/attrs_drag_dismiss.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/attrs_forground_view.xml b/app/src/main/res/values/attrs_forground_view.xml new file mode 100644 index 0000000..cb21271 --- /dev/null +++ b/app/src/main/res/values/attrs_forground_view.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..9d60d8a --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,57 @@ + + + + + + #2980b9 + @color/primary + #1884aa + #11261c + + + #de000000 + #8a000000 + #61000000 + #ffffffff + #b3ffffff + #4dffffff + #99ffffff + @color/mid_grey + #fafafa + #ff333333 + #99000000 + #ff676767 + #ff292929 + #99323232 + #40808080 + #fff5f5f5 + #ffeeeeee + #ffe0e0e0 + #ffe0e0e0 + #99000000 + #ffffffff + #1f000000 + #80333333 + #ff333333 + #ffffffff + + + #43000000 + #8f000000 + + + #03a9f4 --> + #039be5 + #b303a9f4 + + #000133 + #ffeceef1 + #73000000 + + + #ffff9800 + + + #33000000 + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..4ba1aab --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,10 @@ + + + 16dp + 16dp + + 112dp + 5dp + + 2dp + diff --git a/app/src/main/res/values/searchback.xml b/app/src/main/res/values/searchback.xml new file mode 100644 index 0000000..26ef53d --- /dev/null +++ b/app/src/main/res/values/searchback.xml @@ -0,0 +1,55 @@ + + + + + + + + + 48dp + 24dp + 48 + 24 + 2 + #fff + + + M25.39,13.39 A 5.5 5.5 0 1 1 17.61 5.61 A 5.5 5.5 0 1 1 25.39 13.39 + 1 + 0 + 250 + 300 + + + M24.7000008,12.6999998 C24.7000008,12.6999998 31.8173374,19.9066081 31.8173371,19.9066082 C32.7867437,20.7006357 34.4599991,23 37.5,23 C40.5400009,23 43,20.54 43,17.5 C43,14.46 40.5400009,12 37.5,12 C34.4599991,12 33.2173088,12 31.8173371,12 C31.8173374,12 18.8477173,12 18.8477173,12 + 0 + 0.185 + 0.75 + 1 + + 600 + 450 + + + M16.7017297,12.6957157 L24.7043962,4.69304955 + M16.7107986,11.2764828 L24.7221527,19.2878361 + 0 + 1 + 350 + 250 + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..6753e8c --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,20 @@ + + Market Bot + + Sell Orders + Buy Orders + Info + + Mass: + Capacity: + Portion Size: + Volume: + + $d ISK + $s / $s + + transition_toolbar + transition_type_icon + transition_type_name + Range: + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..ad4d058 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/test/java/com/w9jds/marketbot/ExampleUnitTest.java b/app/src/test/java/com/w9jds/marketbot/ExampleUnitTest.java new file mode 100644 index 0000000..eb0be04 --- /dev/null +++ b/app/src/test/java/com/w9jds/marketbot/ExampleUnitTest.java @@ -0,0 +1,16 @@ +package com.w9jds.marketbot; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * To work on unit tests, switch the Test Artifact in the Build Variants view. + */ +public class ExampleUnitTest { + + @Test + public void test_get_market_groups() throws Exception { + + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..4a7d1e8 --- /dev/null +++ b/build.gradle @@ -0,0 +1,24 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.1.0-alpha1' + classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/eveapi/.gitignore b/eveapi/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/eveapi/.gitignore @@ -0,0 +1 @@ +/build diff --git a/eveapi/build.gradle b/eveapi/build.gradle new file mode 100644 index 0000000..231bc43 --- /dev/null +++ b/eveapi/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.2" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + + compile 'com.android.support:appcompat-v7:23.2.0' + compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' + compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' +} diff --git a/eveapi/proguard-rules.pro b/eveapi/proguard-rules.pro new file mode 100644 index 0000000..32cdb4a --- /dev/null +++ b/eveapi/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/JeremyShore/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/eveapi/src/main/AndroidManifest.xml b/eveapi/src/main/AndroidManifest.xml new file mode 100644 index 0000000..eca12fd --- /dev/null +++ b/eveapi/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Callback.java b/eveapi/src/main/java/com/w9jds/eveapi/Callback.java new file mode 100644 index 0000000..ea5b0fa --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Callback.java @@ -0,0 +1,12 @@ +package com.w9jds.eveapi; + +/** + * Created by Jeremy Shore on 2/16/16. + */ +public interface Callback { + + void success(T t); + + void failure(String error); + +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Client/Crest.java b/eveapi/src/main/java/com/w9jds/eveapi/Client/Crest.java new file mode 100644 index 0000000..ce5c9de --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Client/Crest.java @@ -0,0 +1,259 @@ +package com.w9jds.eveapi.Client; + +import android.os.AsyncTask; + +import com.w9jds.eveapi.Callback; +import com.w9jds.eveapi.Models.MarketGroup; +import com.w9jds.eveapi.Models.MarketItemBase; +import com.w9jds.eveapi.Models.OrderType; +import com.w9jds.eveapi.Models.ServerInfo; +import com.w9jds.eveapi.Models.TypeInfo; +import com.w9jds.eveapi.Models.containers.MarketGroups; +import com.w9jds.eveapi.Models.Region; +import com.w9jds.eveapi.Models.containers.MarketOrders; +import com.w9jds.eveapi.Models.containers.Regions; +import com.w9jds.eveapi.Models.containers.Types; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Queue; + +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.Query; +import retrofit2.http.Url; + +/** + * Created by Jeremy Shore on 2/16/16. + */ +public final class Crest { + + public static final String PUBLIC_SINGULARITY = "https://public-crest-sisi.testeveonline.com/"; + public static final String SINGULARITY = "https://api-sisi.testeveonline.com/"; + public static final String PUBLIC_TRANQUILITY = "https://public-crest.eveonline.com/"; + public static final String TRANQUILITY = "https://crest-tq.eveonline.com/"; + + Endpoint crestEndpoint; + + public Crest (Retrofit retrofit) { + this.crestEndpoint = new Builder() + .buildEndpoint(retrofit); + } + + public void setCrestEndpoint(Endpoint crestEndpoint) { + this.crestEndpoint = crestEndpoint; + } + + interface Endpoint { + + @GET + Call getServer(); + + @GET + Call getMarketTypes(@Url String url); + + @GET("/types/{typeId}/") + Call getTypeInfo(@Path("typeId") long id); + + @GET("/market/groups/") + Call getMarketGroups(); + + @GET("/regions/") + Call getRegions(); + + @GET("/market/{regionId}/orders/{orderType}/") + Call getMarketOrders(@Path("regionId") long regionId, @Path("orderType") String orderType, + @Query(value = "type", encoded = true) String typeId); + + } + + public void getServerVersion(final Callback callback) { + Call call = crestEndpoint.getServer(); + call.enqueue(new retrofit2.Callback() { + @Override + public void onResponse(Call call, Response response) { + callback.success(response.body()); + } + + @Override + public void onFailure(Call call, Throwable t) { + callback.failure(t.getMessage()); + } + }); + } + + public void getRegions(final Callback> callback) { + Call call = crestEndpoint.getRegions(); + call.enqueue(new retrofit2.Callback() { + @Override + public void onResponse(Call call, Response response) { + callback.success(response.body().items); + } + + @Override + public void onFailure(Call call, Throwable t) { + callback.failure(t.getMessage()); + } + }); + } + + public void getMarketGroups(final Callback> callback) { + Call call = crestEndpoint.getMarketGroups(); + call.enqueue(new retrofit2.Callback() { + @Override + public void onResponse(Call call, Response response) { + +// callback.success(response.body().groups); + + new buildMarketGroupTree(response.body().groups, callback).execute(); + } + + @Override + public void onFailure(Call call, Throwable t) { + callback.failure(t.getMessage()); + } + }); + } + + public void getMarketTypes(String href, final Callback callback) { + Call call = crestEndpoint.getMarketTypes(href); + call.enqueue(new retrofit2.Callback() { + @Override + public void onResponse(Call call, Response response) { + callback.success(response.body()); + } + + @Override + public void onFailure(Call call, Throwable t) { + callback.failure(t.getMessage()); + } + }); + } + + public void getTypeInfo(long typeId, final Callback callback) { + Call call = crestEndpoint.getTypeInfo(typeId); + call.enqueue(new retrofit2.Callback() { + @Override + public void onResponse(Call call, Response response) { + callback.success(response.body()); + } + + @Override + public void onFailure(Call call, Throwable t) { + callback.failure(t.getMessage()); + } + }); + } + + public void getOrders(long regionId, String typeHref, OrderType orderType, final Callback callback) { + Call call = crestEndpoint.getMarketOrders(regionId, orderType.toString(), typeHref); + call.enqueue(new retrofit2.Callback() { + @Override + public void onResponse(Call call, Response response) { + callback.success(response.body()); + } + + @Override + public void onFailure(Call call, Throwable t) { + callback.failure(t.getMessage()); + } + }); + } + + private class buildMarketGroupTree extends AsyncTask> { + + Hashtable tree = new Hashtable<>(); + Hashtable children = new Hashtable<>(); + final ArrayList groups; + final Callback> callback; + + public buildMarketGroupTree(ArrayList groups, Callback> callback) { + this.groups = groups; + this.callback = callback; + } + + @Override + protected Hashtable doInBackground(Void... params) { + for (MarketGroup group : groups) { + if (!group.hasParent()) { + tree.put(group.getId(), group); + } + else { + children.put(group.getId(), group); + } + } + + while (children.values().size() > 0) { + for (Iterator iterator = children.values().iterator(); iterator.hasNext(); ) { + MarketGroup group = iterator.next(); + if (children.containsKey(group.getParentGroupId())) { + children.get(group.getParentGroupId()).children.put(group.getId(), group); + iterator.remove(); + } + else { + boolean found = bfsForParent(group, tree); + + if (found) { + iterator.remove(); + } + } + } + } + + return tree; + } + + @Override + protected void onPostExecute(Hashtable tree) { + callback.success(tree); + super.onPostExecute(tree); + } + } + + private boolean bfsForParent(MarketItemBase base, Hashtable tree) { + boolean parentFound = false; + MarketGroup kid = (MarketGroup) base; + Queue queue = new LinkedList(); + + for (MarketItemBase group : tree.values()) { + if (parentFound) { + break; + } + + queue.add(group); + + while(!queue.isEmpty()) { + MarketGroup node = (MarketGroup)queue.remove(); + if (node.getId() == kid.getParentGroupId()) { + node.children.put(base.getId(), (MarketGroup)base); + parentFound = true; + queue.clear(); + } + else { + for (MarketGroup child : node.children.values()) { + queue.add(child); + } + } + } + } + + return parentFound; + } + + public static class Builder { + + public Crest build(Retrofit retrofit) { + return new Crest(retrofit); + } + + public Endpoint buildEndpoint(Retrofit retrofit) { + return retrofit.create(Endpoint.class); + } + + } +} \ No newline at end of file diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketGroup.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketGroup.java new file mode 100644 index 0000000..6edb0b2 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketGroup.java @@ -0,0 +1,116 @@ +package com.w9jds.eveapi.Models; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Size; + +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +/** + * Created by Jeremy on 2/17/2016. + */ +public final class MarketGroup extends MarketItemBase implements Parcelable { + + @SerializedName("parentGroup") + private Reference parentGroup; + + @SerializedName("href") + private String href; + + @SerializedName("description") + private String description; + + @SerializedName("types") + private Reference types; + + public HashMap children = new HashMap<>(); + public Map items = new HashMap<>(); + + public MarketGroup() { + + } + + public String getDescription() { + return description; + } + + public String getHref() { + return href; + } + + public String getTypesLocation() { + return types.href; + } + + public String getParentGroup() { + return parentGroup.href; + } + + public Long getParentGroupId() { + String[] query = this.parentGroup.href.split("/"); + + for (int i = query.length; i > 0; i--) { + if (!query[i-1].equals("")) { + return Long.parseLong(query[i-1]); + } + } + + return 0L; + } + + public boolean hasParent() { + return this.parentGroup != null && !this.parentGroup.href.equals(""); + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(this.getId()); + dest.writeString(this.getName()); + dest.writeParcelable(this.parentGroup, 0); + dest.writeString(this.href); + dest.writeString(this.description); + dest.writeParcelable(this.types, 0); + + dest.writeInt(children.size()); + for (MarketGroup group : children.values()) { + dest.writeParcelable(group, 0); + } + } + + protected MarketGroup(Parcel in) { + this.setId(in.readLong()); + this.setName(in.readString()); + this.parentGroup = in.readParcelable(Reference.class.getClassLoader()); + this.href = in.readString(); + this.description = in.readString(); + this.types = in.readParcelable(Reference.class.getClassLoader()); + + int size = in.readInt(); + for (int i = 0; i < size; i++) { + MarketGroup child = in.readParcelable(MarketGroup.class.getClassLoader()); + this.children.put(child.getId(), child); + } + } + + public static final Creator CREATOR = new Creator() { + public MarketGroup createFromParcel(Parcel source) { + return new MarketGroup(source); + } + + public MarketGroup[] newArray(int size) { + return new MarketGroup[size]; + } + }; +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketItemBase.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketItemBase.java new file mode 100644 index 0000000..5e45729 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketItemBase.java @@ -0,0 +1,34 @@ +package com.w9jds.eveapi.Models; + +import com.google.gson.annotations.SerializedName; + +import java.math.BigInteger; + +/** + * Created by Jeremy Shore on 2/21/16. + */ +public class MarketItemBase { + + @SerializedName("id") + private long id; + + @SerializedName("name") + private String name; + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + void setId(long id) { + this.id = id; + } + + void setName(String name) { + this.name = name; + } + +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketOrder.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketOrder.java new file mode 100644 index 0000000..aa956bc --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketOrder.java @@ -0,0 +1,82 @@ +package com.w9jds.eveapi.Models; + +import com.google.gson.annotations.SerializedName; + +import java.util.Date; + +/** + * Created by Jeremy on 3/1/2016. + */ +public final class MarketOrder extends MarketItemBase { + + @SerializedName("buy") + private boolean isBuyOrder; + +// @SerializedName("issued") +// private Date issued; + + @SerializedName("price") + private double price; + + @SerializedName("volume") + private int volume; + + @SerializedName("volumeEntered") + private int volumeStart; + + @SerializedName("range") + private String range; + + @SerializedName("href") + private String href; + + @SerializedName("duration") + private int duration; + + @SerializedName("type") + private Reference type; + + @SerializedName("location") + private Reference location; + + + public String getHref() { + return href; + } + +// public Date getIssued() { +// return issued; +// } + + public double getPrice() { + return price; + } + + public int getDuration() { + return duration; + } + + public int getVolume() { + return volume; + } + + public Reference getLocation() { + return location; + } + + public Reference getType() { + return type; + } + + public String getRange() { + return range; + } + + public int getVolumeStart() { + return volumeStart; + } + + public boolean isBuyOrder() { + return isBuyOrder; + } +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketPriceStorage.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketPriceStorage.java new file mode 100644 index 0000000..4f510c7 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/MarketPriceStorage.java @@ -0,0 +1,45 @@ +//package com.w9jds.eveapi.Models; +// +//import com.google.gson.annotations.SerializedName; +// +///** +// * Created by Jeremy Shore on 2/23/16. +// */ +//public final class MarketPriceStorage { +// +// @SerializedName("adjustedPrice") +// private double adjusted; +// +// @SerializedName("averagePrice") +// private double average; +// +// @SerializedName("type") +// private Type type; +// +// public double getAdjustedPrice() { +// return this.adjusted; +// } +// +// public double getAveragePrice() { +// return this.average; +// } +// +// public String getHref() { +// return type.href; +// } +// +// public Integer getId() { +// return type.getId(); +// } +// +// public String getName() { +// return type.getName(); +// } +// +// private class Type extends MarketItemBase { +// +// @SerializedName("href") +// public String href; +// +// } +//} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/OrderType.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/OrderType.java new file mode 100644 index 0000000..9c4fdb1 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/OrderType.java @@ -0,0 +1,9 @@ +package com.w9jds.eveapi.Models; + +/** + * Created by Jeremy on 3/2/2016. + */ +public enum OrderType { + buy, + sell +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/Reference.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/Reference.java new file mode 100644 index 0000000..d1ef2b6 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/Reference.java @@ -0,0 +1,47 @@ +package com.w9jds.eveapi.Models; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Jeremy on 2/17/2016. + */ +public final class Reference extends MarketItemBase implements Parcelable { + + public Reference(String href) { + this.href = href; + } + + @SerializedName("href") + public String href; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(getId()); + dest.writeString(getName()); + dest.writeString(this.href); + } + + protected Reference(Parcel in) { + this.setId(in.readLong()); + this.setName(in.readString()); + this.href = in.readString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Reference createFromParcel(Parcel source) { + return new Reference(source); + } + + public Reference[] newArray(int size) { + return new Reference[size]; + } + }; +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/Region.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/Region.java new file mode 100644 index 0000000..ce07016 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/Region.java @@ -0,0 +1,18 @@ +package com.w9jds.eveapi.Models; + + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Jeremy Shore on 2/16/16. + */ +public final class Region extends MarketItemBase { + + @SerializedName("href") + private String href; + + public String getHref() { + return href; + } + +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/ServerInfo.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/ServerInfo.java new file mode 100644 index 0000000..7be0046 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/ServerInfo.java @@ -0,0 +1,17 @@ +package com.w9jds.eveapi.Models; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Jeremy Shore on 3/13/16. + */ +public final class ServerInfo { + + @SerializedName("serverVersion") + String serverVersion; + + public String getServerVersion() { + return serverVersion; + } + +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/Type.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/Type.java new file mode 100644 index 0000000..e94e3d1 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/Type.java @@ -0,0 +1,65 @@ +package com.w9jds.eveapi.Models; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Jeremy on 2/17/2016. + */ +public final class Type extends MarketItemBase implements Parcelable { + + @SerializedName("type") + private TypeItem type; + + + @SerializedName("marketGroup") + public Reference marketGroup; + + @Override + public String getName() { + return type.name; + } + + public Type() { + + } + + protected Type(Parcel in) { + this.setId(in.readLong()); + this.type = in.readParcelable(TypeItem.class.getClassLoader()); + this.marketGroup = in.readParcelable(Reference.class.getClassLoader()); + } + + public String getIconLink() { + return type.icon.href; + } + + public String getHref() { + return type.href; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(this.getId()); + dest.writeParcelable(this.type, flags); + dest.writeParcelable(this.marketGroup, 0); + } + + public static final Creator CREATOR = new Creator() { + public Type createFromParcel(Parcel source) { + return new Type(source); + } + + public Type[] newArray(int size) { + return new Type[size]; + } + }; + +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/TypeInfo.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/TypeInfo.java new file mode 100644 index 0000000..df34cbd --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/TypeInfo.java @@ -0,0 +1,51 @@ +package com.w9jds.eveapi.Models; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Jeremy Shore on 3/3/16. + */ +public final class TypeInfo extends MarketItemBase { + + @SerializedName("capacity") + private double capacity; + + @SerializedName("description") + private String description; + + @SerializedName("mass") + private double mass; + + @SerializedName("radius") + private double radius; + + @SerializedName("volume") + private double volume; + + @SerializedName("portionSize") + private double portionSize; + + public String getDescription() { + return description; + } + + public double getCapacity() { + return capacity; + } + + public double getMass() { + return mass; + } + + public double getPortionSize() { + return portionSize; + } + + public double getRadius() { + return radius; + } + + public double getVolume() { + return volume; + } +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/TypeItem.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/TypeItem.java new file mode 100644 index 0000000..9e9f219 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/TypeItem.java @@ -0,0 +1,50 @@ +package com.w9jds.eveapi.Models; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +public final class TypeItem implements Parcelable { + + @SerializedName("name") + public String name; + + @SerializedName("href") + public String href; + + @SerializedName("icon") + public Reference icon; + + public TypeItem() { + + } + + protected TypeItem(Parcel in) { + this.name = in.readString(); + this.href = in.readString(); + this.icon = in.readParcelable(Reference.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.name); + dest.writeString(this.href); + dest.writeParcelable(this.icon, 0); + } + + public static final Creator CREATOR = new Creator() { + public TypeItem createFromParcel(Parcel source) { + return new TypeItem(source); + } + + public TypeItem[] newArray(int size) { + return new TypeItem[size]; + } + }; +} \ No newline at end of file diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/MarketGroups.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/MarketGroups.java new file mode 100644 index 0000000..e6b25ae --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/MarketGroups.java @@ -0,0 +1,25 @@ +package com.w9jds.eveapi.Models.containers; + +import com.google.gson.annotations.SerializedName; +import com.w9jds.eveapi.Models.MarketGroup; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Hashtable; + +/** + * Created by Jeremy Shore on 2/16/16. + */ + +public final class MarketGroups { + + @SerializedName("totalCount") + public int count; + + @SerializedName("items") + public ArrayList groups; + + @SerializedName("pageCount") + public int pageCount; + +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/MarketOrders.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/MarketOrders.java new file mode 100644 index 0000000..457b4a4 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/MarketOrders.java @@ -0,0 +1,19 @@ +package com.w9jds.eveapi.Models.containers; + +import com.google.gson.annotations.SerializedName; +import com.w9jds.eveapi.Models.MarketOrder; + +import java.util.ArrayList; + +/** + * Created by Jeremy on 3/1/2016. + */ +public final class MarketOrders { + + @SerializedName("totalCount") + public int size; + + @SerializedName("items") + public ArrayList orders; + +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/Regions.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/Regions.java new file mode 100644 index 0000000..99792d4 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/Regions.java @@ -0,0 +1,19 @@ +package com.w9jds.eveapi.Models.containers; + +import com.google.gson.annotations.SerializedName; +import com.w9jds.eveapi.Models.Region; + +import java.util.ArrayList; + +/** + * Created by Jeremy on 3/1/2016. + */ +public final class Regions { + + @SerializedName("items") + public ArrayList items; + + @SerializedName("totalCount") + public int size; + +} diff --git a/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/Types.java b/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/Types.java new file mode 100644 index 0000000..ff1e967 --- /dev/null +++ b/eveapi/src/main/java/com/w9jds/eveapi/Models/containers/Types.java @@ -0,0 +1,22 @@ +package com.w9jds.eveapi.Models.containers; + +import com.google.gson.annotations.SerializedName; +import com.w9jds.eveapi.Models.Type; + +import java.util.ArrayList; + +/** + * Created by Jeremy on 2/17/2016. + */ +public final class Types { + + @SerializedName("pageCount") + public int pageCount; + + @SerializedName("totalCount") + public int count; + + @SerializedName("items") + public ArrayList items; + +} diff --git a/eveapi/src/main/res/values/strings.xml b/eveapi/src/main/res/values/strings.xml new file mode 100644 index 0000000..fbcea6c --- /dev/null +++ b/eveapi/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + Eve Api + + title_background + transition_background + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..1d3591c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..122a0dc --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Dec 28 10:00:20 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..190a717 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app', ':eveapi'