Created
May 30, 2018 21:30
-
-
Save boshajones/75c926fd7f8e3bbde077f2b6767aa6cd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.hedgehoglab.hedgehoglab; | |
import android.app.Activity; | |
import android.content.Intent; | |
import android.content.pm.PackageManager; | |
import android.content.pm.ResolveInfo; | |
import android.os.Bundle; | |
import android.support.annotation.AnimRes; | |
import android.support.annotation.IdRes; | |
import android.support.annotation.NonNull; | |
import android.support.annotation.Nullable; | |
import android.support.v4.app.Fragment; | |
import android.support.v4.app.FragmentManager; | |
import android.support.v4.app.FragmentTransaction; | |
import android.support.v7.app.AppCompatActivity; | |
import com.hedgehoglab.hedgehoglab.R; | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.Set; | |
import net.hockeyapp.android.CrashManager; | |
import net.hockeyapp.android.UpdateManager; | |
/** | |
* Base Activity containing methods common to all single Fragment {@link Activity} objects in the application, | |
* including swapping the current fragment, and handling any {@link Fragment} objects that wish to intercept | |
* the Android back button using the {@link OnBackPressedListener} | |
* | |
* @author hedgehog lab | |
* @version 1.0 | |
*/ | |
@SuppressWarnings({"unused", "WeakerAccess"}) | |
public abstract class ActivityBase extends AppCompatActivity { | |
@AnimRes private static final int ENTER_ANIMATION = R.anim.slide_in_from_bottom; | |
@AnimRes private static final int EXIT_ANIMATION = R.anim.fade_out; | |
@AnimRes private static final int POP_ENTER_ANIMATION = R.anim.fade_in; | |
@AnimRes private static final int POP_EXIT_ANIMATION = R.anim.slide_out_to_bottom; | |
/** | |
* Classes that implement the OnBackPressedListener wish to intercept the Android system back | |
* button from the activity. Implementing classes should return true if the back event was consumed, | |
* else false. | |
*/ | |
public interface OnBackPressedListener { | |
boolean onBackPressed(); | |
} | |
/** | |
* Returns the resource ID value of the View container whose fragment(s) are to be replaced | |
* | |
* @return Resource ID | |
*/ | |
protected abstract @IdRes int getContainerViewId(); | |
/** | |
* When the activity is resumed, if crash reports are enabled in the current product flavour the | |
* HockeyApp crash manager is triggered and checks if a new crash was created before. | |
* If there a crash log is present, it presents a dialog to ask the user whether they want to | |
* send the crash log to HockeyApp. | |
* | |
* In addition, if update checks are enabled in the current product flavour and this is the app's | |
* launcher activity then the HockeyApp update manager is triggered and checks for updates in the background. | |
* If there is a new update, an alert dialog is shown and if the user presses Show, | |
* they will be taken to the update activity. | |
*/ | |
@Override | |
protected void onResume() { | |
super.onResume(); | |
if (BuildConfig.ENABLE_HA_CRASH_REPORTS) { | |
CrashManager.register(ActivityBase.this); | |
} | |
if (BuildConfig.ENABLE_HA_UPDATES && isLauncherActivity()) { | |
UpdateManager.register(ActivityBase.this); | |
} | |
} | |
/** | |
* If update checks are enabled in the current product flavour and this is the app's launcher | |
* activity then the HockeyApp update manager is unregistered. | |
*/ | |
@Override | |
protected void onPause() { | |
super.onPause(); | |
if (BuildConfig.ENABLE_HA_UPDATES && isLauncherActivity()) { | |
UpdateManager.unregister(); | |
} | |
} | |
/** | |
* If update checks are enabled in the current product flavour and this is the app's launcher | |
* activity then the HockeyApp update manager is unregistered. | |
*/ | |
@Override | |
protected void onDestroy() { | |
super.onDestroy(); | |
if (BuildConfig.ENABLE_HA_UPDATES && isLauncherActivity()) { | |
UpdateManager.unregister(); | |
} | |
} | |
/** | |
* Overrides the Android back button and checks to see if the current fragment implements | |
* {@link OnBackPressedListener} if so the {@link OnBackPressedListener#onBackPressed()} method is | |
* called so the {@link Fragment} can take action. If the {@link Fragment} does not consume the | |
* back pressed event, by returning false, super is called which takes care of popping the | |
* fragment backstack or finishing the activity as appropriate. | |
*/ | |
@Override | |
public void onBackPressed() { | |
boolean handled = false; | |
Fragment fragment = getSupportFragmentManager().findFragmentById(getContainerViewId()); | |
if (fragment != null && fragment instanceof OnBackPressedListener) { | |
handled = ((OnBackPressedListener) fragment).onBackPressed(); | |
} | |
if (!handled) { | |
super.onBackPressed(); | |
} | |
} | |
/** | |
* Returns the current {@link Fragment} held within the container view ID if one exists. | |
* | |
* @return Current {@link Fragment} instance if one exists | |
*/ | |
@Nullable | |
protected Fragment getCurrentFragment() { | |
return getSupportFragmentManager().findFragmentById(getContainerViewId()); | |
} | |
/** | |
* Returns an array of the animation resources to run when the {@link Fragment}s that are | |
* entering and exiting in a transaction. | |
* | |
* This method can be overridden by implementing classes to provide a specific set of animations, | |
* on an activity by activity basis but provides a default set of animations. | |
* | |
* @return Array of animation resources | |
*/ | |
@NonNull | |
protected int[] getAnimationIntArray() { | |
return new int[]{ENTER_ANIMATION, EXIT_ANIMATION, POP_ENTER_ANIMATION, POP_EXIT_ANIMATION}; | |
} | |
/** | |
* Finds a container with the containerViewId 'R.id.content_fragment' and replaces any content | |
* with the provided {@link Fragment} instance. It handles checking to ensure that the provided | |
* {@link Fragment}is not identical to the current {@link Fragment}, checking bundles if necessary. | |
* | |
* @param fragment {@link Fragment} to load into the container | |
* @param addToBackStack True if the {@link Fragment} should be added to the activity backstack | |
* @param animate True if the {@link Fragment} should animate into and out of view | |
*/ | |
protected void swapFragment(@NonNull Fragment fragment, boolean addToBackStack, boolean animate) { | |
FragmentManager fragmentManager = getSupportFragmentManager(); | |
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); | |
Fragment currentFragment = fragmentManager.findFragmentById(getContainerViewId()); | |
if (currentFragment != null && currentFragment.getClass().equals(fragment.getClass())) { | |
// Check the fragment arguments, if the arguments are the same then yield | |
if (areBundlesEqual(currentFragment.getArguments(), fragment.getArguments())) { | |
return; | |
} | |
} | |
if (animate) { | |
int[] anims = getAnimationIntArray(); | |
fragmentTransaction.setCustomAnimations(anims[0], anims[1], anims[2], anims[3]); | |
} | |
fragmentTransaction.replace(getContainerViewId(), fragment); | |
if (addToBackStack) { | |
fragmentTransaction.addToBackStack(null); | |
} | |
fragmentTransaction.commit(); | |
} | |
/** | |
* Returns true if the provided {@link Bundle} objects are equal. This is done by first checking | |
* for nullity, then checking the sizes of the bundles and yielding if they're not equal. | |
* | |
* If the bundle arguments contain further bundle objects then recursion is used, otherwise as a | |
* value for a key can be null, both values are checked in both bundles and then compared. | |
* | |
* @param args1 {@link Bundle} Arguments to compare | |
* @param args2 {@link Bundle} Arguments to compare | |
* @return True if both bundles are equivalent, else false | |
*/ | |
private boolean areBundlesEqual(@Nullable Bundle args1, @Nullable Bundle args2) { | |
if (args1 == null && args2 == null) { | |
return true; | |
} else if (args1 == null) { | |
return false; | |
} else if (args2 == null) { | |
return false; | |
} | |
if (args1.size() != args2.size()) { | |
return false; | |
} | |
Set<String> setOne = args1.keySet(); | |
Object valueOne; | |
Object valueTwo; | |
for(String key : setOne) { | |
valueOne = args1.get(key); | |
valueTwo = args2.get(key); | |
if(valueOne instanceof Bundle && valueTwo instanceof Bundle && | |
!areBundlesEqual((Bundle) valueOne, (Bundle) valueTwo)) { | |
return false; | |
} else if(valueOne == null) { | |
if(valueTwo != null || !args2.containsKey(key)) { | |
return false; | |
} | |
} else if(!valueOne.equals(valueTwo)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Returns true if the current activity is a launcher activity for the application. This is | |
* defined in the Android Manifest using the android.intent.category.LAUNCHER intent filter category. | |
* | |
* @return True if this activity is a launcher | |
*/ | |
private boolean isLauncherActivity() { | |
Intent intent = getIntent(); | |
String action = intent.getAction(); | |
Set<String> categories = intent.getCategories(); | |
if (categories == null) categories = Collections.emptySet(); | |
return Intent.ACTION_MAIN.equals(action) && categories.contains(Intent.CATEGORY_LAUNCHER); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment