Browse Source
Merge branch 'stamatiap/development' of https://github.com/raniapl/barinsta into stamatiap/development
renovate/org.robolectric-robolectric-4.x
Merge branch 'stamatiap/development' of https://github.com/raniapl/barinsta into stamatiap/development
renovate/org.robolectric-robolectric-4.x
131 changed files with 5100 additions and 3116 deletions
-
19.all-contributorsrc
-
17.github/workflows/github_nightly_release.yml
-
33.github/workflows/github_pre_release.yml
-
2.idea/compiler.xml
-
1.idea/gradle.xml
-
2.idea/misc.xml
-
1.idea/runConfigurations.xml
-
28README.md
-
61app/build.gradle
-
3app/src/main/AndroidManifest.xml
-
59app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
2app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java
-
40app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java
-
16app/src/main/java/awais/instagrabber/adapters/SliderCallbackAdapter.java
-
22app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java
-
77app/src/main/java/awais/instagrabber/adapters/viewholder/SliderPhotoViewHolder.java
-
117app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java
-
3app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemAnimatedMediaViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaShareViewHolder.java
-
9app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java
-
9app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemStoryShareViewHolder.java
-
4app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemXmaViewHolder.java
-
4app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java
-
17app/src/main/java/awais/instagrabber/customviews/ChatMessageLayout.java
-
165app/src/main/java/awais/instagrabber/customviews/FormattedNumberTextView.java
-
75app/src/main/java/awais/instagrabber/customviews/FragmentNavigatorWithDefaultAnimations.java
-
246app/src/main/java/awais/instagrabber/customviews/InsetsAnimationLinearLayout.java
-
33app/src/main/java/awais/instagrabber/customviews/InsetsNotifyingCoordinatorLayout.java
-
35app/src/main/java/awais/instagrabber/customviews/InsetsNotifyingLinearLayout.java
-
60app/src/main/java/awais/instagrabber/customviews/NavHostFragmentWithDefaultAnimations.java
-
29app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
-
7app/src/main/java/awais/instagrabber/customviews/Tooltip.java
-
10app/src/main/java/awais/instagrabber/customviews/VideoPlayerCallbackAdapter.java
-
441app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java
-
2app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableDraweeView.java
-
100app/src/main/java/awais/instagrabber/customviews/emoji/EmojiBottomSheetDialog.java
-
31app/src/main/java/awais/instagrabber/customviews/emoji/EmojiGridAdapter.java
-
163app/src/main/java/awais/instagrabber/customviews/emoji/EmojiPopupWindow.java
-
1app/src/main/java/awais/instagrabber/customviews/emoji/GoogleCompatEmojiDrawable.java
-
320app/src/main/java/awais/instagrabber/customviews/helpers/ChangeText.java
-
87app/src/main/java/awais/instagrabber/customviews/helpers/ControlFocusInsetsAnimationCallback.java
-
17app/src/main/java/awais/instagrabber/customviews/helpers/CustomHideBottomViewOnScrollBehavior.java
-
117app/src/main/java/awais/instagrabber/customviews/helpers/EmojiPickerInsetsAnimationCallback.java
-
19app/src/main/java/awais/instagrabber/customviews/helpers/GridSpacingItemDecoration.java
-
139app/src/main/java/awais/instagrabber/customviews/helpers/RootViewDeferringInsetsCallback.java
-
443app/src/main/java/awais/instagrabber/customviews/helpers/SimpleImeAnimationController.java
-
128app/src/main/java/awais/instagrabber/customviews/helpers/TranslateDeferringInsetsAnimationCallback.java
-
9app/src/main/java/awais/instagrabber/dialogs/ProfilePicDialogFragment.java
-
116app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java
-
10app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java
-
124app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
52app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
-
18app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
-
1144app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
-
19app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
-
14app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java
-
49app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
-
21app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java
-
18app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
-
481app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
65app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
-
86app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
-
18app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
-
1app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java
-
4app/src/main/java/awais/instagrabber/managers/ThreadManager.java
-
24app/src/main/java/awais/instagrabber/models/FeedStoryModel.java
-
70app/src/main/java/awais/instagrabber/repositories/responses/User.java
-
10app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemEmojiReaction.java
-
8app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemReactions.java
-
8app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java
-
15app/src/main/java/awais/instagrabber/utils/CombinedDrawable.java
-
32app/src/main/java/awais/instagrabber/utils/DMUtils.java
-
7app/src/main/java/awais/instagrabber/utils/NavigationExtensions.java
-
91app/src/main/java/awais/instagrabber/utils/NullSafePair.java
-
75app/src/main/java/awais/instagrabber/utils/NumberUtils.java
-
6app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
-
3app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
-
88app/src/main/java/awais/instagrabber/utils/Utils.java
-
49app/src/main/java/awais/instagrabber/utils/ViewUtils.java
-
8app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java
-
6app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java
-
36app/src/main/java/awais/instagrabber/webservices/GraphQLService.java
-
68app/src/main/java/awais/instagrabber/webservices/StoriesService.java
-
1app/src/main/java/awais/instagrabber/webservices/interceptors/IgErrorsInterceptor.java
-
11app/src/main/res/anim/slide_in_right.xml
-
13app/src/main/res/anim/slide_left.xml
-
11app/src/main/res/anim/slide_out_left.xml
-
13app/src/main/res/anim/slide_right.xml
-
10app/src/main/res/drawable/ic_bookmark.xml
-
10app/src/main/res/drawable/ic_round_bookmark_border_24.xml
-
10app/src/main/res/drawable/ic_round_edit_24.xml
-
2app/src/main/res/drawable/shape_oval_light.xml
-
9app/src/main/res/layout/activity_main.xml
-
587app/src/main/res/layout/dialog_post_view.xml
-
2app/src/main/res/layout/fragment_collection_posts.xml
-
563app/src/main/res/layout/fragment_direct_messages_thread.xml
-
3app/src/main/res/layout/fragment_discover.xml
@ -15,18 +15,19 @@ jobs: |
|||
uses: actions/checkout@v2 |
|||
|
|||
- name: set up JDK 1.8 |
|||
uses: actions/setup-java@v1 |
|||
uses: actions/setup-java@v2 |
|||
with: |
|||
java-version: 1.8 |
|||
|
|||
distribution: 'zulu' |
|||
java-version: '8' |
|||
|
|||
- name: Grant execute permission for gradlew |
|||
run: chmod +x gradlew |
|||
|
|||
- name: Build Github unsigned apk |
|||
run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre |
|||
run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre --project-prop split |
|||
|
|||
- name: Sign APK |
|||
uses: r0adkll/sign-android-release@v1 |
|||
uses: ammargitham/[email protected].1 |
|||
# ID used to access action output |
|||
id: sign_app |
|||
with: |
|||
@ -45,7 +46,8 @@ jobs: |
|||
uses: actions/upload-artifact@v2 |
|||
with: |
|||
name: barinsta_nightly_${{ steps.date.outputs.date }} |
|||
path: ${{steps.sign_app.outputs.signedReleaseFile}} |
|||
# path: ${{steps.sign_app.outputs.signedReleaseFile}} |
|||
path: app/build/outputs/apk/github/release/*-signed.apk |
|||
|
|||
# Send success notification |
|||
- name: Send success Telegram notification |
|||
@ -55,7 +57,8 @@ jobs: |
|||
to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }} |
|||
token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }} |
|||
message: "${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} completed successfully.\nhttps://github.com/${{github.repository}}/actions/runs/${{github.run_id}}" |
|||
document: ${{steps.sign_app.outputs.signedReleaseFile}} |
|||
# document: ${{steps.sign_app.outputs.signedReleaseFile}} |
|||
document: app/build/outputs/apk/github/release/*-signed.apk |
|||
|
|||
# Send failure notification |
|||
- name: Send failure Telegram notification |
|||
|
@ -14,20 +14,21 @@ jobs: |
|||
steps: |
|||
- name: Checkout |
|||
uses: actions/checkout@v2 |
|||
|
|||
|
|||
- name: set up JDK 1.8 |
|||
uses: actions/setup-java@v1 |
|||
uses: actions/setup-java@v2 |
|||
with: |
|||
java-version: 1.8 |
|||
|
|||
distribution: 'zulu' |
|||
java-version: '8' |
|||
|
|||
- name: Grant execute permission for gradlew |
|||
run: chmod +x gradlew |
|||
|
|||
|
|||
- name: Build Github unsigned pre-release apk |
|||
run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre |
|||
|
|||
run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre --project-prop split |
|||
|
|||
- name: Sign APK |
|||
uses: r0adkll/sign-android-release@v1 |
|||
uses: ammargitham/[email protected].1 |
|||
# ID used to access action output |
|||
id: sign_app |
|||
with: |
|||
@ -36,18 +37,19 @@ jobs: |
|||
alias: ${{ secrets.ALIAS }} |
|||
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} |
|||
keyPassword: ${{ secrets.KEY_PASSWORD }} |
|||
|
|||
|
|||
- name: Get current date and time |
|||
id: date |
|||
run: echo "::set-output name=date::$(date +'%Y%m%d_%H%M%S')" |
|||
|
|||
# Create artifact |
|||
|
|||
# Create artifact |
|||
- name: Create apk artifact |
|||
uses: actions/upload-artifact@v2 |
|||
with: |
|||
name: barinsta_pre-release_${{ steps.date.outputs.date }} |
|||
path: ${{steps.sign_app.outputs.signedReleaseFile}} |
|||
|
|||
# path: ${{steps.sign_app.outputs.signedReleaseFile}} |
|||
path: app/build/outputs/apk/github/release/*-signed.apk |
|||
|
|||
# Send success notification |
|||
- name: Send success Telegram notification |
|||
if: ${{ success() }} |
|||
@ -56,8 +58,9 @@ jobs: |
|||
to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }} |
|||
token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }} |
|||
message: "${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} completed successfully.\nURL: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}" |
|||
document: ${{steps.sign_app.outputs.signedReleaseFile}} |
|||
|
|||
# document: ${{steps.sign_app.outputs.signedReleaseFile}} |
|||
document: app/build/outputs/apk/github/release/*-signed.apk |
|||
|
|||
# Send failure notification |
|||
- name: Send failure Telegram notification |
|||
if: ${{ failure() }} |
|||
|
@ -1,6 +1,6 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="CompilerConfiguration"> |
|||
<bytecodeTargetLevel target="1.8" /> |
|||
<bytecodeTargetLevel target="11" /> |
|||
</component> |
|||
</project> |
@ -0,0 +1,165 @@ |
|||
package awais.instagrabber.customviews; |
|||
|
|||
import android.content.Context; |
|||
import android.util.AttributeSet; |
|||
import android.util.Log; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.appcompat.widget.AppCompatTextView; |
|||
import androidx.transition.ChangeBounds; |
|||
import androidx.transition.Transition; |
|||
import androidx.transition.TransitionManager; |
|||
import androidx.transition.TransitionSet; |
|||
|
|||
import java.time.Duration; |
|||
|
|||
import awais.instagrabber.customviews.helpers.ChangeText; |
|||
import awais.instagrabber.utils.NumberUtils; |
|||
|
|||
public class FormattedNumberTextView extends AppCompatTextView { |
|||
private static final String TAG = FormattedNumberTextView.class.getSimpleName(); |
|||
private static final Transition TRANSITION; |
|||
|
|||
private long number = Long.MIN_VALUE; |
|||
private boolean showAbbreviation = true; |
|||
private boolean animateChanges = false; |
|||
private boolean toggleOnClick = true; |
|||
private boolean autoToggleToAbbreviation = true; |
|||
private long autoToggleTimeoutMs = Duration.ofSeconds(2).toMillis(); |
|||
private boolean initDone = false; |
|||
|
|||
static { |
|||
final TransitionSet transitionSet = new TransitionSet(); |
|||
final ChangeText changeText = new ChangeText().setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); |
|||
transitionSet.addTransition(changeText).addTransition(new ChangeBounds()); |
|||
TRANSITION = transitionSet; |
|||
} |
|||
|
|||
|
|||
public FormattedNumberTextView(@NonNull final Context context) { |
|||
super(context); |
|||
init(); |
|||
} |
|||
|
|||
public FormattedNumberTextView(@NonNull final Context context, @Nullable final AttributeSet attrs) { |
|||
super(context, attrs); |
|||
init(); |
|||
} |
|||
|
|||
public FormattedNumberTextView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { |
|||
super(context, attrs, defStyleAttr); |
|||
init(); |
|||
} |
|||
|
|||
private void init() { |
|||
if (initDone) return; |
|||
setupClickToggle(); |
|||
initDone = true; |
|||
} |
|||
|
|||
private void setupClickToggle() { |
|||
setOnClickListener(null); |
|||
} |
|||
|
|||
private OnClickListener getWrappedClickListener(@Nullable final OnClickListener l) { |
|||
if (!toggleOnClick) { |
|||
return l; |
|||
} |
|||
return v -> { |
|||
toggleAbbreviation(); |
|||
if (l != null) { |
|||
l.onClick(this); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
public void setNumber(final long number) { |
|||
if (this.number == number) return; |
|||
this.number = number; |
|||
format(); |
|||
} |
|||
|
|||
public void clearNumber() { |
|||
if (number == Long.MIN_VALUE) return; |
|||
number = Long.MIN_VALUE; |
|||
format(); |
|||
} |
|||
|
|||
public void setShowAbbreviation(final boolean showAbbreviation) { |
|||
if (this.showAbbreviation && showAbbreviation) return; |
|||
this.showAbbreviation = showAbbreviation; |
|||
format(); |
|||
} |
|||
|
|||
public boolean isShowAbbreviation() { |
|||
return showAbbreviation; |
|||
} |
|||
|
|||
private void toggleAbbreviation() { |
|||
if (number == Long.MIN_VALUE) return; |
|||
setShowAbbreviation(!showAbbreviation); |
|||
} |
|||
|
|||
public void setToggleOnClick(final boolean toggleOnClick) { |
|||
this.toggleOnClick = toggleOnClick; |
|||
} |
|||
|
|||
public boolean isToggleOnClick() { |
|||
return toggleOnClick; |
|||
} |
|||
|
|||
public void setAutoToggleToAbbreviation(final boolean autoToggleToAbbreviation) { |
|||
this.autoToggleToAbbreviation = autoToggleToAbbreviation; |
|||
} |
|||
|
|||
public boolean isAutoToggleToAbbreviation() { |
|||
return autoToggleToAbbreviation; |
|||
} |
|||
|
|||
public void setAutoToggleTimeoutMs(final long autoToggleTimeoutMs) { |
|||
this.autoToggleTimeoutMs = autoToggleTimeoutMs; |
|||
} |
|||
|
|||
public long getAutoToggleTimeoutMs() { |
|||
return autoToggleTimeoutMs; |
|||
} |
|||
|
|||
public void setAnimateChanges(final boolean animateChanges) { |
|||
this.animateChanges = animateChanges; |
|||
} |
|||
|
|||
public boolean isAnimateChanges() { |
|||
return animateChanges; |
|||
} |
|||
|
|||
@Override |
|||
public void setOnClickListener(@Nullable final OnClickListener l) { |
|||
super.setOnClickListener(getWrappedClickListener(l)); |
|||
} |
|||
|
|||
private void format() { |
|||
post(() -> { |
|||
if (animateChanges) { |
|||
try { |
|||
TransitionManager.beginDelayedTransition((ViewGroup) getParent(), TRANSITION); |
|||
} catch (Exception e) { |
|||
Log.e(TAG, "format: ", e); |
|||
} |
|||
} |
|||
if (number == Long.MIN_VALUE) { |
|||
setText(null); |
|||
return; |
|||
} |
|||
if (showAbbreviation) { |
|||
setText(NumberUtils.abbreviate(number)); |
|||
return; |
|||
} |
|||
setText(String.valueOf(number)); |
|||
if (autoToggleToAbbreviation) { |
|||
getHandler().postDelayed(() -> setShowAbbreviation(true), autoToggleTimeoutMs); |
|||
} |
|||
}); |
|||
} |
|||
} |
@ -0,0 +1,75 @@ |
|||
package awais.instagrabber.customviews; |
|||
|
|||
import android.content.Context; |
|||
import android.os.Bundle; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.fragment.app.FragmentManager; |
|||
import androidx.navigation.NavDestination; |
|||
import androidx.navigation.NavOptions; |
|||
import androidx.navigation.Navigator; |
|||
import androidx.navigation.fragment.FragmentNavigator; |
|||
|
|||
import awais.instagrabber.R; |
|||
|
|||
@Navigator.Name("fragment") |
|||
public class FragmentNavigatorWithDefaultAnimations extends FragmentNavigator { |
|||
|
|||
private final NavOptions emptyNavOptions = new NavOptions.Builder().build(); |
|||
// private final NavOptions defaultNavOptions = new NavOptions.Builder() |
|||
// .setEnterAnim(R.animator.nav_default_enter_anim) |
|||
// .setExitAnim(R.animator.nav_default_exit_anim) |
|||
// .setPopEnterAnim(R.animator.nav_default_pop_enter_anim) |
|||
// .setPopExitAnim(R.animator.nav_default_pop_exit_anim) |
|||
// .build(); |
|||
|
|||
private final NavOptions defaultNavOptions = new NavOptions.Builder() |
|||
.setEnterAnim(R.anim.slide_in_right) |
|||
.setExitAnim(R.anim.slide_out_left) |
|||
.setPopEnterAnim(android.R.anim.slide_in_left) |
|||
.setPopExitAnim(android.R.anim.slide_out_right) |
|||
.build(); |
|||
|
|||
public FragmentNavigatorWithDefaultAnimations(@NonNull final Context context, |
|||
@NonNull final FragmentManager manager, |
|||
final int containerId) { |
|||
super(context, manager, containerId); |
|||
} |
|||
|
|||
@Nullable |
|||
@Override |
|||
public NavDestination navigate(@NonNull final Destination destination, |
|||
@Nullable final Bundle args, |
|||
@Nullable final NavOptions navOptions, |
|||
@Nullable final Navigator.Extras navigatorExtras) { |
|||
// this will try to fill in empty animations with defaults when no shared element transitions are set |
|||
// https://developer.android.com/guide/navigation/navigation-animate-transitions#shared-element |
|||
final boolean shouldUseTransitionsInstead = navigatorExtras != null; |
|||
final NavOptions navOptions1 = shouldUseTransitionsInstead ? navOptions : fillEmptyAnimationsWithDefaults(navOptions); |
|||
return super.navigate(destination, args, navOptions1, navigatorExtras); |
|||
} |
|||
|
|||
private NavOptions fillEmptyAnimationsWithDefaults(@Nullable final NavOptions navOptions) { |
|||
if (navOptions == null) { |
|||
return defaultNavOptions; |
|||
} |
|||
return copyNavOptionsWithDefaultAnimations(navOptions); |
|||
} |
|||
|
|||
@NonNull |
|||
private NavOptions copyNavOptionsWithDefaultAnimations(@NonNull final NavOptions navOptions) { |
|||
return new NavOptions.Builder() |
|||
.setLaunchSingleTop(navOptions.shouldLaunchSingleTop()) |
|||
.setPopUpTo(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive()) |
|||
.setEnterAnim(navOptions.getEnterAnim() == emptyNavOptions.getEnterAnim() |
|||
? defaultNavOptions.getEnterAnim() : navOptions.getEnterAnim()) |
|||
.setExitAnim(navOptions.getExitAnim() == emptyNavOptions.getExitAnim() |
|||
? defaultNavOptions.getExitAnim() : navOptions.getExitAnim()) |
|||
.setPopEnterAnim(navOptions.getPopEnterAnim() == emptyNavOptions.getPopEnterAnim() |
|||
? defaultNavOptions.getPopEnterAnim() : navOptions.getPopEnterAnim()) |
|||
.setPopExitAnim(navOptions.getPopExitAnim() == emptyNavOptions.getPopExitAnim() |
|||
? defaultNavOptions.getPopExitAnim() : navOptions.getPopExitAnim()) |
|||
.build(); |
|||
} |
|||
} |
@ -0,0 +1,246 @@ |
|||
package awais.instagrabber.customviews; |
|||
|
|||
import android.content.Context; |
|||
import android.os.Build; |
|||
import android.util.AttributeSet; |
|||
import android.view.View; |
|||
import android.view.WindowInsetsAnimation; |
|||
import android.widget.LinearLayout; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.view.NestedScrollingParent3; |
|||
import androidx.core.view.NestedScrollingParentHelper; |
|||
import androidx.core.view.ViewCompat; |
|||
import androidx.core.view.WindowInsetsCompat; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
import awais.instagrabber.customviews.helpers.SimpleImeAnimationController; |
|||
import awais.instagrabber.utils.ViewUtils; |
|||
|
|||
import static androidx.core.view.ViewCompat.TYPE_TOUCH; |
|||
|
|||
public final class InsetsAnimationLinearLayout extends LinearLayout implements NestedScrollingParent3 { |
|||
private final NestedScrollingParentHelper nestedScrollingParentHelper = new NestedScrollingParentHelper(this); |
|||
private final SimpleImeAnimationController imeAnimController = new SimpleImeAnimationController(); |
|||
private final int[] tempIntArray2 = new int[2]; |
|||
private final int[] startViewLocation = new int[2]; |
|||
|
|||
private View currentNestedScrollingChild; |
|||
private int dropNextY; |
|||
private boolean scrollImeOffScreenWhenVisible = true; |
|||
private boolean scrollImeOnScreenWhenNotVisible = true; |
|||
private boolean scrollImeOffScreenWhenVisibleOnFling = false; |
|||
private boolean scrollImeOnScreenWhenNotVisibleOnFling = false; |
|||
|
|||
public InsetsAnimationLinearLayout(final Context context, @Nullable final AttributeSet attrs) { |
|||
super(context, attrs); |
|||
} |
|||
|
|||
public InsetsAnimationLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { |
|||
super(context, attrs, defStyleAttr); |
|||
} |
|||
|
|||
public final boolean getScrollImeOffScreenWhenVisible() { |
|||
return scrollImeOffScreenWhenVisible; |
|||
} |
|||
|
|||
public final void setScrollImeOffScreenWhenVisible(boolean scrollImeOffScreenWhenVisible) { |
|||
this.scrollImeOffScreenWhenVisible = scrollImeOffScreenWhenVisible; |
|||
} |
|||
|
|||
public final boolean getScrollImeOnScreenWhenNotVisible() { |
|||
return scrollImeOnScreenWhenNotVisible; |
|||
} |
|||
|
|||
public final void setScrollImeOnScreenWhenNotVisible(boolean scrollImeOnScreenWhenNotVisible) { |
|||
this.scrollImeOnScreenWhenNotVisible = scrollImeOnScreenWhenNotVisible; |
|||
} |
|||
|
|||
public boolean getScrollImeOffScreenWhenVisibleOnFling() { |
|||
return scrollImeOffScreenWhenVisibleOnFling; |
|||
} |
|||
|
|||
public void setScrollImeOffScreenWhenVisibleOnFling(final boolean scrollImeOffScreenWhenVisibleOnFling) { |
|||
this.scrollImeOffScreenWhenVisibleOnFling = scrollImeOffScreenWhenVisibleOnFling; |
|||
} |
|||
|
|||
public boolean getScrollImeOnScreenWhenNotVisibleOnFling() { |
|||
return scrollImeOnScreenWhenNotVisibleOnFling; |
|||
} |
|||
|
|||
public void setScrollImeOnScreenWhenNotVisibleOnFling(final boolean scrollImeOnScreenWhenNotVisibleOnFling) { |
|||
this.scrollImeOnScreenWhenNotVisibleOnFling = scrollImeOnScreenWhenNotVisibleOnFling; |
|||
} |
|||
|
|||
public SimpleImeAnimationController getImeAnimController() { |
|||
return imeAnimController; |
|||
} |
|||
|
|||
@Override |
|||
public boolean onStartNestedScroll(@NonNull final View child, |
|||
@NonNull final View target, |
|||
final int axes, |
|||
final int type) { |
|||
return (axes & SCROLL_AXIS_VERTICAL) != 0 && type == TYPE_TOUCH; |
|||
} |
|||
|
|||
@Override |
|||
public void onNestedScrollAccepted(@NonNull final View child, |
|||
@NonNull final View target, |
|||
final int axes, |
|||
final int type) { |
|||
nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type); |
|||
currentNestedScrollingChild = child; |
|||
} |
|||
|
|||
@Override |
|||
public void onNestedPreScroll(@NonNull final View target, |
|||
final int dx, |
|||
final int dy, |
|||
@NonNull final int[] consumed, |
|||
final int type) { |
|||
if (imeAnimController.isInsetAnimationRequestPending()) { |
|||
consumed[0] = dx; |
|||
consumed[1] = dy; |
|||
} else { |
|||
int deltaY = dy; |
|||
if (dropNextY != 0) { |
|||
consumed[1] = dropNextY; |
|||
deltaY = dy - dropNextY; |
|||
dropNextY = 0; |
|||
} |
|||
|
|||
if (deltaY < 0) { |
|||
if (imeAnimController.isInsetAnimationInProgress()) { |
|||
consumed[1] -= imeAnimController.insetBy(-deltaY); |
|||
} else if (scrollImeOffScreenWhenVisible && !imeAnimController.isInsetAnimationRequestPending()) { |
|||
WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(this); |
|||
if (rootWindowInsets != null) { |
|||
if (rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime())) { |
|||
startControlRequest(); |
|||
consumed[1] = deltaY; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onNestedScroll(@NonNull final View target, |
|||
final int dxConsumed, |
|||
final int dyConsumed, |
|||
final int dxUnconsumed, |
|||
final int dyUnconsumed, |
|||
final int type, |
|||
@NonNull final int[] consumed) { |
|||
if (dyUnconsumed > 0) { |
|||
if (imeAnimController.isInsetAnimationInProgress()) { |
|||
consumed[1] = -imeAnimController.insetBy(-dyUnconsumed); |
|||
} else if (scrollImeOnScreenWhenNotVisible && !imeAnimController.isInsetAnimationRequestPending()) { |
|||
WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(this); |
|||
if (rootWindowInsets != null) { |
|||
if (!rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime())) { |
|||
startControlRequest(); |
|||
consumed[1] = dyUnconsumed; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public boolean onNestedFling(@NonNull final View target, |
|||
final float velocityX, |
|||
final float velocityY, |
|||
final boolean consumed) { |
|||
if (imeAnimController.isInsetAnimationInProgress()) { |
|||
imeAnimController.animateToFinish(velocityY); |
|||
return true; |
|||
} else { |
|||
boolean imeVisible = false; |
|||
final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(this); |
|||
if (rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime())) { |
|||
imeVisible = true; |
|||
} |
|||
if (velocityY > 0 && scrollImeOnScreenWhenNotVisibleOnFling && !imeVisible) { |
|||
imeAnimController.startAndFling(this, velocityY); |
|||
return true; |
|||
} else if (velocityY < 0 && scrollImeOffScreenWhenVisibleOnFling && imeVisible) { |
|||
imeAnimController.startAndFling(this, velocityY); |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onStopNestedScroll(@NonNull final View target, final int type) { |
|||
nestedScrollingParentHelper.onStopNestedScroll(target, type); |
|||
if (imeAnimController.isInsetAnimationInProgress() && !imeAnimController.isInsetAnimationFinishing()) { |
|||
imeAnimController.animateToFinish(null); |
|||
} |
|||
reset(); |
|||
} |
|||
|
|||
@Override |
|||
public void dispatchWindowInsetsAnimationPrepare(@NonNull final WindowInsetsAnimation animation) { |
|||
super.dispatchWindowInsetsAnimationPrepare(animation); |
|||
ViewUtils.suppressLayoutCompat(this, false); |
|||
} |
|||
|
|||
private void startControlRequest() { |
|||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { |
|||
return; |
|||
} |
|||
ViewUtils.suppressLayoutCompat(this, true); |
|||
if (currentNestedScrollingChild != null) { |
|||
currentNestedScrollingChild.getLocationInWindow(startViewLocation); |
|||
} |
|||
imeAnimController.startControlRequest(this, windowInsetsAnimationControllerCompat -> onControllerReady()); |
|||
} |
|||
|
|||
private void onControllerReady() { |
|||
if (currentNestedScrollingChild != null) { |
|||
imeAnimController.insetBy(0); |
|||
int[] location = tempIntArray2; |
|||
currentNestedScrollingChild.getLocationInWindow(location); |
|||
dropNextY = location[1] - startViewLocation[1]; |
|||
} |
|||
|
|||
} |
|||
|
|||
private void reset() { |
|||
dropNextY = 0; |
|||
Arrays.fill(startViewLocation, 0); |
|||
ViewUtils.suppressLayoutCompat(this, false); |
|||
} |
|||
|
|||
@Override |
|||
public void onNestedScrollAccepted(@NonNull final View child, |
|||
@NonNull final View target, |
|||
final int axes) { |
|||
onNestedScrollAccepted(child, target, axes, TYPE_TOUCH); |
|||
} |
|||
|
|||
@Override |
|||
public void onNestedScroll(@NonNull final View target, |
|||
final int dxConsumed, |
|||
final int dyConsumed, |
|||
final int dxUnconsumed, |
|||
final int dyUnconsumed, |
|||
final int type) { |
|||
onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, tempIntArray2); |
|||
} |
|||
|
|||
@Override |
|||
public void onStopNestedScroll(@NonNull final View target) { |
|||
onStopNestedScroll(target, TYPE_TOUCH); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,33 @@ |
|||
package awais.instagrabber.customviews; |
|||
|
|||
import android.content.Context; |
|||
import android.util.AttributeSet; |
|||
import android.view.WindowInsets; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.coordinatorlayout.widget.CoordinatorLayout; |
|||
|
|||
public class InsetsNotifyingCoordinatorLayout extends CoordinatorLayout { |
|||
|
|||
public InsetsNotifyingCoordinatorLayout(@NonNull final Context context) { |
|||
super(context); |
|||
} |
|||
|
|||
public InsetsNotifyingCoordinatorLayout(@NonNull final Context context, @Nullable final AttributeSet attrs) { |
|||
super(context, attrs); |
|||
} |
|||
|
|||
public InsetsNotifyingCoordinatorLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { |
|||
super(context, attrs, defStyleAttr); |
|||
} |
|||
|
|||
@Override |
|||
public WindowInsets onApplyWindowInsets(WindowInsets insets) { |
|||
int childCount = getChildCount(); |
|||
for (int index = 0; index < childCount; index++) { |
|||
getChildAt(index).dispatchApplyWindowInsets(insets); |
|||
} |
|||
return insets; |
|||
} |
|||
} |
@ -0,0 +1,35 @@ |
|||
package awais.instagrabber.customviews; |
|||
|
|||
import android.content.Context; |
|||
import android.util.AttributeSet; |
|||
import android.view.WindowInsets; |
|||
import android.widget.LinearLayout; |
|||
|
|||
import androidx.annotation.Nullable; |
|||
|
|||
public class InsetsNotifyingLinearLayout extends LinearLayout { |
|||
public InsetsNotifyingLinearLayout(final Context context) { |
|||
super(context); |
|||
} |
|||
|
|||
public InsetsNotifyingLinearLayout(final Context context, @Nullable final AttributeSet attrs) { |
|||
super(context, attrs); |
|||
} |
|||
|
|||
public InsetsNotifyingLinearLayout(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { |
|||
super(context, attrs, defStyleAttr); |
|||
} |
|||
|
|||
public InsetsNotifyingLinearLayout(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) { |
|||
super(context, attrs, defStyleAttr, defStyleRes); |
|||
} |
|||
|
|||
@Override |
|||
public WindowInsets onApplyWindowInsets(WindowInsets insets) { |
|||
int childCount = getChildCount(); |
|||
for (int index = 0; index < childCount; index++) { |
|||
getChildAt(index).dispatchApplyWindowInsets(insets); |
|||
} |
|||
return insets; |
|||
} |
|||
} |
@ -0,0 +1,60 @@ |
|||
package awais.instagrabber.customviews; |
|||
|
|||
import android.os.Bundle; |
|||
|
|||
import androidx.annotation.NavigationRes; |
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.navigation.NavController; |
|||
import androidx.navigation.Navigator; |
|||
import androidx.navigation.fragment.FragmentNavigator; |
|||
import androidx.navigation.fragment.NavHostFragment; |
|||
|
|||
public class NavHostFragmentWithDefaultAnimations extends NavHostFragment { |
|||
private static final String KEY_GRAPH_ID = "android-support-nav:fragment:graphId"; |
|||
private static final String KEY_START_DESTINATION_ARGS = |
|||
"android-support-nav:fragment:startDestinationArgs"; |
|||
private static final String KEY_NAV_CONTROLLER_STATE = |
|||
"android-support-nav:fragment:navControllerState"; |
|||
private static final String KEY_DEFAULT_NAV_HOST = "android-support-nav:fragment:defaultHost"; |
|||
|
|||
@NonNull |
|||
public static NavHostFragment create(@NavigationRes int graphResId) { |
|||
return create(graphResId, null); |
|||
} |
|||
|
|||
@NonNull |
|||
public static NavHostFragment create(@NavigationRes int graphResId, |
|||
@Nullable Bundle startDestinationArgs) { |
|||
Bundle b = null; |
|||
if (graphResId != 0) { |
|||
b = new Bundle(); |
|||
b.putInt(KEY_GRAPH_ID, graphResId); |
|||
} |
|||
if (startDestinationArgs != null) { |
|||
if (b == null) { |
|||
b = new Bundle(); |
|||
} |
|||
b.putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs); |
|||
} |
|||
|
|||
final NavHostFragmentWithDefaultAnimations result = new NavHostFragmentWithDefaultAnimations(); |
|||
if (b != null) { |
|||
result.setArguments(b); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() { |
|||
return new FragmentNavigatorWithDefaultAnimations(requireContext(), getChildFragmentManager(), getId()); |
|||
} |
|||
|
|||
@Override |
|||
protected void onCreateNavController(@NonNull final NavController navController) { |
|||
super.onCreateNavController(navController); |
|||
navController.getNavigatorProvider() |
|||
.addNavigator(new FragmentNavigatorWithDefaultAnimations(requireContext(), getChildFragmentManager(), getId())); |
|||
} |
|||
} |
@ -0,0 +1,100 @@ |
|||
package awais.instagrabber.customviews.emoji; |
|||
|
|||
import android.app.Dialog; |
|||
import android.content.Context; |
|||
import android.os.Bundle; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.fragment.app.DialogFragment; |
|||
import androidx.fragment.app.Fragment; |
|||
import androidx.recyclerview.widget.GridLayoutManager; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import com.google.android.material.bottomsheet.BottomSheetDialog; |
|||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
public class EmojiBottomSheetDialog extends BottomSheetDialogFragment { |
|||
public static final String TAG = EmojiBottomSheetDialog.class.getSimpleName(); |
|||
|
|||
private RecyclerView grid; |
|||
private EmojiPicker.OnEmojiClickListener callback; |
|||
|
|||
@NonNull |
|||
public static EmojiBottomSheetDialog newInstance() { |
|||
// Bundle args = new Bundle(); |
|||
// fragment.setArguments(args); |
|||
return new EmojiBottomSheetDialog(); |
|||
} |
|||
|
|||
@Override |
|||
public void onCreate(@Nullable final Bundle savedInstanceState) { |
|||
super.onCreate(savedInstanceState); |
|||
setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog); |
|||
} |
|||
|
|||
@Nullable |
|||
@Override |
|||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { |
|||
final Context context = getContext(); |
|||
if (context == null) return null; |
|||
grid = new RecyclerView(context); |
|||
return grid; |
|||
} |
|||
|
|||
@Override |
|||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { |
|||
init(); |
|||
} |
|||
|
|||
@Override |
|||
public void onStart() { |
|||
super.onStart(); |
|||
final Dialog dialog = getDialog(); |
|||
if (dialog == null) return; |
|||
final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog; |
|||
final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); |
|||
if (bottomSheetInternal == null) return; |
|||
bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; |
|||
bottomSheetInternal.requestLayout(); |
|||
} |
|||
|
|||
@Override |
|||
public void onAttach(@NonNull final Context context) { |
|||
super.onAttach(context); |
|||
final Fragment parentFragment = getParentFragment(); |
|||
if (parentFragment instanceof EmojiPicker.OnEmojiClickListener) { |
|||
callback = (EmojiPicker.OnEmojiClickListener) parentFragment; |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onDestroyView() { |
|||
grid = null; |
|||
super.onDestroyView(); |
|||
} |
|||
|
|||
private void init() { |
|||
final Context context = getContext(); |
|||
if (context == null) return; |
|||
final GridLayoutManager gridLayoutManager = new GridLayoutManager(context, 9); |
|||
grid.setLayoutManager(gridLayoutManager); |
|||
grid.setHasFixedSize(true); |
|||
grid.setClipToPadding(false); |
|||
grid.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(8))); |
|||
final EmojiGridAdapter adapter = new EmojiGridAdapter(null, (view, emoji) -> { |
|||
if (callback != null) { |
|||
callback.onClick(view, emoji); |
|||
} |
|||
dismiss(); |
|||
}, null); |
|||
grid.setAdapter(adapter); |
|||
} |
|||
} |
@ -1,163 +0,0 @@ |
|||
package awais.instagrabber.customviews.emoji; |
|||
|
|||
import android.content.Context; |
|||
import android.graphics.Rect; |
|||
import android.view.Gravity; |
|||
import android.view.View; |
|||
import android.view.WindowManager.LayoutParams; |
|||
import android.widget.PopupWindow; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.customviews.emoji.EmojiPicker.OnBackspaceClickListener; |
|||
import awais.instagrabber.customviews.emoji.EmojiPicker.OnEmojiClickListener; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; |
|||
|
|||
/** |
|||
* https://stackoverflow.com/a/33897583/1436766 |
|||
*/ |
|||
public class EmojiPopupWindow extends PopupWindow { |
|||
|
|||
private int keyBoardHeight = 0; |
|||
private Boolean pendingOpen = false; |
|||
private Boolean isOpened = false; |
|||
private final View rootView; |
|||
private final Context context; |
|||
private final OnEmojiClickListener onEmojiClickListener; |
|||
private final OnBackspaceClickListener onBackspaceClickListener; |
|||
|
|||
private OnSoftKeyboardOpenCloseListener onSoftKeyboardOpenCloseListener; |
|||
|
|||
|
|||
/** |
|||
* Constructor |
|||
* |
|||
* @param rootView The top most layout in your view hierarchy. The difference of this view and the screen height will be used to calculate the keyboard height. |
|||
*/ |
|||
public EmojiPopupWindow(final View rootView, |
|||
final OnEmojiClickListener onEmojiClickListener, |
|||
final OnBackspaceClickListener onBackspaceClickListener) { |
|||
super(rootView.getContext()); |
|||
this.rootView = rootView; |
|||
this.context = rootView.getContext(); |
|||
this.onEmojiClickListener = onEmojiClickListener; |
|||
this.onBackspaceClickListener = onBackspaceClickListener; |
|||
View customView = createCustomView(); |
|||
setContentView(customView); |
|||
setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); |
|||
//default size |
|||
setSize((int) context.getResources().getDimension(R.dimen.keyboard_height), MATCH_PARENT); |
|||
} |
|||
|
|||
/** |
|||
* Set the listener for the event of keyboard opening or closing. |
|||
*/ |
|||
public void setOnSoftKeyboardOpenCloseListener(OnSoftKeyboardOpenCloseListener listener) { |
|||
this.onSoftKeyboardOpenCloseListener = listener; |
|||
} |
|||
|
|||
/** |
|||
* Use this function to show the emoji popup. |
|||
* NOTE: Since, the soft keyboard sizes are variable on different android devices, the |
|||
* library needs you to open the soft keyboard atleast once before calling this function. |
|||
* If that is not possible see showAtBottomPending() function. |
|||
*/ |
|||
public void showAtBottom() { |
|||
showAtLocation(rootView, Gravity.BOTTOM, 0, 0); |
|||
} |
|||
|
|||
/** |
|||
* Use this function when the soft keyboard has not been opened yet. This |
|||
* will show the emoji popup after the keyboard is up next time. |
|||
* Generally, you will be calling InputMethodManager.showSoftInput function after |
|||
* calling this function. |
|||
*/ |
|||
public void showAtBottomPending() { |
|||
if (isKeyBoardOpen()) |
|||
showAtBottom(); |
|||
else |
|||
pendingOpen = true; |
|||
} |
|||
|
|||
/** |
|||
* @return Returns true if the soft keyboard is open, false otherwise. |
|||
*/ |
|||
public Boolean isKeyBoardOpen() { |
|||
return isOpened; |
|||
} |
|||
|
|||
/** |
|||
* Dismiss the popup |
|||
*/ |
|||
@Override |
|||
public void dismiss() { |
|||
super.dismiss(); |
|||
} |
|||
|
|||
/** |
|||
* Call this function to resize the emoji popup according to your soft keyboard size |
|||
*/ |
|||
public void setSizeForSoftKeyboard() { |
|||
rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> { |
|||
Rect r = new Rect(); |
|||
rootView.getWindowVisibleDisplayFrame(r); |
|||
|
|||
int screenHeight = getUsableScreenHeight(); |
|||
int heightDifference = screenHeight - (r.bottom - r.top); |
|||
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); |
|||
if (resourceId > 0) { |
|||
heightDifference -= context.getResources() |
|||
.getDimensionPixelSize(resourceId); |
|||
} |
|||
if (heightDifference > 100) { |
|||
keyBoardHeight = heightDifference; |
|||
setSize(MATCH_PARENT, keyBoardHeight); |
|||
if (!isOpened) { |
|||
if (onSoftKeyboardOpenCloseListener != null) |
|||
onSoftKeyboardOpenCloseListener.onKeyboardOpen(keyBoardHeight); |
|||
} |
|||
isOpened = true; |
|||
if (pendingOpen) { |
|||
showAtBottom(); |
|||
pendingOpen = false; |
|||
} |
|||
} else { |
|||
isOpened = false; |
|||
if (onSoftKeyboardOpenCloseListener != null) |
|||
onSoftKeyboardOpenCloseListener.onKeyboardClose(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private int getUsableScreenHeight() { |
|||
return Utils.displayMetrics.heightPixels; |
|||
} |
|||
|
|||
/** |
|||
* Manually set the popup window size |
|||
* |
|||
* @param width Width of the popup |
|||
* @param height Height of the popup |
|||
*/ |
|||
public void setSize(int width, int height) { |
|||
setWidth(width); |
|||
setHeight(height); |
|||
} |
|||
|
|||
private View createCustomView() { |
|||
final EmojiPicker emojiPicker = new EmojiPicker(context); |
|||
final LayoutParams layoutParams = new LayoutParams(MATCH_PARENT, MATCH_PARENT); |
|||
emojiPicker.setLayoutParams(layoutParams); |
|||
emojiPicker.init(rootView, onEmojiClickListener, onBackspaceClickListener); |
|||
return emojiPicker; |
|||
} |
|||
|
|||
|
|||
public interface OnSoftKeyboardOpenCloseListener { |
|||
void onKeyboardOpen(int keyBoardHeight); |
|||
|
|||
void onKeyboardClose(); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,320 @@ |
|||
package awais.instagrabber.customviews.helpers; |
|||
|
|||
/* |
|||
* Copyright (C) 2013 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
import android.animation.Animator; |
|||
import android.animation.AnimatorListenerAdapter; |
|||
import android.animation.AnimatorSet; |
|||
import android.animation.ValueAnimator; |
|||
import android.graphics.Color; |
|||
import android.util.Log; |
|||
import android.view.ViewGroup; |
|||
import android.widget.EditText; |
|||
import android.widget.TextView; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.transition.Transition; |
|||
import androidx.transition.TransitionListenerAdapter; |
|||
import androidx.transition.TransitionValues; |
|||
|
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
|
|||
/** |
|||
* This transition tracks changes to the text in TextView targets. If the text |
|||
* changes between the start and end scenes, the transition ensures that the |
|||
* starting text stays until the transition ends, at which point it changes |
|||
* to the end text. This is useful in situations where you want to resize a |
|||
* text view to its new size before displaying the text that goes there. |
|||
*/ |
|||
public class ChangeText extends Transition { |
|||
private static final String LOG_TAG = "TextChange"; |
|||
private static final String PROPNAME_TEXT = "android:textchange:text"; |
|||
private static final String PROPNAME_TEXT_SELECTION_START = |
|||
"android:textchange:textSelectionStart"; |
|||
private static final String PROPNAME_TEXT_SELECTION_END = |
|||
"android:textchange:textSelectionEnd"; |
|||
private static final String PROPNAME_TEXT_COLOR = "android:textchange:textColor"; |
|||
private int mChangeBehavior = CHANGE_BEHAVIOR_KEEP; |
|||
private boolean crossFade; |
|||
/** |
|||
* Flag specifying that the text in affected/changing TextView targets will keep |
|||
* their original text during the transition, setting it to the final text when |
|||
* the transition ends. This is the default behavior. |
|||
* |
|||
* @see #setChangeBehavior(int) |
|||
*/ |
|||
public static final int CHANGE_BEHAVIOR_KEEP = 0; |
|||
/** |
|||
* Flag specifying that the text changing animation should first fade |
|||
* out the original text completely. The new text is set on the target |
|||
* view at the end of the fade-out animation. This transition is typically |
|||
* used with a later {@link #CHANGE_BEHAVIOR_IN} transition, allowing more |
|||
* flexibility than the {@link #CHANGE_BEHAVIOR_OUT_IN} by allowing other |
|||
* transitions to be run sequentially or in parallel with these fades. |
|||
* |
|||
* @see #setChangeBehavior(int) |
|||
*/ |
|||
public static final int CHANGE_BEHAVIOR_OUT = 1; |
|||
/** |
|||
* Flag specifying that the text changing animation should fade in the |
|||
* end text into the affected target view(s). This transition is typically |
|||
* used in conjunction with an earlier {@link #CHANGE_BEHAVIOR_OUT} |
|||
* transition, possibly with other transitions running as well, such as |
|||
* a sequence to fade out, then resize the view, then fade in. |
|||
* |
|||
* @see #setChangeBehavior(int) |
|||
*/ |
|||
public static final int CHANGE_BEHAVIOR_IN = 2; |
|||
/** |
|||
* Flag specifying that the text changing animation should first fade |
|||
* out the original text completely and then fade in the |
|||
* new text. |
|||
* |
|||
* @see #setChangeBehavior(int) |
|||
*/ |
|||
public static final int CHANGE_BEHAVIOR_OUT_IN = 3; |
|||
private static final String[] sTransitionProperties = { |
|||
PROPNAME_TEXT, |
|||
PROPNAME_TEXT_SELECTION_START, |
|||
PROPNAME_TEXT_SELECTION_END |
|||
}; |
|||
|
|||
/** |
|||
* Sets the type of changing animation that will be run, one of |
|||
* {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT}, |
|||
* {@link #CHANGE_BEHAVIOR_IN}, and {@link #CHANGE_BEHAVIOR_OUT_IN}. |
|||
* |
|||
* @param changeBehavior The type of fading animation to use when this |
|||
* transition is run. |
|||
* @return this textChange object. |
|||
*/ |
|||
public ChangeText setChangeBehavior(int changeBehavior) { |
|||
if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) { |
|||
mChangeBehavior = changeBehavior; |
|||
} |
|||
return this; |
|||
} |
|||
|
|||
public ChangeText setCrossFade(final boolean crossFade) { |
|||
this.crossFade = crossFade; |
|||
return this; |
|||
} |
|||
|
|||
@Override |
|||
public String[] getTransitionProperties() { |
|||
return sTransitionProperties; |
|||
} |
|||
|
|||
/** |
|||
* Returns the type of changing animation that will be run. |
|||
* |
|||
* @return either {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT}, |
|||
* {@link #CHANGE_BEHAVIOR_IN}, or {@link #CHANGE_BEHAVIOR_OUT_IN}. |
|||
*/ |
|||
public int getChangeBehavior() { |
|||
return mChangeBehavior; |
|||
} |
|||
|
|||
private void captureValues(TransitionValues transitionValues) { |
|||
if (transitionValues.view instanceof TextView) { |
|||
TextView textview = (TextView) transitionValues.view; |
|||
transitionValues.values.put(PROPNAME_TEXT, textview.getText()); |
|||
if (textview instanceof EditText) { |
|||
transitionValues.values.put(PROPNAME_TEXT_SELECTION_START, |
|||
textview.getSelectionStart()); |
|||
transitionValues.values.put(PROPNAME_TEXT_SELECTION_END, |
|||
textview.getSelectionEnd()); |
|||
} |
|||
if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { |
|||
transitionValues.values.put(PROPNAME_TEXT_COLOR, textview.getCurrentTextColor()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void captureStartValues(@NonNull TransitionValues transitionValues) { |
|||
captureValues(transitionValues); |
|||
} |
|||
|
|||
@Override |
|||
public void captureEndValues(@NonNull TransitionValues transitionValues) { |
|||
captureValues(transitionValues); |
|||
} |
|||
|
|||
@Override |
|||
public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues, |
|||
TransitionValues endValues) { |
|||
if (startValues == null || endValues == null || |
|||
!(startValues.view instanceof TextView) || !(endValues.view instanceof TextView)) { |
|||
return null; |
|||
} |
|||
final TextView view = (TextView) endValues.view; |
|||
Map<String, Object> startVals = startValues.values; |
|||
Map<String, Object> endVals = endValues.values; |
|||
final CharSequence startText = startVals.get(PROPNAME_TEXT) != null ? |
|||
(CharSequence) startVals.get(PROPNAME_TEXT) : ""; |
|||
final CharSequence endText = endVals.get(PROPNAME_TEXT) != null ? |
|||
(CharSequence) endVals.get(PROPNAME_TEXT) : ""; |
|||
final int startSelectionStart, startSelectionEnd, endSelectionStart, endSelectionEnd; |
|||
if (view instanceof EditText) { |
|||
startSelectionStart = startVals.get(PROPNAME_TEXT_SELECTION_START) != null ? |
|||
(Integer) startVals.get(PROPNAME_TEXT_SELECTION_START) : -1; |
|||
startSelectionEnd = startVals.get(PROPNAME_TEXT_SELECTION_END) != null ? |
|||
(Integer) startVals.get(PROPNAME_TEXT_SELECTION_END) : startSelectionStart; |
|||
endSelectionStart = endVals.get(PROPNAME_TEXT_SELECTION_START) != null ? |
|||
(Integer) endVals.get(PROPNAME_TEXT_SELECTION_START) : -1; |
|||
endSelectionEnd = endVals.get(PROPNAME_TEXT_SELECTION_END) != null ? |
|||
(Integer) endVals.get(PROPNAME_TEXT_SELECTION_END) : endSelectionStart; |
|||
} else { |
|||
startSelectionStart = startSelectionEnd = endSelectionStart = endSelectionEnd = -1; |
|||
} |
|||
if (!Objects.equals(startText, endText)) { |
|||
final int startColor; |
|||
final int endColor; |
|||
if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { |
|||
view.setText(startText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), startSelectionStart, startSelectionEnd); |
|||
} |
|||
} |
|||
Animator anim; |
|||
if (mChangeBehavior == CHANGE_BEHAVIOR_KEEP) { |
|||
startColor = endColor = 0; |
|||
anim = ValueAnimator.ofFloat(0, 1); |
|||
anim.addListener(new AnimatorListenerAdapter() { |
|||
@Override |
|||
public void onAnimationEnd(Animator animation) { |
|||
if (Objects.equals(startText, view.getText())) { |
|||
// Only set if it hasn't been changed since anim started |
|||
view.setText(endText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), endSelectionStart, endSelectionEnd); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} else { |
|||
startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR); |
|||
endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR); |
|||
// Fade out start text |
|||
ValueAnimator outAnim = null, inAnim = null; |
|||
if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN || |
|||
mChangeBehavior == CHANGE_BEHAVIOR_OUT) { |
|||
outAnim = ValueAnimator.ofInt(Color.alpha(startColor), 0); |
|||
outAnim.addUpdateListener(animation -> { |
|||
int currAlpha = (Integer) animation.getAnimatedValue(); |
|||
view.setTextColor(currAlpha << 24 | startColor & 0xffffff); |
|||
}); |
|||
outAnim.addListener(new AnimatorListenerAdapter() { |
|||
@Override |
|||
public void onAnimationEnd(Animator animation) { |
|||
if (Objects.equals(startText, view.getText())) { |
|||
// Only set if it hasn't been changed since anim started |
|||
view.setText(endText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), endSelectionStart, |
|||
endSelectionEnd); |
|||
} |
|||
} |
|||
// restore opaque alpha and correct end color |
|||
view.setTextColor(endColor); |
|||
} |
|||
}); |
|||
} |
|||
if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN || |
|||
mChangeBehavior == CHANGE_BEHAVIOR_IN) { |
|||
inAnim = ValueAnimator.ofInt(0, Color.alpha(endColor)); |
|||
inAnim.addUpdateListener(animation -> { |
|||
int currAlpha = (Integer) animation.getAnimatedValue(); |
|||
view.setTextColor(currAlpha << 24 | endColor & 0xffffff); |
|||
}); |
|||
inAnim.addListener(new AnimatorListenerAdapter() { |
|||
@Override |
|||
public void onAnimationCancel(Animator animation) { |
|||
// restore opaque alpha and correct end color |
|||
view.setTextColor(endColor); |
|||
} |
|||
}); |
|||
} |
|||
if (outAnim != null && inAnim != null) { |
|||
anim = new AnimatorSet(); |
|||
final AnimatorSet animatorSet = (AnimatorSet) anim; |
|||
if (crossFade) { |
|||
animatorSet.playTogether(outAnim, inAnim); |
|||
} else { |
|||
animatorSet.playSequentially(outAnim, inAnim); |
|||
} |
|||
} else if (outAnim != null) { |
|||
anim = outAnim; |
|||
} else { |
|||
// Must be an in-only animation |
|||
anim = inAnim; |
|||
} |
|||
} |
|||
TransitionListener transitionListener = new TransitionListenerAdapter() { |
|||
int mPausedColor = 0; |
|||
|
|||
@Override |
|||
public void onTransitionPause(@NonNull Transition transition) { |
|||
if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { |
|||
view.setText(endText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), endSelectionStart, endSelectionEnd); |
|||
} |
|||
} |
|||
if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { |
|||
mPausedColor = view.getCurrentTextColor(); |
|||
view.setTextColor(endColor); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onTransitionResume(@NonNull Transition transition) { |
|||
if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { |
|||
view.setText(startText); |
|||
if (view instanceof EditText) { |
|||
setSelection(((EditText) view), startSelectionStart, startSelectionEnd); |
|||
} |
|||
} |
|||
if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { |
|||
view.setTextColor(mPausedColor); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onTransitionEnd(Transition transition) { |
|||
transition.removeListener(this); |
|||
} |
|||
}; |
|||
addListener(transitionListener); |
|||
if (BuildConfig.DEBUG) { |
|||
Log.d(LOG_TAG, "createAnimator returning " + anim); |
|||
} |
|||
return anim; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
private void setSelection(EditText editText, int start, int end) { |
|||
if (start >= 0 && end >= 0) { |
|||
editText.setSelection(start, end); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,87 @@ |
|||
/* |
|||
* Copyright 2020 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package awais.instagrabber.customviews.helpers; |
|||
|
|||
import android.view.View; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.core.view.ViewCompat; |
|||
import androidx.core.view.WindowInsetsAnimationCompat; |
|||
import androidx.core.view.WindowInsetsCompat; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* A [WindowInsetsAnimationCompat.Callback] which will request and clear focus on the given view, |
|||
* depending on the [WindowInsetsCompat.Type.ime] visibility state when an IME |
|||
* [WindowInsetsAnimationCompat] has finished. |
|||
* <p> |
|||
* This is primarily used when animating the [WindowInsetsCompat.Type.ime], so that the |
|||
* appropriate view is focused for accepting input from the IME. |
|||
*/ |
|||
public class ControlFocusInsetsAnimationCallback extends WindowInsetsAnimationCompat.Callback { |
|||
|
|||
private final View view; |
|||
|
|||
public ControlFocusInsetsAnimationCallback(@NonNull final View view) { |
|||
this(view, DISPATCH_MODE_STOP); |
|||
} |
|||
|
|||
/** |
|||
* @param view the view to request/clear focus |
|||
* @param dispatchMode The dispatch mode for this callback. |
|||
* @see WindowInsetsAnimationCompat.Callback.DispatchMode |
|||
*/ |
|||
public ControlFocusInsetsAnimationCallback(@NonNull final View view, final int dispatchMode) { |
|||
super(dispatchMode); |
|||
this.view = view; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public WindowInsetsCompat onProgress(@NonNull final WindowInsetsCompat insets, |
|||
@NonNull final List<WindowInsetsAnimationCompat> runningAnimations) { |
|||
// no-op and return the insets |
|||
return insets; |
|||
} |
|||
|
|||
@Override |
|||
public void onEnd(final WindowInsetsAnimationCompat animation) { |
|||
if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) { |
|||
// The animation has now finished, so we can check the view's focus state. |
|||
// We post the check because the rootWindowInsets has not yet been updated, but will |
|||
// be in the next message traversal |
|||
view.post(this::checkFocus); |
|||
} |
|||
} |
|||
|
|||
private void checkFocus() { |
|||
final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view); |
|||
boolean imeVisible = false; |
|||
if (rootWindowInsets != null) { |
|||
imeVisible = rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime()); |
|||
} |
|||
if (imeVisible && view.getRootView().findFocus() == null) { |
|||
// If the IME will be visible, and there is not a currently focused view in |
|||
// the hierarchy, request focus on our view |
|||
view.requestFocus(); |
|||
} else if (!imeVisible && view.isFocused()) { |
|||
// If the IME will not be visible and our view is currently focused, clear the focus |
|||
view.clearFocus(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,117 @@ |
|||
package awais.instagrabber.customviews.helpers; |
|||
|
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.core.graphics.Insets; |
|||
import androidx.core.view.ViewCompat; |
|||
import androidx.core.view.WindowInsetsAnimationCompat; |
|||
import androidx.core.view.WindowInsetsCompat; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* A customized {@link TranslateDeferringInsetsAnimationCallback} for the emoji picker |
|||
*/ |
|||
public class EmojiPickerInsetsAnimationCallback extends WindowInsetsAnimationCompat.Callback { |
|||
private static final String TAG = EmojiPickerInsetsAnimationCallback.class.getSimpleName(); |
|||
|
|||
private final View view; |
|||
private final int persistentInsetTypes; |
|||
private final int deferredInsetTypes; |
|||
|
|||
private int kbHeight; |
|||
private onKbVisibilityChangeListener listener; |
|||
private boolean shouldTranslate; |
|||
|
|||
public EmojiPickerInsetsAnimationCallback(final View view, |
|||
final int persistentInsetTypes, |
|||
final int deferredInsetTypes) { |
|||
this(view, persistentInsetTypes, deferredInsetTypes, DISPATCH_MODE_STOP); |
|||
} |
|||
|
|||
public EmojiPickerInsetsAnimationCallback(final View view, |
|||
final int persistentInsetTypes, |
|||
final int deferredInsetTypes, |
|||
final int dispatchMode) { |
|||
super(dispatchMode); |
|||
if ((persistentInsetTypes & deferredInsetTypes) != 0) { |
|||
throw new IllegalArgumentException("persistentInsetTypes and deferredInsetTypes can not contain " + |
|||
"any of same WindowInsetsCompat.Type values"); |
|||
} |
|||
this.view = view; |
|||
this.persistentInsetTypes = persistentInsetTypes; |
|||
this.deferredInsetTypes = deferredInsetTypes; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public WindowInsetsCompat onProgress(@NonNull final WindowInsetsCompat insets, |
|||
@NonNull final List<WindowInsetsAnimationCompat> runningAnimations) { |
|||
// onProgress() is called when any of the running animations progress... |
|||
|
|||
// First we get the insets which are potentially deferred |
|||
final Insets typesInset = insets.getInsets(deferredInsetTypes); |
|||
// Then we get the persistent inset types which are applied as padding during layout |
|||
final Insets otherInset = insets.getInsets(persistentInsetTypes); |
|||
|
|||
// Now that we subtract the two insets, to calculate the difference. We also coerce |
|||
// the insets to be >= 0, to make sure we don't use negative insets. |
|||
final Insets subtract = Insets.subtract(typesInset, otherInset); |
|||
final Insets diff = Insets.max(subtract, Insets.NONE); |
|||
|
|||
// The resulting `diff` insets contain the values for us to apply as a translation |
|||
// to the view |
|||
view.setTranslationX(diff.left - diff.right); |
|||
view.setTranslationY(shouldTranslate ? diff.top - diff.bottom : -kbHeight); |
|||
|
|||
return insets; |
|||
} |
|||
|
|||
@Override |
|||
public void onEnd(@NonNull final WindowInsetsAnimationCompat animation) { |
|||
try { |
|||
final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view); |
|||
if (kbHeight == 0) { |
|||
if (rootWindowInsets == null) return; |
|||
final Insets imeInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.ime()); |
|||
final Insets navBarInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()); |
|||
kbHeight = imeInsets.bottom - navBarInsets.bottom; |
|||
final ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); |
|||
if (layoutParams != null) { |
|||
layoutParams.height = kbHeight; |
|||
layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, layoutParams.rightMargin, -kbHeight); |
|||
} |
|||
} |
|||
view.setTranslationX(0f); |
|||
final boolean visible = rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime()); |
|||
float translationY = 0; |
|||
if (!shouldTranslate) { |
|||
translationY = -kbHeight; |
|||
if (visible) { |
|||
translationY = 0; |
|||
} |
|||
} |
|||
view.setTranslationY(translationY); |
|||
|
|||
if (listener != null && rootWindowInsets != null) { |
|||
listener.onChange(visible); |
|||
} |
|||
} finally { |
|||
shouldTranslate = true; |
|||
} |
|||
} |
|||
|
|||
public void setShouldTranslate(final boolean shouldTranslate) { |
|||
this.shouldTranslate = shouldTranslate; |
|||
} |
|||
|
|||
public void setKbVisibilityListener(final onKbVisibilityChangeListener listener) { |
|||
this.listener = listener; |
|||
} |
|||
|
|||
public interface onKbVisibilityChangeListener { |
|||
void onChange(boolean isVisible); |
|||
} |
|||
} |
@ -0,0 +1,139 @@ |
|||
package awais.instagrabber.customviews.helpers;/* |
|||
* Copyright 2020 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
import android.view.View; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.graphics.Insets; |
|||
import androidx.core.view.OnApplyWindowInsetsListener; |
|||
import androidx.core.view.ViewCompat; |
|||
import androidx.core.view.WindowInsetsAnimationCompat; |
|||
import androidx.core.view.WindowInsetsCompat; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* A class which extends/implements both [WindowInsetsAnimationCompat.Callback] and |
|||
* [View.OnApplyWindowInsetsListener], which should be set on the root view in your layout. |
|||
* <p> |
|||
* This class enables the root view is selectively defer handling any insets which match |
|||
* [deferredInsetTypes], to enable better looking [WindowInsetsAnimationCompat]s. |
|||
* <p> |
|||
* An example is the following: when a [WindowInsetsAnimationCompat] is started, the system will dispatch |
|||
* a [WindowInsetsCompat] instance which contains the end state of the animation. For the scenario of |
|||
* the IME being animated in, that means that the insets contains the IME height. If the view's |
|||
* [View.OnApplyWindowInsetsListener] simply always applied the combination of |
|||
* [WindowInsetsCompat.Type.ime] and [WindowInsetsCompat.Type.systemBars] using padding, the viewport of any |
|||
* child views would then be smaller. This results in us animating a smaller (padded-in) view into |
|||
* a larger viewport. Visually, this results in the views looking clipped. |
|||
* <p> |
|||
* This class allows us to implement a different strategy for the above scenario, by selectively |
|||
* deferring the [WindowInsetsCompat.Type.ime] insets until the [WindowInsetsAnimationCompat] is ended. |
|||
* For the above example, you would create a [RootViewDeferringInsetsCallback] like so: |
|||
* <p> |
|||
* ``` |
|||
* val callback = RootViewDeferringInsetsCallback( |
|||
* persistentInsetTypes = WindowInsetsCompat.Type.systemBars(), |
|||
* deferredInsetTypes = WindowInsetsCompat.Type.ime() |
|||
* ) |
|||
* ``` |
|||
* <p> |
|||
* This class is not limited to just IME animations, and can work with any [WindowInsetsCompat.Type]s. |
|||
*/ |
|||
public class RootViewDeferringInsetsCallback extends WindowInsetsAnimationCompat.Callback implements OnApplyWindowInsetsListener { |
|||
|
|||
private final int persistentInsetTypes; |
|||
private final int deferredInsetTypes; |
|||
@Nullable |
|||
private View view = null; |
|||
@Nullable |
|||
private WindowInsetsCompat lastWindowInsets = null; |
|||
private boolean deferredInsets = false; |
|||
|
|||
/** |
|||
* @param persistentInsetTypes the bitmask of any inset types which should always be handled |
|||
* through padding the attached view |
|||
* @param deferredInsetTypes the bitmask of insets types which should be deferred until after |
|||
* any related [WindowInsetsAnimationCompat]s have ended |
|||
*/ |
|||
public RootViewDeferringInsetsCallback(final int persistentInsetTypes, final int deferredInsetTypes) { |
|||
super(DISPATCH_MODE_CONTINUE_ON_SUBTREE); |
|||
if ((persistentInsetTypes & deferredInsetTypes) != 0) { |
|||
throw new IllegalArgumentException("persistentInsetTypes and deferredInsetTypes can not contain " + |
|||
"any of same WindowInsetsCompat.Type values"); |
|||
} |
|||
this.persistentInsetTypes = persistentInsetTypes; |
|||
this.deferredInsetTypes = deferredInsetTypes; |
|||
} |
|||
|
|||
@Override |
|||
public WindowInsetsCompat onApplyWindowInsets(@NonNull final View v, @NonNull final WindowInsetsCompat windowInsets) { |
|||
// Store the view and insets for us in onEnd() below |
|||
view = v; |
|||
lastWindowInsets = windowInsets; |
|||
|
|||
final int types = deferredInsets |
|||
// When the deferred flag is enabled, we only use the systemBars() insets |
|||
? persistentInsetTypes |
|||
// Otherwise we handle the combination of the the systemBars() and ime() insets |
|||
: persistentInsetTypes | deferredInsetTypes; |
|||
|
|||
// Finally we apply the resolved insets by setting them as padding |
|||
final Insets typeInsets = windowInsets.getInsets(types); |
|||
v.setPadding(typeInsets.left, typeInsets.top, typeInsets.right, typeInsets.bottom); |
|||
|
|||
// We return the new WindowInsetsCompat.CONSUMED to stop the insets being dispatched any |
|||
// further into the view hierarchy. This replaces the deprecated |
|||
// WindowInsetsCompat.consumeSystemWindowInsets() and related functions. |
|||
return WindowInsetsCompat.CONSUMED; |
|||
} |
|||
|
|||
@Override |
|||
public void onPrepare(WindowInsetsAnimationCompat animation) { |
|||
if ((animation.getTypeMask() & deferredInsetTypes) != 0) { |
|||
// We defer the WindowInsetsCompat.Type.ime() insets if the IME is currently not visible. |
|||
// This results in only the WindowInsetsCompat.Type.systemBars() being applied, allowing |
|||
// the scrolling view to remain at it's larger size. |
|||
deferredInsets = true; |
|||
} |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public WindowInsetsCompat onProgress(@NonNull final WindowInsetsCompat insets, |
|||
@NonNull final List<WindowInsetsAnimationCompat> runningAnims) { |
|||
// This is a no-op. We don't actually want to handle any WindowInsetsAnimations |
|||
return insets; |
|||
} |
|||
|
|||
@Override |
|||
public void onEnd(@NonNull final WindowInsetsAnimationCompat animation) { |
|||
if (deferredInsets && (animation.getTypeMask() & deferredInsetTypes) != 0) { |
|||
// If we deferred the IME insets and an IME animation has finished, we need to reset |
|||
// the flag |
|||
deferredInsets = false; |
|||
|
|||
// And finally dispatch the deferred insets to the view now. |
|||
// Ideally we would just call view.requestApplyInsets() and let the normal dispatch |
|||
// cycle happen, but this happens too late resulting in a visual flicker. |
|||
// Instead we manually dispatch the most recent WindowInsets to the view. |
|||
if (lastWindowInsets != null && view != null) { |
|||
ViewCompat.dispatchApplyWindowInsets(view, lastWindowInsets); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,443 @@ |
|||
/* |
|||
* Copyright 2020 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package awais.instagrabber.customviews.helpers; |
|||
|
|||
import android.os.CancellationSignal; |
|||
import android.util.Log; |
|||
import android.view.View; |
|||
import android.view.animation.LinearInterpolator; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.graphics.Insets; |
|||
import androidx.core.view.ViewCompat; |
|||
import androidx.core.view.WindowInsetsAnimationControlListenerCompat; |
|||
import androidx.core.view.WindowInsetsAnimationControllerCompat; |
|||
import androidx.core.view.WindowInsetsCompat; |
|||
import androidx.core.view.WindowInsetsControllerCompat; |
|||
import androidx.dynamicanimation.animation.FloatPropertyCompat; |
|||
import androidx.dynamicanimation.animation.SpringAnimation; |
|||
import androidx.dynamicanimation.animation.SpringForce; |
|||
|
|||
import awais.instagrabber.utils.ViewUtils; |
|||
|
|||
/** |
|||
* A wrapper around the [WindowInsetsAnimationControllerCompat] APIs in AndroidX Core, to simplify |
|||
* the implementation of common use-cases around the IME. |
|||
* <p> |
|||
* See [InsetsAnimationLinearLayout] and [InsetsAnimationTouchListener] for examples of how |
|||
* to use this class. |
|||
*/ |
|||
public class SimpleImeAnimationController { |
|||
private static final String TAG = SimpleImeAnimationController.class.getSimpleName(); |
|||
/** |
|||
* Scroll threshold for determining whether to animating to the end state, or to the start state. |
|||
* Currently 15% of the total swipe distance distance |
|||
*/ |
|||
private static final float SCROLL_THRESHOLD = 0.15f; |
|||
|
|||
@Nullable |
|||
private WindowInsetsAnimationControllerCompat insetsAnimationController = null; |
|||
@Nullable |
|||
private CancellationSignal pendingRequestCancellationSignal = null; |
|||
@Nullable |
|||
private OnRequestReadyListener pendingRequestOnReadyListener; |
|||
/** |
|||
* True if the IME was shown at the start of the current animation. |
|||
*/ |
|||
private boolean isImeShownAtStart = false; |
|||
@Nullable |
|||
private SpringAnimation currentSpringAnimation = null; |
|||
private WindowInsetsAnimationControlListenerCompat fwdListener; |
|||
|
|||
/** |
|||
* A LinearInterpolator instance we can re-use across listeners. |
|||
*/ |
|||
private final LinearInterpolator linearInterpolator = new LinearInterpolator(); |
|||
/* To take control of the an WindowInsetsAnimation, we need to pass in a listener to |
|||
controlWindowInsetsAnimation() in startControlRequest(). The listener created here |
|||
keeps track of the current WindowInsetsAnimationController and resets our state. */ |
|||
private final WindowInsetsAnimationControlListenerCompat animationControlListener = new WindowInsetsAnimationControlListenerCompat() { |
|||
/** |
|||
* Once the request is ready, call our [onRequestReady] function |
|||
*/ |
|||
@Override |
|||
public void onReady(@NonNull final WindowInsetsAnimationControllerCompat controller, final int types) { |
|||
onRequestReady(controller); |
|||
if (fwdListener != null) { |
|||
fwdListener.onReady(controller, types); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* If the request is finished, we should reset our internal state |
|||
*/ |
|||
@Override |
|||
public void onFinished(@NonNull final WindowInsetsAnimationControllerCompat controller) { |
|||
reset(); |
|||
if (fwdListener != null) { |
|||
fwdListener.onFinished(controller); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* If the request is cancelled, we should reset our internal state |
|||
*/ |
|||
@Override |
|||
public void onCancelled(@Nullable final WindowInsetsAnimationControllerCompat controller) { |
|||
reset(); |
|||
if (fwdListener != null) { |
|||
fwdListener.onCancelled(controller); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* Start a control request to the [view]s [android.view.WindowInsetsController]. This should |
|||
* be called once the view is in a position to take control over the position of the IME. |
|||
* |
|||
* @param view The view which is triggering this request |
|||
* @param onRequestReadyListener optional listener which will be called when the request is ready and |
|||
* the animation can proceed |
|||
*/ |
|||
public void startControlRequest(@NonNull final View view, |
|||
@Nullable final OnRequestReadyListener onRequestReadyListener) { |
|||
if (isInsetAnimationInProgress()) { |
|||
Log.w(TAG, "startControlRequest: Animation in progress. Can not start a new request to controlWindowInsetsAnimation()"); |
|||
return; |
|||
} |
|||
|
|||
// Keep track of the IME insets, and the IME visibility, at the start of the request |
|||
final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view); |
|||
if (rootWindowInsets != null) { |
|||
isImeShownAtStart = rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime()); |
|||
} |
|||
|
|||
// Create a cancellation signal, which we pass to controlWindowInsetsAnimation() below |
|||
pendingRequestCancellationSignal = new CancellationSignal(); |
|||
// Keep reference to the onReady callback |
|||
pendingRequestOnReadyListener = onRequestReadyListener; |
|||
|
|||
// Finally we make a controlWindowInsetsAnimation() request: |
|||
final WindowInsetsControllerCompat windowInsetsController = ViewCompat.getWindowInsetsController(view); |
|||
if (windowInsetsController != null) { |
|||
windowInsetsController.controlWindowInsetsAnimation( |
|||
// We're only catering for IME animations in this listener |
|||
WindowInsetsCompat.Type.ime(), |
|||
// Animation duration. This is not used by the system, and is only passed to any |
|||
// WindowInsetsAnimation.Callback set on views. We pass in -1 to indicate that we're |
|||
// not starting a finite animation, and that this is completely controlled by |
|||
// the user's touch. |
|||
-1, |
|||
// The time interpolator used in calculating the animation progress. The fraction value |
|||
// we passed into setInsetsAndAlpha() which be passed into this interpolator before |
|||
// being used by the system to inset the IME. LinearInterpolator is a good type |
|||
// to use for scrolling gestures. |
|||
linearInterpolator, |
|||
// A cancellation signal, which allows us to cancel the request to control |
|||
pendingRequestCancellationSignal, |
|||
// The WindowInsetsAnimationControlListener |
|||
animationControlListener |
|||
); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Start a control request to the [view]s [android.view.WindowInsetsController], similar to |
|||
* [startControlRequest], but immediately fling to a finish using [velocityY] once ready. |
|||
* <p> |
|||
* This function is useful for fire-and-forget operations to animate the IME. |
|||
* |
|||
* @param view The view which is triggering this request |
|||
* @param velocityY the velocity of the touch gesture which caused this call |
|||
*/ |
|||
public void startAndFling(@NonNull final View view, final float velocityY) { |
|||
startControlRequest(view, null); |
|||
animateToFinish(velocityY); |
|||
} |
|||
|
|||
/** |
|||
* Update the inset position of the IME by the given [dy] value. This value will be coerced |
|||
* into the hidden and shown inset values. |
|||
* <p> |
|||
* This function should only be called if [isInsetAnimationInProgress] returns true. |
|||
* |
|||
* @return the amount of [dy] consumed by the inset animation, in pixels |
|||
*/ |
|||
public int insetBy(final int dy) { |
|||
if (insetsAnimationController == null) { |
|||
throw new IllegalStateException("Current WindowInsetsAnimationController is null." + |
|||
"This should only be called if isAnimationInProgress() returns true"); |
|||
} |
|||
final WindowInsetsAnimationControllerCompat controller = insetsAnimationController; |
|||
|
|||
// Call updateInsetTo() with the new inset value |
|||
return insetTo(controller.getCurrentInsets().bottom - dy); |
|||
} |
|||
|
|||
/** |
|||
* Update the inset position of the IME to be the given [inset] value. This value will be |
|||
* coerced into the hidden and shown inset values. |
|||
* <p> |
|||
* This function should only be called if [isInsetAnimationInProgress] returns true. |
|||
* |
|||
* @return the distance moved by the inset animation, in pixels |
|||
*/ |
|||
public int insetTo(final int inset) { |
|||
if (insetsAnimationController == null) { |
|||
throw new IllegalStateException("Current WindowInsetsAnimationController is null." + |
|||
"This should only be called if isAnimationInProgress() returns true"); |
|||
} |
|||
final WindowInsetsAnimationControllerCompat controller = insetsAnimationController; |
|||
|
|||
final int hiddenBottom = controller.getHiddenStateInsets().bottom; |
|||
final int shownBottom = controller.getShownStateInsets().bottom; |
|||
final int startBottom = isImeShownAtStart ? shownBottom : hiddenBottom; |
|||
final int endBottom = isImeShownAtStart ? hiddenBottom : shownBottom; |
|||
|
|||
// We coerce the given inset within the limits of the hidden and shown insets |
|||
final int coercedBottom = coerceIn(inset, hiddenBottom, shownBottom); |
|||
|
|||
final int consumedDy = controller.getCurrentInsets().bottom - coercedBottom; |
|||
|
|||
// Finally update the insets in the WindowInsetsAnimationController using |
|||
// setInsetsAndAlpha(). |
|||
controller.setInsetsAndAlpha( |
|||
// Here we update the animating insets. This is what controls where the IME is displayed. |
|||
// It is also passed through to views via their WindowInsetsAnimation.Callback. |
|||
Insets.of(0, 0, 0, coercedBottom), |
|||
// This controls the alpha value. We don't want to alter the alpha so use 1f |
|||
1f, |
|||
// Finally we calculate the animation progress fraction. This value is passed through |
|||
// to any WindowInsetsAnimation.Callbacks, but it is not used by the system. |
|||
(coercedBottom - startBottom) / (float) (endBottom - startBottom) |
|||
); |
|||
|
|||
return consumedDy; |
|||
} |
|||
|
|||
/** |
|||
* Return `true` if an inset animation is in progress. |
|||
*/ |
|||
public boolean isInsetAnimationInProgress() { |
|||
return insetsAnimationController != null; |
|||
} |
|||
|
|||
/** |
|||
* Return `true` if an inset animation is currently finishing. |
|||
*/ |
|||
public boolean isInsetAnimationFinishing() { |
|||
return currentSpringAnimation != null; |
|||
} |
|||
|
|||
/** |
|||
* Return `true` if a request to control an inset animation is in progress. |
|||
*/ |
|||
public boolean isInsetAnimationRequestPending() { |
|||
return pendingRequestCancellationSignal != null; |
|||
} |
|||
|
|||
/** |
|||
* Cancel the current [WindowInsetsAnimationControllerCompat]. We immediately finish |
|||
* the animation, reverting back to the state at the start of the gesture. |
|||
*/ |
|||
public void cancel() { |
|||
if (insetsAnimationController != null) { |
|||
insetsAnimationController.finish(isImeShownAtStart); |
|||
} |
|||
if (pendingRequestCancellationSignal != null) { |
|||
pendingRequestCancellationSignal.cancel(); |
|||
} |
|||
if (currentSpringAnimation != null) { |
|||
// Cancel the current spring animation |
|||
currentSpringAnimation.cancel(); |
|||
} |
|||
reset(); |
|||
} |
|||
|
|||
/** |
|||
* Finish the current [WindowInsetsAnimationControllerCompat] immediately. |
|||
*/ |
|||
public void finish() { |
|||
final WindowInsetsAnimationControllerCompat controller = insetsAnimationController; |
|||
|
|||
if (controller == null) { |
|||
// If we don't currently have a controller, cancel any pending request and return |
|||
if (pendingRequestCancellationSignal != null) { |
|||
pendingRequestCancellationSignal.cancel(); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
final int current = controller.getCurrentInsets().bottom; |
|||
final int shown = controller.getShownStateInsets().bottom; |
|||
final int hidden = controller.getHiddenStateInsets().bottom; |
|||
|
|||
// The current inset matches either the shown/hidden inset, finish() immediately |
|||
if (current == shown) { |
|||
controller.finish(true); |
|||
} else if (current == hidden) { |
|||
controller.finish(false); |
|||
} else { |
|||
// Otherwise, we'll look at the current position... |
|||
if (controller.getCurrentFraction() >= SCROLL_THRESHOLD) { |
|||
// If the IME is past the 'threshold' we snap to the toggled state |
|||
controller.finish(!isImeShownAtStart); |
|||
} else { |
|||
// ...otherwise, we snap back to the original visibility |
|||
controller.finish(isImeShownAtStart); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Finish the current [WindowInsetsAnimationControllerCompat]. We finish the animation, |
|||
* animating to the end state if necessary. |
|||
* |
|||
* @param velocityY the velocity of the touch gesture which caused this call to [animateToFinish]. |
|||
* Can be `null` if velocity is not available. |
|||
*/ |
|||
public void animateToFinish(@Nullable final Float velocityY) { |
|||
final WindowInsetsAnimationControllerCompat controller = insetsAnimationController; |
|||
|
|||
if (controller == null) { |
|||
// If we don't currently have a controller, cancel any pending request and return |
|||
if (pendingRequestCancellationSignal != null) { |
|||
pendingRequestCancellationSignal.cancel(); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
final int current = controller.getCurrentInsets().bottom; |
|||
final int shown = controller.getShownStateInsets().bottom; |
|||
final int hidden = controller.getHiddenStateInsets().bottom; |
|||
|
|||
if (velocityY != null) { |
|||
// If we have a velocity, we can use it's direction to determine |
|||
// the visibility. Upwards == visible |
|||
animateImeToVisibility(velocityY > 0, velocityY); |
|||
} else if (current == shown) { |
|||
// The current inset matches either the shown/hidden inset, finish() immediately |
|||
controller.finish(true); |
|||
} else if (current == hidden) { |
|||
controller.finish(false); |
|||
} else { |
|||
// Otherwise, we'll look at the current position... |
|||
if (controller.getCurrentFraction() >= SCROLL_THRESHOLD) { |
|||
// If the IME is past the 'threshold' we animate it to the toggled state |
|||
animateImeToVisibility(!isImeShownAtStart, null); |
|||
} else { |
|||
// ...otherwise, we animate it back to the original visibility |
|||
animateImeToVisibility(isImeShownAtStart, null); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void onRequestReady(@NonNull final WindowInsetsAnimationControllerCompat controller) { |
|||
// The request is ready, so clear out the pending cancellation signal |
|||
pendingRequestCancellationSignal = null; |
|||
// Store the current WindowInsetsAnimationController |
|||
insetsAnimationController = controller; |
|||
|
|||
// Call any pending callback |
|||
if (pendingRequestOnReadyListener != null) { |
|||
pendingRequestOnReadyListener.onRequestReady(controller); |
|||
} |
|||
pendingRequestOnReadyListener = null; |
|||
} |
|||
|
|||
/** |
|||
* Resets all of our internal state. |
|||
*/ |
|||
private void reset() { |
|||
// Clear all of our internal state |
|||
insetsAnimationController = null; |
|||
pendingRequestCancellationSignal = null; |
|||
isImeShownAtStart = false; |
|||
if (currentSpringAnimation != null) { |
|||
currentSpringAnimation.cancel(); |
|||
} |
|||
currentSpringAnimation = null; |
|||
pendingRequestOnReadyListener = null; |
|||
} |
|||
|
|||
/** |
|||
* Animate the IME to a given visibility. |
|||
* |
|||
* @param visible `true` to animate the IME to it's fully shown state, `false` to it's |
|||
* fully hidden state. |
|||
* @param velocityY the velocity of the touch gesture which caused this call. Can be `null` |
|||
* if velocity is not available. |
|||
*/ |
|||
private void animateImeToVisibility(final boolean visible, @Nullable final Float velocityY) { |
|||
if (insetsAnimationController == null) { |
|||
throw new IllegalStateException("Controller should not be null"); |
|||
} |
|||
final WindowInsetsAnimationControllerCompat controller = insetsAnimationController; |
|||
|
|||
final FloatPropertyCompat<Object> property = new FloatPropertyCompat<Object>("property") { |
|||
@Override |
|||
public float getValue(final Object object) { |
|||
return controller.getCurrentInsets().bottom; |
|||
} |
|||
|
|||
@Override |
|||
public void setValue(final Object object, final float value) { |
|||
if (insetsAnimationController == null) { |
|||
return; |
|||
} |
|||
insetTo((int) value); |
|||
} |
|||
}; |
|||
final float finalPosition = visible ? controller.getShownStateInsets().bottom |
|||
: controller.getHiddenStateInsets().bottom; |
|||
final SpringForce force = new SpringForce(finalPosition) |
|||
// Tweak the damping value, to remove any bounciness. |
|||
.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY) |
|||
// The stiffness value controls the strength of the spring animation, which |
|||
// controls the speed. Medium (the default) is a good value, but feel free to |
|||
// play around with this value. |
|||
.setStiffness(SpringForce.STIFFNESS_MEDIUM); |
|||
ViewUtils.springAnimationOf(this, property, finalPosition) |
|||
.setSpring(force) |
|||
.setStartVelocity(velocityY != null ? velocityY : 0) |
|||
.addEndListener((animation, canceled, value, velocity) -> { |
|||
if (animation == currentSpringAnimation) { |
|||
currentSpringAnimation = null; |
|||
} |
|||
// Once the animation has ended, finish the controller |
|||
finish(); |
|||
}).start(); |
|||
} |
|||
|
|||
private int coerceIn(final int v, final int min, final int max) { |
|||
if (v >= min && v <= max) { |
|||
return v; |
|||
} |
|||
if (v < min) { |
|||
return min; |
|||
} |
|||
return max; |
|||
} |
|||
|
|||
public void setAnimationControlListener(final WindowInsetsAnimationControlListenerCompat listener) { |
|||
fwdListener = listener; |
|||
} |
|||
|
|||
public interface OnRequestReadyListener { |
|||
void onRequestReady(WindowInsetsAnimationControllerCompat windowInsetsAnimationControllerCompat); |
|||
} |
|||
} |
@ -0,0 +1,128 @@ |
|||
/* |
|||
* Copyright 2020 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package awais.instagrabber.customviews.helpers; |
|||
|
|||
import android.view.View; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.core.graphics.Insets; |
|||
import androidx.core.view.ViewCompat; |
|||
import androidx.core.view.WindowInsetsAnimationCompat; |
|||
import androidx.core.view.WindowInsetsCompat; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* A [WindowInsetsAnimationCompat.Callback] which will translate/move the given view during any |
|||
* inset animations of the given inset type. |
|||
* <p> |
|||
* This class works in tandem with [RootViewDeferringInsetsCallback] to support the deferring of |
|||
* certain [WindowInsetsCompat.Type] values during a [WindowInsetsAnimationCompat], provided in |
|||
* [deferredInsetTypes]. The values passed into this constructor should match those which |
|||
* the [RootViewDeferringInsetsCallback] is created with. |
|||
*/ |
|||
public class TranslateDeferringInsetsAnimationCallback extends WindowInsetsAnimationCompat.Callback { |
|||
private final View view; |
|||
private final int persistentInsetTypes; |
|||
private final int deferredInsetTypes; |
|||
|
|||
private boolean shouldTranslate = true; |
|||
private int kbHeight; |
|||
|
|||
public TranslateDeferringInsetsAnimationCallback(final View view, |
|||
final int persistentInsetTypes, |
|||
final int deferredInsetTypes) { |
|||
this(view, persistentInsetTypes, deferredInsetTypes, DISPATCH_MODE_STOP); |
|||
} |
|||
|
|||
/** |
|||
* @param view the view to translate from it's start to end state |
|||
* @param persistentInsetTypes the bitmask of any inset types which were handled as part of the |
|||
* layout |
|||
* @param deferredInsetTypes the bitmask of insets types which should be deferred until after |
|||
* any [WindowInsetsAnimationCompat]s have ended |
|||
* @param dispatchMode The dispatch mode for this callback. |
|||
* See [WindowInsetsAnimationCompat.Callback.getDispatchMode]. |
|||
*/ |
|||
public TranslateDeferringInsetsAnimationCallback(final View view, |
|||
final int persistentInsetTypes, |
|||
final int deferredInsetTypes, |
|||
final int dispatchMode) { |
|||
super(dispatchMode); |
|||
if ((persistentInsetTypes & deferredInsetTypes) != 0) { |
|||
throw new IllegalArgumentException("persistentInsetTypes and deferredInsetTypes can not contain " + |
|||
"any of same WindowInsetsCompat.Type values"); |
|||
} |
|||
this.view = view; |
|||
this.persistentInsetTypes = persistentInsetTypes; |
|||
this.deferredInsetTypes = deferredInsetTypes; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public WindowInsetsCompat onProgress(@NonNull final WindowInsetsCompat insets, |
|||
@NonNull final List<WindowInsetsAnimationCompat> runningAnimations) { |
|||
// onProgress() is called when any of the running animations progress... |
|||
|
|||
// First we get the insets which are potentially deferred |
|||
final Insets typesInset = insets.getInsets(deferredInsetTypes); |
|||
// Then we get the persistent inset types which are applied as padding during layout |
|||
final Insets otherInset = insets.getInsets(persistentInsetTypes); |
|||
|
|||
// Now that we subtract the two insets, to calculate the difference. We also coerce |
|||
// the insets to be >= 0, to make sure we don't use negative insets. |
|||
final Insets subtract = Insets.subtract(typesInset, otherInset); |
|||
final Insets diff = Insets.max(subtract, Insets.NONE); |
|||
|
|||
// The resulting `diff` insets contain the values for us to apply as a translation |
|||
// to the view |
|||
view.setTranslationX(diff.left - diff.right); |
|||
view.setTranslationY(shouldTranslate ? diff.top - diff.bottom : -kbHeight); |
|||
|
|||
return insets; |
|||
} |
|||
|
|||
@Override |
|||
public void onEnd(@NonNull final WindowInsetsAnimationCompat animation) { |
|||
try { |
|||
final WindowInsetsCompat rootWindowInsets = ViewCompat.getRootWindowInsets(view); |
|||
if (kbHeight == 0) { |
|||
if (rootWindowInsets == null) return; |
|||
final Insets imeInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.ime()); |
|||
final Insets navBarInsets = rootWindowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()); |
|||
kbHeight = imeInsets.bottom - navBarInsets.bottom; |
|||
} |
|||
// Once the animation has ended, reset the translation values |
|||
view.setTranslationX(0f); |
|||
final boolean visible = rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsetsCompat.Type.ime()); |
|||
float translationY = 0; |
|||
if (!shouldTranslate) { |
|||
translationY = -kbHeight; |
|||
if (visible) { |
|||
translationY = 0; |
|||
} |
|||
} |
|||
view.setTranslationY(translationY); |
|||
} finally { |
|||
shouldTranslate = true; |
|||
} |
|||
} |
|||
|
|||
public void setShouldTranslate(final boolean shouldTranslate) { |
|||
this.shouldTranslate = shouldTranslate; |
|||
} |
|||
} |
1144
app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,91 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
/* |
|||
* Copyright (C) 2009 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.util.ObjectsCompat; |
|||
|
|||
/** |
|||
* Container to ease passing around a tuple of two objects. This object provides a sensible |
|||
* implementation of equals(), returning true if equals() is true on each of the contained |
|||
* objects. |
|||
*/ |
|||
public class NullSafePair<F, S> { |
|||
public final @NonNull |
|||
F first; |
|||
public final @NonNull |
|||
S second; |
|||
|
|||
/** |
|||
* Constructor for a Pair. |
|||
* |
|||
* @param first the first object in the Pair |
|||
* @param second the second object in the pair |
|||
*/ |
|||
public NullSafePair(@NonNull F first, @NonNull S second) { |
|||
this.first = first; |
|||
this.second = second; |
|||
} |
|||
|
|||
/** |
|||
* Checks the two objects for equality by delegating to their respective |
|||
* {@link Object#equals(Object)} methods. |
|||
* |
|||
* @param o the {@link androidx.core.util.Pair} to which this one is to be checked for equality |
|||
* @return true if the underlying objects of the Pair are both considered |
|||
* equal |
|||
*/ |
|||
@Override |
|||
public boolean equals(Object o) { |
|||
if (!(o instanceof androidx.core.util.Pair)) { |
|||
return false; |
|||
} |
|||
androidx.core.util.Pair<?, ?> p = (androidx.core.util.Pair<?, ?>) o; |
|||
return ObjectsCompat.equals(p.first, first) && ObjectsCompat.equals(p.second, second); |
|||
} |
|||
|
|||
/** |
|||
* Compute a hash code using the hash codes of the underlying objects |
|||
* |
|||
* @return a hashcode of the Pair |
|||
*/ |
|||
@Override |
|||
public int hashCode() { |
|||
return first.hashCode() ^ second.hashCode(); |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public String toString() { |
|||
return "Pair{" + first + " " + second + "}"; |
|||
} |
|||
|
|||
/** |
|||
* Convenience method for creating an appropriately typed pair. |
|||
* |
|||
* @param a the first object in the Pair |
|||
* @param b the second object in the pair |
|||
* @return a Pair that is templatized with the types of a and b |
|||
*/ |
|||
@NonNull |
|||
public static <A, B> androidx.core.util.Pair<A, B> create(@Nullable A a, @Nullable B b) { |
|||
return new androidx.core.util.Pair<A, B>(a, b); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<set xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
<translate |
|||
android:duration="@android:integer/config_mediumAnimTime" |
|||
android:fromXDelta="50%p" |
|||
android:toXDelta="0" /> |
|||
<alpha |
|||
android:duration="@android:integer/config_mediumAnimTime" |
|||
android:fromAlpha="0.0" |
|||
android:toAlpha="1.0" /> |
|||
</set> |
@ -1,10 +1,5 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<set xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:shareInterpolator="false"> |
|||
<translate |
|||
android:duration="300" |
|||
android:fromXDelta="100%" |
|||
android:fromYDelta="0%" |
|||
android:toXDelta="0%" |
|||
android:toYDelta="0%" /> |
|||
</set> |
|||
<translate xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:duration="300" |
|||
android:fromXDelta="100%" |
|||
android:toXDelta="0%" /> |
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<set xmlns:android="http://schemas.android.com/apk/res/android"> |
|||
<translate |
|||
android:duration="@android:integer/config_mediumAnimTime" |
|||
android:fromXDelta="0" |
|||
android:toXDelta="-50%p" /> |
|||
<alpha |
|||
android:duration="@android:integer/config_mediumAnimTime" |
|||
android:fromAlpha="1.0" |
|||
android:toAlpha="0.0" /> |
|||
</set> |
@ -1,10 +1,5 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<set xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:shareInterpolator="false"> |
|||
<translate |
|||
android:duration="300" |
|||
android:fromXDelta="0%" |
|||
android:fromYDelta="0%" |
|||
android:toXDelta="100%" |
|||
android:toYDelta="0%" /> |
|||
</set> |
|||
<translate xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:duration="300" |
|||
android:fromXDelta="0%" |
|||
android:toXDelta="100%" /> |
@ -1,10 +1,10 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:tint="?colorControlNormal" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="#333333"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/> |
|||
android:viewportHeight="24"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z" /> |
|||
</vector> |
@ -0,0 +1,10 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="?attr/colorControlNormal"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M17,3L7,3c-1.1,0 -2,0.9 -2,2v16l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,6c0,-0.55 0.45,-1 1,-1h8c0.55,0 1,0.45 1,1v12z"/> |
|||
</vector> |
@ -0,0 +1,10 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="?attr/colorControlNormal"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M3,17.46v3.04c0,0.28 0.22,0.5 0.5,0.5h3.04c0.13,0 0.26,-0.05 0.35,-0.15L17.81,9.94l-3.75,-3.75L3.15,17.1c-0.1,0.1 -0.15,0.22 -0.15,0.36zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/> |
|||
</vector> |
@ -1,5 +1,5 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:shape="oval"> |
|||
<solid android:color="@color/black" /> |
|||
<solid android:color="@android:color/transparent" /> |
|||
</shape> |
@ -1,477 +1,128 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:background="@color/black_a80"> |
|||
android:background="?colorSurface" |
|||
tools:context=".fragments.PostViewV2Fragment"> |
|||
|
|||
<awais.instagrabber.customviews.drawee.DraggableZoomableDraweeView |
|||
android:id="@+id/post_image" |
|||
<androidx.constraintlayout.widget.ConstraintLayout |
|||
android:id="@+id/content_root" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:background="@null" |
|||
android:clickable="true" |
|||
android:focusable="true" |
|||
android:transitionName="post_image" |
|||
app:actualImageScaleType="fitCenter" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="parent" |
|||
tools:background="@mipmap/ic_launcher" |
|||
tools:visibility="visible" /> |
|||
android:layout_height="wrap_content"> |
|||
|
|||
<include |
|||
android:id="@+id/video_post" |
|||
layout="@layout/layout_video_player_with_thumbnail" |
|||
android:visibility="gone" /> |
|||
|
|||
<androidx.viewpager2.widget.ViewPager2 |
|||
android:id="@+id/slider_parent" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:visibility="gone" |
|||
tools:visibility="visible" /> |
|||
|
|||
<View |
|||
android:id="@+id/top_bg" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:background="@color/black_a80" |
|||
app:layout_constraintBottom_toBottomOf="@id/profile_pic" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="parent" /> |
|||
|
|||
<awais.instagrabber.customviews.ProfilePicView |
|||
android:id="@+id/profile_pic" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_margin="12dp" |
|||
android:transitionName="profile_pic" |
|||
app:layout_constraintBottom_toBottomOf="@id/top_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/title" |
|||
app:layout_constraintHorizontal_bias="0" |
|||
app:layout_constraintHorizontal_chainStyle="packed" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="@id/top_bg" |
|||
app:size="regular" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/title" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="marquee" |
|||
android:singleLine="true" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" |
|||
android:textColor="@color/white" |
|||
app:layout_constrainedWidth="true" |
|||
app:layout_constraintBottom_toTopOf="@id/subtitle" |
|||
app:layout_constraintEnd_toStartOf="@id/options" |
|||
app:layout_constraintStart_toEndOf="@id/profile_pic" |
|||
app:layout_constraintTop_toTopOf="@id/profile_pic" |
|||
tools:text="Username Username Username" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/subtitle" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="end" |
|||
android:singleLine="true" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" |
|||
android:textColor="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="@id/profile_pic" |
|||
app:layout_constraintEnd_toStartOf="@id/options" |
|||
app:layout_constraintStart_toStartOf="@id/title" |
|||
app:layout_constraintTop_toBottomOf="@id/title" |
|||
tools:text="Full name Full name Full name Full name Full name Full name Full name " /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatImageView |
|||
android:id="@+id/options" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="0dp" |
|||
android:paddingStart="8dp" |
|||
android:paddingEnd="8dp" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/top_bg" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintTop_toTopOf="@id/top_bg" |
|||
app:srcCompat="@drawable/ic_more_vert_24" |
|||
app:tint="@color/white" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.constraintlayout.widget.Group |
|||
android:id="@+id/user_details_group" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
app:constraint_referenced_ids="top_bg, profile_pic,title,subtitle,options" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/media_counter" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="16dp" |
|||
android:background="@drawable/rounder_corner_semi_black_bg" |
|||
android:gravity="center" |
|||
android:padding="8dp" |
|||
android:textAppearance="@style/TextAppearance.AppCompat.Caption" |
|||
android:textColor="@android:color/white" |
|||
android:visibility="gone" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/top_bg" |
|||
tools:text="1/5" |
|||
tools:visibility="visible" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/location" |
|||
style="?borderlessButtonStyle" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginTop="16dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:elevation="0dp" |
|||
android:ellipsize="end" |
|||
android:insetTop="0dp" |
|||
android:insetBottom="0dp" |
|||
android:maxWidth="200dp" |
|||
android:maxLines="1" |
|||
android:minHeight="32dp" |
|||
android:paddingStart="8dp" |
|||
android:paddingEnd="8dp" |
|||
android:textAlignment="viewStart" |
|||
android:textAllCaps="false" |
|||
android:textColor="@android:color/white" |
|||
android:visibility="gone" |
|||
app:backgroundTint="@color/black_a50" |
|||
app:elevation="0dp" |
|||
app:icon="@drawable/ic_round_location_on_24" |
|||
app:iconSize="16dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/top_bg" |
|||
app:rippleColor="@color/grey_600" |
|||
tools:text="Location, Location, Location, Location, " |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.coordinatorlayout.widget.CoordinatorLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="0dp" |
|||
android:background="@null" |
|||
app:layout_constraintBottom_toBottomOf="@id/bottom_bg_barrier" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="parent" |
|||
tools:visibility="visible"> |
|||
|
|||
<androidx.core.widget.NestedScrollView |
|||
android:id="@+id/caption_parent" |
|||
android:layout_width="match_parent" |
|||
<awais.instagrabber.customviews.ProfilePicView |
|||
android:id="@+id/profile_pic" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:background="@color/black_a80" |
|||
app:behavior_hideable="true" |
|||
app:behavior_peekHeight="100dp" |
|||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> |
|||
|
|||
<ScrollView |
|||
android:id="@+id/bottom_scroll_view" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:background="@null"> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:orientation="vertical"> |
|||
|
|||
<awais.instagrabber.customviews.RamboTextViewV2 |
|||
android:id="@+id/caption" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="bottom" |
|||
android:background="@null" |
|||
android:clickable="true" |
|||
android:focusable="true" |
|||
android:padding="16dp" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" |
|||
android:textColor="@color/white" |
|||
tools:text="Text text text" /> |
|||
|
|||
<!--<androidx.appcompat.widget.AppCompatTextView--> |
|||
<!-- android:id="@+id/editCaption"--> |
|||
<!-- android:layout_width="match_parent"--> |
|||
<!-- android:layout_height="wrap_content"--> |
|||
<!-- android:layout_marginStart="16dp"--> |
|||
<!-- android:layout_marginTop="8dp"--> |
|||
<!-- android:background="@null"--> |
|||
<!-- android:gravity="center_vertical"--> |
|||
<!-- android:text="@string/edit_caption"--> |
|||
<!-- android:textColor="?android:textColorSecondary"--> |
|||
<!-- android:textSize="16sp"--> |
|||
<!-- android:visibility="gone"--> |
|||
<!-- app:layout_constraintBottom_toTopOf="@id/translatedCaption"--> |
|||
<!-- app:layout_constraintTop_toBottomOf="@id/caption"--> |
|||
<!-- tools:visibility="visible" />--> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/translate" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:background="@null" |
|||
android:gravity="center_vertical" |
|||
android:paddingStart="16dp" |
|||
android:paddingTop="8dp" |
|||
android:paddingEnd="16dp" |
|||
android:paddingBottom="8dp" |
|||
android:text="@string/translate_caption" |
|||
android:textColor="@color/blue_600" |
|||
android:textSize="16sp" |
|||
android:visibility="visible" /> |
|||
|
|||
<!--<awais.instagrabber.customviews.RamboTextViewV2--> |
|||
<!-- android:id="@+id/translatedCaption"--> |
|||
<!-- android:layout_width="match_parent"--> |
|||
<!-- android:layout_height="wrap_content"--> |
|||
<!-- android:layout_gravity="bottom"--> |
|||
<!-- android:background="@null"--> |
|||
<!-- android:clickable="true"--> |
|||
<!-- android:focusable="true"--> |
|||
<!-- android:padding="16dp"--> |
|||
<!-- android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"--> |
|||
<!-- android:textColor="@color/white"--> |
|||
<!-- android:visibility="gone"--> |
|||
<!-- app:layout_constraintBottom_toBottomOf="parent"--> |
|||
<!-- app:layout_constraintTop_toBottomOf="@id/translateTitle"--> |
|||
<!-- tools:text="Text text text"--> |
|||
<!-- tools:visibility="visible" />--> |
|||
</LinearLayout> |
|||
</ScrollView> |
|||
</androidx.core.widget.NestedScrollView> |
|||
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
|||
|
|||
<!--<include--> |
|||
<!-- android:id="@+id/player_controls"--> |
|||
<!-- layout="@layout/layout_exo_custom_controls"--> |
|||
<!-- android:layout_width="0dp"--> |
|||
<!-- android:layout_height="wrap_content"--> |
|||
<!-- android:visibility="gone"--> |
|||
<!-- app:layout_constraintBottom_toTopOf="@id/bottom_bg_barrier"--> |
|||
<!-- app:layout_constraintEnd_toEndOf="parent"--> |
|||
<!-- app:layout_constraintStart_toStartOf="parent"--> |
|||
<!-- tools:visibility="gone" />--> |
|||
|
|||
<View |
|||
android:id="@+id/bottom_bg" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:background="@color/black_a80" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="@id/bottom_bg_barrier" /> |
|||
|
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/bottom_bg_barrier" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
app:barrierAllowsGoneWidgets="true" |
|||
app:barrierDirection="top" |
|||
app:constraint_referenced_ids="likes_count,comments_count,views_count" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/likes_count" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:padding="8dp" |
|||
android:textColor="@color/white" |
|||
app:layout_constraintBottom_toTopOf="@id/counts_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/comments_count" |
|||
app:layout_constraintHorizontal_bias="0" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/bottom_bg_barrier" |
|||
tools:text="9999999999 likes" |
|||
tools:visibility="gone" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/comments_count" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:padding="8dp" |
|||
android:textColor="@color/white" |
|||
app:layout_constraintBottom_toTopOf="@id/counts_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/views_count" |
|||
app:layout_constraintHorizontal_bias="0" |
|||
app:layout_constraintStart_toEndOf="@id/likes_count" |
|||
app:layout_constraintTop_toBottomOf="@id/bottom_bg_barrier" |
|||
tools:text="9999999 comments" |
|||
tools:visibility="gone" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/views_count" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:padding="8dp" |
|||
android:textColor="@color/white" |
|||
app:layout_constraintBottom_toTopOf="@id/counts_barrier" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@id/comments_count" |
|||
app:layout_constraintTop_toBottomOf="@id/bottom_bg_barrier" |
|||
tools:text="9999999999 views" |
|||
tools:visibility="gone" /> |
|||
|
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/counts_barrier" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
app:barrierAllowsGoneWidgets="true" |
|||
app:barrierDirection="top" |
|||
app:constraint_referenced_ids="date" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/date" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:padding="8dp" |
|||
android:textColor="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="@id/buttons_barrier" |
|||
app:layout_constraintTop_toBottomOf="@id/counts_barrier" |
|||
tools:text="2020-11-07 11:18:55" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/buttons_barrier" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
app:barrierAllowsGoneWidgets="true" |
|||
app:barrierDirection="bottom" |
|||
app:constraint_referenced_ids="date" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/caption_toggle" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="0dp" |
|||
android:layout_height="48dp" |
|||
android:visibility="visible" |
|||
app:icon="@drawable/ic_notes_24" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/like" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_barrier" |
|||
app:rippleColor="@color/grey_300" |
|||
tools:visibility="visible" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/like" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="0dp" |
|||
android:layout_height="48dp" |
|||
android:visibility="visible" |
|||
app:icon="@drawable/ic_not_liked" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/comment" |
|||
app:layout_constraintStart_toEndOf="@id/caption_toggle" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_barrier" |
|||
app:rippleColor="@color/grey_300" |
|||
tools:visibility="visible" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/comment" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="0dp" |
|||
android:layout_height="48dp" |
|||
android:visibility="visible" |
|||
app:icon="@drawable/ic_outline_comments_24" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/save" |
|||
app:layout_constraintStart_toEndOf="@id/like" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_barrier" |
|||
app:rippleColor="@color/grey_300" |
|||
tools:visibility="visible" /> |
|||
|
|||
<!--<com.google.android.material.button.MaterialButton--> |
|||
<!-- android:id="@+id/player_controls_toggle"--> |
|||
<!-- style="@style/Widget.MaterialComponents.Button.TextButton"--> |
|||
<!-- android:layout_width="0dp"--> |
|||
<!-- android:layout_height="48dp"--> |
|||
<!-- android:visibility="gone"--> |
|||
<!-- app:icon="@drawable/ic_play_circle_outline_24"--> |
|||
<!-- app:iconGravity="textStart"--> |
|||
<!-- app:iconPadding="0dp"--> |
|||
<!-- app:iconSize="24dp"--> |
|||
<!-- app:iconTint="@color/white"--> |
|||
<!-- app:layout_constraintBottom_toBottomOf="parent"--> |
|||
<!-- app:layout_constraintEnd_toStartOf="@id/save"--> |
|||
<!-- app:layout_constraintStart_toEndOf="@id/comment"--> |
|||
<!-- app:layout_constraintTop_toBottomOf="@id/buttons_barrier"--> |
|||
<!-- app:rippleColor="@color/grey_300"--> |
|||
<!-- tools:visibility="visible" />--> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/save" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="0dp" |
|||
android:layout_height="48dp" |
|||
android:visibility="visible" |
|||
app:icon="@drawable/ic_outline_class_24" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/share" |
|||
app:layout_constraintStart_toEndOf="@id/comment" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_barrier" |
|||
app:rippleColor="@color/grey_300" |
|||
tools:visibility="visible" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/share" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="0dp" |
|||
android:layout_height="48dp" |
|||
android:visibility="visible" |
|||
app:icon="?attr/actionModeShareDrawable" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/download" |
|||
app:layout_constraintStart_toEndOf="@id/save" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_barrier" |
|||
app:rippleColor="@color/grey_300" |
|||
tools:visibility="visible" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/download" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="0dp" |
|||
android:layout_height="48dp" |
|||
android:visibility="visible" |
|||
app:icon="@drawable/ic_download" |
|||
app:iconGravity="textStart" |
|||
app:iconPadding="0dp" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@id/share" |
|||
app:layout_constraintTop_toBottomOf="@id/buttons_barrier" |
|||
app:rippleColor="@color/grey_300" |
|||
tools:visibility="visible" /> |
|||
|
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
|||
android:layout_margin="12dp" |
|||
android:transitionName="profile_pic" |
|||
app:layout_constraintBottom_toTopOf="@id/top_barrier" |
|||
app:layout_constraintEnd_toStartOf="@id/title" |
|||
app:layout_constraintHorizontal_bias="0" |
|||
app:layout_constraintHorizontal_chainStyle="packed" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="parent" |
|||
app:size="regular" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/title" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="marquee" |
|||
android:singleLine="true" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" |
|||
app:layout_constrainedWidth="true" |
|||
app:layout_constraintBottom_toTopOf="@id/subtitle" |
|||
app:layout_constraintEnd_toStartOf="@id/options" |
|||
app:layout_constraintStart_toEndOf="@id/profile_pic" |
|||
app:layout_constraintTop_toTopOf="@id/profile_pic" |
|||
tools:text="Username Username Username" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/subtitle" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="end" |
|||
android:singleLine="true" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" |
|||
app:layout_constraintBottom_toBottomOf="@id/profile_pic" |
|||
app:layout_constraintEnd_toStartOf="@id/options" |
|||
app:layout_constraintStart_toStartOf="@id/title" |
|||
app:layout_constraintTop_toBottomOf="@id/title" |
|||
tools:text="Full name Full name Full name Full name Full name Full name Full name " |
|||
tools:visibility="gone" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatImageView |
|||
android:id="@+id/options" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="0dp" |
|||
android:paddingStart="8dp" |
|||
android:paddingEnd="8dp" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/profile_pic" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintTop_toTopOf="@id/profile_pic" |
|||
app:srcCompat="@drawable/ic_more_vert_24" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/top_barrier" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
app:barrierAllowsGoneWidgets="true" |
|||
app:barrierDirection="bottom" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/media_counter" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="16dp" |
|||
android:background="@drawable/rounder_corner_semi_black_bg" |
|||
android:gravity="center" |
|||
android:padding="8dp" |
|||
android:textAppearance="@style/TextAppearance.AppCompat.Caption" |
|||
android:textColor="@android:color/white" |
|||
android:visibility="gone" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/profile_pic" |
|||
tools:text="1/5" |
|||
tools:visibility="visible" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/location" |
|||
style="?borderlessButtonStyle" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginTop="16dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:elevation="0dp" |
|||
android:ellipsize="end" |
|||
android:insetTop="0dp" |
|||
android:insetBottom="0dp" |
|||
android:maxWidth="200dp" |
|||
android:maxLines="1" |
|||
android:minHeight="32dp" |
|||
android:paddingStart="8dp" |
|||
android:paddingEnd="8dp" |
|||
android:textAlignment="viewStart" |
|||
android:textAllCaps="false" |
|||
android:textColor="@android:color/white" |
|||
android:visibility="gone" |
|||
app:backgroundTint="@color/black_a50" |
|||
app:elevation="0dp" |
|||
app:icon="@drawable/ic_round_location_on_24" |
|||
app:iconSize="16dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/profile_pic" |
|||
app:rippleColor="@color/grey_600" |
|||
tools:text="Location, Location, Location, Location, " |
|||
tools:visibility="visible" /> |
|||
|
|||
<include layout="@layout/layout_post_view_bottom" /> |
|||
|
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
|||
</androidx.core.widget.NestedScrollView> |
@ -1,312 +1,315 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
<awais.instagrabber.customviews.InsetsAnimationLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:clipToPadding="false"> |
|||
android:clipToPadding="false" |
|||
android:orientation="vertical"> |
|||
|
|||
<androidx.recyclerview.widget.RecyclerView |
|||
android:id="@+id/chats" |
|||
android:layout_width="0dp" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="0dp" |
|||
android:layout_weight="1" |
|||
android:scrollbars="none" |
|||
app:layout_constraintBottom_toTopOf="@id/chats_barrier" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="parent" |
|||
tools:listitem="@layout/layout_dm_base" /> |
|||
|
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/chats_barrier" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
app:barrierDirection="bottom" /> |
|||
<androidx.constraintlayout.widget.ConstraintLayout |
|||
android:id="@+id/input_holder" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content"> |
|||
|
|||
<View |
|||
android:id="@+id/reply_bg" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:background="@drawable/bg_input" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toEndOf="@id/input_bg" |
|||
app:layout_constraintStart_toStartOf="@id/input_bg" |
|||
app:layout_constraintTop_toTopOf="@id/reply_info" |
|||
tools:visibility="gone" /> |
|||
<androidx.constraintlayout.widget.Barrier |
|||
android:id="@+id/chats_barrier" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
app:barrierDirection="bottom" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/reply_info" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="end" |
|||
android:paddingStart="16dp" |
|||
android:paddingTop="8dp" |
|||
android:paddingEnd="16dp" |
|||
android:paddingBottom="4dp" |
|||
android:singleLine="true" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toTopOf="@id/reply_preview_text" |
|||
app:layout_constraintEnd_toStartOf="@id/reply_preview_image" |
|||
app:layout_constraintStart_toStartOf="@id/input_bg" |
|||
app:layout_constraintTop_toBottomOf="@id/chats_barrier" |
|||
tools:text="Replying to yourself" |
|||
tools:visibility="gone" /> |
|||
<View |
|||
android:id="@+id/reply_bg" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:background="@drawable/bg_input" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toEndOf="@id/input_bg" |
|||
app:layout_constraintStart_toStartOf="@id/input_bg" |
|||
app:layout_constraintTop_toTopOf="@id/reply_info" |
|||
tools:visibility="gone" /> |
|||
|
|||
<androidx.emoji.widget.EmojiAppCompatTextView |
|||
android:id="@+id/reply_preview_text" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="end" |
|||
android:paddingStart="16dp" |
|||
android:paddingTop="4dp" |
|||
android:paddingEnd="16dp" |
|||
android:paddingBottom="8dp" |
|||
android:singleLine="true" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toTopOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/reply_preview_image" |
|||
app:layout_constraintStart_toStartOf="@id/input_bg" |
|||
app:layout_constraintTop_toBottomOf="@id/reply_info" |
|||
app:layout_goneMarginTop="8dp" |
|||
tools:text="Post" |
|||
tools:visibility="gone" /> |
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/reply_info" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="end" |
|||
android:paddingStart="16dp" |
|||
android:paddingTop="8dp" |
|||
android:paddingEnd="16dp" |
|||
android:paddingBottom="4dp" |
|||
android:singleLine="true" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toTopOf="@id/reply_preview_text" |
|||
app:layout_constraintEnd_toStartOf="@id/reply_preview_image" |
|||
app:layout_constraintStart_toStartOf="@id/input_bg" |
|||
app:layout_constraintTop_toBottomOf="@id/chats_barrier" |
|||
tools:text="Replying to yourself" |
|||
tools:visibility="gone" /> |
|||
|
|||
<com.facebook.drawee.view.SimpleDraweeView |
|||
android:id="@+id/reply_preview_image" |
|||
android:layout_width="@dimen/dm_inbox_avatar_size_small" |
|||
android:layout_height="@dimen/dm_inbox_avatar_size_small" |
|||
android:layout_marginEnd="8dp" |
|||
android:visibility="gone" |
|||
app:actualImageScaleType="centerCrop" |
|||
app:layout_constraintBottom_toBottomOf="@id/reply_preview_text" |
|||
app:layout_constraintEnd_toStartOf="@id/reply_cancel" |
|||
app:layout_constraintStart_toEndOf="@id/reply_preview_text" |
|||
app:layout_constraintTop_toTopOf="@id/reply_info" |
|||
tools:background="@mipmap/ic_launcher" |
|||
tools:visibility="gone" /> |
|||
<androidx.emoji.widget.EmojiAppCompatTextView |
|||
android:id="@+id/reply_preview_text" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="end" |
|||
android:paddingStart="16dp" |
|||
android:paddingTop="4dp" |
|||
android:paddingEnd="16dp" |
|||
android:paddingBottom="8dp" |
|||
android:singleLine="true" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toTopOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/reply_preview_image" |
|||
app:layout_constraintStart_toStartOf="@id/input_bg" |
|||
app:layout_constraintTop_toBottomOf="@id/reply_info" |
|||
app:layout_goneMarginTop="8dp" |
|||
tools:text="Post" |
|||
tools:visibility="gone" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatImageView |
|||
android:id="@+id/reply_cancel" |
|||
android:layout_width="24dp" |
|||
android:layout_height="24dp" |
|||
android:layout_marginEnd="12dp" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/reply_preview_text" |
|||
app:layout_constraintEnd_toEndOf="@id/input_bg" |
|||
app:layout_constraintStart_toEndOf="@id/reply_preview_image" |
|||
app:layout_constraintTop_toTopOf="@id/reply_info" |
|||
app:srcCompat="@drawable/ic_close_24" |
|||
tools:visibility="gone" /> |
|||
<com.facebook.drawee.view.SimpleDraweeView |
|||
android:id="@+id/reply_preview_image" |
|||
android:layout_width="@dimen/dm_inbox_avatar_size_small" |
|||
android:layout_height="@dimen/dm_inbox_avatar_size_small" |
|||
android:layout_marginEnd="8dp" |
|||
android:visibility="gone" |
|||
app:actualImageScaleType="centerCrop" |
|||
app:layout_constraintBottom_toBottomOf="@id/reply_preview_text" |
|||
app:layout_constraintEnd_toStartOf="@id/reply_cancel" |
|||
app:layout_constraintStart_toEndOf="@id/reply_preview_text" |
|||
app:layout_constraintTop_toTopOf="@id/reply_info" |
|||
tools:background="@mipmap/ic_launcher" |
|||
tools:visibility="gone" /> |
|||
|
|||
<!--<androidx.constraintlayout.widget.Group--> |
|||
<!-- android:id="@+id/reply_group"--> |
|||
<!-- android:layout_width="0dp"--> |
|||
<!-- android:layout_height="0dp"--> |
|||
<!-- android:visibility="gone"--> |
|||
<!-- app:constraint_referenced_ids="reply_bg,reply_cancel,reply_info,reply_item_type,reply_preview"--> |
|||
<!-- tools:visibility="visible" />--> |
|||
<androidx.appcompat.widget.AppCompatImageView |
|||
android:id="@+id/reply_cancel" |
|||
android:layout_width="24dp" |
|||
android:layout_height="24dp" |
|||
android:layout_marginEnd="12dp" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/reply_preview_text" |
|||
app:layout_constraintEnd_toEndOf="@id/input_bg" |
|||
app:layout_constraintStart_toEndOf="@id/reply_preview_image" |
|||
app:layout_constraintTop_toTopOf="@id/reply_info" |
|||
app:srcCompat="@drawable/ic_close_24" |
|||
tools:visibility="gone" /> |
|||
|
|||
<View |
|||
android:id="@+id/input_bg" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:layout_marginStart="4dp" |
|||
android:layout_marginEnd="4dp" |
|||
android:background="@drawable/bg_input" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input" |
|||
app:layout_constraintEnd_toStartOf="@id/send" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
tools:visibility="visible" /> |
|||
<!--<androidx.constraintlayout.widget.Group--> |
|||
<!-- android:id="@+id/reply_group"--> |
|||
<!-- android:layout_width="0dp"--> |
|||
<!-- android:layout_height="0dp"--> |
|||
<!-- android:visibility="gone"--> |
|||
<!-- app:constraint_referenced_ids="reply_bg,reply_cancel,reply_info,reply_item_type,reply_preview"--> |
|||
<!-- tools:visibility="visible" />--> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/emoji_toggle" |
|||
style="@style/Widget.MaterialComponents.Button.Icon.NoInsets" |
|||
android:layout_width="24dp" |
|||
android:layout_height="24dp" |
|||
android:layout_marginStart="8dp" |
|||
android:layout_marginEnd="2dp" |
|||
android:background="@android:color/transparent" |
|||
android:scrollbars="none" |
|||
android:visibility="gone" |
|||
app:icon="@drawable/ic_face_24" |
|||
app:iconGravity="textStart" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/grey_700" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/input" |
|||
app:layout_constraintStart_toStartOf="@id/input_bg" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
app:rippleColor="@color/grey_500" |
|||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle" |
|||
app:strokeColor="@color/black" |
|||
app:strokeWidth="1dp" |
|||
tools:visibility="visible" /> |
|||
<View |
|||
android:id="@+id/input_bg" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:layout_marginStart="4dp" |
|||
android:layout_marginEnd="4dp" |
|||
android:background="@drawable/bg_input" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input" |
|||
app:layout_constraintEnd_toStartOf="@id/send" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
tools:visibility="visible" /> |
|||
|
|||
<awais.instagrabber.customviews.KeyNotifyingEmojiEditText |
|||
android:id="@+id/input" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="4dp" |
|||
android:background="@android:color/transparent" |
|||
android:hint="@string/message" |
|||
android:paddingTop="12dp" |
|||
android:paddingBottom="12dp" |
|||
android:textColor="?dmInputTextColor" |
|||
android:textColorHint="@color/grey_500" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/gif" |
|||
app:layout_constraintStart_toEndOf="@id/emoji_toggle" |
|||
app:layout_constraintTop_toBottomOf="@id/reply_preview_text" |
|||
app:layout_goneMarginBottom="4dp" |
|||
app:layout_goneMarginEnd="24dp" |
|||
tools:visibility="visible" /> |
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/emoji_toggle" |
|||
style="@style/Widget.MaterialComponents.Button.Icon.NoInsets" |
|||
android:layout_width="24dp" |
|||
android:layout_height="24dp" |
|||
android:layout_marginStart="8dp" |
|||
android:layout_marginEnd="2dp" |
|||
android:background="@android:color/transparent" |
|||
android:scrollbars="none" |
|||
android:visibility="gone" |
|||
app:icon="@drawable/ic_face_24" |
|||
app:iconGravity="textStart" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/grey_700" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/input" |
|||
app:layout_constraintStart_toStartOf="@id/input_bg" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
app:rippleColor="@color/grey_500" |
|||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle" |
|||
app:strokeColor="@color/black" |
|||
app:strokeWidth="1dp" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatImageButton |
|||
android:id="@+id/gif" |
|||
android:layout_width="32dp" |
|||
android:layout_height="0dp" |
|||
android:background="@android:color/transparent" |
|||
android:scaleType="fitCenter" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/camera" |
|||
app:layout_constraintStart_toEndOf="@id/input" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
app:srcCompat="@drawable/ic_round_gif_24" |
|||
tools:visibility="visible" /> |
|||
<awais.instagrabber.customviews.KeyNotifyingEmojiEditText |
|||
android:id="@+id/input" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="4dp" |
|||
android:background="@android:color/transparent" |
|||
android:hint="@string/message" |
|||
android:paddingTop="12dp" |
|||
android:paddingBottom="12dp" |
|||
android:textColor="?dmInputTextColor" |
|||
android:textColorHint="@color/grey_500" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/gif" |
|||
app:layout_constraintStart_toEndOf="@id/emoji_toggle" |
|||
app:layout_constraintTop_toBottomOf="@id/reply_preview_text" |
|||
app:layout_goneMarginBottom="4dp" |
|||
app:layout_goneMarginEnd="24dp" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatImageButton |
|||
android:id="@+id/camera" |
|||
android:layout_width="32dp" |
|||
android:layout_height="0dp" |
|||
android:layout_marginStart="4dp" |
|||
android:background="@android:color/transparent" |
|||
android:paddingStart="4dp" |
|||
android:paddingEnd="4dp" |
|||
android:scaleType="fitCenter" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/gallery" |
|||
app:layout_constraintStart_toEndOf="@id/gif" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
app:srcCompat="@drawable/ic_camera_24" |
|||
tools:visibility="visible" /> |
|||
<androidx.appcompat.widget.AppCompatImageButton |
|||
android:id="@+id/gif" |
|||
android:layout_width="32dp" |
|||
android:layout_height="0dp" |
|||
android:background="@android:color/transparent" |
|||
android:scaleType="fitCenter" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/camera" |
|||
app:layout_constraintStart_toEndOf="@id/input" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
app:srcCompat="@drawable/ic_round_gif_24" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatImageButton |
|||
android:id="@+id/gallery" |
|||
android:layout_width="32dp" |
|||
android:layout_height="0dp" |
|||
android:layout_marginStart="4dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:background="@android:color/transparent" |
|||
android:paddingStart="4dp" |
|||
android:paddingEnd="4dp" |
|||
android:scaleType="fitCenter" |
|||
android:src="@drawable/ic_image_24" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/send" |
|||
app:layout_constraintStart_toEndOf="@id/camera" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
tools:visibility="visible" /> |
|||
<androidx.appcompat.widget.AppCompatImageButton |
|||
android:id="@+id/camera" |
|||
android:layout_width="32dp" |
|||
android:layout_height="0dp" |
|||
android:layout_marginStart="4dp" |
|||
android:background="@android:color/transparent" |
|||
android:paddingStart="4dp" |
|||
android:paddingEnd="4dp" |
|||
android:scaleType="fitCenter" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/gallery" |
|||
app:layout_constraintStart_toEndOf="@id/gif" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
app:srcCompat="@drawable/ic_camera_24" |
|||
tools:visibility="visible" /> |
|||
|
|||
<awais.instagrabber.customviews.RecordView |
|||
android:id="@+id/record_view" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:visibility="gone" |
|||
app:counter_time_color="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toEndOf="@id/input_bg" |
|||
app:layout_constraintStart_toStartOf="@id/input" |
|||
app:layout_constraintTop_toBottomOf="@id/chats_barrier" |
|||
app:slide_to_cancel_arrow="@drawable/recv_ic_arrow" |
|||
app:slide_to_cancel_arrow_color="@color/white" |
|||
app:slide_to_cancel_bounds="0dp" |
|||
app:slide_to_cancel_margin_right="16dp" |
|||
app:slide_to_cancel_text="Slide To Cancel" |
|||
app:slide_to_cancel_text_color="@color/white" |
|||
tools:visibility="visible" /> |
|||
<androidx.appcompat.widget.AppCompatImageButton |
|||
android:id="@+id/gallery" |
|||
android:layout_width="32dp" |
|||
android:layout_height="0dp" |
|||
android:layout_marginStart="4dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:background="@android:color/transparent" |
|||
android:paddingStart="4dp" |
|||
android:paddingEnd="4dp" |
|||
android:scaleType="fitCenter" |
|||
android:src="@drawable/ic_image_24" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toStartOf="@id/send" |
|||
app:layout_constraintStart_toEndOf="@id/camera" |
|||
app:layout_constraintTop_toTopOf="@id/input" |
|||
tools:visibility="visible" /> |
|||
|
|||
<awais.instagrabber.customviews.RecordButton |
|||
android:id="@+id/send" |
|||
style="@style/Widget.MaterialComponents.Button.Icon.NoInsets" |
|||
android:layout_width="48dp" |
|||
android:layout_height="48dp" |
|||
android:visibility="gone" |
|||
app:backgroundTint="@color/blue_900" |
|||
app:elevation="4dp" |
|||
app:icon="@drawable/avd_mic_to_send_anim" |
|||
app:iconGravity="textStart" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@id/input_bg" |
|||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle" |
|||
tools:visibility="visible" /> |
|||
<awais.instagrabber.customviews.RecordView |
|||
android:id="@+id/record_view" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:visibility="gone" |
|||
app:counter_time_color="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toEndOf="@id/input_bg" |
|||
app:layout_constraintStart_toStartOf="@id/input" |
|||
app:layout_constraintTop_toBottomOf="@id/chats_barrier" |
|||
app:slide_to_cancel_arrow="@drawable/recv_ic_arrow" |
|||
app:slide_to_cancel_arrow_color="@color/white" |
|||
app:slide_to_cancel_bounds="0dp" |
|||
app:slide_to_cancel_margin_right="16dp" |
|||
app:slide_to_cancel_text="Slide To Cancel" |
|||
app:slide_to_cancel_text_color="@color/white" |
|||
tools:visibility="visible" /> |
|||
|
|||
<awais.instagrabber.customviews.emoji.EmojiPicker |
|||
android:id="@+id/emoji_picker" |
|||
android:layout_width="0dp" |
|||
android:layout_height="250dp" |
|||
android:translationY="250dp" |
|||
android:visibility="visible" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
tools:visibility="visible" /> |
|||
<awais.instagrabber.customviews.RecordButton |
|||
android:id="@+id/send" |
|||
style="@style/Widget.MaterialComponents.Button.Icon.NoInsets" |
|||
android:layout_width="48dp" |
|||
android:layout_height="48dp" |
|||
android:visibility="gone" |
|||
app:backgroundTint="@color/blue_900" |
|||
app:elevation="4dp" |
|||
app:icon="@drawable/avd_mic_to_send_anim" |
|||
app:iconGravity="textStart" |
|||
app:iconSize="24dp" |
|||
app:iconTint="@color/white" |
|||
app:layout_constraintBottom_toBottomOf="@id/input_bg" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@id/input_bg" |
|||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/accept_pending_request_question" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:paddingTop="16dp" |
|||
android:paddingBottom="8dp" |
|||
android:text="@string/accept_request_from_user" |
|||
android:textAlignment="center" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toTopOf="@id/decline" |
|||
app:layout_constraintTop_toBottomOf="@id/chats_barrier" |
|||
tools:visibility="gone" /> |
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/accept_pending_request_question" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:paddingTop="16dp" |
|||
android:paddingBottom="8dp" |
|||
android:text="@string/accept_request_from_user" |
|||
android:textAlignment="center" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toTopOf="@id/decline" |
|||
app:layout_constraintTop_toBottomOf="@id/chats_barrier" |
|||
tools:visibility="gone" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/decline" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:background="?selectableItemBackground" |
|||
android:padding="16dp" |
|||
android:text="@string/decline" |
|||
android:textAlignment="center" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" |
|||
android:textColor="@color/red_500" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/accept" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question" |
|||
tools:visibility="gone" /> |
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/decline" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:background="?selectableItemBackground" |
|||
android:padding="16dp" |
|||
android:text="@string/decline" |
|||
android:textAlignment="center" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" |
|||
android:textColor="@color/red_500" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toStartOf="@id/accept" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question" |
|||
tools:visibility="gone" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/accept" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:background="?selectableItemBackground" |
|||
android:padding="16dp" |
|||
android:text="@string/accept" |
|||
android:textAlignment="center" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" |
|||
android:visibility="gone" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@id/decline" |
|||
app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question" |
|||
tools:visibility="gone" /> |
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/accept" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:background="?selectableItemBackground" |
|||
android:padding="16dp" |
|||
android:text="@string/accept" |
|||
android:textAlignment="center" |
|||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" |
|||
android:visibility="gone" |
|||
<awais.instagrabber.customviews.emoji.EmojiPicker |
|||
android:id="@+id/emoji_picker" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="250dp" |
|||
android:layout_marginBottom="-250dp" |
|||
android:alpha="0" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@id/decline" |
|||
app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question" |
|||
tools:visibility="gone" /> |
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
|||
app:layout_constraintStart_toStartOf="parent" /> |
|||
</awais.instagrabber.customviews.InsetsAnimationLinearLayout> |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue