Java tutorial
/* * Copyright (C) 2016 Jorge Ruesga * * 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.ruesga.rview; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.Keep; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener; import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; import android.view.View; import com.mikepenz.aboutlibraries.Libs; import com.mikepenz.aboutlibraries.LibsBuilder; import com.mikepenz.aboutlibraries.util.Colors; import com.ruesga.rview.databinding.ContentBinding; import com.ruesga.rview.databinding.NavigationHeaderBinding; import com.ruesga.rview.drawer.DrawerNavigationMenu; import com.ruesga.rview.drawer.DrawerNavigationSubMenu; import com.ruesga.rview.drawer.DrawerNavigationView; import com.ruesga.rview.fragments.ChangeListByFilterFragment; import com.ruesga.rview.fragments.DashboardFragment; import com.ruesga.rview.fragments.PageableFragment; import com.ruesga.rview.fragments.SetAccountStatusDialogFragment; import com.ruesga.rview.fragments.StatsFragment; import com.ruesga.rview.gerrit.GerritApi; import com.ruesga.rview.gerrit.filter.ChangeQuery; import com.ruesga.rview.gerrit.model.ChangeInfo; import com.ruesga.rview.gerrit.model.Features; import com.ruesga.rview.misc.ActivityHelper; import com.ruesga.rview.misc.CacheHelper; import com.ruesga.rview.misc.EmojiHelper; import com.ruesga.rview.misc.Formatter; import com.ruesga.rview.misc.ModelHelper; import com.ruesga.rview.misc.NotificationsHelper; import com.ruesga.rview.misc.PicassoHelper; import com.ruesga.rview.misc.SerializationManager; import com.ruesga.rview.model.Account; import com.ruesga.rview.model.CustomFilter; import com.ruesga.rview.preferences.Constants; import com.ruesga.rview.preferences.Preferences; import com.ruesga.rview.providers.NotificationEntity; import com.ruesga.rview.services.AccountStatusFetcherService; import com.ruesga.rview.wizards.AuthorizationAccountSetupActivity; import com.ruesga.rview.wizards.SetupAccountActivity; import java.util.List; import static com.ruesga.rview.preferences.Constants.MY_FILTERS_GROUP_BASE_ID; import static com.ruesga.rview.preferences.Constants.OTHER_ACCOUNTS_GROUP_BASE_ID; public class MainActivity extends ChangeListBaseActivity { private static final String TAG = "MainActivity"; private static final int REQUEST_WIZARD = 1; private static final int REQUEST_ACCOUNT_SETTINGS = 2; private static final int MESSAGE_NAVIGATE_TO = 0; private static final int MESSAGE_DELETE_ACCOUNT = 1; @Keep public static class Model implements Parcelable { public String accountName; public String accountRepository; public boolean isAccountExpanded; int currentNavigationItemId = INVALID_ITEM; String filterName; String filterQuery; int selectedChangeId = INVALID_ITEM; public Model() { } protected Model(Parcel in) { accountName = in.readString(); accountRepository = in.readString(); isAccountExpanded = in.readByte() != 0; currentNavigationItemId = in.readInt(); if (in.readByte() == 1) { filterName = in.readString(); } if (in.readByte() == 1) { filterQuery = in.readString(); } selectedChangeId = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(accountName); dest.writeString(accountRepository); dest.writeByte((byte) (isAccountExpanded ? 1 : 0)); dest.writeInt(currentNavigationItemId); dest.writeByte((byte) (filterName != null ? 1 : 0)); dest.writeString(filterName); dest.writeByte((byte) (filterQuery != null ? 1 : 0)); dest.writeString(filterQuery); dest.writeInt(selectedChangeId); } @Override public int describeContents() { return 0; } public static final Creator<Model> CREATOR = new Creator<Model>() { @Override public Model createFromParcel(Parcel in) { return new Model(in); } @Override public Model[] newArray(int size) { return new Model[size]; } }; } @Keep @SuppressWarnings({ "UnusedParameters", "unused" }) public static class EventHandlers { private final MainActivity mActivity; public EventHandlers(MainActivity activity) { mActivity = activity; } public void onSwitcherPressed(View v) { mActivity.performShowAccount(!mActivity.mModel.isAccountExpanded); } } private final Handler.Callback mMessenger = message -> { // Has the activity saved its state? Ignore any operation from here then. if (hasStateSaved()) { return true; } if (message.what == MESSAGE_NAVIGATE_TO) { performNavigateTo(); return true; } if (message.what == MESSAGE_DELETE_ACCOUNT) { performDeleteAccount((String) message.obj); } return false; }; private final BroadcastReceiver mAccountStatusChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (mAccount != null && intent != null) { String account = intent.getStringExtra(AccountStatusFetcherService.EXTRA_ACCOUNT); if (mAccount.getAccountHash().equals(account)) { mAccount = Preferences.getAccount(context); updateAccountStatus(); performUpdateNavigationDrawer(mModel.isAccountExpanded); } } } }; private ContentBinding mBinding; private NavigationHeaderBinding mHeaderDrawerBinding; private Model mModel; private final EventHandlers mEventHandlers = new EventHandlers(this); private Account mAccount; private List<Account> mAccounts; private List<CustomFilter> mCustomFilters; private Handler mUiHandler; private boolean mIsTwoPane; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { mUiHandler = new Handler(mMessenger); mIsTwoPane = getResources().getBoolean(R.bool.config_is_two_pane); onRestoreInstanceState(savedInstanceState); super.onCreate(savedInstanceState); if (getIntent() != null) { // Set the account if requested and dismiss notifications for this account String accountId = getIntent().getStringExtra(Constants.EXTRA_ACCOUNT_HASH); if (!TextUtils.isEmpty(accountId)) { Preferences.setAccount(this, ModelHelper.getAccountFromHash(this, accountId)); NotificationEntity.markAccountNotificationsAsRead(this, accountId); NotificationEntity.dismissAccountNotifications(this, accountId); } } mBinding = DataBindingUtil.setContentView(this, R.layout.content); if (mModel == null) { mModel = new Model(); } setupActivity(); loadAccounts(); launchAddAccountIfNeeded(); setupNavigationDrawer(); updateAccountCustomFilters(); IntentFilter filter = new IntentFilter(); filter.addAction(AccountStatusFetcherService.ACCOUNT_STATUS_FETCHER_ACTION); LocalBroadcastManager.getInstance(this).registerReceiver(mAccountStatusChangedReceiver, filter); if (mAccount != null) { fetchAccountStatus(mAccount); } mHeaderDrawerBinding.setModel(mModel); mHeaderDrawerBinding.setHandlers(mEventHandlers); if (getSupportActionBar() != null) { if (mModel.filterName != null) { getSupportActionBar().setTitle(mModel.filterName); if (mModel.filterQuery != null) { getSupportActionBar().setSubtitle(mModel.filterQuery); } } } int defaultMenu = Preferences.getAccountHomePageId(this, mAccount); if (savedInstanceState != null) { Fragment detailsFragment = getSupportFragmentManager().getFragment(savedInstanceState, FRAGMENT_TAG_DETAILS); Fragment listFragment = getSupportFragmentManager().getFragment(savedInstanceState, FRAGMENT_TAG_LIST); if (listFragment != null) { FragmentTransaction tx = getSupportFragmentManager().beginTransaction().setReorderingAllowed(false); tx.replace(R.id.content, listFragment, FRAGMENT_TAG_LIST); if (detailsFragment != null) { tx.replace(R.id.details, detailsFragment, FRAGMENT_TAG_DETAILS); } tx.commit(); } else { // Navigate to current item requestNavigateTo(mModel.currentNavigationItemId == INVALID_ITEM ? defaultMenu : mModel.currentNavigationItemId, true, false); } } else { // Navigate to current item requestNavigateTo( mModel.currentNavigationItemId == INVALID_ITEM ? defaultMenu : mModel.currentNavigationItemId, true, false); } } @Override protected void onDestroy() { super.onDestroy(); // Unregister services LocalBroadcastManager.getInstance(this).unregisterReceiver(mAccountStatusChangedReceiver); } @Override public void onResume() { super.onResume(); updateAccountCustomFilters(); } @Override public int getSelectedChangeId() { return mModel.selectedChangeId; } @Override public void onPause() { super.onPause(); mUiHandler.removeCallbacksAndMessages(null); } @Override protected void onRestoreInstanceState(Bundle savedState) { if (savedState != null) { super.onRestoreInstanceState(savedState); mModel = savedState.getParcelable(getClass().getSimpleName() + "_model"); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable(getClass().getSimpleName() + "_model", mModel); //Save the fragment's instance Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_LIST); if (fragment != null) { getSupportFragmentManager().putFragment(outState, FRAGMENT_TAG_LIST, fragment); } fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DETAILS); if (fragment != null) { getSupportFragmentManager().putFragment(outState, FRAGMENT_TAG_DETAILS, fragment); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_WIZARD) { if (resultCode == RESULT_OK) { // Save the account Account newAccount = data.getParcelableExtra(SetupAccountActivity.EXTRA_ACCOUNT); boolean accountExists = false; for (Account account : mAccounts) { // Current account if (account.isSameAs(newAccount)) { accountExists = true; break; } } if (!accountExists) { mAccount = newAccount; mAccounts = Preferences.addOrUpdateAccount(this, mAccount); Preferences.setAccount(this, mAccount); Formatter.refreshCachedPreferences(this); CacheHelper.createAccountCacheDir(this, mAccount); setupAccountUrlHandlingStatus(mAccount, true); NotificationsHelper.createNotificationChannel(this, mAccount); } else { showWarning(R.string.account_exists); } // Switch to the new account performAccountSwitch(); } else { // If we don't have account, then close the activity. // Otherwise, do nothing if (Preferences.getAccount(this) == null) { finish(); } } } else if (requestCode == REQUEST_ACCOUNT_SETTINGS) { // Refresh current view mAccount = Preferences.getAccount(this); if (mModel.currentNavigationItemId == INVALID_ITEM) { mModel.currentNavigationItemId = Preferences.getAccountHomePageId(this, mAccount); } Formatter.refreshCachedPreferences(this); performNavigateTo(); } } @Override public ContentBinding getContentBinding() { return mBinding; } @Override public DrawerLayout getDrawerLayout() { return mBinding.drawerLayout; } private void loadAccounts() { mAccount = Preferences.getAccount(this); mAccounts = Preferences.getAccounts(this); if (mAccount != null) { Formatter.refreshCachedPreferences(this); } } private void setupNavigationDrawer() { // Bind the header, in code rather than in layout so we can have access to // binding variables mHeaderDrawerBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.navigation_header, mBinding.drawerNavigationView, false); mBinding.drawerNavigationView.addHeaderView(mHeaderDrawerBinding.getRoot()); mBinding.drawerNavigationView.setOnMenuButtonClickListener(this::onNavigationMenuItemClick); // Listen for click events and select the current one mBinding.drawerNavigationView.setDrawerNavigationItemSelectedListener(item -> { requestNavigateTo(item.getItemId(), false, true); return true; }); // Update the accounts and current account information if (mAccount != null) { updateCurrentAccountDrawerInfo(); updateAccountCustomFilters(); updateAccountsDrawerInfo(); } internalPerformShowAccount(mModel.isAccountExpanded); if (getMiniDrawerLayout() != null) { mBinding.drawerNavigationView.addMiniDrawerListener(new PanelSlideListener() { @Override public void onPanelSlide(View panel, float slideOffset) { // Ignore } @Override public void onPanelOpened(View panel) { // Ignore } @Override public void onPanelClosed(View panel) { internalPerformShowAccount(false); } }); } } private void performShowAccount(boolean show) { if (getMiniDrawerLayout() != null && !getMiniDrawerLayout().isOpen()) { getMiniDrawerLayout().openPane(); return; } internalPerformShowAccount(show); } private void internalPerformShowAccount(boolean show) { performUpdateNavigationDrawer(show); // Sync the current selection status if (mModel.currentNavigationItemId != -1) { performSelectItem(mModel.currentNavigationItemId, true); } } private void performUpdateNavigationDrawer(boolean show) { final boolean auth = mAccount != null && mAccount.hasAuthenticatedAccessMode(); final boolean supportNotifications = mAccount != null && mAccount.hasNotificationsSupport() && Preferences.isAccountNotificationsEnabled(this, mAccount); final Menu menu = mBinding.drawerNavigationView.getMenu(); menu.setGroupVisible(R.id.category_all, !show); menu.setGroupVisible(R.id.category_my_menu, !show && auth); menu.setGroupVisible(R.id.category_my_filters, !show && mCustomFilters != null && !mCustomFilters.isEmpty()); menu.setGroupVisible(R.id.category_my_account, show); menu.setGroupVisible(R.id.category_other_accounts, show); menu.setGroupVisible(R.id.category_info, show); menu.findItem(R.id.menu_account_stats).setVisible(show && auth); menu.findItem(R.id.menu_account_notifications).setVisible(show && supportNotifications); if (auth && mAccount.getServerVersion() != null) { GerritApi api = ModelHelper.getGerritApi(this, mAccount); boolean hasSupportAccountStatus = api.supportsFeature(Features.ACCOUNT_STATUS, mAccount.getServerVersion()); menu.findItem(R.id.menu_account_status).setVisible(show && hasSupportAccountStatus); updateAccountStatus(); } else { menu.findItem(R.id.menu_account_status).setVisible(false); } mModel.isAccountExpanded = show; mHeaderDrawerBinding.setModel(mModel); } private boolean performSelectItem(int itemId, boolean force) { final Menu menu = mBinding.drawerNavigationView.getMenu(); final MenuItem item = menu.findItem(itemId); if (item != null) { if (item.isCheckable()) { boolean changed = itemId != mModel.currentNavigationItemId; mBinding.drawerNavigationView.setCheckedItem(item.getItemId()); mModel.currentNavigationItemId = itemId; return force || changed; } else { performNavigationAction(item); } } return false; } private void requestNavigateTo(int itemId, boolean force, boolean async) { // Select the item final boolean navigate = performSelectItem(itemId, force); // Close the drawer if (mBinding.drawerLayout != null) { if (mBinding.drawerLayout.isDrawerOpen(GravityCompat.START)) { mBinding.drawerLayout.closeDrawer(GravityCompat.START, true); } } if (navigate) { if (async) { Message.obtain(mUiHandler, MESSAGE_NAVIGATE_TO).sendToTarget(); } else { performNavigateTo(); } } } private void performNavigationAction(MenuItem item) { switch (item.getItemId()) { case R.id.menu_account_settings: openAccountSettings(); break; case R.id.menu_account_notifications: openAccountNotifications(); break; case R.id.menu_account_status: openAccountStatusChooser(); break; case R.id.menu_account_stats: openAccountStats(); break; case R.id.menu_account_password: openAccountSetup(); break; case R.id.menu_drop_account: requestAccountDeletion(mAccount); break; case R.id.menu_add_account: Intent i = new Intent(this, SetupAccountActivity.class); startActivityForResult(i, REQUEST_WIZARD); break; case R.id.menu_share: String action = getString(R.string.action_share); String title = getString(R.string.share_app_title); final String deepLink = getString(R.string.link_play_store, getPackageName()); String text = getString(R.string.share_app_text, deepLink); ActivityHelper.share(this, action, title, text); break; case R.id.menu_privacy: final String link = getString(R.string.link_privacy); ActivityHelper.openUriInCustomTabs(this, link); break; case R.id.menu_about: String[] libraries = getResources().getStringArray(R.array.libraries_ids); LibsBuilder builder = new LibsBuilder().withAboutAppName(getString(R.string.app_name)) .withActivityColor(new Colors(ContextCompat.getColor(this, R.color.primaryDark), ContextCompat.getColor(this, R.color.primaryDark))) .withAboutIconShown(true).withActivityTitle(getString(R.string.menu_about)) .withAboutVersionShown(true).withLibraries(libraries).withFields(R.string.class.getFields()) .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR); builder.start(this); break; default: if (item.getGroupId() == R.id.category_other_accounts) { // Other accounts ground action int accountIndex = item.getItemId() - OTHER_ACCOUNTS_GROUP_BASE_ID; Account account = mAccounts.get(accountIndex); if (account != null) { mAccount = account; performAccountSwitch(); } } break; } } private void performNavigateTo() { final Menu menu = mBinding.drawerNavigationView.getMenu(); final MenuItem item = menu.findItem(mModel.currentNavigationItemId); if (item == null || mAccount == null) { return; } switch (item.getItemId()) { case R.id.menu_dashboard: openDashboardFragment(); break; default: // Is a filter menu? mModel.filterQuery = getQueryFilterExpressionFromMenuItemId(item.getItemId()); if (mModel.filterQuery != null) { mModel.filterName = item.getTitle().toString().split(DrawerNavigationView.SEPARATOR)[0]; openFilterFragment(item.getItemId(), mModel.filterName, mModel.filterQuery); } break; } } private void updateAccountCustomFilters() { // Remove all custom filters and re-add them final DrawerNavigationMenu menu = (DrawerNavigationMenu) mBinding.drawerNavigationView.getMenu(); int myFiltersGroupIndex = menu.findGroupIndex(R.id.category_my_filters); MenuItem group = menu.getItem(myFiltersGroupIndex); SubMenu myFiltersSubMenu = group.getSubMenu(); int count = myFiltersSubMenu.size() - 1; for (int i = count; i >= 0; i--) { ((DrawerNavigationSubMenu) myFiltersSubMenu).removeItemAt(i); } mCustomFilters = Preferences.getAccountCustomFilters(this, mAccount); if (mCustomFilters != null) { int i = 0; for (CustomFilter filter : mCustomFilters) { int id = MY_FILTERS_GROUP_BASE_ID + i; String title = filter.mName + DrawerNavigationView.SEPARATOR + filter.mQuery.toString() + DrawerNavigationView.SEPARATOR + "ic_close"; MenuItem item = myFiltersSubMenu.add(group.getGroupId(), id, Menu.NONE, title); item.setIcon(R.drawable.ic_filter); item.setCheckable(true); i++; } } menu.setGroupVisible(R.id.category_my_filters, !mModel.isAccountExpanded && mCustomFilters != null && !mCustomFilters.isEmpty()); mBinding.drawerNavigationView.setCheckedItem(mModel.currentNavigationItemId); } private void updateAccountsDrawerInfo() { // Remove all accounts and re-add them final DrawerNavigationMenu menu = (DrawerNavigationMenu) mBinding.drawerNavigationView.getMenu(); int otherAccountGroupIndex = menu.findGroupIndex(R.id.category_other_accounts); MenuItem group = menu.getItem(otherAccountGroupIndex); SubMenu otherAccountsSubMenu = group.getSubMenu(); int count = otherAccountsSubMenu.size() - 1; for (int i = count; i > 0; i--) { ((DrawerNavigationSubMenu) otherAccountsSubMenu).removeItemAt(i); } int i = 0; for (Account account : mAccounts) { // Current account if (mAccount.isSameAs(account)) { i++; continue; } int id = OTHER_ACCOUNTS_GROUP_BASE_ID + i; String title = account.getAccountDisplayName() + DrawerNavigationView.SEPARATOR + account.getRepositoryDisplayName() + DrawerNavigationView.SEPARATOR + "ic_delete" + DrawerNavigationView.SEPARATOR + "false"; MenuItem item = otherAccountsSubMenu.add(group.getGroupId(), id, Menu.NONE, title); PicassoHelper.bindAvatar(this, PicassoHelper.getPicassoClient(this), account, account.mAccount, item, PicassoHelper.getDefaultAvatar(this, R.color.primaryDarkForeground)); i++; } if (mAccount != null) { updateAccountStatus(); } } private void updateCurrentAccountDrawerInfo() { mModel.accountName = mAccount.getAccountDisplayName(); mModel.accountRepository = mAccount.getRepositoryDisplayName(); mHeaderDrawerBinding.setModel(mModel); mHeaderDrawerBinding.executePendingBindings(); PicassoHelper.bindAvatar(this, PicassoHelper.getPicassoClient(this), mAccount.mAccount, mHeaderDrawerBinding.accountAvatar, PicassoHelper.getDefaultAvatar(this, android.R.color.white)); } private void updateAccountStatus() { final DrawerNavigationMenu menu = (DrawerNavigationMenu) mBinding.drawerNavigationView.getMenu(); String title = getString(R.string.menu_account_status); if (!TextUtils.isEmpty(mAccount.mAccount.status)) { title += DrawerNavigationView.SEPARATOR + EmojiHelper.resolveEmojiStatus(this, mAccount.mAccount.status); } MenuItem item = menu.findItem(R.id.menu_account_status); item.setTitle(title); } private void performAccountSwitch() { // Check that we are in a valid status before update the ui if (mAccount == null) { if (mAccounts.size() == 0) { return; } // Use the first one account mAccount = mAccounts.get(0); } Preferences.setAccount(this, mAccount); Formatter.refreshCachedPreferences(this); fetchAccountStatus(mAccount); // Refresh the ui updateCurrentAccountDrawerInfo(); updateAccountCustomFilters(); updateAccountsDrawerInfo(); internalPerformShowAccount(false); // And navigate to the default menu requestNavigateTo(Preferences.getAccountHomePageId(this, mAccount), true, true); } private void requestAccountDeletion(Account account) { AlertDialog dialog = new AlertDialog.Builder(this).setTitle(R.string.account_deletion_title) .setMessage(R.string.account_deletion_message) .setPositiveButton(R.string.action_delete, (dialogInterface, i) -> Message .obtain(mUiHandler, MESSAGE_DELETE_ACCOUNT, account.getAccountHash()).sendToTarget()) .setNegativeButton(R.string.action_cancel, null).create(); dialog.show(); } @SuppressWarnings("Convert2streamapi") private void performDeleteAccount(String accountHash) { List<Account> accounts = Preferences.getAccounts(this); for (Account acct : accounts) { if (acct.getAccountHash().equals(accountHash)) { boolean currentAccount = mAccount != null && mAccount.getAccountHash().equals(acct.getAccountHash()); if (currentAccount) { performDeleteAccount(mAccount, true); internalPerformShowAccount(false); } else { performDeleteAccount(acct, false); } // Close the drawer if (mBinding.drawerLayout != null) { if (mBinding.drawerLayout.isDrawerOpen(GravityCompat.START)) { mBinding.drawerLayout.closeDrawer(GravityCompat.START, true); } } break; } } } private void performDeleteAccount(Account acct, boolean isCurrent) { // Remove the account boolean accountDeleted = false; int count = mAccounts.size(); for (int i = 0; i < count; i++) { Account account = mAccounts.get(i); if (account.isSameAs(acct)) { accountDeleted = true; mAccounts.remove(i); break; } } if (!accountDeleted) { showError(R.string.account_deletion_confirm_message); return; } // Remove the account Preferences.removeAccount(this, acct); if (isCurrent) { Preferences.setAccount(this, null); } Formatter.refreshCachedPreferences(this); Preferences.removeAccountPreferences(this, acct); CacheHelper.removeAccountCacheDir(this, acct); NotificationEntity.deleteAccountNotifications(this, acct.getAccountHash()); NotificationsHelper.deleteNotificationChannel(this, acct); // Unregister the url handling for this repository if no other account for the // same repository is active if (ModelHelper.canAccountHandleUrls(this, acct)) { boolean unregisterUrlHandler = true; List<Account> accounts = Preferences.getAccounts(getApplicationContext()); for (Account account : accounts) { if (acct.mRepository.mUrl.equals(account.mRepository.mUrl)) { unregisterUrlHandler = false; break; } } if (unregisterUrlHandler) { ModelHelper.setAccountUrlHandlingStatus(getApplicationContext(), acct, false); } } // Show message Snackbar.make(mBinding.getRoot(), R.string.account_deletion_confirm_message, Snackbar.LENGTH_SHORT).show(); if (isCurrent) { mAccount = null; performAccountSwitch(); // Have any account? No, then launch setup account wizard launchAddAccountIfNeeded(); } else { updateAccountsDrawerInfo(); } } private void openAccountSetup() { mUiHandler.post(() -> { if (mAccount != null) { Intent i = new Intent(this, AuthorizationAccountSetupActivity.class); startActivity(i); internalPerformShowAccount(false); } }); } private void openAccountSettings() { mUiHandler.post(() -> { if (mAccount != null) { Intent i = new Intent(MainActivity.this, AccountSettingsActivity.class); startActivityForResult(i, REQUEST_ACCOUNT_SETTINGS); internalPerformShowAccount(false); } }); } private void openAccountNotifications() { mUiHandler.post(() -> { if (mAccount != null) { Intent i = new Intent(MainActivity.this, NotificationsActivity.class); i.putExtra(Constants.EXTRA_ACCOUNT_HASH, mAccount.getAccountHash()); i.putExtra(Constants.EXTRA_HAS_PARENT, false); startActivity(i); internalPerformShowAccount(false); } }); } private void openAccountStatusChooser() { mUiHandler.post(() -> { if (mAccount != null) { SetAccountStatusDialogFragment fragment = SetAccountStatusDialogFragment.newInstance(); fragment.show(getSupportFragmentManager(), SetAccountStatusDialogFragment.TAG); } }); } private void openAccountStats() { mUiHandler.post(() -> { if (mAccount != null) { ChangeQuery filter = new ChangeQuery().owner(ModelHelper.getSafeAccountOwner(mAccount.mAccount)); String title = getString(R.string.account_details); String displayName = ModelHelper.getAccountDisplayName(mAccount.mAccount); String extra = SerializationManager.getInstance().toJson(mAccount.mAccount); ActivityHelper.openStatsActivity(this, title, displayName, StatsFragment.ACCOUNT_STATS, String.valueOf(mAccount.mAccount.accountId), filter, extra); } }); } private void openDashboardFragment() { mModel.filterName = getString(R.string.menu_dashboard); mModel.filterQuery = null; // Setup the title invalidateTabs(); if (getSupportActionBar() != null) { getSupportActionBar().setTitle(R.string.menu_dashboard); getSupportActionBar().setSubtitle(null); } // Open the dashboard fragment mModel.selectedChangeId = INVALID_ITEM; FragmentTransaction tx = getSupportFragmentManager().beginTransaction().setReorderingAllowed(false); Fragment oldFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_LIST); if (oldFragment != null) { tx.remove(oldFragment); } oldFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DETAILS); if (oldFragment != null) { tx.remove(oldFragment); } Fragment newFragment = DashboardFragment.newInstance(); tx.replace(R.id.content, newFragment, FRAGMENT_TAG_LIST).commit(); } private void openFilterFragment(int id, CharSequence title, String filter) { // Setup the title and tabs invalidateTabs(); if (getSupportActionBar() != null) { getSupportActionBar().setTitle(title); getSupportActionBar().setSubtitle(filter); } // Open the filter fragment mModel.selectedChangeId = INVALID_ITEM; FragmentTransaction tx = getSupportFragmentManager().beginTransaction().setReorderingAllowed(false); Fragment oldFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_LIST); if (oldFragment != null) { tx.remove(oldFragment); } oldFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DETAILS); if (oldFragment != null) { tx.remove(oldFragment); } Fragment newFragment = ChangeListByFilterFragment.newInstance(filter, false, true, true); tx.replace(R.id.content, newFragment, FRAGMENT_TAG_LIST).commit(); // Select the drawer item mBinding.drawerNavigationView.setCheckedItem(id); mModel.currentNavigationItemId = id; } private void onNavigationMenuItemClick(int menuId) { if (mCustomFilters != null && menuId >= MY_FILTERS_GROUP_BASE_ID && menuId < OTHER_ACCOUNTS_GROUP_BASE_ID) { performDeleteCustomFilter(menuId); } else if (menuId >= OTHER_ACCOUNTS_GROUP_BASE_ID) { int accountIndex = menuId - OTHER_ACCOUNTS_GROUP_BASE_ID; Account account = mAccounts.get(accountIndex); if (account != null) { requestAccountDeletion(account); } } } private void performDeleteCustomFilter(int menuId) { CustomFilter filter = mCustomFilters.get(menuId - MY_FILTERS_GROUP_BASE_ID); mCustomFilters.remove(filter); Preferences.setAccountCustomFilters(this, mAccount, mCustomFilters); if (mModel.currentNavigationItemId == menuId) { int defaultMenu = Preferences.getAccountHomePageId(this, mAccount); requestNavigateTo(defaultMenu, true, true); } updateAccountCustomFilters(); } private boolean launchAddAccountIfNeeded() { if (mAccount == null) { Intent i = new Intent(this, SetupAccountActivity.class); startActivityForResult(i, REQUEST_WIZARD); return true; } return false; } private String getQueryFilterExpressionFromMenuItemId(int itemId) { // Custom filters if (mCustomFilters != null && itemId >= MY_FILTERS_GROUP_BASE_ID && itemId <= OTHER_ACCOUNTS_GROUP_BASE_ID) { int filterIndex = itemId - MY_FILTERS_GROUP_BASE_ID; CustomFilter customFilter = mCustomFilters.get(filterIndex); return customFilter.mQuery.toString(); } // Predefined filters String[] names = getResources().getStringArray(R.array.query_filters_ids_names); String[] filters = getResources().getStringArray(R.array.query_filters_values); int count = names.length; for (int i = 0; i < count; i++) { int id = getResources().getIdentifier(names[i], "id", getPackageName()); if (itemId == id) { return filters[i]; } } return null; } @Override @SuppressWarnings("unchecked") public <T> void onRefreshEnd(Fragment from, T result) { super.onRefreshEnd(from, result); if (result == null) { return; } if (mIsTwoPane && result instanceof List) { Fragment current = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_LIST); if (current instanceof PageableFragment) { current = ((PageableFragment) current).getCurrentFragment(); if (current != null && !current.equals(from)) { // This is not the visible fragment. ignore its results return; } } else { if (!current.equals(from)) { // This is not the visible fragment. ignore its results return; } } List<ChangeInfo> changes = (List<ChangeInfo>) result; if (!changes.isEmpty() && mModel.selectedChangeId == INVALID_ITEM) { onChangeItemPressed(changes.get(0)); } } else if (result instanceof ChangeInfo) { mModel.selectedChangeId = ((ChangeInfo) result).legacyChangeId; } } private void setupAccountUrlHandlingStatus(Account account, boolean status) { Preferences.setAccountHandleLinks(this, account, status); if (ModelHelper.canAccountHandleUrls(getApplicationContext(), account)) { ModelHelper.setAccountUrlHandlingStatus(getApplicationContext(), account, status); } } private void fetchAccountStatus(Account account) { // This is running while the app is in foreground so there is not risk on // perform this operation. In addition, we need the account information asap, so // just try to start the service directly. try { Intent intent = new Intent(this, AccountStatusFetcherService.class); intent.setAction(AccountStatusFetcherService.ACCOUNT_STATUS_FETCHER_ACTION); intent.putExtra(AccountStatusFetcherService.EXTRA_ACCOUNT, account.getAccountHash()); startService(intent); } catch (IllegalStateException ex) { Log.w(TAG, "Can't start account fetcher service.", ex); } } }