/* * Copyright (C) 2013 - 2016 Alexander "Evisceration" Martinz * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.namelessrom.devicecontrol; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import android.support.design.widget.NavigationView; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.CheckedTextView; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.Toast; import com.balysv.materialmenu.MaterialMenuDrawable; import org.namelessrom.devicecontrol.activities.BaseActivity; import org.namelessrom.devicecontrol.listeners.OnBackPressedListener; import org.namelessrom.devicecontrol.modules.about.AboutFragment; import org.namelessrom.devicecontrol.modules.appmanager.AppListFragment; import org.namelessrom.devicecontrol.modules.bootup.BootupFragment; import org.namelessrom.devicecontrol.modules.cpu.CpuSettingsFragment; import org.namelessrom.devicecontrol.modules.cpu.GovernorFragment; import org.namelessrom.devicecontrol.modules.device.DeviceFeatureFragment; import org.namelessrom.devicecontrol.modules.device.sub.FastChargeFragment; import org.namelessrom.devicecontrol.modules.device.sub.SoundControlFragment; import org.namelessrom.devicecontrol.modules.editor.BuildPropEditorFragment; import org.namelessrom.devicecontrol.modules.editor.SysctlEditorFragment; import org.namelessrom.devicecontrol.modules.editor.SysctlFragment; import org.namelessrom.devicecontrol.modules.flasher.FlasherFragment; import org.namelessrom.devicecontrol.modules.info.DeviceInfoFragment; import org.namelessrom.devicecontrol.modules.info.HardwareInfoFragment; import org.namelessrom.devicecontrol.modules.info.PerformanceInfoFragment; import org.namelessrom.devicecontrol.modules.info.SoftwareInfoFragment; import org.namelessrom.devicecontrol.modules.performance.FilesystemFragment; import org.namelessrom.devicecontrol.modules.performance.GpuSettingsFragment; import org.namelessrom.devicecontrol.modules.performance.ThermalFragment; import org.namelessrom.devicecontrol.modules.performance.sub.EntropyFragment; import org.namelessrom.devicecontrol.modules.performance.sub.IoSchedConfigFragment; import org.namelessrom.devicecontrol.modules.performance.sub.KsmFragment; import org.namelessrom.devicecontrol.modules.performance.sub.UksmFragment; import org.namelessrom.devicecontrol.modules.performance.sub.VoltageFragment; import org.namelessrom.devicecontrol.modules.preferences.PreferencesActivity; import org.namelessrom.devicecontrol.modules.tasker.TaskerFragment; import org.namelessrom.devicecontrol.modules.tools.ToolsMoreFragment; import org.namelessrom.devicecontrol.modules.tools.WirelessFileManagerFragment; import org.namelessrom.devicecontrol.theme.AppResources; import org.namelessrom.devicecontrol.thirdparty.PollFishImpl; import org.namelessrom.devicecontrol.thirdparty.Sense360Impl; import org.namelessrom.devicecontrol.utils.AppHelper; import org.namelessrom.devicecontrol.utils.Utils; import at.amartinz.execution.RootCheck; import at.amartinz.execution.ShellManager; import timber.log.Timber; public class MainActivity extends BaseActivity implements ActivityCallbacks, NavigationView.OnNavigationItemSelectedListener { private static long mBackPressed; private Toast mToast; private Runnable mDrawerRunnable; private DrawerLayout mDrawerLayout; public static boolean sDisableFragmentAnimations; private Fragment mCurrentFragment; private int mTitle = R.string.home; private int mFragmentTitle = R.string.home; private int mSubFragmentTitle = -1; private CheckRequirementsTask mCheckRequirementsTask; private static final int[] menuItemsNeedRoot = { // controls R.id.nav_item_controls_device, R.id.nav_item_controls_processor, R.id.nav_item_controls_graphics, R.id.nav_item_controls_file_system, R.id.nav_item_controls_thermal, // tools R.id.nav_item_tools_bootup_restoration, R.id.nav_item_tools_tasker, R.id.nav_item_tools_flasher, // TODO: enable in non root mode R.id.nav_item_tools_app_manager, R.id.nav_item_tools_more }; @Override protected void onResume() { super.onResume(); PollFishImpl.initPollFish(this); Sense360Impl.init(getApplicationContext()); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); PollFishImpl.initPollFish(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setupToolbar(); setupMaterialMenu(this); // lock the drawer so we can only open it AFTER we are done with our checks mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() { @Override public void onDrawerClosed(View drawerView) { if (mDrawerRunnable != null) { mDrawerLayout.post(mDrawerRunnable); } } }); mNavigationView = (NavigationView) findViewById(R.id.navigation_view_content); mNavigationView.setNavigationItemSelectedListener(this); loadFragmentPrivate(DeviceConstants.ID_HOME, false); getSupportFragmentManager().executePendingTransactions(); mCheckRequirementsTask = new CheckRequirementsTask(this); mCheckRequirementsTask.setPostExecuteHook(new Runnable() { @Override public void run() { setupDrawerItems(); } }); mCheckRequirementsTask.execute(); } @Override protected void setupToolbar() { final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); if (toolbar != null) { setSupportActionBar(toolbar); ViewCompat.setElevation(toolbar, 4.0f); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mSubFragmentTitle == -1) { toggleDrawer(); } else { onCustomBackPressed(true); } } }); } } private void setupDrawerItems() { // manually check home drawer entry mPreviousMenuItem = findMenuItem(R.id.nav_item_home); if (mPreviousMenuItem != null) { mPreviousMenuItem.setChecked(true); } final boolean isRooted; if (mCheckRequirementsTask != null) { isRooted = mCheckRequirementsTask.hasRootGranted; } else { isRooted = RootCheck.isRooted(); } if (isRooted || BuildConfig.DEBUG) { if (!Utils.fileExists(getString(R.string.directory_msm_thermal)) && !Utils.fileExists(getString(R.string.file_intelli_thermal_base))) { final MenuItem thermalItem = findMenuItem(R.id.nav_item_controls_thermal); if (thermalItem != null) { thermalItem.setEnabled(false); } } } else { for (final int menuItemId : menuItemsNeedRoot) { enableMenuItem(menuItemId, false); } } final View headerView = mNavigationView.getHeaderView(0); final ImageView headerImage = (ImageView) headerView.findViewById(R.id.drawer_header_image); headerImage.setImageDrawable(AppResources.get().getDrawerHeader(this)); final ImageButton headerSettings = (ImageButton) headerView.findViewById(R.id.drawer_header_settings); headerSettings.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final Intent intent = new Intent(MainActivity.this, PreferencesActivity.class); startActivityForResult(intent, PreferencesActivity.REQUEST_PREFERENCES); } }); final FrameLayout footerContainer = (FrameLayout) findViewById(R.id.navigation_view_footer_container); ViewCompat.setElevation(footerContainer, 4f); final CheckedTextView footerAppVersion = (CheckedTextView) findViewById(R.id.nav_item_footer_version); footerAppVersion.setDuplicateParentStateEnabled(true); if (!TextUtils.isEmpty(BuildConfig.VERSION_NAME)) { footerAppVersion.setText(BuildConfig.VERSION_NAME); // extract the git short log from the version name final String versionName = BuildConfig.VERSION_NAME.replace("-dev", "").trim().toLowerCase(); if (versionName.contains("-git-")) { final String[] splitted = versionName.split("-git-"); if (splitted.length == 2) { final String commitUrl = String.format(Constants.URL_GITHUB_DC_COMMITS_BASE, splitted[1]); // preheat a bit ((App) getApplicationContext()).getCustomTabsHelper().mayLaunchUrl(commitUrl); footerContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ((App) getApplicationContext()).getCustomTabsHelper().launchUrl(MainActivity.this, commitUrl); } }); } } } } private void enableMenuItem(int menuItemId, boolean enabled) { final MenuItem item = findMenuItem(menuItemId); if (item != null) { item.setEnabled(enabled); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (PreferencesActivity.REQUEST_PREFERENCES == requestCode) { if (resultCode == PreferencesActivity.RESULT_NEEDS_RESTART) { // restart the activity and cleanup AppResources to update effects and theme AppResources.get().cleanup(); Utils.restartActivity(MainActivity.this); } return; } super.onActivityResult(requestCode, resultCode, data); } @Override public boolean onCreateOptionsMenu(Menu menu) { restoreActionBar(); return super.onCreateOptionsMenu(menu); } private void restoreActionBar() { final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setTitle(mTitle); } } @Override public void toggleDrawer() { if (mDrawerLayout == null) { return; } if (mDrawerLayout.getDrawerLockMode(GravityCompat.START) != DrawerLayout.LOCK_MODE_UNLOCKED) { return; } if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { mDrawerLayout.closeDrawer(GravityCompat.START); } else { mDrawerLayout.openDrawer(GravityCompat.START); } } @Override public boolean closeDrawerIfShowing() { if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) { mDrawerLayout.closeDrawer(GravityCompat.START); return true; } return false; } @Override public void setDrawerLockState(int lockMode) { if (mDrawerLayout != null) { mDrawerLayout.setDrawerLockMode(lockMode, GravityCompat.START); } } @Override public boolean onNavigationItemSelected(final MenuItem item) { final int id = item.getItemId(); shouldLoadFragment(id); return true; } private void onCustomBackPressed(final boolean animatePressed) { // toggle menu if it is showing and return if (closeDrawerIfShowing()) { return; } // if we have a OnBackPressedListener at the fragment, go in if (mCurrentFragment instanceof OnBackPressedListener) { final OnBackPressedListener listener = ((OnBackPressedListener) mCurrentFragment); // if our listener handles onBackPressed(), return if (listener.onBackPressed()) { Timber.v("onBackPressed() handled by current fragment"); return; } // else we will have to go back or exit. // in this case, lets get the correct icons final MaterialMenuDrawable.IconState iconState; if (listener.showBurger()) { iconState = MaterialMenuDrawable.IconState.BURGER; } else { iconState = MaterialMenuDrawable.IconState.ARROW; } // we can separate actionbar back actions and back key presses if (animatePressed) { mMaterialMenu.animatePressedState(iconState); } else { mMaterialMenu.animateState(iconState); } // after animating, go further } // we we have at least one fragment in the BackStack, pop it and return if (getSupportFragmentManager().getBackStackEntryCount() > 0) { getSupportFragmentManager().popBackStack(); // restore title / actionbar if (mSubFragmentTitle != -1) { mTitle = mSubFragmentTitle; } else { mTitle = mFragmentTitle; } restoreActionBar(); return; } // if nothing matched by now, we do not have any fragments in the BackStack, nor we have // the menu open. in that case lets detect a double back press and exit the activity if (mBackPressed + 2000 > System.currentTimeMillis()) { if (mToast != null) { mToast.cancel(); } finish(); } else { mToast = Toast.makeText(getBaseContext(), getString(R.string.action_press_again), Toast.LENGTH_SHORT); mToast.show(); } mBackPressed = System.currentTimeMillis(); } @Override public void onBackPressed() { onCustomBackPressed(false); } @Override protected void onDestroy() { Timber.d("closing shells"); ShellManager.get().cleanupShells(); if (mCheckRequirementsTask != null) { mCheckRequirementsTask.destroy(); mCheckRequirementsTask = null; } AppResources.get().cleanup(); super.onDestroy(); } //============================================================================================== // Methods //============================================================================================== public void setFragment(final Fragment fragment) { if (fragment == null) { return; } mCurrentFragment = fragment; } @Override public void shouldLoadFragment(final int id) { shouldLoadFragment(id, false); } @Override public void shouldLoadFragment(final int id, final boolean onResume) { if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { // close drawer mDrawerLayout.closeDrawer(GravityCompat.START); mDrawerRunnable = new Runnable() { @Override public void run() { loadFragmentPrivate(id, onResume); mDrawerRunnable = null; } }; return; } loadFragmentPrivate(id, onResume); } private void loadFragmentPrivate(final int i, final boolean onResume) { switch (i) { default: // slip through... //-------------------------------------------------------------------------------------- case DeviceConstants.ID_HOME: if (!onResume) { mCurrentFragment = new AboutFragment(); } mTitle = mFragmentTitle = R.string.app_name; mSubFragmentTitle = -1; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_INFO_DEVICE: if (!onResume) { mCurrentFragment = new DeviceInfoFragment(); } mTitle = mFragmentTitle = R.string.device; mSubFragmentTitle = -1; break; case DeviceConstants.ID_INFO_HARDWARE: if (!onResume) { mCurrentFragment = new HardwareInfoFragment(); } mTitle = mFragmentTitle = R.string.hardware; mSubFragmentTitle = -1; break; case DeviceConstants.ID_INFO_SOFTWARE: if (!onResume) { mCurrentFragment = new SoftwareInfoFragment(); } mTitle = mFragmentTitle = R.string.software; mSubFragmentTitle = -1; break; case DeviceConstants.ID_INFO_PERFORMANCE: if (!onResume) { mCurrentFragment = new PerformanceInfoFragment(); } mTitle = mFragmentTitle = R.string.performance; mSubFragmentTitle = -1; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_CTRL_DEVICE: if (!onResume) { mCurrentFragment = new DeviceFeatureFragment(); } mTitle = mFragmentTitle = R.string.device; mSubFragmentTitle = -1; break; case DeviceConstants.ID_FAST_CHARGE: if (!onResume) { mCurrentFragment = new FastChargeFragment(); } mTitle = mSubFragmentTitle = R.string.fast_charge; break; case DeviceConstants.ID_SOUND_CONTROL: if (!onResume) { mCurrentFragment = new SoundControlFragment(); } mTitle = mSubFragmentTitle = R.string.sound_control; break; case DeviceConstants.ID_KSM: if (!onResume) { mCurrentFragment = new KsmFragment(); } mTitle = mSubFragmentTitle = R.string.ksm; break; case DeviceConstants.ID_UKSM: if (!onResume) { mCurrentFragment = new UksmFragment(); } mTitle = mSubFragmentTitle = R.string.uksm; break; case DeviceConstants.ID_VOLTAGE: if (!onResume) { mCurrentFragment = new VoltageFragment(); } mTitle = mSubFragmentTitle = R.string.voltage_control; break; case DeviceConstants.ID_ENTROPY: if (!onResume) { mCurrentFragment = new EntropyFragment(); } mTitle = mSubFragmentTitle = R.string.entropy; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_CTRL_PROCESSOR: if (!onResume) { mCurrentFragment = new CpuSettingsFragment(); } mTitle = mFragmentTitle = R.string.processor; mSubFragmentTitle = -1; break; case DeviceConstants.ID_GOVERNOR_TUNABLE: if (!onResume) { mCurrentFragment = new GovernorFragment(); } mTitle = mSubFragmentTitle = R.string.governor_tuning; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_CTRL_GRAPHICS: if (!onResume) { mCurrentFragment = new GpuSettingsFragment(); } mTitle = mFragmentTitle = R.string.graphics; mSubFragmentTitle = -1; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_CTRL_FILE_SYSTEM: if (!onResume) { mCurrentFragment = new FilesystemFragment(); } mTitle = mFragmentTitle = R.string.file_system; mSubFragmentTitle = -1; break; case DeviceConstants.ID_IOSCHED_TUNING: if (!onResume) { mCurrentFragment = new IoSchedConfigFragment(); } mTitle = mSubFragmentTitle = R.string.io; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_CTRL_THERMAL: if (!onResume) { mCurrentFragment = new ThermalFragment(); } mTitle = mFragmentTitle = R.string.thermal; mSubFragmentTitle = -1; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_TOOLS_BOOTUP_RESTORATION: if (!onResume) { mCurrentFragment = new BootupFragment(); } mTitle = mFragmentTitle = R.string.bootup_restoration; mSubFragmentTitle = -1; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_TOOLS_APP_MANAGER: if (!onResume) { mCurrentFragment = new AppListFragment(); } mTitle = mFragmentTitle = R.string.app_manager; mSubFragmentTitle = -1; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_TOOLS_TASKER: if (!onResume) { mCurrentFragment = new TaskerFragment(); } mTitle = mFragmentTitle = R.string.tasker; mSubFragmentTitle = -1; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_TOOLS_FLASHER: if (!onResume) { mCurrentFragment = new FlasherFragment(); } mTitle = mFragmentTitle = R.string.flasher; mSubFragmentTitle = -1; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_TOOLS_MORE: if (!onResume) { mCurrentFragment = new ToolsMoreFragment(); } mTitle = mFragmentTitle = R.string.more; mSubFragmentTitle = -1; break; case DeviceConstants.ID_TOOLS_VM: if (!onResume) { mCurrentFragment = new SysctlFragment(); } mTitle = mSubFragmentTitle = R.string.sysctl_vm; break; case DeviceConstants.ID_TOOLS_EDITORS_VM: if (!onResume) { mCurrentFragment = new SysctlEditorFragment(); } mTitle = mSubFragmentTitle = R.string.sysctl_vm; break; case DeviceConstants.ID_TOOLS_EDITORS_BUILD_PROP: if (!onResume) { mCurrentFragment = new BuildPropEditorFragment(); } mTitle = mSubFragmentTitle = R.string.buildprop; break; case DeviceConstants.ID_TOOLS_WIRELESS_FM: if (!onResume) { mCurrentFragment = new WirelessFileManagerFragment(); } mTitle = mSubFragmentTitle = R.string.wireless_file_manager; break; //-------------------------------------------------------------------------------------- case DeviceConstants.ID_APP_INFO_LICENSE: ((App) getApplicationContext()).getCustomTabsHelper().launchUrl(this, DeviceConstants.URL_DC_LICENSE); break; case DeviceConstants.ID_APP_INFO_PRIVACY: ((App) getApplicationContext()).getCustomTabsHelper().launchUrl(this, DeviceConstants.URL_DC_PRIVACY); break; } restoreActionBar(); if (onResume) { return; } final boolean isSubFragment = mSubFragmentTitle != -1; final FragmentManager fragmentManager = getSupportFragmentManager(); if (!isSubFragment && fragmentManager.getBackStackEntryCount() > 0) { // set a lock to prevent calling setFragment as onResume gets called AppHelper.preventOnResume = true; MainActivity.sDisableFragmentAnimations = true; try { fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); } catch (IllegalArgumentException ignored) { } MainActivity.sDisableFragmentAnimations = false; // release the lock AppHelper.preventOnResume = false; } final FragmentTransaction ft = fragmentManager.beginTransaction(); if (isSubFragment) { ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right, R.anim.slide_in_left, R.anim.slide_out_left); ft.addToBackStack(null); } ft.replace(R.id.container, mCurrentFragment); ft.commit(); final MaterialMenuDrawable.IconState iconState; if (isSubFragment) { iconState = MaterialMenuDrawable.IconState.ARROW; } else { iconState = MaterialMenuDrawable.IconState.BURGER; } mMaterialMenu.animateState(iconState); } }