package com.jdroid.android.activity; import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresPermission; import android.support.v4.app.ActivityCompat; import android.support.v4.app.TaskStackBuilder; import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import com.google.android.gms.appinvite.AppInvite; import com.google.android.gms.appinvite.AppInviteInvitationResult; import com.google.android.gms.appinvite.AppInviteReferral; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.location.LocationServices; import com.google.firebase.appindexing.Action; import com.google.firebase.appindexing.FirebaseUserActions; import com.jdroid.android.application.AbstractApplication; import com.jdroid.android.application.AppModule; import com.jdroid.android.context.UsageStats; import com.jdroid.android.google.GooglePlayServicesUtils; import com.jdroid.android.google.SafeResultCallback; import com.jdroid.android.loading.ActivityLoading; import com.jdroid.android.loading.DefaultBlockingLoading; import com.jdroid.android.location.LocationHelper; import com.jdroid.android.navdrawer.NavDrawer; import com.jdroid.android.notification.NotificationBuilder; import com.jdroid.android.uri.ReferrerUtils; import com.jdroid.android.uri.UriHandler; import com.jdroid.android.utils.AndroidUtils; import com.jdroid.android.utils.AppUtils; import com.jdroid.android.utils.ToastUtils; import com.jdroid.java.collections.Maps; import com.jdroid.java.collections.Sets; import com.jdroid.java.utils.IdGenerator; import com.jdroid.java.utils.LoggerUtils; import org.slf4j.Logger; import java.util.Map; import java.util.Set; public class ActivityHelper implements ActivityIf { private final static Logger LOGGER = LoggerUtils.getLogger(ActivityHelper.class); private static final String REFERRER = "referrer"; private static final int LOCATION_UPDATE_TIMER_CODE = IdGenerator.getIntId(); private AbstractFragmentActivity activity; private Handler locationHandler; private boolean isDestroyed = false; private ActivityLoading loading; private NavDrawer navDrawer; private Map<AppModule, ActivityDelegate> activityDelegatesMap; private GoogleApiClient googleApiClient; private Action appIndexingAction; private static Boolean firstAppLoad; private static Boolean isGooglePlayServicesAvailable; private static Boolean isGooglePlayServicesDialogDisplayed = false; private String referrer; public ActivityHelper(AbstractFragmentActivity activity) { this.activity = activity; } public ActivityIf getActivityIf() { return activity; } protected AbstractFragmentActivity getActivity() { return activity; } // //////////////////////// Layout //////////////////////// // @Override public int getContentView() { return getActivityIf().getContentView(); } @SuppressWarnings("unchecked") @Override public <V extends View> V findView(int id) { return (V)activity.findViewById(id); } @Override public View inflate(int resource) { return LayoutInflater.from(activity).inflate(resource, null); } // //////////////////////// Life cycle //////////////////////// // @Override public Boolean onBeforeSetContentView() { return true; } @Override public void onAfterSetContentView(Bundle savedInstanceState) { // Do Nothing } public void beforeOnCreate() { // Do nothing } public void onCreate(final Bundle savedInstanceState) { LOGGER.debug("Executing onCreate on " + activity); AbstractApplication.get().setCurrentActivity(activity); verifyGooglePlayServicesAvailability(); activityDelegatesMap = Maps.newHashMap(); for (AppModule appModule : AbstractApplication.get().getAppModules()) { ActivityDelegate activityDelegate = getActivityIf().createActivityDelegate(appModule); if (activityDelegate != null) { activityDelegatesMap.put(appModule, activityDelegate); } } if (firstAppLoad == null) { firstAppLoad = true; UsageStats.incrementAppLoad(); } else { firstAppLoad = false; } overrideStatusBarColor(); AbstractApplication.get().initExceptionHandlers(); initGoogleApiClient(); if (savedInstanceState == null) { final UriHandler uriHandler = getActivityIf().createUriHandler(); final Boolean uriHandled = AbstractApplication.get().getUriMapper().handleUri(activity, activity.getIntent(), uriHandler, true); referrer = ReferrerUtils.getReferrerCategory(activity); if (googleApiClient != null && getActivityIf().isAppInviteEnabled() && (uriHandled || isHomeActivity())) { PendingResult<AppInviteInvitationResult> pendingResult = AppInvite.AppInviteApi.getInvitation(googleApiClient, getActivity(), false); pendingResult.setResultCallback(new SafeResultCallback<AppInviteInvitationResult>() { @Override public void onSuccessResult(@NonNull AppInviteInvitationResult result) { String deepLink = AppInviteReferral.getDeepLink(result.getInvitationIntent()); LOGGER.debug("AppInvite invitation deep link: " + deepLink); String invitationId = AppInviteReferral.getInvitationId(result.getInvitationIntent()); LOGGER.debug("AppInvite invitation id: " + invitationId); getActivityIf().onAppInvite(deepLink, invitationId); if (!uriHandled && isHomeActivity()) { if (uriHandler != null && deepLink.equals(uriHandler.getUrl(activity))) { LOGGER.debug("Skipping reopening invitation deepLink"); } else { Intent targetIntent = new Intent(); targetIntent.setData(Uri.parse(deepLink)); targetIntent.setPackage(AppUtils.getApplicationId()); ReferrerUtils.setReferrer(targetIntent, ActivityCompat.getReferrer(activity)); activity.finish(); activity.startActivity(targetIntent); } } } @Override public void onFailedResult(@NonNull AppInviteInvitationResult result) { LOGGER.debug("AppInvite invitation not found. Status code: " + result.getStatus().getStatusCode()); } }); } } else { referrer = (String)savedInstanceState.getSerializable(REFERRER); } if (getActivityIf().onBeforeSetContentView() && getContentView() != 0) { activity.setContentView(getContentView()); getActivityIf().onAfterSetContentView(savedInstanceState); } for (ActivityDelegate each : activityDelegatesMap.values()) { each.onCreate(savedInstanceState); } if (savedInstanceState == null) { trackNotificationOpened(activity.getIntent()); } } @Override public Boolean isAppInviteEnabled() { return true; } private Boolean isHomeActivity() { return AbstractApplication.get().getHomeActivityClass().equals(getActivity().getClass()); } private void initGoogleApiClient() { if (isGooglePlayServicesAvailable) { Set<Api<? extends Api.ApiOptions.NotRequiredOptions>> googleApis = Sets.newHashSet(); if (getActivityIf().isAppInviteEnabled()) { googleApis.add(AppInvite.API); } if (getActivityIf().isLocationServicesEnabled()) { googleApis.add(LocationServices.API); } googleApis.addAll(getCustomGoogleApis()); if (!googleApis.isEmpty()) { GoogleApiClient.Builder builder = new GoogleApiClient.Builder(activity); for(Api<? extends Api.ApiOptions.NotRequiredOptions> api : googleApis) { builder.addApi(api); } builder.enableAutoManage(getActivity(), new GoogleApiClient.OnConnectionFailedListener() { @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { AbstractApplication.get().getExceptionHandler().logHandledException(connectionResult.getErrorMessage()); } }); onInitGoogleApiClientBuilder(builder); googleApiClient = builder.build(); } } } protected Set<Api<? extends Api.ApiOptions.NotRequiredOptions>> getCustomGoogleApis() { return Sets.newHashSet(); } protected void onInitGoogleApiClientBuilder(GoogleApiClient.Builder builder) { // Do nothing } @Override public void onAppInvite(String deepLink, String invitationId) { // Do Nothing } @TargetApi(Build.VERSION_CODES.LOLLIPOP) protected void overrideStatusBarColor() { if (!AndroidUtils.isPreLollipop()) { activity.getWindow().setStatusBarColor(activity.getResources().getColor(android.R.color.transparent)); } } public void onPostCreate(Bundle savedInstanceState) { if (navDrawer != null) { navDrawer.onPostCreate(savedInstanceState); } } public void onConfigurationChanged(Configuration newConfig) { if (navDrawer != null) { navDrawer.onConfigurationChanged(newConfig); } } @Override public Boolean isLauncherActivity() { return false; } public void onSaveInstanceState(Bundle outState) { LOGGER.debug("Executing onSaveInstanceState on " + activity); outState.putSerializable(REFERRER, referrer); dismissLoading(); } public void onRestoreInstanceState(Bundle savedInstanceState) { LOGGER.debug("Executing onRestoreInstanceState on " + activity); } @SuppressLint("HandlerLeak") public void onStart() { LOGGER.debug("Executing onStart on " + activity); AbstractApplication.get().setCurrentActivity(activity); AbstractApplication.get().getAnalyticsSender().onActivityStart(activity.getClass(), referrer, getOnActivityStartData()); final Long locationFrequency = getActivityIf().getLocationFrequency(); if (locationFrequency != null) { locationHandler = new Handler() { @Override @RequiresPermission(anyOf = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}) public void handleMessage(Message m) { LocationHelper.get().startLocalization(); locationHandler.sendMessageDelayed(Message.obtain(locationHandler, LOCATION_UPDATE_TIMER_CODE), locationFrequency); } }; locationHandler.sendMessage(Message.obtain(locationHandler, LOCATION_UPDATE_TIMER_CODE)); } for (ActivityDelegate each : activityDelegatesMap.values()) { each.onStart(); } UriHandler uriHandler = getActivityIf().createUriHandler(); if (uriHandler != null && isGooglePlayServicesAvailable) { if (appIndexingAction == null) { appIndexingAction = uriHandler.getAppIndexingAction(activity); } if (appIndexingAction != null) { FirebaseUserActions.getInstance().start(appIndexingAction); } } } @Nullable protected Object getOnActivityStartData() { return null; } public void onResume() { LOGGER.debug("Executing onResume on " + activity); AbstractApplication.get().setCurrentActivity(activity); verifyGooglePlayServicesAvailability(getActivityIf().isGooglePlayServicesVerificationEnabled()); for (ActivityDelegate each : activityDelegatesMap.values()) { each.onResume(); } if (navDrawer != null) { navDrawer.onResume(); } } private void verifyGooglePlayServicesAvailability() { verifyGooglePlayServicesAvailability(false); } private void verifyGooglePlayServicesAvailability(Boolean displayDialog) { Boolean oldIsGooglePlayServicesAvailable = isGooglePlayServicesAvailable; isGooglePlayServicesAvailable = displayDialog && !isGooglePlayServicesDialogDisplayed ? GooglePlayServicesUtils.verifyGooglePlayServices(activity).isAvailable() : GooglePlayServicesUtils.isGooglePlayServicesAvailable(activity); if (!isGooglePlayServicesAvailable && displayDialog) { isGooglePlayServicesDialogDisplayed = true; } if (oldIsGooglePlayServicesAvailable != null && !oldIsGooglePlayServicesAvailable && isGooglePlayServicesAvailable) { LOGGER.info("Google Play Services updated"); for (AppModule appModule : AbstractApplication.get().getAppModules()) { appModule.onGooglePlayServicesUpdated(); } } } public void onBeforePause() { for (ActivityDelegate each : activityDelegatesMap.values()) { each.onBeforePause(); } } public void onPause() { LOGGER.debug("Executing onPause on " + activity); for (ActivityDelegate each : activityDelegatesMap.values()) { each.onPause(); } } public void onBeforeStop() { if (appIndexingAction != null && isGooglePlayServicesAvailable) { FirebaseUserActions.getInstance().end(appIndexingAction); } } public void onStop() { LOGGER.debug("Executing onStop on " + activity); UsageStats.setLastStopTime(); ToastUtils.cancelCurrentToast(); AbstractApplication.get().getAnalyticsSender().onActivityStop(activity); if (locationHandler != null) { locationHandler.removeCallbacksAndMessages(null); } for (ActivityDelegate each : activityDelegatesMap.values()) { each.onStop(); } } public void onBeforeDestroy() { for (ActivityDelegate each : activityDelegatesMap.values()) { each.onBeforeDestroy(); } } public void onDestroy() { isDestroyed = true; LOGGER.debug("Executing onDestroy on " + activity); dismissLoading(); for (ActivityDelegate each : activityDelegatesMap.values()) { each.onDestroy(); } } public void onActivityResult(int requestCode, int resultCode, Intent data) { AbstractApplication.get().setCurrentActivity(activity); } public boolean onCreateOptionsMenu(Menu menu) { if (getActivityIf().getMenuResourceId() != null) { MenuInflater inflater = getActivityIf().getMenuInflater(); inflater.inflate(getActivityIf().getMenuResourceId(), menu); getActivityIf().doOnCreateOptionsMenu(menu); } return true; } @Override public Integer getMenuResourceId() { return null; } @Override public void doOnCreateOptionsMenu(Menu menu) { // Do nothing } public void onPrepareOptionsMenu(Menu menu) { // Do nothing } @Override public boolean onOptionsItemSelected(MenuItem item) { if (navDrawer != null && navDrawer.onOptionsItemSelected(item)) { return true; } else if (item.getItemId() == android.R.id.home) { return onHomeOptionItemSelected(); } else { return false; } } protected boolean onHomeOptionItemSelected() { Intent upIntent = getParentActivityIntent(); if (activity.shouldUpRecreateTask(upIntent)) { // This activity is NOT part of this app's task, so create a new task // when navigating up, with a synthesized back stack. TaskStackBuilder builder = TaskStackBuilder.create(activity); // Add all of this activity's parents to the back stack builder.addNextIntentWithParentStack(upIntent); // Navigate up to the closest parent builder.startActivities(); } else { // This activity is part of this app's task, so simply // navigate up to the logical parent activity. activity.startActivity(upIntent); activity.finish(); } return true; } @NonNull private Intent getParentActivityIntent() { Intent intent = activity.getParentActivityIntent(); if (intent == null) { intent = new Intent(getActivity(), AbstractApplication.get().getHomeActivityClass()); } intent.addFlags(getParentActivityIntentFlags()); return intent; } protected int getParentActivityIntentFlags() { return Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP; } @SuppressWarnings("unchecked") @Override public <E> E getExtra(String key) { Bundle extras = activity.getIntent().getExtras(); return extras != null ? (E)extras.get(key) : null; } public void onNewIntent(Intent intent) { LOGGER.debug("Executing onNewIntent on " + activity); UriHandler uriHandler = getActivityIf().createUriHandler(); if (uriHandler != null) { Boolean uriHandled = AbstractApplication.get().getUriMapper().handleUri(activity, intent, uriHandler, false); if (!uriHandled) { activity.setIntent(intent); } } else { activity.setIntent(intent); } referrer = ReferrerUtils.getReferrerCategory(activity); trackNotificationOpened(intent); } private void trackNotificationOpened(Intent intent) { try { if (NotificationBuilder.generateNotificationsReferrer().equals(referrer)) { String notificationName = intent.getStringExtra(NotificationBuilder.NOTIFICATION_NAME); if (notificationName != null) { AbstractApplication.get().getAnalyticsSender().trackNotificationOpened(notificationName); } } } catch (Exception e) { AbstractApplication.get().getExceptionHandler().logHandledException(e); } } @Override public MenuInflater getMenuInflater() { return null; } @Override public Boolean isActivityDestroyed() { return isDestroyed; } @Override public Boolean onBackPressedHandled() { if (navDrawer != null) { return navDrawer.onBackPressed(); } return false; } // //////////////////////// Delegates //////////////////////// // public ActivityDelegate createActivityDelegate(AppModule appModule) { return appModule.createActivityDelegate(activity); } public ActivityDelegate getActivityDelegate(AppModule appModule) { return activityDelegatesMap.get(appModule); } // //////////////////////// Loading //////////////////////// // @Override public void showLoading() { executeOnUIThread(new Runnable() { @Override public void run() { if (loading == null) { loading = getActivityIf().getDefaultLoading(); } loading.show(getActivityIf()); } }); } @Override public void dismissLoading() { executeOnUIThread(new Runnable() { @Override public void run() { if (loading != null) { loading.dismiss(getActivityIf()); } } }); } @NonNull @Override public ActivityLoading getDefaultLoading() { return new DefaultBlockingLoading(); } @Override public void setLoading(ActivityLoading loading) { this.loading = loading; } // //////////////////////// Navigation Drawer //////////////////////// // public void initNavDrawer(Toolbar appBar) { if (getActivityIf().isNavDrawerEnabled()) { navDrawer = getActivityIf().createNavDrawer(activity, appBar); navDrawer.init(); } } @Override public Boolean isNavDrawerEnabled() { return false; } @Override public NavDrawer createNavDrawer(AbstractFragmentActivity activity, Toolbar appBar) { return null; } // //////////////////////// Location //////////////////////// // @Override public Long getLocationFrequency() { return null; } @Override public Boolean isLocationServicesEnabled() { return false; } // //////////////////////// Others //////////////////////// // @Override public void executeOnUIThread(Runnable runnable) { if (activity.equals(AbstractApplication.get().getCurrentActivity())) { activity.runOnUiThread(runnable); } } @Override public UriHandler createUriHandler() { return null; } @Override public Boolean isGooglePlayServicesVerificationEnabled() { return false; } @Nullable public GoogleApiClient getGoogleApiClient() { return googleApiClient; } }