Java tutorial
/* * Copyright (c) 2016, Apptentive, Inc. All Rights Reserved. * Please refer to the LICENSE file for the terms and conditions * under which redistribution and use of this file is permitted. */ package com.apptentive.android.sdk; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; import android.support.v4.content.IntentCompat; import android.support.v4.content.res.ResourcesCompat; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.View; import android.view.ViewTreeObserver; import android.view.WindowManager; import com.apptentive.android.sdk.adapter.ApptentiveViewPagerAdapter; import com.apptentive.android.sdk.model.FragmentFactory; import com.apptentive.android.sdk.module.engagement.EngagementModule; import com.apptentive.android.sdk.module.engagement.interaction.fragment.ApptentiveBaseFragment; import com.apptentive.android.sdk.module.metric.MetricModule; import com.apptentive.android.sdk.notifications.ApptentiveNotification; import com.apptentive.android.sdk.util.Constants; import com.apptentive.android.sdk.util.Util; public class ApptentiveViewActivity extends ApptentiveBaseActivity implements ApptentiveBaseFragment.OnFragmentTransitionListener { private static final String FRAGMENT_TAG = "fragmentTag"; private int fragmentType; private Toolbar toolbar; private ViewPager viewPager; private ApptentiveViewPagerAdapter viewPager_Adapter; private int current_tab; private View decorView; private View contentView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle bundle = FragmentFactory.addDisplayModeToFragmentBundle(getIntent().getExtras()); boolean isInteractionModal = bundle.getBoolean(Constants.FragmentConfigKeys.MODAL); ApptentiveBaseFragment newFragment = null; if (savedInstanceState != null) { // retrieve the retained fragment after orientation change using saved tag String savedFragmentTag = savedInstanceState.getString(FRAGMENT_TAG); newFragment = (ApptentiveBaseFragment) getSupportFragmentManager().findFragmentByTag(savedFragmentTag); /* Since we always store tags of fragments in the ViewPager upon orientation change, * failure of retrieval indicate internal error */ if (newFragment == null) { finish(); return; } } try { fragmentType = bundle.getInt(Constants.FragmentConfigKeys.TYPE, Constants.FragmentTypes.UNKNOWN); if (fragmentType != Constants.FragmentTypes.UNKNOWN) { if (fragmentType == Constants.FragmentTypes.INTERACTION || fragmentType == Constants.FragmentTypes.MESSAGE_CENTER_ERROR || fragmentType == Constants.FragmentTypes.ABOUT) { bundle.putInt("toolbarLayoutId", R.id.apptentive_toolbar); if (newFragment == null) { newFragment = FragmentFactory.createFragmentInstance(bundle); isInteractionModal = newFragment.isShownAsModalDialog(); } if (newFragment != null) { applyApptentiveTheme(isInteractionModal); newFragment.setOnTransitionListener(this); } } if (newFragment == null) { if (fragmentType == Constants.FragmentTypes.ENGAGE_INTERNAL_EVENT) { String eventName = getIntent().getStringExtra(Constants.FragmentConfigKeys.EXTRA); if (eventName != null) { EngagementModule.engageInternal(this, eventName); } } finish(); return; } } } catch (Exception e) { ApptentiveLog.e("Error creating ApptentiveViewActivity.", e); MetricModule.sendError(e, null, null); } setContentView(R.layout.apptentive_viewactivity); toolbar = (Toolbar) findViewById(R.id.apptentive_toolbar); setSupportActionBar(toolbar); /* Add top padding by the amount of Status Bar height to avoid toolbar being covered when * status bar is translucent */ toolbar.setPadding(0, getToolbarHeightAdjustment(!isInteractionModal), 0, 0); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); int navIconResId = newFragment.getToolbarNavigationIconResourceId(getTheme()); // Check if fragment may show an alternative navigation icon if (navIconResId != 0) { /* In order for the alternative icon has the same color used by toolbar icon, * need to apply the same color in toolbar theme * By default colorControlNormal has same value as textColorPrimary defined in toolbar theme overlay */ final Drawable alternateUpArrow = ResourcesCompat.getDrawable(getResources(), navIconResId, getTheme()); int colorControlNormal = Util.getThemeColor( ApptentiveInternal.getInstance().getApptentiveToolbarTheme(), R.attr.colorControlNormal); alternateUpArrow.setColorFilter(colorControlNormal, PorterDuff.Mode.SRC_ATOP); actionBar.setHomeAsUpIndicator(alternateUpArrow); } } //current_tab = extra.getInt(SELECTED_TAB_EXTRA_KEY, 0); current_tab = 0; addFragmentToAdapter(newFragment, newFragment.getTitle()); // Get the ViewPager and set it's PagerAdapter so that it can display items viewPager = (ViewPager) findViewById(R.id.apptentive_vp); viewPager.setAdapter(viewPager_Adapter); ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() { Boolean first = true; @Override public void onPageSelected(int position) { final ApptentiveBaseFragment currentFragment = (ApptentiveBaseFragment) viewPager_Adapter .getItem(viewPager.getCurrentItem()); // Set the Activity title for TalkBack support final String title = currentFragment.getTitle(); if (currentFragment != null && currentFragment.getActivity() != null) { currentFragment.getActivity().setTitle(title); } if (!currentFragment.isShownAsModalDialog()) { toolbar.post(new Runnable() { @Override public void run() { toolbar.setVisibility(View.VISIBLE); toolbar.setTitle(title); } }); } else { toolbar.setVisibility(View.GONE); } current_tab = position; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (first && positionOffset == 0 && positionOffsetPixels == 0) { onPageSelected(current_tab); first = false; } } @Override public void onPageScrollStateChanged(int pos) { // TODO Auto-generated method stub } }; viewPager.addOnPageChangeListener(pageChangeListener); // Needed to prevent the window from being panned up when the keyboard is opened. getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: exitActivity(ApptentiveViewExitType.MENU_ITEM); return true; default: return super.onOptionsItemSelected(item); } } /** * Helper to clean up the Activity, whether it is exited through the toolbar back button, or the hardware back button. */ private void exitActivity(ApptentiveViewExitType exitType) { ApptentiveBaseFragment currentFragment = (ApptentiveBaseFragment) viewPager_Adapter .getItem(viewPager.getCurrentItem()); if (currentFragment != null && currentFragment.isVisible()) { if (currentFragment.onFragmentExit(exitType)) { return; } FragmentManager childFragmentManager = currentFragment.getChildFragmentManager(); if (childFragmentManager.getBackStackEntryCount() > 0) { childFragmentManager.popBackStack(); } } super.onBackPressed(); startLauncherActivityIfRoot(); } public void onBackPressed() { exitActivity(ApptentiveViewExitType.BACK_BUTTON); } @Override public void finish() { super.finish(); overridePendingTransition(0, R.anim.apptentive_slide_down_out); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); overridePendingTransition(R.anim.apptentive_slide_up_in, 0); } @Override public void onSaveInstanceState(Bundle outState) { // Save the tag of the current fragment before orientation change outState.putString(FRAGMENT_TAG, viewPager_Adapter.getFragmentTag(current_tab)); super.onSaveInstanceState(outState); } @Override public void onFragmentTransition(ApptentiveBaseFragment currentFragment) { if (currentFragment != null) { int numberOfPages = viewPager_Adapter.getCount(); for (int i = 0; i < numberOfPages; ++i) { ApptentiveBaseFragment fragment = (ApptentiveBaseFragment) viewPager_Adapter.getItem(i); if (currentFragment == fragment) { if (numberOfPages == 1) { finish(); } else { currentFragment.dismiss(); viewPager_Adapter.removeItem(i); viewPager_Adapter.notifyDataSetChanged(); } return; } } } } private void applyApptentiveTheme(boolean isModalInteraction) { // Update the activity theme to reflect current attributes try { ApptentiveInternal.getInstance().updateApptentiveInteractionTheme(getTheme(), this); if (isModalInteraction) { getTheme().applyStyle(R.style.ApptentiveBaseDialogTheme, true); setStatusBarColor(); } // Change the thumbnail header color in task list if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { int colorPrimary = Util.getThemeColor(getTheme(), R.attr.colorPrimary); ActivityManager.TaskDescription taskDes = new ActivityManager.TaskDescription(null, null, colorPrimary); setTaskDescription(taskDes); } } catch (Exception e) { ApptentiveLog.e("Error apply Apptentive Theme.", e); } } private void addFragmentToAdapter(ApptentiveBaseFragment f, String title) { if (viewPager_Adapter == null) { viewPager_Adapter = new ApptentiveViewPagerAdapter(getSupportFragmentManager()); } viewPager_Adapter.add(f, title); viewPager_Adapter.notifyDataSetChanged(); } /* If Apptentive interaction activity is the only activity of a task, backing from it will * automatically launch the app main activity. * * This is to make sure when Apptentive interaction is * launched from non-activity context, such as pending intent when application is not running,service * context, or application context, exiting fom Apptentive interaction will land on a default app * activity, instead of desktop. * */ private void startLauncherActivityIfRoot() { if (isTaskRoot()) { PackageManager packageManager = getPackageManager(); Intent intent = packageManager.getLaunchIntentForPackage(getPackageName()); ComponentName componentName = intent.getComponent(); /** Backwards compatible method that will clear all activities in the stack. */ Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName); startActivity(mainIntent); } } /* Android versions starting with API Level 19, setting translucent statusbar would have two implications: * 1. toolbar of non-model interaction would be partially covered * 2. Keyboard launch won't resize window. (Bug: https://code.google.com/p/android/issues/detail?id=63777) * The following method will fix both issues */ private int getToolbarHeightAdjustment(boolean bToolbarShown) { int adjustAmount = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { boolean translucentStatus = false; // check theme attrs to see if translucent statusbar is set explicitly int[] attrs = { android.R.attr.windowTranslucentStatus }; TypedArray a = getTheme().obtainStyledAttributes(attrs); try { translucentStatus = a.getBoolean(0, false); } finally { a.recycle(); } // also check window flags in case translucent statusbar is set implicitly WindowManager.LayoutParams winParams = getWindow().getAttributes(); int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; if ((winParams.flags & bits) != 0) { translucentStatus = true; } if (translucentStatus) { if (bToolbarShown) { int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { adjustAmount = getResources().getDimensionPixelSize(resourceId); } } /* Add layout listener to ensure keyboard launch resize the screen when android:windowTranslucentStatus=true * Fixing workaround found here: * http://stackoverflow.com/questions/8398102/androidwindowsoftinputmode-adjustresize-doesnt-make-any-difference */ decorView = getWindow().getDecorView(); contentView = decorView.findViewById(android.R.id.content); decorView.getViewTreeObserver().addOnGlobalLayoutListener(keyboardPresencelLayoutListener); } } return adjustAmount; } ViewTreeObserver.OnGlobalLayoutListener keyboardPresencelLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect r = new Rect(); decorView.getWindowVisibleDisplayFrame(r); int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels; int diff = height - r.bottom; // Detect keyboard launch when use-able screen height differs from the total screen height if (diff != 0) { //check if the padding is 0 (if yes set the padding for the keyboard) if (contentView.getPaddingBottom() != diff) { //set the padding of the contentView for the keyboard contentView.setPadding(0, 0, 0, diff); } } else { //check if the padding is != 0 (if yes reset the padding) if (contentView.getPaddingBottom() != 0) { //reset the padding of the contentView contentView.setPadding(0, 0, 0, 0); } } } }; /* Set status bar color when dialog style modal interactions, such as Rating prompt, Note .. are shown. * It is the default status color alpha blended with the Apptentive translucent * color defined by apptentive_activity_frame * @param statusBarDefaultColor the default activity status bar color specified by the app */ private void setStatusBarColor() { // Changing status bar color is a post-21 feature if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { int statusBarDefaultColor = ApptentiveInternal.getInstance().getDefaultStatusBarColor(); int overlayColor = ContextCompat.getColor(this, R.color.apptentive_activity_frame_dark); getWindow().setStatusBarColor(Util.alphaMixColors(statusBarDefaultColor, overlayColor)); } } //region ApptentiveNotificationObserver @Override public void onReceiveNotification(ApptentiveNotification notification) { if (notification.getName().equals(ApptentiveInternal.NOTIFICATION_INTERACTIONS_SHOULD_DISMISS)) { if (!isFinishing()) { exitActivity(ApptentiveViewExitType.NOTIFICATION); } } } //endregion }