From 2cf1e366b862b1903417058beb6f3d833671fef4 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Wed, 15 Apr 2020 18:57:53 +0200 Subject: [PATCH] convert MainActivity to Kotlin and upgrade MaterialDrawer to version 8 (#1748) * 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 contrast --- app/build.gradle | 8 +- .../keylesspalace/tusky/AccountActivity.kt | 2 +- .../tusky/EditProfileActivity.kt | 18 +- .../com/keylesspalace/tusky/ListsActivity.kt | 8 +- .../com/keylesspalace/tusky/MainActivity.java | 663 ------------------ .../com/keylesspalace/tusky/MainActivity.kt | 650 +++++++++++++++++ .../tusky/adapter/NotificationsAdapter.java | 5 +- .../tusky/adapter/StatusBaseViewHolder.java | 4 +- .../components/compose/ComposeActivity.kt | 15 +- .../components/compose/view/TootButton.kt | 6 +- .../tusky/components/search/SearchActivity.kt | 1 - .../preference/AccountPreferencesFragment.kt | 10 +- .../preference/PreferencesFragment.kt | 14 +- .../tusky/util/CustomEmojiHelper.java | 5 +- .../tusky/view/BezelImageView.java | 2 - app/src/main/res/layout/activity_main.xml | 104 +-- .../res/layout/material_drawer_header.xml | 115 ++- .../main/res/values-night/theme_colors.xml | 2 + app/src/main/res/values/colors.xml | 4 +- app/src/main/res/values/styles.xml | 29 +- app/src/main/res/values/theme_colors.xml | 2 + .../com/keylesspalace/tusky/FilterTest.kt | 2 +- 22 files changed, 878 insertions(+), 791 deletions(-) delete mode 100644 app/src/main/java/com/keylesspalace/tusky/MainActivity.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/MainActivity.kt diff --git a/app/build.gradle b/app/build.gradle index 8e3931eb2..f6349f723 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -101,6 +101,7 @@ ext.retrofitVersion = '2.8.1' ext.okhttpVersion = '4.4.0' ext.glideVersion = '4.11.0' ext.daggerVersion = '2.27' +ext.materialdrawerVersion = '8.0.1' // if libraries are changed here, they should also be changed in LicenseActivity dependencies { @@ -159,10 +160,9 @@ dependencies { implementation "com.github.chrisbanes:PhotoView:2.3.0" - implementation("com.mikepenz:materialdrawer:6.1.2@aar") { - transitive = true - } - implementation "com.mikepenz:google-material-typeface:3.0.1.3.original@aar" + implementation "com.mikepenz:materialdrawer:$materialdrawerVersion" + implementation "com.mikepenz:materialdrawer-iconics:$materialdrawerVersion" + implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar' implementation "com.theartofdev.edmodo:android-image-cropper:2.8.0" diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt index 485f6a8a6..97d87b358 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt @@ -138,7 +138,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI */ private fun loadResources() { toolbarColor = ThemeUtils.getColor(this, R.attr.colorSurface) - statusBarColorTransparent = ContextCompat.getColor(this, R.color.header_background_filter) + statusBarColorTransparent = ContextCompat.getColor(this, R.color.transparent_statusbar_background) statusBarColorOpaque = ThemeUtils.getColor(this, R.attr.colorPrimaryDark) avatarSize = resources.getDimension(R.dimen.account_activity_avatar_size) titleVisibleHeight = resources.getDimensionPixelSize(R.dimen.account_activity_scroll_title_visible_height) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 58da33df9..22d25f098 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -17,26 +17,26 @@ package com.keylesspalace.tusky import android.Manifest import android.app.Activity -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer import android.content.Intent import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.Color import android.net.Uri import android.os.Bundle -import com.google.android.material.snackbar.Snackbar -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat -import androidx.recyclerview.widget.LinearLayoutManager import android.view.Menu import android.view.MenuItem import android.view.View import android.widget.ImageView import androidx.activity.viewModels +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.bitmap.FitCenter import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.adapter.AccountFieldEditAdapter import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.ViewModelFactory @@ -44,8 +44,10 @@ import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Instance import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.viewmodel.EditProfileViewModel -import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.iconics.utils.colorInt +import com.mikepenz.iconics.utils.sizeDp import com.theartofdev.edmodo.cropper.CropImage import kotlinx.android.synthetic.main.activity_edit_profile.* import kotlinx.android.synthetic.main.toolbar_basic.* @@ -103,7 +105,7 @@ class EditProfileActivity : BaseActivity(), Injectable { fieldList.layoutManager = LinearLayoutManager(this) fieldList.adapter = accountFieldEditAdapter - val plusDrawable = IconicsDrawable(this, GoogleMaterial.Icon.gmd_add).sizeDp(12).color(Color.WHITE) + val plusDrawable = IconicsDrawable(this, GoogleMaterial.Icon.gmd_add).apply { sizeDp = 12; colorInt = Color.WHITE } addFieldButton.setCompoundDrawablesRelativeWithIntrinsicBounds(plusDrawable, null, null, null) diff --git a/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt index 539dcfe01..163d91661 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt @@ -39,8 +39,12 @@ import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.viewmodel.ListsViewModel import com.keylesspalace.tusky.viewmodel.ListsViewModel.Event.* import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.* -import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.iconics.utils.color +import com.mikepenz.iconics.utils.colorInt +import com.mikepenz.iconics.utils.sizeDp +import com.mikepenz.iconics.utils.toIconicsColor import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from import com.uber.autodispose.autoDispose import dagger.android.DispatchingAndroidInjector @@ -235,7 +239,7 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector { .apply { val context = nameTextView.context val iconColor = ThemeUtils.getColor(context, android.R.attr.textColorTertiary) - val icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_list).sizeDp(20).color(iconColor) + val icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_list).apply { sizeDp = 20; colorInt = iconColor } nameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null) } diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java deleted file mode 100644 index 452cdafcf..000000000 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ /dev/null @@ -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 . */ - -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 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 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 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 allAccounts = accountManager.getAllAccountsOrderedByActive(); - - List 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 androidInjector() { - return androidInjector; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt new file mode 100644 index 000000000..d1da667ea --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt @@ -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 . */ + +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 + + @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 = 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 + } + } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index ba8870b0b..3ddbbe559 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -54,13 +54,14 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions; import com.keylesspalace.tusky.util.TimestampUtils; import com.keylesspalace.tusky.viewdata.NotificationViewData; import com.keylesspalace.tusky.viewdata.StatusViewData; -import com.mikepenz.iconics.utils.Utils; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; +import at.connyduck.sparkbutton.helpers.Utils; + public class NotificationsAdapter extends RecyclerView.Adapter { public interface AdapterDataSource { @@ -140,7 +141,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter { view.setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, - Utils.convertDpToPx(parent.getContext(), 24) + Utils.dpToPx(parent.getContext(), 24) ) ); return new RecyclerView.ViewHolder(view) { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index ae0dfdbb5..82d1dbabb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -49,7 +49,6 @@ import com.keylesspalace.tusky.viewdata.PollOptionViewData; import com.keylesspalace.tusky.viewdata.PollViewData; import com.keylesspalace.tusky.viewdata.PollViewDataKt; import com.keylesspalace.tusky.viewdata.StatusViewData; -import com.mikepenz.iconics.utils.Utils; import java.text.NumberFormat; import java.text.SimpleDateFormat; @@ -59,6 +58,7 @@ import java.util.Locale; import java.util.Objects; import at.connyduck.sparkbutton.SparkButton; +import at.connyduck.sparkbutton.helpers.Utils; import kotlin.collections.CollectionsKt; import static com.keylesspalace.tusky.viewdata.PollViewDataKt.buildDescription; @@ -294,7 +294,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { avatarRadius = avatarRadius48dp; } else { - int padding = Utils.convertDpToPx(avatar.getContext(), 12); + int padding = Utils.dpToPx(avatar.getContext(), 12); avatar.setPaddingRelative(0, 0, padding, padding); avatarInset.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt index 87bc3e440..dfb812de5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt @@ -65,7 +65,6 @@ import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener import com.keylesspalace.tusky.components.compose.dialog.makeCaptionDialog import com.keylesspalace.tusky.components.compose.dialog.showAddPollDialog import com.keylesspalace.tusky.components.compose.view.ComposeOptionsListener -import com.keylesspalace.tusky.components.compose.view.ComposeScheduleView import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.ViewModelFactory @@ -74,8 +73,10 @@ import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.NewPoll import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.util.* -import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.iconics.utils.colorInt +import com.mikepenz.iconics.utils.sizeDp import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.activity_compose.* import java.io.File @@ -231,7 +232,7 @@ class ComposeActivity : BaseActivity(), if (replyingStatusAuthor != null) { composeReplyView.show() composeReplyView.text = getString(R.string.replying_to, replyingStatusAuthor) - val arrowDownIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_down).sizeDp(12) + val arrowDownIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_down).apply { sizeDp = 12 } ThemeUtils.setDrawableTint(this, arrowDownIcon, android.R.attr.textColorTertiary) composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null) @@ -244,7 +245,7 @@ class ComposeActivity : BaseActivity(), composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null) } else { composeReplyContentView.show() - val arrowUpIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_up).sizeDp(12) + val arrowUpIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_up).apply { sizeDp = 12 } ThemeUtils.setDrawableTint(this, arrowUpIcon, android.R.attr.textColorTertiary) composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowUpIcon, null) @@ -364,13 +365,13 @@ class ComposeActivity : BaseActivity(), val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary) - val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).color(textColor).sizeDp(18) + val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).apply { colorInt = textColor; sizeDp = 18 } actionPhotoTake.setCompoundDrawablesRelativeWithIntrinsicBounds(cameraIcon, null, null, null) - val imageIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_image).color(textColor).sizeDp(18) + val imageIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_image).apply { colorInt = textColor; sizeDp = 18 } actionPhotoPick.setCompoundDrawablesRelativeWithIntrinsicBounds(imageIcon, null, null, null) - val pollIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_poll).color(textColor).sizeDp(18) + val pollIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_poll).apply { colorInt = textColor; sizeDp = 18 } addPollTextActionTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(pollIcon, null, null, null) actionPhotoTake.setOnClickListener { initiateCameraApp() } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/TootButton.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/TootButton.kt index c641f345c..f7ba7ee69 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/TootButton.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/TootButton.kt @@ -21,8 +21,10 @@ import android.util.AttributeSet import com.google.android.material.button.MaterialButton import com.keylesspalace.tusky.R import com.keylesspalace.tusky.entity.Status -import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.iconics.utils.colorInt +import com.mikepenz.iconics.utils.sizeDp class TootButton @JvmOverloads constructor( @@ -59,7 +61,7 @@ class TootButton Status.Visibility.PRIVATE, Status.Visibility.DIRECT -> { setText(R.string.action_send) - IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).sizeDp(18).color(Color.WHITE) + IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).apply { sizeDp = 18; colorInt = Color.WHITE } } else -> { null diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt index db31e49fa..8b8d1ef45 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt @@ -121,7 +121,6 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector { override fun androidInjector() = androidInjector companion object { - @JvmStatic fun getIntent(context: Context) = Intent(context, SearchActivity::class.java) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt index 093f1b409..1698270fb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt @@ -35,8 +35,10 @@ import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.ThemeUtils -import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.iconics.utils.colorInt +import com.mikepenz.iconics.utils.sizeRes import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -72,8 +74,6 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), private lateinit var publicFiltersPreference: Preference private lateinit var threadFiltersPreference: Preference - private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) } - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.account_preferences) @@ -92,9 +92,9 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), publicFiltersPreference = requirePreference("publicFilters") threadFiltersPreference = requirePreference("threadFilters") - notificationPreference.icon = IconicsDrawable(notificationPreference.context, GoogleMaterial.Icon.gmd_notifications).sizePx(iconSize).color(ThemeUtils.getColor(notificationPreference.context, R.attr.iconColor)) + notificationPreference.icon = IconicsDrawable(notificationPreference.context, GoogleMaterial.Icon.gmd_notifications).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(notificationPreference.context, R.attr.iconColor) } mutedUsersPreference.icon = getTintedIcon(R.drawable.ic_mute_24dp) - blockedUsersPreference.icon = IconicsDrawable(blockedUsersPreference.context, GoogleMaterial.Icon.gmd_block).sizePx(iconSize).color(ThemeUtils.getColor(blockedUsersPreference.context, R.attr.iconColor)) + blockedUsersPreference.icon = IconicsDrawable(blockedUsersPreference.context, GoogleMaterial.Icon.gmd_block).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(blockedUsersPreference.context, R.attr.iconColor) } mutedDomainsPreference.icon = getTintedIcon(R.drawable.ic_mute_24dp) notificationPreference.onPreferenceClickListener = this diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt index d7550103c..825275a1a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt @@ -22,8 +22,10 @@ import com.keylesspalace.tusky.PreferencesActivity import com.keylesspalace.tusky.R import com.keylesspalace.tusky.util.ThemeUtils import com.keylesspalace.tusky.util.getNonNullString -import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.iconics.utils.colorInt +import com.mikepenz.iconics.utils.sizeRes fun PreferenceFragmentCompat.requirePreference(key: String): Preference { return findPreference(key)!! @@ -31,20 +33,18 @@ fun PreferenceFragmentCompat.requirePreference(key: String): Preference { class PreferencesFragment : PreferenceFragmentCompat() { - private val iconSize by lazy {resources.getDimensionPixelSize(R.dimen.preference_icon_size)} - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.preferences) val themePreference: Preference = requirePreference("appTheme") - themePreference.icon = IconicsDrawable(themePreference.context, GoogleMaterial.Icon.gmd_palette).sizePx(iconSize).color(ThemeUtils.getColor(themePreference.context, R.attr.iconColor)) + themePreference.icon = IconicsDrawable(themePreference.context, GoogleMaterial.Icon.gmd_palette).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } val emojiPreference: Preference = requirePreference("emojiCompat") - emojiPreference.icon = IconicsDrawable(emojiPreference.context, GoogleMaterial.Icon.gmd_sentiment_satisfied).sizePx(iconSize).color(ThemeUtils.getColor(emojiPreference.context, R.attr.iconColor)) + emojiPreference.icon = IconicsDrawable(emojiPreference.context, GoogleMaterial.Icon.gmd_sentiment_satisfied).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } val textSizePreference: Preference = requirePreference("statusTextSize") - textSizePreference.icon = IconicsDrawable(textSizePreference.context, GoogleMaterial.Icon.gmd_format_size).sizePx(iconSize).color(ThemeUtils.getColor(textSizePreference.context, R.attr.iconColor)) + textSizePreference.icon = IconicsDrawable(textSizePreference.context, GoogleMaterial.Icon.gmd_format_size).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } val timelineFilterPreferences: Preference = requirePreference("timelineFilterPreferences") timelineFilterPreferences.setOnPreferenceClickListener { @@ -67,7 +67,7 @@ class PreferencesFragment : PreferenceFragmentCompat() { } val languagePreference: Preference = requirePreference("language") - languagePreference.icon = IconicsDrawable(languagePreference.context, GoogleMaterial.Icon.gmd_translate).sizePx(iconSize).color(ThemeUtils.getColor(languagePreference.context, R.attr.iconColor)) + languagePreference.icon = IconicsDrawable(languagePreference.context, GoogleMaterial.Icon.gmd_translate).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } val botIndicatorPreference = requirePreference("showBotOverlay") diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java index 9152b61a9..a808b0f46 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java @@ -49,6 +49,7 @@ public class CustomEmojiHelper { * @param view a reference to the a view the emojis will be shown in (should be the TextView, but parents of the TextView are also acceptable) * @return the text with the shortcodes replaced by EmojiSpans */ + @NonNull public static Spanned emojifyText(@NonNull Spanned text, @Nullable List emojis, @NonNull final View view) { if (emojis != null && !emojis.isEmpty()) { @@ -74,6 +75,7 @@ public class CustomEmojiHelper { return text; } + @NonNull public static Spanned emojifyString(@NonNull String string, @Nullable List emojis, @NonNull final View ciew) { return emojifyText(new SpannedString(string), emojis, ciew); } @@ -81,7 +83,8 @@ public class CustomEmojiHelper { public static class EmojiSpan extends ReplacementSpan { - private @Nullable Drawable imageDrawable; + @Nullable + private Drawable imageDrawable; private WeakReference viewWeakReference; EmojiSpan(View view) { diff --git a/app/src/main/java/com/keylesspalace/tusky/view/BezelImageView.java b/app/src/main/java/com/keylesspalace/tusky/view/BezelImageView.java index af1ac1ccd..c31b37e73 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/BezelImageView.java +++ b/app/src/main/java/com/keylesspalace/tusky/view/BezelImageView.java @@ -21,7 +21,6 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; - /** * override BezelImageView from MaterialDrawer library to provide custom outline */ @@ -39,7 +38,6 @@ public class BezelImageView extends com.mikepenz.materialdrawer.view.BezelImageV super(context, attrs, defStyle); } - @Override protected void onSizeChanged(int w, int h, int old_w, int old_h) { setOutlineProvider(new CustomOutline(w, h)); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4ea46117f..168e41b71 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,57 +1,71 @@ - + android:fitsSystemWindows="true"> - - - - - - - - - - - + tools:context="com.keylesspalace.tusky.MainActivity"> - + + + + + + + + + + + + + + + + + + + android:layout_height="match_parent" + android:layout_gravity="start" + android:fitsSystemWindows="true" /> - + - diff --git a/app/src/main/res/layout/material_drawer_header.xml b/app/src/main/res/layout/material_drawer_header.xml index 070409ffd..2b6d3b88b 100644 --- a/app/src/main/res/layout/material_drawer_header.xml +++ b/app/src/main/res/layout/material_drawer_header.xml @@ -1,10 +1,12 @@ - - + + android:clickable="true" + tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> + app:layout_constraintTop_toBottomOf="@+id/material_drawer_statusbar_guideline" + app:materialDrawerMaskDrawable="@drawable/materialdrawer_shape_large" /> + + + + + + + + - + diff --git a/app/src/main/res/values-night/theme_colors.xml b/app/src/main/res/values-night/theme_colors.xml index f30787f65..3e4d032c6 100644 --- a/app/src/main/res/values-night/theme_colors.xml +++ b/app/src/main/res/values-night/theme_colors.xml @@ -19,6 +19,8 @@ @color/tusky_orange + @color/header_background_filter_dark + false \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index e7c298eb2..a9fe4fc9c 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -26,7 +26,9 @@ #8c2b90d9 #8f000000 - #44000000 + #44000000 + #66FFFFFF + #44000000 #121419 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 269115bc7..24a4352f2 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -67,14 +67,8 @@ @color/textColorDisabled - @color/colorBackground - @color/textColorSecondary - @color/iconColor - @color/textColorTertiary - @color/textColorTertiary - ?attr/dividerColor - @color/white - @color/white + @style/TuskyDrawerStyle + @style/TuskyDrawerHeaderStyle @style/TuskyDialog @style/TuskyButton.TextButton @@ -142,17 +136,26 @@ @color/tusky_grey_20 @color/tusky_grey_10 - - @color/black - @color/tusky_grey_40 + + + diff --git a/app/src/main/res/values/theme_colors.xml b/app/src/main/res/values/theme_colors.xml index df8dfc0dc..b0e770463 100644 --- a/app/src/main/res/values/theme_colors.xml +++ b/app/src/main/res/values/theme_colors.xml @@ -19,6 +19,8 @@ @color/tusky_orange_light + @color/header_background_filter_light + true \ No newline at end of file diff --git a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt index 12f7bab54..6b35d4a40 100644 --- a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt @@ -115,7 +115,7 @@ class FilterTest { activity.supportFragmentManager.beginTransaction() - .replace(R.id.activity_main, fragment, "fragment") + .replace(R.id.mainDrawerLayout, fragment, "fragment") .commit() fragment.reloadFilters(false)