Java tutorial
/* * Copyright (c) 2016 - Bernie 2016, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.berniesanders.fieldthebern; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.AppBarLayout; import android.support.design.widget.CollapsingToolbarLayout; import android.support.v4.view.GravityCompat; import android.support.v4.view.MenuItemCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.graphics.Palette; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.ImageView; import butterknife.Bind; import butterknife.BindColor; import butterknife.ButterKnife; import com.berniesanders.fieldthebern.config.Actions; import com.berniesanders.fieldthebern.controllers.ActionBarController; import com.berniesanders.fieldthebern.controllers.ActionBarService; import com.berniesanders.fieldthebern.controllers.DialogController; import com.berniesanders.fieldthebern.controllers.DialogService; import com.berniesanders.fieldthebern.controllers.FacebookController; import com.berniesanders.fieldthebern.controllers.FacebookService; import com.berniesanders.fieldthebern.controllers.LocationController; import com.berniesanders.fieldthebern.controllers.LocationService; import com.berniesanders.fieldthebern.controllers.PermissionController; import com.berniesanders.fieldthebern.controllers.PermissionService; import com.berniesanders.fieldthebern.controllers.PhotoController; import com.berniesanders.fieldthebern.controllers.PhotoService; import com.berniesanders.fieldthebern.controllers.ProgressDialogController; import com.berniesanders.fieldthebern.controllers.ProgressDialogService; import com.berniesanders.fieldthebern.controllers.ToastController; import com.berniesanders.fieldthebern.controllers.ToastService; import com.berniesanders.fieldthebern.dagger.FtbActivityScope; import com.berniesanders.fieldthebern.db.SearchMatrixCursor; import com.berniesanders.fieldthebern.models.ApiItem; import com.berniesanders.fieldthebern.models.Collection; import com.berniesanders.fieldthebern.models.Page; import com.berniesanders.fieldthebern.mortar.GsonParceler; import com.berniesanders.fieldthebern.mortar.MortarScreenSwitcherFrame; import com.berniesanders.fieldthebern.repositories.RankingsRepo; import com.berniesanders.fieldthebern.screens.CollectionScreen; import com.berniesanders.fieldthebern.screens.InitialScreen; import com.berniesanders.fieldthebern.screens.PageScreen; import com.berniesanders.fieldthebern.views.PaletteTransformation; import com.facebook.CallbackManager; import com.facebook.FacebookSdk; import com.google.gson.Gson; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; import flow.Flow; import flow.FlowDelegate; import flow.History; import java.util.Set; import javax.inject.Inject; import mortar.MortarScope; import mortar.bundler.BundleServiceRunner; import timber.log.Timber; import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; import uk.co.chrisjenx.calligraphy.TypefaceUtils; import static com.berniesanders.fieldthebern.apilevels.ApiLevel.isLollipopOrAbove; import static mortar.MortarScope.buildChild; import static mortar.MortarScope.findChild; @FtbActivityScope public class MainActivity extends AppCompatActivity implements ActionBarController.Activity, DialogController.Activity, FacebookController.Activity, PermissionController.Activity, ProgressDialogController.Activity, ToastController.Activity, PhotoController.Activity, Flow.Dispatcher { private MortarScope activityScope; private FlowDelegate flowDelegate; private Menu menu; SearchView searchView; private ActionBarController.MenuAction actionBarMenuAction; private ActionBarDrawerToggle drawerToggle; private InitialScreen initialScreen; CallbackManager callbackManager; @Bind(R.id.container_main) MortarScreenSwitcherFrame container; @Bind(R.id.backdrop) ImageView backgroundImage; @Bind(R.id.shading) View shading; @Bind(R.id.collapsing_toolbar) CollapsingToolbarLayout collapsingToolbar; @Bind(R.id.appbar) AppBarLayout appBarLayout; @Bind(R.id.drawer_layout) DrawerLayout drawerLayout; @Bind(R.id.toolbar) Toolbar toolbar; @BindColor(R.color.bernie_dark_blue) int bernieDarkBlue; @Inject Gson gson; @Inject ActionBarController actionBarController; @Inject ToastController toastController; @Inject DialogController dialogController; @Inject FacebookController facebookController; @Inject LocationController locationController; @Inject ProgressDialogController progressDialogController; @Inject PermissionController permissionController; @Inject PhotoController photoController; @Inject RankingsRepo rankingsRepo; @Override public void dispatch(Flow.Traversal traversal, Flow.TraversalCallback callback) { if (menu != null && menu.findItem(R.id.menu_search) != null) { MenuItemCompat.collapseActionView(menu.findItem(R.id.menu_search)); //hide any half-open SearchView } container.dispatch(traversal, callback); } //------------- Mortar ----------------// @Override public Object getSystemService(String name) { if (flowDelegate != null) { Object flowService = flowDelegate.getSystemService(name); if (flowService != null) { return flowService; } } if (activityScope != null && activityScope.hasService(name)) { return activityScope.getService(name); } return super.getSystemService(name); } FlowDelegate.NonConfigurationInstance nonConfig; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initialScreen = new InitialScreen(); FTBApplication.getComponent().inject(this); FTBApplication.getComponent().inject(initialScreen); FacebookSdk.sdkInitialize(getApplicationContext()); Timber.d("Facebook app signature: %s", FacebookSdk.getApplicationSignature(getApplicationContext())); initActivityScope(); GsonParceler parceler = new GsonParceler(gson); FlowDelegate.NonConfigurationInstance nonConfig = (FlowDelegate.NonConfigurationInstance) getLastCustomNonConfigurationInstance(); //BundleService helps us save state using the standard activity bundle BundleServiceRunner.getBundleServiceRunner(this).onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); flowDelegate = FlowDelegate.onCreate(nonConfig, getIntent(), savedInstanceState, parceler, getHistory(savedInstanceState, parceler), this); setSupportActionBar(toolbar); FTBApplication.getEventBus().register(this); setToolbarStyle(); setupDrawerToggle(); handleIntent(getIntent()); dialogController.takeView(this); actionBarController.takeView(this); facebookController.takeView(this); permissionController.takeView(this); progressDialogController.takeView(this); toastController.takeView(this); photoController.takeView(this); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); facebookController.onActivityResult(requestCode, resultCode, data); photoController.onResult(requestCode, resultCode, data); } /** * Callback received when a permissions request has been completed. */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { permissionController.onResult(requestCode, permissions, grantResults); } private void setupDrawerToggle() { drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close); drawerLayout.setDrawerListener(drawerToggle); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); drawerToggle.syncState(); } private void initActivityScope() { activityScope = findChild(getApplicationContext(), getScopeName()); if (activityScope == null) { activityScope = buildChild(getApplicationContext()) // .withService(BundleServiceRunner.SERVICE_NAME, new BundleServiceRunner()) .withService(ActionBarService.NAME, actionBarController) .withService(ToastService.NAME, toastController) .withService(DialogService.NAME, dialogController) .withService(FacebookService.NAME, facebookController) .withService(LocationService.NAME, locationController) .withService(ProgressDialogService.NAME, progressDialogController) .withService(PermissionService.NAME, permissionController) .withService(PhotoService.NAME, photoController).build(getScopeName()); } } private History getHistory(Bundle savedInstanceState, GsonParceler parceler) { if (savedInstanceState != null && savedInstanceState.getParcelableArrayList("ENTRIES") != null) { return History.from(savedInstanceState, parceler); } return History.single(initialScreen.get()); } @Override protected void onResume() { super.onResume(); flowDelegate.onResume(); } @Override protected void onPause() { flowDelegate.onPause(); super.onPause(); } @Override public Object onRetainCustomNonConfigurationInstance() { return flowDelegate.onRetainNonConfigurationInstance(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); BundleServiceRunner.getBundleServiceRunner(this).onSaveInstanceState(outState); flowDelegate.onSaveInstanceState(outState); } @Override protected void onDestroy() { actionBarController.dropView(this); dialogController.dropView(this); facebookController.dropView(this); permissionController.dropView(this); progressDialogController.dropView(this); toastController.dropView(this); photoController.dropView(this); // activityScope may be null in case isWrongInstance() returned true in onCreate() if (isFinishing() && activityScope != null) { activityScope.destroy(); activityScope = null; } FTBApplication.getEventBus().unregister(this); super.onDestroy(); } private String getScopeName() { return getClass().getName(); } /** * Inform the view about back events. */ @Override public void onBackPressed() { if (drawerLayout.isDrawerOpen(GravityCompat.START)) { drawerLayout.closeDrawer(GravityCompat.START); } else { if (!container.onBackPressed()) { super.onBackPressed(); } } } /** * Required for the custom font library */ @Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)); } private void setToolbarStyle() { Typeface typeface = TypefaceUtils.load(getAssets(), "fonts/Lato-Heavy.ttf"); collapsingToolbar.setCollapsedTitleTypeface(typeface); collapsingToolbar.setExpandedTitleTypeface(typeface); if (isLollipopOrAbove()) { setStatusBarColor(bernieDarkBlue); } } void animateShading(boolean show) { shading.setVisibility(View.VISIBLE); float toAlpha = show ? 1 : 0; ObjectAnimator.ofFloat(shading, "alpha", shading.getAlpha(), toAlpha).setDuration(100).start(); } void animateBg(boolean show) { backgroundImage.setVisibility(View.VISIBLE); float toAlpha = show ? 1 : 0; ObjectAnimator.ofFloat(backgroundImage, "alpha", backgroundImage.getAlpha(), toAlpha).setDuration(100) .start(); } @TargetApi(21) void setStatusBarColor(int color) { Window window = getWindow(); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(color); } @Override public boolean onPrepareOptionsMenu(Menu menu) { this.menu = menu; handleOptionsMenu(menu); return true; } private void handleOptionsMenu(Menu menu) { if (actionBarMenuAction != null) { if (actionBarMenuAction.isSearch()) { setupSearchMenu(menu); } else { menu.add(actionBarMenuAction.label()).setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS) .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { actionBarMenuAction.action().call(); return true; } }); } // // Inflate the options menu from XML // MenuInflater inflater = getMenuInflater(); // inflater.inflate(R.menu.menu_cancel, menu); } } private void setupSearchMenu(Menu menu) { // Inflate the options menu from XML MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_main, menu); // Get the SearchView and set the searchable configuration SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); // Assumes current activity is the searchable activity searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default searchView.setBackgroundColor(bernieDarkBlue); } @Override public boolean onCreateOptionsMenu(Menu menu) { //handleOptionsMenu(menu); return true; } @Override protected void onNewIntent(Intent intent) { flowDelegate.onNewIntent(intent); handleIntent(intent); } private void handleIntent(Intent intent) { if (Actions.SEARCH_SELECTION_PAGE.equals(intent.getAction())) { String title = intent.getExtras().getString(SearchManager.EXTRA_DATA_KEY); showPage(title); MenuItemCompat.collapseActionView(this.menu.findItem(R.id.menu_search)); } else if (Actions.SEARCH_SELECTION_COLLECTION.equals(intent.getAction())) { String title = intent.getExtras().getString(SearchManager.EXTRA_DATA_KEY); showCollection(title); MenuItemCompat.collapseActionView(this.menu.findItem(R.id.menu_search)); } } private void showPage(String title) { Set<ApiItem> items = SearchMatrixCursor.allItems; for (ApiItem item : items) { if (item.getTitle().equals(title)) { final Page searchItem = (Page) item; Timber.v("Showing page from search: %s", searchItem.getTitle()); container.post(new Runnable() { @Override public void run() { Flow.get(MainActivity.this).set(new PageScreen(searchItem)); } }); break; } } } private void showCollection(String title) { Set<ApiItem> items = SearchMatrixCursor.allItems; for (ApiItem item : items) { if (item.getTitle().equals(title)) { final Collection searchItem = (Collection) item; Timber.v("Showing Collection from search: %s", item.getTitle()); container.post(new Runnable() { @Override public void run() { Flow.get(MainActivity.this).set(new CollectionScreen(searchItem)); } }); break; } } } @Override public void setMenu(ActionBarController.MenuAction action) { if (action != actionBarMenuAction) { actionBarMenuAction = action; invalidateOptionsMenu(); } } @Override public AppCompatActivity getActivity() { return this; } @Override public void setTitle(CharSequence title) { collapsingToolbar.setTitle(title); } /** * TODO: move this animation and toolbar code to it's own controller thing... */ @Override public void setMainImage(String url) { animateShading(false); Picasso.with(getApplicationContext()).load(url).transform(PaletteTransformation.instance()) .placeholder(backgroundImage.getDrawable()).into(backgroundImage, new Callback.EmptyCallback() { @Override public void onSuccess() { Bitmap bitmap = ((BitmapDrawable) backgroundImage.getDrawable()).getBitmap(); // Ew! Palette palette = PaletteTransformation.getPalette(bitmap); if (isLollipopOrAbove()) { setStatusBarColor(palette.getDarkVibrantColor(Color.BLACK)); } animateShading(true); animateBg(true); } }); if (url == null) { backgroundImage.setImageDrawable(null); } if (url == null && isLollipopOrAbove()) { setStatusBarColor(bernieDarkBlue); } } @Override public void hideToolbar() { AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams(); params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL); collapsingToolbar.setLayoutParams(params); collapsingToolbar.requestLayout(); } @Override public void showToolbar() { AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams(); params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED); collapsingToolbar.setLayoutParams(params); collapsingToolbar.requestLayout(); appBarLayout.setExpanded(false, true); } @Override public void openAppbar() { appBarLayout.setExpanded(true, true); } @Override public void closeAppbar() { appBarLayout.setExpanded(false, true); } @Override public void lockDrawer() { drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); drawerToggle.setDrawerIndicatorEnabled(false); drawerToggle.syncState(); } @Override public void unlockDrawer() { drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); drawerToggle.setDrawerIndicatorEnabled(true); drawerToggle.syncState(); } }