mirror of https://github.com/tuskyapp/Tusky.git
Browse Source
* convert MainActivity to Kotlin * migrate to MaterialDrawer 8 * fix drawer styles * revert removing BezelImageView and material_drawer_header override * fix tests * add lost comment back to material_drawer_header.xml * add tools:parentTag to material_drawer_header.xml * use when instead of if in MainActivity * fix statusbar color over the drawer * cleanup drawer item creation * tint secondary drawer items as well * remove unnecessary ids * fix header text color in the light theme * improve header text contrastpull/1757/head
22 changed files with 871 additions and 784 deletions
@ -1,663 +0,0 @@
|
||||
/* Copyright 2017 Andrew Dawson |
||||
* |
||||
* This file is a part of Tusky. |
||||
* |
||||
* 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. |
||||
* |
||||
* Tusky 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 Tusky; if not, |
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky; |
||||
|
||||
import android.content.Intent; |
||||
import android.graphics.Color; |
||||
import android.graphics.drawable.Drawable; |
||||
import android.net.Uri; |
||||
import android.os.Bundle; |
||||
import android.os.Handler; |
||||
import android.util.Log; |
||||
import android.view.KeyEvent; |
||||
import android.widget.ImageView; |
||||
|
||||
import androidx.annotation.Nullable; |
||||
import androidx.appcompat.app.AlertDialog; |
||||
import androidx.core.content.ContextCompat; |
||||
import androidx.core.content.pm.ShortcutManagerCompat; |
||||
import androidx.emoji.text.EmojiCompat; |
||||
import androidx.fragment.app.Fragment; |
||||
import androidx.lifecycle.Lifecycle; |
||||
import androidx.preference.PreferenceManager; |
||||
import androidx.viewpager2.widget.MarginPageTransformer; |
||||
import androidx.viewpager2.widget.ViewPager2; |
||||
|
||||
import com.bumptech.glide.Glide; |
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton; |
||||
import com.google.android.material.tabs.TabLayout; |
||||
import com.google.android.material.tabs.TabLayoutMediator; |
||||
import com.keylesspalace.tusky.appstore.CacheUpdater; |
||||
import com.keylesspalace.tusky.appstore.EventHub; |
||||
import com.keylesspalace.tusky.appstore.MainTabsChangedEvent; |
||||
import com.keylesspalace.tusky.appstore.ProfileEditedEvent; |
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity; |
||||
import com.keylesspalace.tusky.components.conversation.ConversationsRepository; |
||||
import com.keylesspalace.tusky.components.scheduled.ScheduledTootActivity; |
||||
import com.keylesspalace.tusky.components.search.SearchActivity; |
||||
import com.keylesspalace.tusky.db.AccountEntity; |
||||
import com.keylesspalace.tusky.entity.Account; |
||||
import com.keylesspalace.tusky.fragment.SFragment; |
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity; |
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment; |
||||
import com.keylesspalace.tusky.pager.MainPagerAdapter; |
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper; |
||||
import com.keylesspalace.tusky.util.NotificationHelper; |
||||
import com.keylesspalace.tusky.util.ShareShortcutHelper; |
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial; |
||||
import com.mikepenz.materialdrawer.AccountHeader; |
||||
import com.mikepenz.materialdrawer.AccountHeaderBuilder; |
||||
import com.mikepenz.materialdrawer.Drawer; |
||||
import com.mikepenz.materialdrawer.DrawerBuilder; |
||||
import com.mikepenz.materialdrawer.model.DividerDrawerItem; |
||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; |
||||
import com.mikepenz.materialdrawer.model.ProfileDrawerItem; |
||||
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem; |
||||
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem; |
||||
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; |
||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile; |
||||
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader; |
||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import javax.inject.Inject; |
||||
|
||||
import dagger.android.AndroidInjector; |
||||
import dagger.android.DispatchingAndroidInjector; |
||||
import dagger.android.HasAndroidInjector; |
||||
import io.reactivex.android.schedulers.AndroidSchedulers; |
||||
|
||||
import static com.keylesspalace.tusky.util.MediaUtilsKt.deleteStaleCachedMedia; |
||||
import static com.uber.autodispose.AutoDispose.autoDisposable; |
||||
import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from; |
||||
|
||||
public final class MainActivity extends BottomSheetActivity implements ActionButtonActivity, |
||||
HasAndroidInjector { |
||||
|
||||
private static final String TAG = "MainActivity"; // logging tag
|
||||
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13; |
||||
private static final long DRAWER_ITEM_EDIT_PROFILE = 0; |
||||
private static final long DRAWER_ITEM_FAVOURITES = 1; |
||||
private static final long DRAWER_ITEM_BOOKMARKS = 2; |
||||
private static final long DRAWER_ITEM_LISTS = 3; |
||||
private static final long DRAWER_ITEM_SEARCH = 4; |
||||
private static final long DRAWER_ITEM_SAVED_TOOT = 5; |
||||
private static final long DRAWER_ITEM_ACCOUNT_SETTINGS = 6; |
||||
private static final long DRAWER_ITEM_SETTINGS = 7; |
||||
private static final long DRAWER_ITEM_ABOUT = 8; |
||||
private static final long DRAWER_ITEM_LOG_OUT = 9; |
||||
private static final long DRAWER_ITEM_FOLLOW_REQUESTS = 10; |
||||
private static final long DRAWER_ITEM_SCHEDULED_TOOT = 11; |
||||
public static final String STATUS_URL = "statusUrl"; |
||||
|
||||
@Inject |
||||
public DispatchingAndroidInjector<Object> androidInjector; |
||||
@Inject |
||||
public EventHub eventHub; |
||||
@Inject |
||||
public CacheUpdater cacheUpdater; |
||||
@Inject |
||||
ConversationsRepository conversationRepository; |
||||
|
||||
private FloatingActionButton composeButton; |
||||
private AccountHeader headerResult; |
||||
private Drawer drawer; |
||||
private TabLayout tabLayout; |
||||
private ViewPager2 viewPager; |
||||
|
||||
private int notificationTabPosition; |
||||
private MainPagerAdapter adapter; |
||||
|
||||
private final EmojiCompat.InitCallback emojiInitCallback = new EmojiCompat.InitCallback() { |
||||
@Override |
||||
public void onInitialized() { |
||||
if(!isDestroyed()) { |
||||
updateProfiles(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
@Override |
||||
protected void onCreate(Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
|
||||
if (accountManager.getActiveAccount() == null) { |
||||
// will be redirected to LoginActivity by BaseActivity
|
||||
return; |
||||
} |
||||
|
||||
Intent intent = getIntent(); |
||||
boolean showNotificationTab = false; |
||||
|
||||
if (intent != null) { |
||||
|
||||
/** there are two possibilities the accountId can be passed to MainActivity: |
||||
- from our code as long 'account_id' |
||||
- from share shortcuts as String 'android.intent.extra.shortcut.ID' |
||||
*/ |
||||
long accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1); |
||||
if(accountId == -1) { |
||||
String accountIdString = intent.getStringExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID); |
||||
if(accountIdString != null) { |
||||
accountId = Long.parseLong(accountIdString); |
||||
} |
||||
} |
||||
boolean accountRequested = (accountId != -1); |
||||
|
||||
if (accountRequested) { |
||||
AccountEntity account = accountManager.getActiveAccount(); |
||||
if (account == null || accountId != account.getId()) { |
||||
accountManager.setActiveAccount(accountId); |
||||
} |
||||
} |
||||
|
||||
if (ComposeActivity.canHandleMimeType(intent.getType())) { |
||||
// Sharing to Tusky from an external app
|
||||
if (accountRequested) { |
||||
// The correct account is already active
|
||||
forwardShare(intent); |
||||
} else { |
||||
// No account was provided, show the chooser
|
||||
showAccountChooserDialog(getString(R.string.action_share_as), true, account -> { |
||||
long requestedId = account.getId(); |
||||
AccountEntity activeAccount = accountManager.getActiveAccount(); |
||||
if (activeAccount != null && requestedId == activeAccount.getId()) { |
||||
// The correct account is already active
|
||||
forwardShare(intent); |
||||
} else { |
||||
// A different account was requested, restart the activity
|
||||
intent.putExtra(NotificationHelper.ACCOUNT_ID, requestedId); |
||||
changeAccount(requestedId, intent); |
||||
} |
||||
}); |
||||
} |
||||
} else if (accountRequested) { |
||||
// user clicked a notification, show notification tab and switch user if necessary
|
||||
showNotificationTab = true; |
||||
} |
||||
} |
||||
setContentView(R.layout.activity_main); |
||||
|
||||
composeButton = findViewById(R.id.floating_btn); |
||||
tabLayout = findViewById(R.id.tab_layout); |
||||
viewPager = findViewById(R.id.pager); |
||||
|
||||
composeButton.setOnClickListener(v -> { |
||||
Intent composeIntent = new Intent(getApplicationContext(), ComposeActivity.class); |
||||
startActivity(composeIntent); |
||||
}); |
||||
|
||||
setupDrawer(); |
||||
|
||||
/* Fetch user info while we're doing other things. This has to be done after setting up the |
||||
* drawer, though, because its callback touches the header in the drawer. */ |
||||
fetchUserInfo(); |
||||
|
||||
setupTabs(showNotificationTab); |
||||
|
||||
int pageMargin = getResources().getDimensionPixelSize(R.dimen.tab_page_margin); |
||||
viewPager.setPageTransformer(new MarginPageTransformer(pageMargin)); |
||||
|
||||
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { |
||||
@Override |
||||
public void onTabSelected(TabLayout.Tab tab) { |
||||
viewPager.setCurrentItem(tab.getPosition()); |
||||
|
||||
if (tab.getPosition() == notificationTabPosition) { |
||||
NotificationHelper.clearNotificationsForActiveAccount(MainActivity.this, accountManager); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onTabUnselected(TabLayout.Tab tab) { |
||||
} |
||||
|
||||
@Override |
||||
public void onTabReselected(TabLayout.Tab tab) { |
||||
if (adapter != null) { |
||||
Fragment fragment = adapter.getFragment(tab.getPosition()); |
||||
if (fragment instanceof ReselectableFragment) { |
||||
((ReselectableFragment) fragment).onReselect(); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
// Setup push notifications
|
||||
if (NotificationHelper.areNotificationsEnabled(this, accountManager)) { |
||||
NotificationHelper.enablePullNotifications(); |
||||
} else { |
||||
NotificationHelper.disablePullNotifications(); |
||||
} |
||||
|
||||
eventHub.getEvents() |
||||
.observeOn(AndroidSchedulers.mainThread()) |
||||
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) |
||||
.subscribe(event -> { |
||||
if (event instanceof ProfileEditedEvent) { |
||||
onFetchUserInfoSuccess(((ProfileEditedEvent) event).getNewProfileData()); |
||||
} |
||||
if (event instanceof MainTabsChangedEvent) { |
||||
setupTabs(false); |
||||
} |
||||
}); |
||||
|
||||
// Flush old media that was cached for sharing
|
||||
deleteStaleCachedMedia(getApplicationContext().getExternalFilesDir("Tusky")); |
||||
} |
||||
|
||||
@Override |
||||
protected void onResume() { |
||||
super.onResume(); |
||||
|
||||
NotificationHelper.clearNotificationsForActiveAccount(this, accountManager); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void onBackPressed() { |
||||
if (drawer != null && drawer.isDrawerOpen()) { |
||||
drawer.closeDrawer(); |
||||
} else if (viewPager.getCurrentItem() != 0) { |
||||
viewPager.setCurrentItem(0); |
||||
} else { |
||||
super.onBackPressed(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean onKeyDown(int keyCode, KeyEvent event) { |
||||
switch (keyCode) { |
||||
case KeyEvent.KEYCODE_MENU: { |
||||
if (drawer.isDrawerOpen()) { |
||||
drawer.closeDrawer(); |
||||
} else { |
||||
drawer.openDrawer(); |
||||
} |
||||
return true; |
||||
} |
||||
case KeyEvent.KEYCODE_SEARCH: { |
||||
startActivityWithSlideInAnimation(SearchActivity.getIntent(this)); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
if (event.isCtrlPressed() || event.isShiftPressed()) { |
||||
// FIXME: blackberry keyONE raises SHIFT key event even CTRL IS PRESSED
|
||||
switch (keyCode) { |
||||
case KeyEvent.KEYCODE_N: { |
||||
// open compose activity by pressing SHIFT + N (or CTRL + N)
|
||||
Intent composeIntent = new Intent(getApplicationContext(), ComposeActivity.class); |
||||
startActivity(composeIntent); |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return super.onKeyDown(keyCode, event); |
||||
} |
||||
|
||||
@Override |
||||
public void onPostCreate(Bundle savedInstanceState) { |
||||
super.onPostCreate(savedInstanceState); |
||||
Intent intent = getIntent(); |
||||
if (intent != null) { |
||||
String statusUrl = intent.getStringExtra(STATUS_URL); |
||||
if (statusUrl != null) { |
||||
viewUrl(statusUrl, PostLookupFallbackBehavior.DISPLAY_ERROR); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void onDestroy() { |
||||
super.onDestroy(); |
||||
EmojiCompat.get().unregisterInitCallback(emojiInitCallback); |
||||
} |
||||
|
||||
private void forwardShare(Intent intent) { |
||||
Intent composeIntent = new Intent(this, ComposeActivity.class); |
||||
composeIntent.setAction(intent.getAction()); |
||||
composeIntent.setType(intent.getType()); |
||||
composeIntent.putExtras(intent); |
||||
composeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); |
||||
startActivity(composeIntent); |
||||
finish(); |
||||
} |
||||
|
||||
private void setupDrawer() { |
||||
headerResult = new AccountHeaderBuilder() |
||||
.withActivity(this) |
||||
.withDividerBelowHeader(false) |
||||
.withHeaderBackgroundScaleType(ImageView.ScaleType.CENTER_CROP) |
||||
.withCurrentProfileHiddenInList(true) |
||||
.withOnAccountHeaderListener((view, profile, current) -> handleProfileClick(profile, current)) |
||||
.addProfiles( |
||||
new ProfileSettingDrawerItem() |
||||
.withIdentifier(DRAWER_ITEM_ADD_ACCOUNT) |
||||
.withName(R.string.add_account_name) |
||||
.withDescription(R.string.add_account_description) |
||||
.withIcon(GoogleMaterial.Icon.gmd_add)) |
||||
.build(); |
||||
|
||||
headerResult.getView() |
||||
.findViewById(R.id.material_drawer_account_header_current) |
||||
.setContentDescription(getString(R.string.action_view_profile)); |
||||
|
||||
ImageView background = headerResult.getHeaderBackgroundView(); |
||||
background.setColorFilter(ContextCompat.getColor(this, R.color.header_background_filter)); |
||||
background.setBackgroundColor(ContextCompat.getColor(this, R.color.tusky_grey_10)); |
||||
|
||||
final boolean animateAvatars = PreferenceManager.getDefaultSharedPreferences(this) |
||||
.getBoolean("animateGifAvatars", false); |
||||
|
||||
DrawerImageLoader.init(new AbstractDrawerImageLoader() { |
||||
@Override |
||||
public void set(ImageView imageView, Uri uri, Drawable placeholder, String tag) { |
||||
if(animateAvatars) { |
||||
Glide.with(MainActivity.this) |
||||
.load(uri) |
||||
.placeholder(placeholder) |
||||
.into(imageView); |
||||
} else { |
||||
Glide.with(MainActivity.this) |
||||
.asBitmap() |
||||
.load(uri) |
||||
.placeholder(placeholder) |
||||
.into(imageView); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void cancel(ImageView imageView) { |
||||
Glide.with(MainActivity.this).clear(imageView); |
||||
} |
||||
}); |
||||
|
||||
List<IDrawerItem> listItems = new ArrayList<>(11); |
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_EDIT_PROFILE).withName(R.string.action_edit_profile).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_person)); |
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_FAVOURITES).withName(R.string.action_view_favourites).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_star)); |
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_BOOKMARKS).withName(R.string.action_view_bookmarks).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_bookmark)); |
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_LISTS).withName(R.string.action_lists).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_list)); |
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SEARCH).withName(R.string.action_search).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_search)); |
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SAVED_TOOT).withName(R.string.action_access_saved_toot).withSelectable(false).withIcon(R.drawable.ic_notebook).withIconTintingEnabled(true)); |
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SCHEDULED_TOOT).withName(R.string.action_access_scheduled_toot).withSelectable(false).withIcon(R.drawable.ic_access_time).withIconTintingEnabled(true)); |
||||
listItems.add(new DividerDrawerItem()); |
||||
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_ACCOUNT_SETTINGS).withName(R.string.action_view_account_preferences).withSelectable(false).withIcon(R.drawable.ic_account_settings).withIconTintingEnabled(true)); |
||||
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_SETTINGS).withName(R.string.action_view_preferences).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_settings)); |
||||
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_ABOUT).withName(R.string.about_title_activity).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_info)); |
||||
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_LOG_OUT).withName(R.string.action_logout).withSelectable(false).withIcon(R.drawable.ic_logout).withIconTintingEnabled(true)); |
||||
|
||||
drawer = new DrawerBuilder() |
||||
.withActivity(this) |
||||
.withAccountHeader(headerResult) |
||||
.withHasStableIds(true) |
||||
.withSelectedItem(-1) |
||||
.withDrawerItems(listItems) |
||||
.withToolbar(findViewById(R.id.main_toolbar)) |
||||
.withOnDrawerItemClickListener((view, position, drawerItem) -> { |
||||
if (drawerItem != null) { |
||||
long drawerItemIdentifier = drawerItem.getIdentifier(); |
||||
|
||||
if (drawerItemIdentifier == DRAWER_ITEM_EDIT_PROFILE) { |
||||
Intent intent = new Intent(MainActivity.this, EditProfileActivity.class); |
||||
startActivityWithSlideInAnimation(intent); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_FAVOURITES) { |
||||
Intent intent = StatusListActivity.newFavouritesIntent(MainActivity.this); |
||||
startActivityWithSlideInAnimation(intent); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_BOOKMARKS) { |
||||
Intent intent = StatusListActivity.newBookmarksIntent(MainActivity.this); |
||||
startActivityWithSlideInAnimation(intent); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_SEARCH) { |
||||
startActivityWithSlideInAnimation(SearchActivity.getIntent(this)); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_ACCOUNT_SETTINGS) { |
||||
Intent intent = PreferencesActivity.newIntent(MainActivity.this, PreferencesActivity.ACCOUNT_PREFERENCES); |
||||
startActivityWithSlideInAnimation(intent); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_SETTINGS) { |
||||
Intent intent = PreferencesActivity.newIntent(MainActivity.this, PreferencesActivity.GENERAL_PREFERENCES); |
||||
startActivityWithSlideInAnimation(intent); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_ABOUT) { |
||||
Intent intent = new Intent(MainActivity.this, AboutActivity.class); |
||||
startActivityWithSlideInAnimation(intent); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_LOG_OUT) { |
||||
logout(); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_FOLLOW_REQUESTS) { |
||||
Intent intent = new Intent(MainActivity.this, AccountListActivity.class); |
||||
intent.putExtra("type", AccountListActivity.Type.FOLLOW_REQUESTS); |
||||
startActivityWithSlideInAnimation(intent); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_SAVED_TOOT) { |
||||
Intent intent = new Intent(MainActivity.this, SavedTootActivity.class); |
||||
startActivityWithSlideInAnimation(intent); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_SCHEDULED_TOOT) { |
||||
startActivityWithSlideInAnimation(ScheduledTootActivity.newIntent(this)); |
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_LISTS) { |
||||
startActivityWithSlideInAnimation(ListsActivity.newIntent(this)); |
||||
} |
||||
|
||||
} |
||||
|
||||
return false; |
||||
}) |
||||
.build(); |
||||
|
||||
if (BuildConfig.DEBUG) { |
||||
IDrawerItem debugItem = new SecondaryDrawerItem() |
||||
.withIdentifier(1337) |
||||
.withName("debug") |
||||
.withDisabledTextColor(Color.GREEN) |
||||
.withSelectable(false) |
||||
.withEnabled(false); |
||||
drawer.addItem(debugItem); |
||||
} |
||||
|
||||
EmojiCompat.get().registerInitCallback(emojiInitCallback); |
||||
} |
||||
|
||||
private void setupTabs(boolean selectNotificationTab) { |
||||
List<TabData> tabs = accountManager.getActiveAccount().getTabPreferences(); |
||||
|
||||
adapter = new MainPagerAdapter(tabs, this); |
||||
viewPager.setAdapter(adapter); |
||||
|
||||
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { }).attach(); |
||||
|
||||
tabLayout.removeAllTabs(); |
||||
for (int i = 0; i < tabs.size(); i++) { |
||||
TabLayout.Tab tab = tabLayout.newTab() |
||||
.setIcon(tabs.get(i).getIcon()); |
||||
if (tabs.get(i).getId().equals(TabDataKt.LIST)) { |
||||
tab.setContentDescription(tabs.get(i).getArguments().get(1)); |
||||
} else { |
||||
tab.setContentDescription(tabs.get(i).getText()); |
||||
} |
||||
tabLayout.addTab(tab); |
||||
if (tabs.get(i).getId().equals(TabDataKt.NOTIFICATIONS)) { |
||||
notificationTabPosition = i; |
||||
if (selectNotificationTab) { |
||||
tab.select(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
private boolean handleProfileClick(IProfile profile, boolean current) { |
||||
AccountEntity activeAccount = accountManager.getActiveAccount(); |
||||
|
||||
//open profile when active image was clicked
|
||||
if (current && activeAccount != null) { |
||||
Intent intent = AccountActivity.getIntent(this, activeAccount.getAccountId()); |
||||
startActivityWithSlideInAnimation(intent); |
||||
new Handler().postDelayed(() -> drawer.closeDrawer(), 100); |
||||
return true; |
||||
} |
||||
//open LoginActivity to add new account
|
||||
if (profile.getIdentifier() == DRAWER_ITEM_ADD_ACCOUNT) { |
||||
startActivityWithSlideInAnimation(LoginActivity.getIntent(this, true)); |
||||
new Handler().postDelayed(() -> drawer.closeDrawer(), 100); |
||||
return true; |
||||
} |
||||
//change Account
|
||||
changeAccount(profile.getIdentifier(), null); |
||||
return false; |
||||
} |
||||
|
||||
|
||||
private void changeAccount(long newSelectedId, @Nullable Intent forward) { |
||||
cacheUpdater.stop(); |
||||
SFragment.flushFilters(); |
||||
accountManager.setActiveAccount(newSelectedId); |
||||
|
||||
Intent intent = new Intent(this, MainActivity.class); |
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); |
||||
if (forward != null) { |
||||
intent.setType(forward.getType()); |
||||
intent.setAction(forward.getAction()); |
||||
intent.putExtras(forward); |
||||
} |
||||
startActivity(intent); |
||||
finishWithoutSlideOutAnimation(); |
||||
|
||||
overridePendingTransition(R.anim.explode, R.anim.explode); |
||||
} |
||||
|
||||
private void logout() { |
||||
|
||||
AccountEntity activeAccount = accountManager.getActiveAccount(); |
||||
|
||||
if (activeAccount != null) { |
||||
|
||||
new AlertDialog.Builder(this) |
||||
.setTitle(R.string.action_logout) |
||||
.setMessage(getString(R.string.action_logout_confirm, activeAccount.getFullName())) |
||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> { |
||||
|
||||
NotificationHelper.deleteNotificationChannelsForAccount(accountManager.getActiveAccount(), MainActivity.this); |
||||
cacheUpdater.clearForUser(activeAccount.getId()); |
||||
conversationRepository.deleteCacheForAccount(activeAccount.getId()); |
||||
ShareShortcutHelper.removeShortcut(this, activeAccount); |
||||
|
||||
AccountEntity newAccount = accountManager.logActiveAccountOut(); |
||||
|
||||
if (!NotificationHelper.areNotificationsEnabled(MainActivity.this, accountManager)) { |
||||
NotificationHelper.disablePullNotifications(); |
||||
} |
||||
|
||||
Intent intent; |
||||
if (newAccount == null) { |
||||
intent = LoginActivity.getIntent(MainActivity.this, false); |
||||
} else { |
||||
intent = new Intent(MainActivity.this, MainActivity.class); |
||||
} |
||||
startActivity(intent); |
||||
finishWithoutSlideOutAnimation(); |
||||
}) |
||||
.setNegativeButton(android.R.string.no, null) |
||||
.show(); |
||||
} |
||||
} |
||||
|
||||
private void fetchUserInfo() { |
||||
mastodonApi.accountVerifyCredentials() |
||||
.observeOn(AndroidSchedulers.mainThread()) |
||||
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) |
||||
.subscribe(this::onFetchUserInfoSuccess, MainActivity::onFetchUserInfoFailure); |
||||
} |
||||
|
||||
private void onFetchUserInfoSuccess(Account me) { |
||||
|
||||
// Add the header image and avatar from the account, into the navigation drawer header.
|
||||
|
||||
ImageView background = headerResult.getHeaderBackgroundView(); |
||||
|
||||
Glide.with(MainActivity.this) |
||||
.asBitmap() |
||||
.load(me.getHeader()) |
||||
.into(background); |
||||
|
||||
accountManager.updateActiveAccount(me); |
||||
|
||||
NotificationHelper.createNotificationChannelsForAccount(accountManager.getActiveAccount(), this); |
||||
|
||||
// Show follow requests in the menu, if this is a locked account.
|
||||
if (me.getLocked() && drawer.getDrawerItem(DRAWER_ITEM_FOLLOW_REQUESTS) == null) { |
||||
PrimaryDrawerItem followRequestsItem = new PrimaryDrawerItem() |
||||
.withIdentifier(DRAWER_ITEM_FOLLOW_REQUESTS) |
||||
.withName(R.string.action_view_follow_requests) |
||||
.withSelectable(false) |
||||
.withIcon(GoogleMaterial.Icon.gmd_person_add); |
||||
drawer.addItemAtPosition(followRequestsItem, 4); |
||||
} else if (!me.getLocked()) { |
||||
drawer.removeItem(DRAWER_ITEM_FOLLOW_REQUESTS); |
||||
} |
||||
|
||||
updateProfiles(); |
||||
|
||||
ShareShortcutHelper.updateShortcut(this, accountManager.getActiveAccount()); |
||||
|
||||
} |
||||
|
||||
private void updateProfiles() { |
||||
|
||||
List<AccountEntity> allAccounts = accountManager.getAllAccountsOrderedByActive(); |
||||
|
||||
List<IProfile> profiles = new ArrayList<>(allAccounts.size() + 1); |
||||
|
||||
for (AccountEntity acc : allAccounts) { |
||||
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(acc.getDisplayName(), acc.getEmojis(), headerResult.getView()); |
||||
emojifiedName = EmojiCompat.get().process(emojifiedName); |
||||
|
||||
profiles.add( |
||||
new ProfileDrawerItem() |
||||
.withSetSelected(acc.isActive()) |
||||
.withName(emojifiedName) |
||||
.withIcon(acc.getProfilePictureUrl()) |
||||
.withNameShown(true) |
||||
.withIdentifier(acc.getId()) |
||||
.withEmail(acc.getFullName())); |
||||
|
||||
} |
||||
|
||||
// reuse the already existing "add account" item
|
||||
for (IProfile profile : headerResult.getProfiles()) { |
||||
if (profile.getIdentifier() == DRAWER_ITEM_ADD_ACCOUNT) { |
||||
profiles.add(profile); |
||||
break; |
||||
} |
||||
} |
||||
headerResult.clear(); |
||||
headerResult.setProfiles(profiles); |
||||
headerResult.setActiveProfile(accountManager.getActiveAccount().getId()); |
||||
} |
||||
|
||||
private static void onFetchUserInfoFailure(Throwable throwable) { |
||||
Log.e(TAG, "Failed to fetch user info. " + throwable.getMessage()); |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public FloatingActionButton getActionButton() { |
||||
return composeButton; |
||||
} |
||||
|
||||
@Override |
||||
public AndroidInjector<Object> androidInjector() { |
||||
return androidInjector; |
||||
} |
||||
} |
||||
@ -0,0 +1,650 @@
|
||||
/* Copyright 2020 Tusky Contributors |
||||
* |
||||
* This file is a part of Tusky. |
||||
* |
||||
* 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. |
||||
* |
||||
* Tusky 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 Tusky; if not, |
||||
* see <http://www.gnu.org/licenses>. */ |
||||
|
||||
package com.keylesspalace.tusky |
||||
|
||||
import android.content.Context |
||||
import android.content.DialogInterface |
||||
import android.content.Intent |
||||
import android.content.res.ColorStateList |
||||
import android.content.res.Configuration |
||||
import android.graphics.Color |
||||
import android.graphics.drawable.Drawable |
||||
import android.net.Uri |
||||
import android.os.Bundle |
||||
import android.util.Log |
||||
import android.view.KeyEvent |
||||
import android.view.MenuItem |
||||
import android.view.View |
||||
import android.widget.ImageView |
||||
import androidx.appcompat.app.ActionBarDrawerToggle |
||||
import androidx.appcompat.app.AlertDialog |
||||
import androidx.core.content.ContextCompat |
||||
import androidx.core.content.pm.ShortcutManagerCompat |
||||
import androidx.emoji.text.EmojiCompat |
||||
import androidx.emoji.text.EmojiCompat.InitCallback |
||||
import androidx.lifecycle.Lifecycle |
||||
import androidx.preference.PreferenceManager |
||||
import androidx.viewpager2.widget.MarginPageTransformer |
||||
import com.bumptech.glide.Glide |
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton |
||||
import com.google.android.material.tabs.TabLayout |
||||
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener |
||||
import com.google.android.material.tabs.TabLayoutMediator |
||||
import com.google.android.material.tabs.TabLayoutMediator.TabConfigurationStrategy |
||||
import com.keylesspalace.tusky.appstore.* |
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity |
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity.Companion.canHandleMimeType |
||||
import com.keylesspalace.tusky.components.conversation.ConversationsRepository |
||||
import com.keylesspalace.tusky.components.scheduled.ScheduledTootActivity |
||||
import com.keylesspalace.tusky.components.search.SearchActivity |
||||
import com.keylesspalace.tusky.db.AccountEntity |
||||
import com.keylesspalace.tusky.entity.Account |
||||
import com.keylesspalace.tusky.fragment.SFragment |
||||
import com.keylesspalace.tusky.interfaces.AccountSelectionListener |
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity |
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment |
||||
import com.keylesspalace.tusky.pager.MainPagerAdapter |
||||
import com.keylesspalace.tusky.util.* |
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial |
||||
import com.mikepenz.materialdrawer.iconics.iconicsIcon |
||||
import com.mikepenz.materialdrawer.model.* |
||||
import com.mikepenz.materialdrawer.model.interfaces.* |
||||
import com.mikepenz.materialdrawer.util.* |
||||
import com.mikepenz.materialdrawer.widget.AccountHeaderView |
||||
import com.uber.autodispose.android.lifecycle.autoDispose |
||||
import dagger.android.DispatchingAndroidInjector |
||||
import dagger.android.HasAndroidInjector |
||||
import io.reactivex.android.schedulers.AndroidSchedulers |
||||
import kotlinx.android.synthetic.main.activity_main.* |
||||
import javax.inject.Inject |
||||
|
||||
class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector { |
||||
@Inject |
||||
lateinit var androidInjector: DispatchingAndroidInjector<Any> |
||||
|
||||
@Inject |
||||
lateinit var eventHub: EventHub |
||||
|
||||
@Inject |
||||
lateinit var cacheUpdater: CacheUpdater |
||||
|
||||
@Inject |
||||
lateinit var conversationRepository: ConversationsRepository |
||||
|
||||
private lateinit var header: AccountHeaderView |
||||
private lateinit var drawerToggle: ActionBarDrawerToggle |
||||
|
||||
private var notificationTabPosition = 0 |
||||
|
||||
private var adapter: MainPagerAdapter? = null |
||||
|
||||
private val emojiInitCallback = object : InitCallback() { |
||||
override fun onInitialized() { |
||||
if (!isDestroyed) { |
||||
updateProfiles() |
||||
} |
||||
} |
||||
} |
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) { |
||||
super.onCreate(savedInstanceState) |
||||
if (accountManager.activeAccount == null) { |
||||
// will be redirected to LoginActivity by BaseActivity |
||||
return |
||||
} |
||||
var showNotificationTab = false |
||||
if (intent != null) { |
||||
/** there are two possibilities the accountId can be passed to MainActivity: |
||||
* - from our code as long 'account_id' |
||||
* - from share shortcuts as String 'android.intent.extra.shortcut.ID' |
||||
*/ |
||||
var accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1) |
||||
if (accountId == -1L) { |
||||
val accountIdString = intent.getStringExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID) |
||||
if (accountIdString != null) { |
||||
accountId = accountIdString.toLong() |
||||
} |
||||
} |
||||
val accountRequested = accountId != -1L |
||||
if (accountRequested) { |
||||
val account = accountManager.activeAccount |
||||
if (account == null || accountId != account.id) { |
||||
accountManager.setActiveAccount(accountId) |
||||
} |
||||
} |
||||
if (canHandleMimeType(intent.type)) { |
||||
// Sharing to Tusky from an external app |
||||
if (accountRequested) { |
||||
// The correct account is already active |
||||
forwardShare(intent) |
||||
} else { |
||||
// No account was provided, show the chooser |
||||
showAccountChooserDialog(getString(R.string.action_share_as), true, object : AccountSelectionListener { |
||||
override fun onAccountSelected(account: AccountEntity) { |
||||
val requestedId = account.id |
||||
val activeAccount = accountManager.activeAccount |
||||
if (activeAccount != null && requestedId == activeAccount.id) { |
||||
// The correct account is already active |
||||
forwardShare(intent) |
||||
} else { |
||||
// A different account was requested, restart the activity |
||||
intent.putExtra(NotificationHelper.ACCOUNT_ID, requestedId) |
||||
changeAccount(requestedId, intent) |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
} else if (accountRequested) { |
||||
// user clicked a notification, show notification tab and switch user if necessary |
||||
showNotificationTab = true |
||||
} |
||||
} |
||||
window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own |
||||
setContentView(R.layout.activity_main) |
||||
composeButton.setOnClickListener { |
||||
val composeIntent = Intent(applicationContext, ComposeActivity::class.java) |
||||
startActivity(composeIntent) |
||||
} |
||||
setupDrawer(savedInstanceState) |
||||
|
||||
/* Fetch user info while we're doing other things. This has to be done after setting up the |
||||
* drawer, though, because its callback touches the header in the drawer. */ |
||||
fetchUserInfo() |
||||
|
||||
setupTabs(showNotificationTab) |
||||
|
||||
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin) |
||||
viewPager.setPageTransformer(MarginPageTransformer(pageMargin)) |
||||
tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener { |
||||
override fun onTabSelected(tab: TabLayout.Tab) { |
||||
if (tab.position == notificationTabPosition) { |
||||
NotificationHelper.clearNotificationsForActiveAccount(this@MainActivity, accountManager) |
||||
} |
||||
} |
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) {} |
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) { |
||||
val fragment = adapter?.getFragment(tab.position) |
||||
if (fragment is ReselectableFragment) { |
||||
(fragment as ReselectableFragment).onReselect() |
||||
} |
||||
} |
||||
}) |
||||
|
||||
// Setup push notifications |
||||
if (NotificationHelper.areNotificationsEnabled(this, accountManager)) { |
||||
NotificationHelper.enablePullNotifications() |
||||
} else { |
||||
NotificationHelper.disablePullNotifications() |
||||
} |
||||
eventHub.events |
||||
.observeOn(AndroidSchedulers.mainThread()) |
||||
.autoDispose(this, Lifecycle.Event.ON_DESTROY) |
||||
.subscribe { event: Event? -> |
||||
when (event) { |
||||
is ProfileEditedEvent -> onFetchUserInfoSuccess(event.newProfileData) |
||||
is MainTabsChangedEvent -> setupTabs(false) |
||||
} |
||||
} |
||||
|
||||
// Flush old media that was cached for sharing |
||||
deleteStaleCachedMedia(applicationContext.getExternalFilesDir("Tusky")) |
||||
} |
||||
|
||||
override fun onResume() { |
||||
super.onResume() |
||||
NotificationHelper.clearNotificationsForActiveAccount(this, accountManager) |
||||
} |
||||
|
||||
override fun onBackPressed() { |
||||
when { |
||||
mainDrawerLayout.isOpen -> { |
||||
mainDrawerLayout.close() |
||||
} |
||||
viewPager.currentItem != 0 -> { |
||||
viewPager.currentItem = 0 |
||||
} |
||||
else -> { |
||||
super.onBackPressed() |
||||
} |
||||
} |
||||
} |
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { |
||||
when (keyCode) { |
||||
KeyEvent.KEYCODE_MENU -> { |
||||
if (mainDrawerLayout.isOpen) { |
||||
mainDrawerLayout.close() |
||||
} else { |
||||
mainDrawerLayout.open() |
||||
} |
||||
return true |
||||
} |
||||
KeyEvent.KEYCODE_SEARCH -> { |
||||
startActivityWithSlideInAnimation(SearchActivity.getIntent(this)) |
||||
return true |
||||
} |
||||
} |
||||
if (event.isCtrlPressed || event.isShiftPressed) { |
||||
// FIXME: blackberry keyONE raises SHIFT key event even CTRL IS PRESSED |
||||
when (keyCode) { |
||||
KeyEvent.KEYCODE_N -> { |
||||
|
||||
// open compose activity by pressing SHIFT + N (or CTRL + N) |
||||
val composeIntent = Intent(applicationContext, ComposeActivity::class.java) |
||||
startActivity(composeIntent) |
||||
return true |
||||
} |
||||
} |
||||
} |
||||
return super.onKeyDown(keyCode, event) |
||||
} |
||||
|
||||
public override fun onPostCreate(savedInstanceState: Bundle?) { |
||||
super.onPostCreate(savedInstanceState) |
||||
|
||||
drawerToggle.syncState() |
||||
|
||||
if (intent != null) { |
||||
val statusUrl = intent.getStringExtra(STATUS_URL) |
||||
if (statusUrl != null) { |
||||
viewUrl(statusUrl, PostLookupFallbackBehavior.DISPLAY_ERROR) |
||||
} |
||||
} |
||||
} |
||||
|
||||
override fun onDestroy() { |
||||
super.onDestroy() |
||||
EmojiCompat.get().unregisterInitCallback(emojiInitCallback) |
||||
} |
||||
|
||||
private fun forwardShare(intent: Intent) { |
||||
val composeIntent = Intent(this, ComposeActivity::class.java) |
||||
composeIntent.action = intent.action |
||||
composeIntent.type = intent.type |
||||
composeIntent.putExtras(intent) |
||||
composeIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK |
||||
startActivity(composeIntent) |
||||
finish() |
||||
} |
||||
|
||||
private fun setupDrawer(savedInstanceState: Bundle?) { |
||||
|
||||
drawerToggle = ActionBarDrawerToggle(this, mainDrawerLayout, mainToolbar, com.mikepenz.materialdrawer.R.string.material_drawer_open, com.mikepenz.materialdrawer.R.string.material_drawer_close) |
||||
|
||||
header = AccountHeaderView(this).apply { |
||||
headerBackgroundScaleType = ImageView.ScaleType.CENTER_CROP |
||||
currentHiddenInList = true |
||||
onAccountHeaderListener = { _: View?, profile: IProfile, current: Boolean -> handleProfileClick(profile, current) } |
||||
addProfile(ProfileSettingDrawerItem().apply { |
||||
identifier = DRAWER_ITEM_ADD_ACCOUNT |
||||
nameRes = R.string.add_account_name |
||||
descriptionRes = R.string.add_account_description |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_add |
||||
}, 0) |
||||
attachToSliderView(mainDrawer) |
||||
dividerBelowHeader = false |
||||
closeDrawerOnProfileListClick = true |
||||
} |
||||
|
||||
header.accountHeaderBackground.setColorFilter(ContextCompat.getColor(this, R.color.headerBackgroundFilter)) |
||||
header.accountHeaderBackground.setBackgroundColor(ThemeUtils.getColor(this, R.attr.colorBackgroundAccent)) |
||||
val animateAvatars = PreferenceManager.getDefaultSharedPreferences(this) |
||||
.getBoolean("animateGifAvatars", false) |
||||
|
||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() { |
||||
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) { |
||||
if (animateAvatars) { |
||||
Glide.with(imageView.context) |
||||
.load(uri) |
||||
.placeholder(placeholder) |
||||
.into(imageView) |
||||
} else { |
||||
Glide.with(imageView.context) |
||||
.asBitmap() |
||||
.load(uri) |
||||
.placeholder(placeholder) |
||||
.into(imageView) |
||||
} |
||||
} |
||||
|
||||
override fun cancel(imageView: ImageView) { |
||||
Glide.with(imageView.context).clear(imageView) |
||||
} |
||||
|
||||
override fun placeholder(ctx: Context, tag: String?): Drawable { |
||||
if (tag == DrawerImageLoader.Tags.PROFILE.name || tag == DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name) { |
||||
return ctx.getDrawable(R.drawable.avatar_default)!! |
||||
} |
||||
|
||||
return super.placeholder(ctx, tag) |
||||
} |
||||
}) |
||||
|
||||
mainDrawer.apply { |
||||
tintStatusBar = true |
||||
addItems( |
||||
primaryDrawerItem { |
||||
nameRes = R.string.action_edit_profile |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_person |
||||
onClick = { |
||||
val intent = Intent(context, EditProfileActivity::class.java) |
||||
startActivityWithSlideInAnimation(intent) |
||||
} |
||||
}, |
||||
primaryDrawerItem { |
||||
nameRes = R.string.action_view_favourites |
||||
isSelectable = false |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_star |
||||
onClick = { |
||||
val intent = StatusListActivity.newFavouritesIntent(context) |
||||
startActivityWithSlideInAnimation(intent) |
||||
} |
||||
}, |
||||
primaryDrawerItem { |
||||
nameRes = R.string.action_view_bookmarks |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_bookmark |
||||
onClick = { |
||||
val intent = StatusListActivity.newBookmarksIntent(context) |
||||
startActivityWithSlideInAnimation(intent) |
||||
} |
||||
}, |
||||
primaryDrawerItem { |
||||
nameRes = R.string.action_lists |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_list |
||||
onClick = { |
||||
startActivityWithSlideInAnimation(ListsActivity.newIntent(context)) |
||||
} |
||||
}, |
||||
primaryDrawerItem { |
||||
nameRes = R.string.action_search |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_search |
||||
onClick = { |
||||
startActivityWithSlideInAnimation(SearchActivity.getIntent(context)) |
||||
} |
||||
}, |
||||
primaryDrawerItem { |
||||
nameRes = R.string.action_access_saved_toot |
||||
iconRes = R.drawable.ic_notebook |
||||
onClick = { |
||||
val intent = Intent(context, SavedTootActivity::class.java) |
||||
startActivityWithSlideInAnimation(intent) |
||||
} |
||||
}, |
||||
primaryDrawerItem { |
||||
nameRes = R.string.action_access_scheduled_toot |
||||
iconRes = R.drawable.ic_access_time |
||||
onClick = { |
||||
startActivityWithSlideInAnimation(ScheduledTootActivity.newIntent(context)) |
||||
} |
||||
}, |
||||
DividerDrawerItem(), |
||||
secondaryDrawerItem { |
||||
nameRes = R.string.action_view_account_preferences |
||||
iconRes = R.drawable.ic_account_settings |
||||
onClick = { |
||||
val intent = PreferencesActivity.newIntent(context, PreferencesActivity.ACCOUNT_PREFERENCES) |
||||
startActivityWithSlideInAnimation(intent) |
||||
} |
||||
}, |
||||
secondaryDrawerItem { |
||||
nameRes = R.string.action_view_preferences |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_settings |
||||
onClick = { |
||||
val intent = PreferencesActivity.newIntent(context, PreferencesActivity.GENERAL_PREFERENCES) |
||||
startActivityWithSlideInAnimation(intent) |
||||
} |
||||
}, |
||||
secondaryDrawerItem { |
||||
nameRes = R.string.about_title_activity |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_info |
||||
onClick = { |
||||
val intent = Intent(context, AboutActivity::class.java) |
||||
startActivityWithSlideInAnimation(intent) |
||||
} |
||||
}, |
||||
secondaryDrawerItem { |
||||
nameRes = R.string.action_logout |
||||
iconRes = R.drawable.ic_logout |
||||
onClick = ::logout |
||||
} |
||||
) |
||||
setSavedInstance(savedInstanceState) |
||||
} |
||||
|
||||
if (BuildConfig.DEBUG) { |
||||
mainDrawer.addItems( |
||||
secondaryDrawerItem { |
||||
nameText = "debug" |
||||
isEnabled = false |
||||
textColor = ColorStateList.valueOf(Color.GREEN) |
||||
} |
||||
) |
||||
} |
||||
EmojiCompat.get().registerInitCallback(emojiInitCallback) |
||||
} |
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) { |
||||
super.onConfigurationChanged(newConfig) |
||||
drawerToggle.onConfigurationChanged(newConfig) |
||||
} |
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean { |
||||
if (drawerToggle.onOptionsItemSelected(item)) { |
||||
return true |
||||
} |
||||
return super.onOptionsItemSelected(item) |
||||
} |
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) { |
||||
super.onSaveInstanceState(mainDrawer.saveInstanceState(outState)) |
||||
} |
||||
|
||||
private fun setupTabs(selectNotificationTab: Boolean) { |
||||
val tabs = accountManager.activeAccount!!.tabPreferences |
||||
adapter = MainPagerAdapter(tabs, this) |
||||
viewPager.adapter = adapter |
||||
TabLayoutMediator(tabLayout, viewPager, TabConfigurationStrategy { _: TabLayout.Tab?, _: Int -> }).attach() |
||||
tabLayout.removeAllTabs() |
||||
for (i in tabs.indices) { |
||||
val tab = tabLayout.newTab() |
||||
.setIcon(tabs[i].icon) |
||||
if (tabs[i].id == LIST) { |
||||
tab.contentDescription = tabs[i].arguments[1] |
||||
} else { |
||||
tab.setContentDescription(tabs[i].text) |
||||
} |
||||
tabLayout.addTab(tab) |
||||
if (tabs[i].id == NOTIFICATIONS) { |
||||
notificationTabPosition = i |
||||
if (selectNotificationTab) { |
||||
tab.select() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun handleProfileClick(profile: IProfile, current: Boolean): Boolean { |
||||
val activeAccount = accountManager.activeAccount |
||||
|
||||
//open profile when active image was clicked |
||||
if (current && activeAccount != null) { |
||||
val intent = AccountActivity.getIntent(this, activeAccount.accountId) |
||||
startActivityWithSlideInAnimation(intent) |
||||
return false |
||||
} |
||||
//open LoginActivity to add new account |
||||
if (profile.identifier == DRAWER_ITEM_ADD_ACCOUNT) { |
||||
startActivityWithSlideInAnimation(LoginActivity.getIntent(this, true)) |
||||
return false |
||||
} |
||||
//change Account |
||||
changeAccount(profile.identifier, null) |
||||
return false |
||||
} |
||||
|
||||
private fun changeAccount(newSelectedId: Long, forward: Intent?) { |
||||
cacheUpdater.stop() |
||||
SFragment.flushFilters() |
||||
accountManager.setActiveAccount(newSelectedId) |
||||
val intent = Intent(this, MainActivity::class.java) |
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK |
||||
if (forward != null) { |
||||
intent.type = forward.type |
||||
intent.action = forward.action |
||||
intent.putExtras(forward) |
||||
} |
||||
startActivity(intent) |
||||
finishWithoutSlideOutAnimation() |
||||
overridePendingTransition(R.anim.explode, R.anim.explode) |
||||
} |
||||
|
||||
private fun logout() { |
||||
accountManager.activeAccount?.let { activeAccount -> |
||||
AlertDialog.Builder(this) |
||||
.setTitle(R.string.action_logout) |
||||
.setMessage(getString(R.string.action_logout_confirm, activeAccount.fullName)) |
||||
.setPositiveButton(android.R.string.yes) { _: DialogInterface?, _: Int -> |
||||
NotificationHelper.deleteNotificationChannelsForAccount(activeAccount, this@MainActivity) |
||||
cacheUpdater.clearForUser(activeAccount.id) |
||||
conversationRepository.deleteCacheForAccount(activeAccount.id) |
||||
removeShortcut(this, activeAccount) |
||||
val newAccount = accountManager.logActiveAccountOut() |
||||
if (!NotificationHelper.areNotificationsEnabled(this@MainActivity, accountManager)) { |
||||
NotificationHelper.disablePullNotifications() |
||||
} |
||||
val intent: Intent |
||||
intent = if (newAccount == null) { |
||||
LoginActivity.getIntent(this@MainActivity, false) |
||||
} else { |
||||
Intent(this@MainActivity, MainActivity::class.java) |
||||
} |
||||
startActivity(intent) |
||||
finishWithoutSlideOutAnimation() |
||||
} |
||||
.setNegativeButton(android.R.string.no, null) |
||||
.show() |
||||
} |
||||
} |
||||
|
||||
private fun fetchUserInfo() { |
||||
mastodonApi.accountVerifyCredentials() |
||||
.observeOn(AndroidSchedulers.mainThread()) |
||||
.autoDispose(this, Lifecycle.Event.ON_DESTROY) |
||||
.subscribe( |
||||
{ userInfo -> |
||||
onFetchUserInfoSuccess(userInfo) |
||||
}, |
||||
{ throwable -> |
||||
Log.e(TAG, "Failed to fetch user info. " + throwable.message) |
||||
} |
||||
) |
||||
} |
||||
|
||||
private fun onFetchUserInfoSuccess(me: Account) { |
||||
Glide.with(this) |
||||
.asBitmap() |
||||
.load(me.header) |
||||
.into(header.accountHeaderBackground) |
||||
|
||||
accountManager.updateActiveAccount(me) |
||||
NotificationHelper.createNotificationChannelsForAccount(accountManager.activeAccount!!, this) |
||||
|
||||
// Show follow requests in the menu, if this is a locked account. |
||||
if (me.locked && mainDrawer.getDrawerItem(DRAWER_ITEM_FOLLOW_REQUESTS) == null) { |
||||
val followRequestsItem = primaryDrawerItem { |
||||
identifier = DRAWER_ITEM_FOLLOW_REQUESTS |
||||
nameRes = R.string.action_view_follow_requests |
||||
iconicsIcon = GoogleMaterial.Icon.gmd_person_add |
||||
onClick = { |
||||
val intent = Intent(this@MainActivity, AccountListActivity::class.java) |
||||
intent.putExtra("type", AccountListActivity.Type.FOLLOW_REQUESTS) |
||||
startActivityWithSlideInAnimation(intent) |
||||
} |
||||
} |
||||
mainDrawer.addItemAtPosition(4, followRequestsItem) |
||||
} else if (!me.locked) { |
||||
mainDrawer.removeItems(DRAWER_ITEM_FOLLOW_REQUESTS) |
||||
} |
||||
updateProfiles() |
||||
updateShortcut(this, accountManager.activeAccount!!) |
||||
} |
||||
|
||||
private fun updateProfiles() { |
||||
val profiles: MutableList<IProfile> = accountManager.getAllAccountsOrderedByActive().map { acc -> |
||||
val emojifiedName = EmojiCompat.get().process(CustomEmojiHelper.emojifyString(acc.displayName, acc.emojis, header)) |
||||
|
||||
ProfileDrawerItem().apply { |
||||
isSelected = acc.isActive |
||||
nameText = emojifiedName |
||||
iconUrl = acc.profilePictureUrl |
||||
isNameShown = true |
||||
identifier = acc.id |
||||
descriptionText = acc.fullName |
||||
} |
||||
}.toMutableList() |
||||
|
||||
// reuse the already existing "add account" item |
||||
for (profile in header.profiles.orEmpty()) { |
||||
if (profile.identifier == DRAWER_ITEM_ADD_ACCOUNT) { |
||||
profiles.add(profile) |
||||
break |
||||
} |
||||
} |
||||
header.clear() |
||||
header.profiles = profiles |
||||
header.setActiveProfile(accountManager.activeAccount!!.id) |
||||
} |
||||
|
||||
override fun getActionButton(): FloatingActionButton? = composeButton |
||||
|
||||
override fun androidInjector() = androidInjector |
||||
|
||||
companion object { |
||||
private const val TAG = "MainActivity" // logging tag |
||||
private const val DRAWER_ITEM_ADD_ACCOUNT: Long = -13 |
||||
private const val DRAWER_ITEM_FOLLOW_REQUESTS: Long = 10 |
||||
const val STATUS_URL = "statusUrl" |
||||
} |
||||
} |
||||
|
||||
private inline fun primaryDrawerItem(block: PrimaryDrawerItem.() -> Unit): PrimaryDrawerItem { |
||||
return PrimaryDrawerItem() |
||||
.apply { |
||||
isSelectable = false |
||||
isIconTinted = true |
||||
} |
||||
.apply(block) |
||||
} |
||||
|
||||
private inline fun secondaryDrawerItem(block: SecondaryDrawerItem.() -> Unit): SecondaryDrawerItem { |
||||
return SecondaryDrawerItem() |
||||
.apply { |
||||
isSelectable = false |
||||
isIconTinted = true |
||||
} |
||||
.apply(block) |
||||
} |
||||
|
||||
private var AbstractDrawerItem<*, *>.onClick: () -> Unit |
||||
get() = throw UnsupportedOperationException() |
||||
set(value) { |
||||
onDrawerItemClickListener = { _, _, _ -> |
||||
value() |
||||
true |
||||
} |
||||
} |
||||
Loading…
Reference in new issue