Java tutorial
/* * Copyright (C) 2015 The Pure Nexus Project * * 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.android.purenexussettings; import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.TypedArray; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.preference.Preference; import android.provider.Settings; import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Layout; import android.text.SpannableString; import android.text.style.AlignmentSpan; import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; import android.view.Surface; import android.view.View; import java.util.Arrays; import java.util.Stack; public class TinkerActivity extends AppCompatActivity { private DrawerLayout mDrawerLayout; private NavigationView mNavView; private ActionBarDrawerToggle mDrawerToggle; public static String mPackageName; private FragmentManager fragmentManager; // this might be handy later - something that can tell other things if root public static boolean isRoot; // this allows first # entries in stringarray to be skipped from navdrawer public static int FRAG_ARRAY_START; // stuff for widget calls to open fragments public static final String EXTRA_START_FRAGMENT = "com.android.purenexussettings.tinkerings.EXTRA_START_FRAGMENT"; public static final int REQUEST_CREATE_SHORTCUT = 3; public static final String PROJFI_PACKAGE_NAME = "com.google.android.apps.tycho"; public static final String KEY_LOCK_CLOCK_PACKAGE_NAME = "com.cyanogenmod.lockclock"; public static final String KEY_LOCK_CLOCK_CLASS_NAME = "com.cyanogenmod.lockclock.preference.Preferences"; // example - used to retain slidetab position public static int LAST_SLIDE_BAR_TAB; // nav drawer title private CharSequence mDrawerTitle; // used to store app title/position private CharSequence mTitle; private int mItemPosition; // for backstack tracking private Stack<String> fragmentStack; // various bools for this or that private boolean mBackPress; private boolean mIgnoreBack; private boolean mFromClick; private boolean mIgnore; private boolean mMenu; private boolean fullyClosed; private boolean openingHalf; // slide menu items private String[] navMenuTitles; private String[] navMenuFrags; // info for buildprop editor public static String mEditName; public static String mEditKey; // info for app picker public static String mPrefKey; public static int mTitleArray; public static int mIconArray; public static int mKeyArray; // For handling quick back/About presses Handler myHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tinker); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // set up some defaults boolean cLockInstalled; FRAG_ARRAY_START = getResources().getIntArray(R.array.nav_drawer_cat_nums)[0]; mTitle = mDrawerTitle = getTitle(); mPackageName = getPackageName(); LAST_SLIDE_BAR_TAB = 0; mBackPress = false; mIgnoreBack = false; mFromClick = false; mMenu = false; fullyClosed = true; openingHalf = true; // for backstack tracking fragmentStack = new Stack<>(); // check if cLock installed try { PackageInfo pi = getPackageManager().getPackageInfo(KEY_LOCK_CLOCK_PACKAGE_NAME, 0); cLockInstalled = pi.applicationInfo.enabled; } catch (PackageManager.NameNotFoundException e) { cLockInstalled = false; } // load slide menu items - titles and frag names navMenuTitles = getResources().getStringArray(R.array.nav_drawer_items); navMenuFrags = getResources().getStringArray(R.array.nav_drawer_fragments); // nav drawer icons from resources TypedArray navMenuIcons = getResources().obtainTypedArray(R.array.nav_drawer_icons); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mNavView = (NavigationView) findViewById(R.id.slidermenu); // create navigationview items Menu menu = mNavView.getMenu(); // pulled in crap menu in xml, need to clear it first menu.clear(); // pull in category names and numbers in each String[] navMenuCats = getResources().getStringArray(R.array.nav_drawer_cats); int[] navMenuCatCounts = getResources().getIntArray(R.array.nav_drawer_cat_nums); // set up some counters int j = 0; int total = 0; SubMenu submenu = null; // go through the total possible menu list for (int i = 0; i < navMenuTitles.length; i++) { // when the count equals a threshold value, increment/sum and add submenu if (i == (total + navMenuCatCounts[j])) { total += navMenuCatCounts[j]; // format submenu headings SpannableString strcat = new SpannableString(navMenuCats[j]); strcat.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.alphawhite)), 0, strcat.length(), 0); strcat.setSpan(new RelativeSizeSpan(0.85f), 0, strcat.length(), 0); strcat.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0, strcat.length(), 0); // is the 10 * (j + 1) bit needed...? Maybe not... meh submenu = menu.addSubMenu((j + 1), 10 * (j + 1), 10 * (j + 1), strcat); j++; } // assuming all are skipped before first submenu, only add menu items if total <> 0 if (total > 0) { // format menu item title SpannableString stritem = new SpannableString(navMenuTitles[i]); stritem.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.alphawhite)), 0, stritem.length(), 0); // group id is j, i is item id and order..., then title - includes logic for conditional entries if (cLockInstalled || !(navMenuTitles[i].equals("cLock"))) { // an attempt to add icon if included... if (navMenuIcons.getResourceId(i, -1) != -1) { submenu.add(j, i, i, stritem).setIcon(navMenuIcons.getResourceId(i, -1)); } else { submenu.add(j, i, i, stritem); } } } } // remove icon tint from NavView mNavView.setItemIconTintList(null); mNavView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(MenuItem item) { // check for external app launching navdrawer items if (navMenuTitles[item.getItemId()].equals("cLock")) { mIgnore = true; mDrawerLayout.closeDrawer(mNavView); launchcLock(); } // if nothing was caught in the above, do the usual prep to show frag stuff if (!mIgnore) { mItemPosition = item.getItemId(); mFromClick = true; setTitle(navMenuTitles[mItemPosition]); removeCurrent(); mDrawerLayout.closeDrawer(mNavView); } return true; } }); // Recycle the typed array navMenuIcons.recycle(); // enabling action bar app icon and behaving it as toggle button getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, toolbar, //nav menu toggle icon R.string.app_name, // nav drawer open - description for accessibility R.string.app_name // nav drawer close - description for accessibility ) { @Override public void onDrawerClosed(View view) { mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mNavView); getSupportActionBar().setTitle(mTitle); // calling onPrepareOptionsMenu() to show action bar icons openingHalf = true; invalidateOptionsMenu(); // now that the drawer animation is done - load fragment if (mIgnore || !mFromClick) { mIgnore = false; } else { displayView(mItemPosition); } } @Override public void onDrawerOpened(View drawerView) { getSupportActionBar().setTitle(mDrawerTitle); // calling onPrepareOptionsMenu() to hide action bar icons openingHalf = false; invalidateOptionsMenu(); } @Override public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, slideOffset); fullyClosed = (slideOffset == 0.0f); if (slideOffset < 0.5f && !openingHalf) { openingHalf = true; invalidateOptionsMenu(); // calls onPrepareOptionsMenu() } else if (slideOffset > 0.5f && openingHalf) { openingHalf = false; invalidateOptionsMenu(); // calls onPrepareOptionsMenu() } } }; mDrawerLayout.setDrawerListener(mDrawerToggle); fragmentManager = getFragmentManager(); if (savedInstanceState == null) { // on first time display view for first nav item displayView(mItemPosition = getIntent().getIntExtra(EXTRA_START_FRAGMENT, 0)); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } private boolean checkPosition(int position) { // identify if position should skip stack clearing // these are the non-About frags not shown in navdrawer return (position < FRAG_ARRAY_START) && (position != 0); } private int checkSubFrag(int origposition, int newposition) { // see if current frag is further subfrag to force origfrag on backpress switch (origposition) { case 2: //editprop frag return 1; //buildprop frag default: return newposition; } } /* Displaying fragment view for selected nav drawer list item */ private void displayView(int position) { // before anything else - check to see if position matches intent-launching "frags" - for example // if ( navMenuTitles[position].equals("TARGETNAME") ) { position = 0; do something} boolean mKeepStack = checkPosition(position); // update the main content by replacing fragments Fragment frags = null; String fragname = navMenuFrags[position]; try { frags = (Fragment) Class.forName(mPackageName + "." + fragname).newInstance(); } catch (Exception e) { frags = null; } if (frags != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true); try { FragmentTransaction fragtrans = fragmentManager.beginTransaction(); if (mFromClick || mMenu || mBackPress) { fragtrans.setCustomAnimations(R.anim.fadein, R.anim.fadeout, R.anim.fadein, R.anim.fadeout); } fragtrans.add(R.id.frame_container, frags); // The backstack should be cleared if not coming from a fragment flagged as stack keeping or from a backpress // After clearing the only entry should be About/main if (!mKeepStack && !mBackPress) { fragmentStack.clear(); fragmentStack.push(navMenuFrags[0]); } // add fragment name to custom stack for backstack tracking // only do it if not a backpress, flagged as stack keeping, or dup of last entry if (!mBackPress && !mKeepStack && !(fragmentStack.size() >= 1 && fragmentStack.peek().equals(navMenuFrags[position]))) { fragmentStack.push(navMenuFrags[position]); } fragtrans.commit(); } catch (Exception e) { } // update selected item and title, then close the drawer if (mFromClick || mBackPress) { mFromClick = false; mBackPress = false; } else { setTitle(navMenuTitles[position]); if (mMenu) { mMenu = false; mItemPosition = position; } else { mDrawerLayout.closeDrawer(mNavView); } } invalidateOptionsMenu(); // calls onPrepareOptionsMenu() } else { // error in creating fragment Log.e("TinkerActivity", "Error in creating fragment"); } } private void removeCurrent() { // update the main content by replacing fragments, first by removing the old FragmentTransaction fragtrans = fragmentManager.beginTransaction(); fragtrans.setCustomAnimations(R.anim.fadein, R.anim.fadeout, R.anim.fadein, R.anim.fadeout); fragtrans.remove(fragmentManager.findFragmentById(R.id.frame_container)); fragtrans.commit(); } public void displayAppPicker(Preference object, int titles, int icons, int keys) { // stuff for apppicker fragment mPrefKey = object.getKey(); mTitleArray = titles; mIconArray = icons; mKeyArray = keys; myHandler.removeCallbacksAndMessages(null); mMenu = true; removeCurrent(); // below replicates the visual delay seen when launching frags from navdrawer myHandler.postDelayed(new Runnable() { @Override public void run() { displayView(3); } }, 400); } public void displayEditProp(String name, String key) { // put the name and key strings in here for editprop access mEditName = name; mEditKey = key; myHandler.removeCallbacksAndMessages(null); mMenu = true; removeCurrent(); // below replicates the visual delay seen when launching frags from navdrawer myHandler.postDelayed(new Runnable() { @Override public void run() { displayView(2); } }, 400); } public void displaySubFrag(String title) { int poscheck = -1; // Look for title in array of titles to get position for (int i = 0; i < navMenuTitles.length; i++) { if (navMenuTitles[i].equals(title)) { poscheck = i; break; } } // needs to be final for myHandler final int position = poscheck; // only do this if something was found - i.e. position != -1 - otherwise do nothing if (position >= 0) { myHandler.removeCallbacksAndMessages(null); mMenu = true; removeCurrent(); // below replicates the visual delay seen when launching frags from navdrawer myHandler.postDelayed(new Runnable() { @Override public void run() { displayView(position); } }, 400); } else { Snackbar.make(findViewById(R.id.frame_container), getString(R.string.general_error), Snackbar.LENGTH_SHORT).show(); } } public void launchcLock() { Intent link = new Intent(Intent.ACTION_MAIN); ComponentName cn = new ComponentName(KEY_LOCK_CLOCK_PACKAGE_NAME, KEY_LOCK_CLOCK_CLASS_NAME); link.setComponent(cn); startActivity(link); } @Override public void onBackPressed() { boolean mKeepStack = checkPosition(mItemPosition); if (!fullyClosed || mDrawerLayout.isDrawerOpen(mNavView)) { // backpress closes drawer if open mDrawerLayout.closeDrawer(mNavView); } else if (fragmentStack.size() > 1 || mKeepStack) { if (!mIgnoreBack) { mIgnoreBack = true; // cancels any pending postdelays just in case myHandler.removeCallbacksAndMessages(null); // removes latest (current) entry in custom stack if it wasn't one flagged for stack keeping and not added to stack if (!mKeepStack) { fragmentStack.pop(); } // uses fragment name to find displayview-relevant position final int position = Arrays.asList(navMenuFrags).indexOf(fragmentStack.lastElement()); // set position based on above or origfrag if nested subfrag mItemPosition = checkSubFrag(mItemPosition, position); // a setup similar to onclickitem setTitle(navMenuTitles[mItemPosition]); removeCurrent(); // below replicates the visual delay seen when launching frags from navdrawer myHandler.postDelayed(new Runnable() { @Override public void run() { mBackPress = true; displayView(mItemPosition); mIgnoreBack = false; } }, 400); } } else { super.onBackPressed(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_tinker, menu); menu.findItem(R.id.action_launchhide).setChecked(!isLauncherIconEnabled()); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // toggle nav drawer on selecting action bar app icon/title if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action bar actions click switch (item.getItemId()) { case R.id.action_about: if (mItemPosition != 0) { myHandler.removeCallbacksAndMessages(null); mMenu = true; removeCurrent(); // below replicates the visual delay seen when launching frags from navdrawer myHandler.postDelayed(new Runnable() { @Override public void run() { displayView(0); } }, 400); } return true; case R.id.action_launchhide: boolean checked = item.isChecked(); item.setChecked(!checked); setLauncherIconEnabled(checked); return true; default: return super.onOptionsItemSelected(item); } } /* Called when invalidateOptionsMenu() is triggered */ @Override public boolean onPrepareOptionsMenu(Menu menu) { // if nav drawer is opened/opening, hide the action items // add in bits to enable/disable menu items that are fragment specific if (openingHalf) { menu.setGroupVisible(R.id.action_items, true); boolean isbuildprop = (mItemPosition == 1); boolean iseditprop = (mItemPosition == 2); boolean isapppicker = (mItemPosition == 3); boolean isfiswitch = (mItemPosition == 4); menu.findItem(R.id.action_backup).setVisible(isbuildprop); menu.findItem(R.id.action_restore).setVisible(isbuildprop); menu.findItem(R.id.action_search).setVisible(isbuildprop); menu.findItem(R.id.action_discard).setVisible(iseditprop); menu.findItem(R.id.action_delete).setVisible(iseditprop); menu.findItem(R.id.action_fabhide).setVisible(isfiswitch); menu.findItem(R.id.action_launchhide) .setVisible(!(isbuildprop || iseditprop || isapppicker || isfiswitch)); menu.findItem(R.id.action_about).setVisible(!(isbuildprop || iseditprop || isapppicker || isfiswitch)); } else { menu.setGroupVisible(R.id.action_items, false); } return super.onPrepareOptionsMenu(menu); } @Override public void setTitle(CharSequence title) { mTitle = title; getSupportActionBar().setTitle(mTitle); } /* When using the mDrawerToggle, you must call it during onPostCreate() and onConfigurationChanged()... */ @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Pass any configuration change to the drawer toggle mDrawerToggle.onConfigurationChanged(newConfig); } /* Methods related to showing/hiding app from app drawer */ public void setLauncherIconEnabled(boolean enabled) { int newState; PackageManager packman = getPackageManager(); if (enabled) { newState = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; } else { newState = PackageManager.COMPONENT_ENABLED_STATE_DISABLED; } packman.setComponentEnabledSetting(new ComponentName(this, LauncherActivity.class), newState, PackageManager.DONT_KILL_APP); } public boolean isLauncherIconEnabled() { PackageManager packman = getPackageManager(); return (packman.getComponentEnabledSetting(new ComponentName(this, LauncherActivity.class)) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED); } @Override protected void onActivityResult(int paramRequest, int paramResult, Intent paramData) { super.onActivityResult(paramRequest, paramResult, paramData); if (paramResult != -1 || paramData == null) { return; } switch (paramRequest) { case REQUEST_CREATE_SHORTCUT: Intent localIntent = paramData.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); localIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, paramData.getStringExtra(Intent.EXTRA_SHORTCUT_NAME)); String keystring = localIntent.toUri(0).replaceAll("com.android.contacts.action.QUICK_CONTACT", Intent.ACTION_VIEW); Settings.Global.putString(getContentResolver(), TinkerActivity.mPrefKey, keystring); onBackPressed(); return; default: } } public static void lockCurrentOrientation(Activity activity) { int currentRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int orientation = activity.getResources().getConfiguration().orientation; int frozenRotation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; switch (currentRotation) { case Surface.ROTATION_0: frozenRotation = orientation == Configuration.ORIENTATION_LANDSCAPE ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; break; case Surface.ROTATION_90: frozenRotation = orientation == Configuration.ORIENTATION_PORTRAIT ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; break; case Surface.ROTATION_180: frozenRotation = orientation == Configuration.ORIENTATION_LANDSCAPE ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; break; case Surface.ROTATION_270: frozenRotation = orientation == Configuration.ORIENTATION_PORTRAIT ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; break; } activity.setRequestedOrientation(frozenRotation); } }