package com.github.andlyticsproject; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Activity; import android.support.v4.app.LoaderManager; import android.content.Context; import android.support.v4.content.Loader; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.ViewSwitcher; import com.github.andlyticsproject.Preferences.Timeframe; import com.github.andlyticsproject.adsense.AdSenseClient; import com.github.andlyticsproject.chart.Chart.ChartSet; import com.github.andlyticsproject.db.AndlyticsDb; import com.github.andlyticsproject.model.AdmobStats; import com.github.andlyticsproject.model.AdmobStatsSummary; import com.github.andlyticsproject.model.StatsSummary; import com.github.andlyticsproject.util.DetachableAsyncTask; import com.github.andlyticsproject.util.LoaderBase; import com.github.andlyticsproject.util.LoaderResult; import com.github.andlyticsproject.util.Utils; import com.github.andlyticsproject.view.ViewSwitcher3D; import com.google.android.gms.auth.GoogleAuthException; import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException; import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.googleapis.json.GoogleJsonResponseException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; public class AdmobFragment extends ChartFragment<AdmobStats> implements LoaderManager.LoaderCallbacks<LoaderResult<AdmobStatsSummary>> { static final String ARG_PACKAGE_NAME = "packageName"; static final String ARG_TIMEFRAME = "timeframe"; static final String ARG_LOAD_REMOTE = "loadRemote"; private static final String TAG = AdmobFragment.class.getSimpleName(); private AdmobListAdapter admobListAdapter; public Integer heighestRatingChange; public Integer lowestRatingChange; private ViewSwitcher3D mainViewSwitcher; private ViewGroup accountList; private ViewSwitcher configSwitcher; private ViewGroup siteList; private LoadAdUnitsTask loadAdUnitsTask; private String selectedAdmobAccount; static class AdmobDbLoader extends LoaderBase<AdmobStatsSummary> { static final String ARG_PACKAGE_NAME = "packageName"; static final String ARG_TIMEFRAME = "timeframe"; static final String ARG_LOAD_REMOTE = "loadRemote"; private ContentAdapter db; private String packageName; private Timeframe timeframe; private boolean loadRemote; public AdmobDbLoader(Context context, String packageName, Timeframe timeframe, boolean loadRemote) { super(context); db = ContentAdapter.getInstance(AndlyticsApp.getInstance()); this.packageName = packageName; this.timeframe = timeframe; this.loadRemote = loadRemote; } @Override protected AdmobStatsSummary load() throws Exception { if (packageName == null || timeframe == null) { return null; } String[] admobDetails = AndlyticsDb.getInstance(getContext()).getAdmobDetails( packageName); if (admobDetails == null) { Log.w(TAG, "Admob account and site ID not founf for " + packageName); return null; } // String currentAdmobAccount = admobDetails[0]; String currentSiteId = admobDetails[1]; String adUnitId = admobDetails[2]; Log.d(TAG, "Loading Admob stats..."); if (loadRemote) { AdSenseClient.foregroundSyncStats(getContext(), currentAdmobAccount, Arrays.asList(adUnitId)); } Log.d(TAG, "Loading Admob stats from DB..."); return db.getAdmobStats(currentSiteId, adUnitId, timeframe); } @Override protected void releaseResult(LoaderResult<AdmobStatsSummary> result) { // just a string, nothing to do } @Override protected boolean isActive(LoaderResult<AdmobStatsSummary> result) { return false; } } public AdmobFragment() { setHasOptionsMenu(true); setRetainInstance(true); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // init loader loadCurrentData(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); mainViewSwitcher = new ViewSwitcher3D( (ViewGroup) view.findViewById(R.id.base_chart_main_frame)); mainViewSwitcher.setListener(this); admobListAdapter = new AdmobListAdapter(getActivity()); setAdapter(admobListAdapter); String[] admobDetails = AndlyticsDb.getInstance(getActivity()).getAdmobDetails( statsActivity.getPackage()); if (admobDetails == null) { mainViewSwitcher.swap(); if (configSwitcher.getCurrentView().getId() != R.id.base_chart_config) { configSwitcher.showPrevious(); } showAccountList(); } return view; } @Override public void onResume() { super.onResume(); Log.d(TAG, "onResume()"); if (statsActivity.shouldRemoteUpdateStats()) { loadRemoteData(); } else { loadCurrentData(); } } private void loadRemoteData() { loadData(getCurrentTimeFrame(), true); } private void loadData(Timeframe timeframe, boolean loadRemote) { Bundle args = new Bundle(); args.putString(ARG_PACKAGE_NAME, statsActivity.getPackage()); args.putSerializable(ARG_TIMEFRAME, timeframe); args.putBoolean(ARG_LOAD_REMOTE, loadRemote); statsActivity.refreshStarted(); Log.d(TAG, "Restarting loader"); getLoaderManager().restartLoader(0, args, this); } @Override protected void loadCurrentData() { Bundle args = new Bundle(); args.putString(ARG_PACKAGE_NAME, statsActivity.getPackage()); args.putSerializable(ARG_TIMEFRAME, getCurrentTimeFrame()); args.putBoolean(ARG_LOAD_REMOTE, false); statsActivity.refreshStarted(); getLoaderManager().initLoader(0, args, this); } @Override public void updateView(StatsSummary<AdmobStats> statsSummary) { admobListAdapter.setOverallStats(statsSummary.getOverallStats()); List<AdmobStats> admobStats = statsSummary.getStats(); loadChartData(admobStats); // make shallow copy List<AdmobStats> reversedAdmobStats = new ArrayList<AdmobStats>(); reversedAdmobStats.addAll(admobStats); Collections.reverse(reversedAdmobStats); admobListAdapter.setStats(reversedAdmobStats); // admobListAdapter.setCurrentChart(currentChart); admobListAdapter.notifyDataSetChanged(); restoreChartSelection(); } @Override public void setupListAdapter(ChartListAdapter<AdmobStats> listAdapter, StatsSummary<AdmobStats> statsSummary) { // nothing to do } @Override public void onDestroyView() { super.onDestroyView(); // The CursorLoader example doesn't do this, but if we get an update // while the UI is // destroyed, it will crash. Why is this necessary? getLoaderManager().destroyLoader(0); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.clear(); inflater.inflate(R.menu.admob_fragment_menu, menu); // MenuItem activeTimeFrame = null; switch (currentTimeFrame) { case LAST_SEVEN_DAYS: activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframe7); break; case LAST_THIRTY_DAYS: activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframe30); break; case LAST_NINETY_DAYS: activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframe90); break; case UNLIMITED: activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframeUnlimited); break; case MONTH_TO_DATE: activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframeMonthToDate); break; } activeTimeFrame.setChecked(true); String[] admobDetails = AndlyticsDb.getInstance(getActivity()).getAdmobDetails( statsActivity.getPackage()); if (statsActivity.isRefreshing()) { menu.findItem(R.id.itemAdmobsmenuRefresh).setActionView( R.layout.action_bar_indeterminate_progress); } if (admobDetails == null) { menu.findItem(R.id.itemAdmobsmenuRemove).setVisible(false); menu.findItem(R.id.itemAdmobsmenuTimeframe).setVisible(false); menu.findItem(R.id.itemAdmobsmenuRefresh).setVisible(statsActivity.isRefreshing()); } } @Override public boolean onOptionsItemSelected(MenuItem item) { Context ctx = getActivity(); if (ctx == null) { return false; } switch (item.getItemId()) { case R.id.itemAdmobsmenuRefresh: setChartIgnoreCallLayouts(true); loadRemoteEntries(); return true; case R.id.itemAdmobsmenuRemove: AndlyticsDb.getInstance(getActivity()).saveAdmobDetails(statsActivity.getPackage(), null, null); showAccountList(); if (configSwitcher.getCurrentView().getId() != R.id.base_chart_config) { configSwitcher.showPrevious(); } mainViewSwitcher.swap(); getActivity().invalidateOptionsMenu(); return true; case R.id.itemAdmobsmenuTimeframe7: currentTimeFrame = Timeframe.LAST_SEVEN_DAYS; executeLoadData(currentTimeFrame); Preferences.saveChartTimeframe(Timeframe.LAST_SEVEN_DAYS, ctx); item.setChecked(true); return true; case R.id.itemAdmobsmenuTimeframe30: currentTimeFrame = Timeframe.LAST_THIRTY_DAYS; executeLoadData(currentTimeFrame); Preferences.saveChartTimeframe(Timeframe.LAST_THIRTY_DAYS, ctx); item.setChecked(true); return true; case R.id.itemAdmobsmenuTimeframe90: currentTimeFrame = Timeframe.LAST_NINETY_DAYS; executeLoadData(currentTimeFrame); Preferences.saveChartTimeframe(Timeframe.LAST_NINETY_DAYS, ctx); item.setChecked(true); return true; case R.id.itemAdmobsmenuTimeframeUnlimited: currentTimeFrame = Timeframe.UNLIMITED; executeLoadData(currentTimeFrame); Preferences.saveChartTimeframe(Timeframe.UNLIMITED, ctx); item.setChecked(true); return true; case R.id.itemAdmobsmenuTimeframeMonthToDate: currentTimeFrame = Timeframe.MONTH_TO_DATE; executeLoadData(currentTimeFrame); Preferences.saveChartTimeframe(Timeframe.MONTH_TO_DATE, ctx); item.setChecked(true); return true; default: return (super.onOptionsItemSelected(item)); } } private void loadRemoteEntries() { loadData(currentTimeFrame, true); } @Override protected void executeLoadData(Timeframe currentTimeFrame) { loadData(currentTimeFrame, false); } @Override protected String getChartHint() { return "8 " + this.getString(R.string.admob__charts_available) + " ->"; } private void showAccountList() { final AccountManager manager = AccountManager.get(getActivity()); final Account[] accounts = manager.getAccountsByType("com.google"); final int size = accounts.length; String[] names = new String[size]; accountList.removeAllViews(); for (int i = 0; i < size; i++) { names[i] = accounts[i].name; View inflate = getActivity().getLayoutInflater().inflate( R.layout.admob_account_list_item, null); TextView accountName = (TextView) inflate .findViewById(R.id.admob_account_list_item_text); accountName.setText(accounts[i].name); inflate.setTag(accounts[i].name); inflate.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { String currentAdmobAccount = (String) view.getTag(); selectedAdmobAccount = currentAdmobAccount; configSwitcher.showNext(); loadAdUnits(); } }); accountList.addView(inflate); } } void loadAdUnits() { if (getActivity() == null) { return; } // can't have two loaders with different interface, use // AsyncTask and retained fragment if (loadAdUnitsTask != null) { loadAdUnitsTask.cancel(true); loadAdUnitsTask = null; } loadAdUnitsTask = new LoadAdUnitsTask(getActivity(), this, selectedAdmobAccount); Utils.execute(loadAdUnitsTask); } @Override public ChartSet getChartSet() { return ChartSet.ADMOB; } @Override public String getTitle() { // this can be called before the fragment is attached Context ctx = AndlyticsApp.getInstance(); return ctx.getString(R.string.admob); } private void loadChartData(List<AdmobStats> statsForApp) { if (statsForApp != null && statsForApp.size() > 0) { updateCharts(statsForApp); DateFormat dateFormat = Preferences.getDateFormatLong(getActivity()); if (statsForApp.size() > 0) { timetext = dateFormat.format(statsForApp.get(0).getDate()) + " - " + dateFormat.format(statsForApp.get(statsForApp.size() - 1).getDate()); updateChartHeadline(); } // chartFrame.showNext(); } } @Override protected void notifyChangedDataformat() { loadData(getCurrentTimeFrame(), false); } @Override protected List<View> getExtraFullViews(View view) { configSwitcher = (ViewSwitcher) view.findViewById(R.id.base_chart_viewswitcher_config); configSwitcher.setInAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_in_right)); configSwitcher.setOutAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_out_left)); List<View> ret = new ArrayList<View>(); RelativeLayout container = (RelativeLayout) getActivity().getLayoutInflater().inflate( R.layout.admob_config_selectapp, null); siteList = (ViewGroup) container.findViewById(R.id.admob_sitelist); ret.add(container); container = (RelativeLayout) getActivity().getLayoutInflater().inflate( R.layout.admob_config_addaccount, null); accountList = (ViewGroup) container.findViewById(R.id.admob_accountlist); ret.add(container); return ret; } @Override public Loader<LoaderResult<AdmobStatsSummary>> onCreateLoader(int id, Bundle args) { String packageName = null; Timeframe timeframe = null; boolean loadRemote = false; if (args != null) { packageName = args.getString(ARG_PACKAGE_NAME); timeframe = (Timeframe) args.getSerializable(ARG_TIMEFRAME); loadRemote = args.getBoolean(ARG_LOAD_REMOTE); } return new AdmobDbLoader(getActivity(), packageName, timeframe, loadRemote); } @Override public void onLoadFinished(Loader<LoaderResult<AdmobStatsSummary>> loader, LoaderResult<AdmobStatsSummary> result) { statsActivity.refreshFinished(); if (result.isFailed()) { statsActivity.handleUserVisibleException(result.getError()); return; } if (result.getData() == null) { return; } updateView(result.getData()); } @Override public void onLoaderReset(Loader<LoaderResult<AdmobStatsSummary>> arg0) { } @Override public void initLoader(Bundle args) { // NOOP, to fulfill ChartFragment interface } @Override public void restartLoader(Bundle args) { // NOOP, to fulfill ChartFragment interface } private static class LoadAdUnitsTask extends DetachableAsyncTask<Void, Void, Exception, Activity> { // ad unit -> name private Map<String, String> adUnits; private AdmobFragment admobFragment; private DetailedStatsActivity statsActivity; private String admobAccount; public LoadAdUnitsTask(Activity activity, AdmobFragment admobFragment, String admobAccount) { super(activity); this.statsActivity = (DetailedStatsActivity) activity; this.admobFragment = admobFragment; this.admobAccount = admobAccount; } @Override protected void onPreExecute() { if (activity == null) { return; } statsActivity.refreshStarted(); } @Override protected Exception doInBackground(Void... params) { if (activity == null) { return null; } try { adUnits = AdSenseClient.getAdUnits(activity, admobAccount); } catch (Exception e) { return e; } return null; } @Override protected void onPostExecute(Exception error) { if (activity == null) { return; } statsActivity.refreshFinished(); if (error != null) { if (error instanceof UserRecoverableAuthIOException) { activity.startActivityForResult( ((UserRecoverableAuthIOException) error).getIntent(), BaseActivity.REQUEST_AUTHORIZATION); return; } else if (error instanceof GoogleJsonResponseException) { GoogleJsonError details = ((GoogleJsonResponseException) error).getDetails(); String message = error.getMessage(); if (details != null) { message = details.getMessage(); } Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); admobFragment.configSwitcher.showPrevious(); return; } else if (error.getCause() instanceof GoogleAuthException) { String message = ((GoogleAuthException) error.getCause()).getMessage(); Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); admobFragment.configSwitcher.showPrevious(); return; } statsActivity.handleUserVisibleException(error); return; } if (adUnits != null && adUnits.size() > 0) { admobFragment.siteList.removeAllViews(); Set<String> keySet = adUnits.keySet(); for (String siteId : keySet) { String siteName = adUnits.get(siteId); // pull the id from the data View inflate = activity.getLayoutInflater().inflate( R.layout.admob_account_list_item, null); TextView accountName = (TextView) inflate .findViewById(R.id.admob_account_list_item_text); accountName.setText(siteName); inflate.setTag(siteId); inflate.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { String admobAdUnitId = (String) view.getTag(); AndlyticsDb.getInstance(activity).saveAdmobAdUnitId( statsActivity.getPackage(), admobAccount, admobAdUnitId); admobFragment.mainViewSwitcher.swap(); admobFragment.loadRemoteData(); activity.invalidateOptionsMenu(); } }); admobFragment.siteList.addView(inflate); } } } } @Override public ChartListAdapter<AdmobStats> createChartAdapter() { return new AdmobListAdapter(getActivity()); } }