package net.wigle.wigleandroid; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Parcelable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.view.MenuItemCompat; import android.support.v4.widget.SwipeRefreshLayout; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import net.wigle.wigleandroid.background.ApiDownloader; import net.wigle.wigleandroid.background.ApiListener; import net.wigle.wigleandroid.background.DownloadHandler; import net.wigle.wigleandroid.model.RankUser; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; public class RankStatsFragment extends Fragment { private static final int MSG_RANKING_DONE = 100; private static final int MENU_USER_STATS = 200; private static final int MENU_SITE_STATS = 201; private static final int MENU_RANK_SWAP = 202; private static final int MENU_USER_CENTRIC_SWAP = 203; private static final String RESULT_LIST_KEY = "results"; /* { eventView: false, myUsername: "arkasha", pageEnd: 100, pageStart: 0, results:[ { discoveredCell: 999911, discoveredCellGPS: 718583, discoveredWiFi: 41781793, discoveredWiFiGPS: 26209080, discoveredWiFiGPSPercent: 8.64536, eventMonthCount: 20526, eventPrevMonthCount: 431245, first: "20011003-00001", last: "20170103-00583", monthRank: 1, prevMonthRank: 1, prevRank: 1, rank: 1, self: false, totalWiFiLocations: 185993637, userName: "anonymous" }, ...]} */ private static final String KEY_MONTH_WIFI_GPS = "eventMonthCount"; private static final String KEY_TOTAL_WIFI_GPS = "discoveredWiFiGPS"; private static final String KEY_TOTAL_CELL_GPS = "discoveredCellGPS"; private static final String KEY_RANK = "rank"; private static final String KEY_USERNAME = "userName"; private static final String KEY_PREV_RANK = "prevRank"; private static final String KEY_PREV_MONTH_RANK = "prevMonthRank"; private static final String KEY_SELECTED = "selected"; private static final int ROW_COUNT = 100; private static final String[] ALL_ROW_KEYS = new String[] { KEY_MONTH_WIFI_GPS, KEY_TOTAL_WIFI_GPS, KEY_TOTAL_CELL_GPS, KEY_RANK, KEY_PREV_RANK, KEY_PREV_MONTH_RANK, }; private AtomicBoolean finishing; private NumberFormat numberFormat; private RankListAdapter listAdapter; private AtomicBoolean monthRanking; private AtomicBoolean userCentric; private RankDownloadHandler handler; /** Called when the activity is first created. */ @Override public void onCreate( final Bundle savedInstanceState ) { MainActivity.info("RANKSTATS: onCreate"); super.onCreate(savedInstanceState); setHasOptionsMenu(true); // set language MainActivity.setLocale(getActivity()); // media volume getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC); finishing = new AtomicBoolean(false); monthRanking = new AtomicBoolean(false); userCentric = new AtomicBoolean(true); numberFormat = NumberFormat.getNumberInstance(Locale.US); if (numberFormat instanceof DecimalFormat) { numberFormat.setMinimumFractionDigits(0); numberFormat.setMaximumFractionDigits(2); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final int orientation = getResources().getConfiguration().orientation; MainActivity.info("RANKSTATS: onCreateView.a orientation: " + orientation); final LinearLayout rootView = (LinearLayout) inflater.inflate(R.layout.rankstats, container, false); setupSwipeRefresh(rootView); setupListView(rootView); handler = new RankDownloadHandler(rootView, numberFormat, getActivity().getPackageName(), getResources(), monthRanking); handler.setRankListAdapter(listAdapter); final SharedPreferences prefs = getActivity().getSharedPreferences(ListFragment.SHARED_PREFS, 0); //TODO: we should only perform user DL if there's a user set UserStatsFragment.executeUserDownload(this, new UserStatsFragment.UserDownloadApiListener(new Handler() { @Override public void handleMessage(final Message msg) { final Bundle bundle = msg.getData(); final boolean isCache = bundle.getBoolean(UserStatsFragment.KEY_IS_CACHE); MainActivity.info("got user message, isCache: " + isCache); final SharedPreferences.Editor editor = prefs.edit(); editor.putLong( ListFragment.PREF_RANK, bundle.getLong(UserStatsFragment.KEY_RANK) ); editor.putLong( ListFragment.PREF_MONTH_RANK, bundle.getLong(UserStatsFragment.KEY_MONTH_RANK) ); editor.apply(); downloadRanks(isCache); } })); return rootView; } private void setupSwipeRefresh(final LinearLayout rootView) { // Lookup the swipe container view final SwipeRefreshLayout swipeContainer = (SwipeRefreshLayout) rootView.findViewById(R.id.rank_swipe_container); // Setup refresh listener which triggers new data loading swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { // Your code to refresh the list here. // Make sure you call swipeContainer.setRefreshing(false) // once the network request has completed successfully. downloadRanks(false); } }); } private void downloadRanks(final boolean isCache) { if (handler == null) { MainActivity.error("downloadRanks: handler is null"); return; } final FragmentActivity fragmentActivity = getActivity(); if (fragmentActivity == null) { MainActivity.error("downloadRanks: fragmentActivity is null"); return; } final boolean doMonthRanking = monthRanking.get(); final String sort = doMonthRanking ? "monthcount" : "discovered"; String top = "top"; long pageStart = 0; long selected = 0; if (userCentric.get()) { top = ""; final String userRankKey = doMonthRanking ? ListFragment.PREF_MONTH_RANK : ListFragment.PREF_RANK; final SharedPreferences prefs = getActivity().getSharedPreferences(ListFragment.SHARED_PREFS, 0); final long userRank = prefs.getLong(userRankKey, 0); final long startRank = userRank - 50; pageStart = startRank > 0 ? startRank : 0; selected = startRank < 0 ? userRank : 50; selected -= 5; if (selected < 0) selected = 0; } final long finalSelected = selected; final String cacheName = (doMonthRanking ? "month" : "all") + top; final String monthUrl = MainActivity.RANK_STATS_URL + "?pagestart=" + pageStart + "&pageend=" + (pageStart + ROW_COUNT) + "&sort=" + sort; final ApiDownloader task = new ApiDownloader(getActivity(), ListFragment.lameStatic.dbHelper, "rank-stats-" + cacheName + "-cache.json", monthUrl, false, false, false, ApiDownloader.REQUEST_GET, new ApiListener() { @Override public void requestComplete(final JSONObject json, final boolean isCache) { handleRankStats(json, handler, finalSelected); } }); task.setCacheOnly(isCache); try { task.startDownload(this); } catch (WiGLEAuthException waex) { //TODO: toast? *shouldn't* be authed, but a UserStats call may have been issued in error MainActivity.info("Rank Stats Download Failed due to failed auth"); } } private void setupListView(final View view) { final SharedPreferences prefs = getActivity().getSharedPreferences(ListFragment.SHARED_PREFS, 0); if (listAdapter == null) { listAdapter = new RankListAdapter(getActivity().getApplicationContext(), R.layout.rankrow); } else if (!listAdapter.isEmpty() && !TokenAccess.hasApiToken(prefs)) { listAdapter.clear(); } // always set our current list adapter final ListView listView = (ListView) view.findViewById(R.id.rank_list_view); listView.setAdapter(listAdapter); } private final static class RankDownloadHandler extends DownloadHandler { private RankListAdapter rankListAdapter; final AtomicBoolean monthRanking; private RankDownloadHandler(final View view, final NumberFormat numberFormat, final String packageName, final Resources resources, final AtomicBoolean monthRanking) { super(view, numberFormat, packageName, resources); this.monthRanking = monthRanking; } public void setRankListAdapter(final RankListAdapter rankListAdapter) { this.rankListAdapter = rankListAdapter; } @Override public void handleMessage(final Message msg) { final Bundle bundle = msg.getData(); final ArrayList<Parcelable> results = bundle.getParcelableArrayList(RESULT_LIST_KEY); // MainActivity.info("handleMessage. results: " + results); if (msg.what == MSG_RANKING_DONE && results != null && rankListAdapter != null) { TextView tv = (TextView) view.findViewById(R.id.rankstats_type); final boolean doMonthRanking = monthRanking.get(); tv.setText(doMonthRanking ? R.string.monthcount_title : R.string.all_time_title); final String rankDiffKey = doMonthRanking ? KEY_PREV_MONTH_RANK : KEY_PREV_RANK; final long selected = bundle.getLong(KEY_SELECTED); rankListAdapter.clear(); rankListAdapter.setMonthRanking(monthRanking.get()); for (final Parcelable result : results) { if (result instanceof Bundle) { final Bundle row = (Bundle) result; final long rankDiff = row.getLong(rankDiffKey) - row.getLong(KEY_RANK); final RankUser rankUser = new RankUser(row.getLong(KEY_RANK), rankDiff, row.getString(KEY_USERNAME), row.getLong(KEY_MONTH_WIFI_GPS), row.getLong(KEY_TOTAL_WIFI_GPS), row.getLong(KEY_TOTAL_CELL_GPS)); rankListAdapter.add(rankUser); } } final ListView listView = (ListView) view.findViewById(R.id.rank_list_view); listView.setSelectionFromTop((int)selected, 20); final SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.rank_swipe_container); swipeRefreshLayout.setRefreshing(false); } //TODO: swipeRefreshLayout.setRefreshing(false); anyway if request is done? } } private void handleRankStats(final JSONObject json, final Handler handler, final long selected) { MainActivity.info("handleRankStats"); if (json == null) { MainActivity.info("handleRankStats null json, returning"); return; } final Bundle bundle = new Bundle(); bundle.putLong(KEY_SELECTED, selected); try { final JSONArray list = json.getJSONArray(RESULT_LIST_KEY); final ArrayList<Parcelable> resultList = new ArrayList<>(list.length()); for (int i = 0; i < list.length(); i++) { final JSONObject row = list.getJSONObject(i); final Bundle rowBundle = new Bundle(); for (final String key : ALL_ROW_KEYS) { if (row.has(key)) { rowBundle.putLong(key, row.getLong(key)); } } rowBundle.putString(KEY_USERNAME, row.getString(KEY_USERNAME)); resultList.add(rowBundle); } bundle.putParcelableArrayList(RESULT_LIST_KEY, resultList); } catch (final JSONException ex) { //TODO: better error for bundle MainActivity.error("json error: " + ex, ex); } catch (final Exception e) { //TODO: better error for bundle MainActivity.error("rank error: " + e, e); } final Message message = new Message(); message.setData(bundle); message.what = MSG_RANKING_DONE; handler.sendMessage(message); } @Override public void onDestroy() { MainActivity.info( "RANKSTATS: onDestroy" ); finishing.set( true ); super.onDestroy(); } @Override public void onResume() { MainActivity.info("RANKSTATS: onResume"); super.onResume(); getActivity().setTitle(R.string.rank_stats_app_name); } @Override public void onStart() { MainActivity.info( "RANKSTATS: onStart" ); super.onStart(); } @Override public void onPause() { MainActivity.info( "RANKSTATS: onPause" ); super.onPause(); } @Override public void onStop() { MainActivity.info( "RANKSTATS: onStop" ); super.onStop(); } @Override public void onConfigurationChanged( final Configuration newConfig ) { MainActivity.info("RANKSTATS: config changed"); super.onConfigurationChanged( newConfig ); } /* Creates the menu items */ @Override public void onCreateOptionsMenu (final Menu menu, final MenuInflater inflater) { MenuItem item = menu.add(0, MENU_USER_STATS, 0, getString(R.string.user_stats_app_name)); item.setIcon( android.R.drawable.ic_menu_myplaces ); MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); item = menu.add(0, MENU_USER_STATS, 0, getString(R.string.user_stats_app_name)); item.setIcon(android.R.drawable.ic_menu_myplaces); item = menu.add(0, MENU_SITE_STATS, 0, getString(R.string.site_stats_app_name)); item.setIcon( R.drawable.wiglewifi_small_black_white ); MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); item = menu.add(0, MENU_SITE_STATS, 0, getString(R.string.site_stats_app_name)); item.setIcon(R.drawable.wiglewifi_small_black_white); item = menu.add(0, MENU_RANK_SWAP, 0, getRankSwapString()); item.setIcon(android.R.drawable.ic_menu_sort_alphabetically); item = menu.add(0, MENU_USER_CENTRIC_SWAP, 0, getUserCentricSwapString()); item.setIcon(android.R.drawable.picture_frame); super.onCreateOptionsMenu(menu, inflater); } private String getRankSwapString() { return getString(monthRanking.get() ? R.string.rank_all_time : R.string.rank_month); } private String getUserCentricSwapString() { return getString(userCentric.get() ? R.string.not_user_centric : R.string.user_centric); } /* Handles item selections */ @Override public boolean onOptionsItemSelected( final MenuItem item ) { final MainActivity main = MainActivity.getMainActivity(); switch ( item.getItemId() ) { case MENU_USER_STATS: main.selectFragment(MainActivity.USER_STATS_TAB_POS); return true; case MENU_SITE_STATS: main.selectFragment(MainActivity.SITE_STATS_TAB_POS); return true; case MENU_RANK_SWAP: monthRanking.set(!monthRanking.get()); item.setTitle(getRankSwapString()); downloadRanks(false); return true; case MENU_USER_CENTRIC_SWAP: userCentric.set(!userCentric.get()); item.setTitle(getUserCentricSwapString()); downloadRanks(false); return true; } return false; } }