diff --git a/.all-contributorsrc b/.all-contributorsrc index edf8dd87..aa7840b9 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -42,15 +42,6 @@ "bug" ] }, - { - "login": "Zopieux", - "name": "Alexandre Macabies", - "avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4", - "profile": "https://github.com/Zopieux", - "contributions": [ - "code" - ] - }, { "login": "MeLlamoPablo", "name": "Pablo Rodríguez", @@ -61,10 +52,10 @@ ] }, { - "login": "AwaisKing", - "name": "AWAiS", - "avatar_url": "https://avatars3.githubusercontent.com/u/5278488", - "profile": "http://rerolledgeek.blogspot.com/", + "login": "Zopieux", + "name": "Alexandre Macabies", + "avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4", + "profile": "https://github.com/Zopieux", "contributions": [ "code" ] @@ -119,16 +110,6 @@ "translation" ] }, - { - "login": "e-edgren", - "name": "Airikr", - "avatar_url": "https://avatars0.githubusercontent.com/u/53869451", - "profile": "https://airikr.me/", - "contributions": [ - "ideas", - "question" - ] - }, { "login": "Akrai", "name": "Akrai", @@ -274,6 +255,15 @@ "translation" ] }, + { + "login": "Pyrobauve", + "name": "Pyrobauve", + "avatar_url": "https://avatars.githubusercontent.com/u/48654473?v=4", + "profile": "https://github.com/Pyrobauve", + "contributions": [ + "translation" + ] + }, { "login": "RAMAR-RAR", "name": "RAMAR-RAR", @@ -319,6 +309,15 @@ "translation" ] }, + { + "login": "Sitavi", + "name": "Sitavi", + "avatar_url": "https://avatars.githubusercontent.com/u/80586127?v=4", + "profile": "https://github.com/Sitavi", + "contributions": [ + "translation" + ] + }, { "login": "Still34", "name": "Still Hsu", diff --git a/.github/workflows/github_nightly_release.yml b/.github/workflows/github_nightly_release.yml new file mode 100644 index 00000000..ba4517a2 --- /dev/null +++ b/.github/workflows/github_nightly_release.yml @@ -0,0 +1,67 @@ +name: Github nightly + +on: + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '27 10 * * *' # Everyday at 10:27:00 + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build Github unsigned apk + run: ./gradlew assembleGithubRelease --stacktrace --project-prop pre + + - name: Sign APK + uses: r0adkll/sign-android-release@v1 + # ID used to access action output + id: sign_app + with: + releaseDirectory: app/build/outputs/apk/github/release + signingKeyBase64: ${{ secrets.SIGNING_KEY }} + 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 + - name: Create apk artifact + uses: actions/upload-artifact@v2 + with: + name: barinsta_nightly_${{ steps.date.outputs.date }} + path: ${{steps.sign_app.outputs.signedReleaseFile}} + + # Send success notification + - name: Send success Telegram notification + if: ${{ success() }} + uses: appleboy/telegram-action@master + with: + 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}} + + # Send failure notification + - name: Send failure Telegram notification + if: ${{ failure() }} + uses: appleboy/telegram-action@master + with: + to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }} + token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }} + message: "${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} failed.\nhttps://github.com/${{github.repository}}/actions/runs/${{github.run_id}}" diff --git a/.github/workflows/github_pre_release.yml b/.github/workflows/github_pre_release.yml new file mode 100644 index 00000000..22bf3620 --- /dev/null +++ b/.github/workflows/github_pre_release.yml @@ -0,0 +1,68 @@ +name: Github pre-release + +on: workflow_dispatch +# push: +# branches: [ master ] +# pull_request: +# branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.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 + + - name: Sign APK + uses: r0adkll/sign-android-release@v1 + # ID used to access action output + id: sign_app + with: + releaseDirectory: app/build/outputs/apk/github/release + signingKeyBase64: ${{ secrets.SIGNING_KEY }} + 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 + - name: Create apk artifact + uses: actions/upload-artifact@v2 + with: + name: barinsta_pre-release_${{ steps.date.outputs.date }} + path: ${{steps.sign_app.outputs.signedReleaseFile}} + + # Send success notification + - name: Send success Telegram notification + if: ${{ success() }} + uses: appleboy/telegram-action@master + with: + 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}} + + # Send failure notification + - name: Send failure Telegram notification + if: ${{ failure() }} + uses: appleboy/telegram-action@master + with: + to: ${{ secrets.TELEGRAM_BUILDS_CHANNEL_TO }} + token: ${{ secrets.TELEGRAM_BUILDS_BOT_TOKEN }} + message: "${{ github.workflow }} ${{ github.job }} #${{ github.run_number }} failed.\nURL: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}" diff --git a/.github/workflows/label-bugs.yml b/.github/workflows/label-bugs.yml new file mode 100644 index 00000000..6bbb86e8 --- /dev/null +++ b/.github/workflows/label-bugs.yml @@ -0,0 +1,18 @@ +name: Label bugs + +on: + issues: + types: [opened] + +jobs: + add-labels: + runs-on: ubuntu-latest + if: contains(github.event.issue.body, 'New Trace collected:') == true + steps: + - name: Add labels + uses: actions-cool/issues-helper@v2.2.1 + with: + actions: 'add-labels' + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.issue.number }} + labels: 'bug' diff --git a/.github/workflows/label-duplicates.yml b/.github/workflows/label-duplicates.yml new file mode 100644 index 00000000..bffe2a54 --- /dev/null +++ b/.github/workflows/label-duplicates.yml @@ -0,0 +1,18 @@ +name: Label duplicates + +on: + issue_comment: + types: [created] + +jobs: + add-labels: + runs-on: ubuntu-latest + if: contains(github.event.comment.body, 'Duplicate of') == true + steps: + - name: Add labels + uses: actions-cool/issues-helper@v2.2.1 + with: + actions: 'add-labels' + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.issue.number }} + labels: 'duplicate' diff --git a/.gitignore b/.gitignore index d849b086..6b4a96c1 100755 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,6 @@ .externalNativeBuild .cxx app/release -/sentry.properties \ No newline at end of file +/sentry.properties +/app/fdroid/ +/app/github/ diff --git a/.idea/.name b/.idea/.name index 0e33933c..b973c11a 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -InstaGrabber \ No newline at end of file +Barinsta \ No newline at end of file diff --git a/README.md b/README.md index f6a3f323..16258554 100755 --- a/README.md +++ b/README.md @@ -57,46 +57,46 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
Austin Huang

💻 📖 💬 🌍 🤔
Ammar Githam

💻 🎨 🤔 🚧 💬
Anderson Mesquita

💻 🐛 -
Alexandre Macabies

💻
Pablo Rodríguez

💻 -
AWAiS

💻 +
Alexandre Macabies

💻 +
Stefan Najdovski

🎨 🌍 -
Stefan Najdovski

🎨 🌍
CrazyMarvin

💵
Kevin Thomas

💵
Shadowspear123

📝 🐛 🤔 💬
Ricardo

🐛 🌍 -
Airikr

🤔 💬 - -
Akrai

🤔 🌍
avtkal

🌍 + +
Cézar Augusto

🌍
Dimitris T

🌍
farzadx

🌍
Fatih Aydın

🌍 - -
fouze555

🌍
Galang23

🌍 + +
Initdebugs

🌍
Jakub Janek

🌍
GenosseFlosse

🌍
kernoeb

🌍 - -
MoaufmKlo

🌍
nalinalini

🌍 -
peterge1998

🌍 -
PierreM0

🌍 -
RAMAR-RAR

🌍 -
rohang02

🌍 +
peterge1998

🌍 +
PierreM0

🌍 +
Pyrobauve

🌍 +
RAMAR-RAR

🌍 +
rohang02

🌍
retiolus

🌍 + +
rikishi0071

🌍
Alexey Peschany

🌍 +
Sitavi

🌍
Still Hsu

🌍
Ten_Lego

🌍
wagnim

🌍 diff --git a/app/build.gradle b/app/build.gradle index 493366c3..9b39a1a2 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,6 +2,15 @@ apply plugin: 'com.android.application' apply plugin: "androidx.navigation.safeargs" apply from: 'sentry.gradle' +def getGitHash = { -> + def stdout = new ByteArrayOutputStream() + exec { + commandLine 'git', 'rev-parse', '--short', 'HEAD' + standardOutput = stdout + } + return stdout.toString().trim() +} + android { compileSdkVersion 29 @@ -55,7 +64,7 @@ android { productFlavors { github { dimension "repo" - versionNameSuffix "-github" + // versionNameSuffix "-github" // appended in assemble task buildConfigField("String", "dsn", SENTRY_DSN) } @@ -64,6 +73,25 @@ android { versionNameSuffix "-fdroid" } } + + android.applicationVariants.all { variant -> + if (variant.flavorName != "github") return + variant.outputs.all { output -> + def builtType = variant.buildType.name + def versionName = variant.versionName + // def versionCode = variant.versionCode + def flavor = variant.flavorName + + def suffix = "${versionName}-${flavor}_${builtType}" // eg. 19.1.0-github_debug or release + if (builtType.toString() == 'release' && project.hasProperty("pre")) { + // append latest commit short hash for pre-release + suffix = "${versionName}.${getGitHash()}-${flavor}" // eg. 19.1.0.b123456-github + } + + output.versionNameOverride = suffix + outputFileName = "barinsta_${suffix}.apk" + } + } } configurations.all { @@ -85,7 +113,7 @@ dependencies { implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "androidx.appcompat:appcompat-resources:$appcompat_version" - implementation "androidx.recyclerview:recyclerview:1.2.0-beta02" + implementation "androidx.recyclerview:recyclerview:1.2.0-rc01" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation "androidx.viewpager2:viewpager2:1.0.0" implementation "androidx.navigation:navigation-fragment:$nav_version" @@ -105,7 +133,7 @@ dependencies { annotationProcessor "androidx.room:room-compiler:$room_version" // CameraX - def camerax_version = "1.1.0-alpha02" + def camerax_version = "1.1.0-alpha03" implementation "androidx.camera:camera-camera2:$camerax_version" implementation "androidx.camera:camera-lifecycle:$camerax_version" implementation "androidx.camera:camera-view:1.0.0-alpha22" @@ -129,9 +157,9 @@ dependencies { implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2' implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4' - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' githubImplementation 'io.sentry:sentry-android:4.3.0' - + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' } diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java index 4b55c59f..0360b6b7 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java @@ -18,7 +18,6 @@ import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback; import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmProfileBinding; import awais.instagrabber.models.enums.DirectItemType; -import awais.instagrabber.repositories.responses.ImageVersions2; import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.User; @@ -97,7 +96,13 @@ public class DirectItemProfileViewHolder extends DirectItemViewHolder { if (profile == null) return; binding.profilePic.setImageURI(profile.getProfilePicUrl()); binding.username.setText(profile.getUsername()); - binding.fullName.setText(profile.getFullName()); + final String fullName = profile.getFullName(); + if (!TextUtils.isEmpty(fullName)) { + binding.fullName.setVisibility(View.VISIBLE); + binding.fullName.setText(fullName); + } else { + binding.fullName.setVisibility(View.GONE); + } binding.isVerified.setVisibility(profile.isVerified() ? View.VISIBLE : View.GONE); itemView.setOnClickListener(v -> openProfile(profile.getUsername())); } diff --git a/app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveformSeekBar.java b/app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveformSeekBar.java index 9e1cfdb8..a2054df1 100755 --- a/app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveformSeekBar.java +++ b/app/src/main/java/awais/instagrabber/customviews/masoudss_waveform/WaveformSeekBar.java @@ -1,6 +1,7 @@ package awais.instagrabber.customviews.masoudss_waveform; import android.content.Context; +import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; @@ -50,8 +51,21 @@ public final class WaveformSeekBar extends View { public WaveformSeekBar(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { super(context, attrs, defStyleAttr); - this.waveBackgroundColor = context.getResources().getColor(R.color.white); - this.waveProgressColor = context.getResources().getColor(R.color.blue_800); + final TypedArray a = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.WaveformSeekBar, + 0, + 0); + final int backgroundColor; + final int progressColor; + try { + backgroundColor = a.getResourceId(R.styleable.WaveformSeekBar_waveformBackgroundColor, R.color.white); + progressColor = a.getResourceId(R.styleable.WaveformSeekBar_waveformProgressColor, R.color.blue_800); + } finally { + a.recycle(); + } + this.waveBackgroundColor = context.getResources().getColor(backgroundColor); + this.waveProgressColor = context.getResources().getColor(progressColor); } private float getSampleMax() { diff --git a/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java b/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java index bfedd70e..9a850045 100644 --- a/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java @@ -292,7 +292,7 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay else if (item.getItemId() == R.id.delete) { final Context context = getContext(); new AlertDialog.Builder(context) - .setTitle(R.string.edit_collection) + .setTitle(R.string.delete_collection) .setMessage(R.string.delete_collection_note) .setPositiveButton(R.string.confirm, (d, w) -> { collectionService.deleteCollection( diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java index 57a9d7be..ee997c86 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java @@ -79,7 +79,7 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr public void onHighlightClick(final HighlightModel model, final int position) { if (model == null) return; final NavDirections action = StoryListViewerFragmentDirections - .actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forStoryArchive(position)); + .actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forStoryArchive(model.getId())); NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); } diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index 4b2108a8..82438999 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -65,6 +65,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import awais.instagrabber.BuildConfig; import awais.instagrabber.R; @@ -304,11 +306,18 @@ public class StoryViewerFragment extends Fragment { // isNotification = fragmentArgs.getIsNotification(); final Type type = options.getType(); if (currentFeedStoryIndex >= 0) { - viewModel = type == Type.HIGHLIGHT - ? type == Type.STORY_ARCHIVE - ? new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class) - : new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class) - : new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class); + switch (type) { + case HIGHLIGHT: + viewModel = new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class); + break; + case STORY_ARCHIVE: + viewModel = new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class); + break; + default: + case FEED_STORY_POSITION: + viewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class); + break; + } } setupStories(); } @@ -728,9 +737,9 @@ public class StoryViewerFragment extends Fragment { return; } final HighlightModel model = models.get(currentFeedStoryIndex); - currentStoryMediaId = model.getId(); + currentStoryMediaId = parseStoryMediaId(model.getId()); currentStoryUsername = model.getTitle(); - fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername); + fetchOptions = StoryViewerOptions.forStoryArchive(model.getId()); break; } } @@ -1139,4 +1148,20 @@ public class StoryViewerFragment extends Fragment { resetView(); } } + + /** + * Parses the Story's media ID. For user stories this is a number, but for archive stories + * this is "archiveDay:" plus a number. + */ + private static String parseStoryMediaId(String rawId) { + final String regex = "(?:archiveDay:)?(.+)"; + final Pattern pattern = Pattern.compile(regex); + final Matcher matcher = pattern.matcher(rawId); + + if (matcher.matches() && matcher.groupCount() >= 1) { + return matcher.group(1); + } + + return rawId; + } } diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java index 64c7fbca..a960cd36 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java @@ -140,6 +140,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi viewModel.isViewerAdmin().observe(getViewLifecycleOwner(), this::setApprovalRelatedUI); viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required)); viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests); + viewModel.isGroup().observe(getViewLifecycleOwner(), this::setupSettings); final NavController navController = NavHostFragment.findNavController(this); final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry(); if (backStackEntry != null) { @@ -207,13 +208,11 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi } private void init() { - setupSettings(); + // setupSettings(); setupMembers(); } - private void setupSettings() { - Boolean isGroup = viewModel.isGroup().getValue(); - if (isGroup == null) isGroup = false; + private void setupSettings(final boolean isGroup) { binding.groupSettings.setVisibility(isGroup ? View.VISIBLE : View.GONE); binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle()); binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> { diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java index 2962c078..acb1dcde 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java @@ -1227,7 +1227,10 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact if (!isEmojiPickerShown) { binding.emojiPicker.setAlpha(0); } - imm.showSoftInput(binding.input, InputMethodManager.SHOW_IMPLICIT); + final boolean shown = imm.showSoftInput(binding.input, InputMethodManager.SHOW_IMPLICIT); + if (!shown) { + Log.e(TAG, "showKeyboard: System did not display the keyboard"); + } if (!isEmojiPickerShown) { animatePan(keyboardHeight); } diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index 2109f935..f5700280 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -592,10 +592,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe binding.swipeRefreshLayout.setEnabled(false); binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24); binding.privatePage2.setText(R.string.no_acc); - final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.privatePage.getLayoutParams(); - layoutParams.topMargin = 0; - layoutParams.gravity = Gravity.CENTER; - binding.privatePage.setLayoutParams(layoutParams); binding.privatePage.setVisibility(View.VISIBLE); return; } @@ -685,6 +681,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe binding.postsRecyclerView.refresh(); } profileDetailsBinding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); + profileDetailsBinding.isPrivate.setVisibility(profileModel.isPrivate() ? View.VISIBLE : View.GONE); final long profileId = profileModel.getPk(); if (isLoggedIn) { fetchStoryAndHighlights(profileId); @@ -919,11 +916,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } else { profileDetailsBinding.mainFollowers.setClickable(false); profileDetailsBinding.mainFollowing.setClickable(false); - // error binding.privatePage1.setImageResource(R.drawable.lock); binding.privatePage2.setText(R.string.priv_acc); binding.privatePage.setVisibility(View.VISIBLE); binding.postsRecyclerView.setVisibility(View.GONE); + binding.swipeRefreshLayout.setRefreshing(false); } } @@ -1210,7 +1207,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } private void updateSwipeRefreshState() { - Log.d("austin_debug", "usrs: " + binding.postsRecyclerView.isFetching()); binding.swipeRefreshLayout.setRefreshing(binding.postsRecyclerView.isFetching()); } diff --git a/app/src/main/java/awais/instagrabber/managers/ThreadManager.java b/app/src/main/java/awais/instagrabber/managers/ThreadManager.java index 90d67df3..8efff504 100644 --- a/app/src/main/java/awais/instagrabber/managers/ThreadManager.java +++ b/app/src/main/java/awais/instagrabber/managers/ThreadManager.java @@ -179,6 +179,9 @@ public final class ThreadManager { return null; } final DirectInbox inbox = inboxResource.data; + if (inbox == null) { + return null; + } final List threads = inbox.getThreads(); if (threads == null || threads.isEmpty()) { return null; @@ -264,7 +267,10 @@ public final class ThreadManager { } private List getUsersWithCurrentUser(final DirectThread t) { - final ImmutableList.Builder builder = ImmutableList.builder().add(currentUser); + final ImmutableList.Builder builder = ImmutableList.builder(); + if (currentUser != null) { + builder.add(currentUser); + } final List users = t.getUsers(); if (users != null) { builder.addAll(users); diff --git a/app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java b/app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java index 2981d4ec..d8308c8a 100644 --- a/app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java +++ b/app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java @@ -57,8 +57,8 @@ public class StoryViewerOptions implements Serializable { return new StoryViewerOptions(position, Type.FEED_STORY_POSITION); } - public static StoryViewerOptions forStoryArchive(final int position) { - return new StoryViewerOptions(position, Type.STORY_ARCHIVE); + public static StoryViewerOptions forStoryArchive(final String id) { + return new StoryViewerOptions(id, Type.STORY_ARCHIVE); } public long getId() { diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemActionLog.java b/app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemActionLog.java index 33d620e2..c6b40581 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemActionLog.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemActionLog.java @@ -44,7 +44,7 @@ public class DirectItemActionLog implements Serializable { return Objects.hash(description, bold, textAttributes); } - public static class TextRange { + public static class TextRange implements Serializable { private final int start; private final int end; private final String color; diff --git a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java index dbd14027..85f45a21 100644 --- a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java @@ -1,10 +1,8 @@ package awais.instagrabber.utils; import android.Manifest; -import android.app.Activity; import android.content.Context; import android.content.DialogInterface; -import android.content.pm.PackageManager; import android.os.Environment; import android.util.Log; import android.webkit.MimeTypeMap; @@ -13,8 +11,6 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import androidx.work.Constraints; import androidx.work.Data; import androidx.work.NetworkType; @@ -90,17 +86,17 @@ public final class DownloadUtils { return dir; } -// public static void dmDownload(@NonNull final Context context, -// @Nullable final String username, -// final String modelId, -// final String url) { -// if (url == null) return; -// if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) { -// dmDownloadImpl(context, username, modelId, url); -// } else if (context instanceof Activity) { -// ActivityCompat.requestPermissions((Activity) context, PERMS, 8020); -// } -// } + // public static void dmDownload(@NonNull final Context context, + // @Nullable final String username, + // final String modelId, + // final String url) { + // if (url == null) return; + // if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) { + // dmDownloadImpl(context, username, modelId, url); + // } else if (context instanceof Activity) { + // ActivityCompat.requestPermissions((Activity) context, PERMS, 8020); + // } + // } private static void dmDownloadImpl(@NonNull final Context context, @Nullable final String username, @@ -294,7 +290,8 @@ public final class DownloadUtils { final int childPositionIfSingle) { final Map map = new HashMap<>(); for (final Media media : feedModels) { - final File downloadDir = getDownloadDir(context, "@" + media.getUser().getUsername()); + final User mediaUser = media.getUser(); + final File downloadDir = getDownloadDir(context, mediaUser == null ? "" : "@" + mediaUser.getUsername()); if (downloadDir == null) return; switch (media.getMediaType()) { case MEDIA_TYPE_IMAGE: @@ -307,9 +304,8 @@ public final class DownloadUtils { case MEDIA_TYPE_VOICE: { final String url = getUrlOfType(media); String fileName = media.getId(); - final User user = media.getUser(); - if (user != null) { - fileName = user.getUsername() + "_" + fileName; + if (mediaUser != null) { + fileName = mediaUser.getUsername() + "_" + fileName; } final File file = getDownloadSaveFile(downloadDir, fileName, url); map.put(url, file.getAbsolutePath()); diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index 21047942..3c5eb0f6 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -943,8 +943,7 @@ public final class ResponseBodyUtils { // } public static StoryModel parseStoryItem(final JSONObject data, - final boolean isLoc, - final boolean isHashtag, + final boolean isLocOrHashtag, final String username) throws JSONException { final boolean isVideo = data.has("video_duration"); final StoryModel model = new StoryModel(data.getString("id"), @@ -952,9 +951,7 @@ public final class ResponseBodyUtils { .getString("url"), null, isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, data.optLong("taken_at", 0), - (isLoc || isHashtag) - ? data.getJSONObject("user").getString("username") - : username, + isLocOrHashtag ? data.getJSONObject("user").getString("username") : username, data.getJSONObject("user").getLong("pk"), data.optBoolean("can_reply")); diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java index 3e2a3603..1ca3c263 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java @@ -181,6 +181,7 @@ public class DirectThreadViewModel extends AndroidViewModel { MediaUtils.getVoiceInfo(contentResolver, uri, new MediaUtils.OnInfoLoadListener() { @Override public void onLoad(@Nullable final MediaUtils.VideoInfo videoInfo) { + if (videoInfo == null) return; threadManager.sendVoice(data, uri, result.getWaveform(), diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java index fd05af17..9b47839b 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java @@ -95,7 +95,7 @@ public class StoriesService extends BaseService { } try { final JSONObject itemJson = new JSONObject(body).getJSONArray("items").getJSONObject(0); - callback.onSuccess(ResponseBodyUtils.parseStoryItem(itemJson, false, false, null)); + callback.onSuccess(ResponseBodyUtils.parseStoryItem(itemJson, false, null)); } catch (JSONException e) { callback.onFailure(e); } @@ -187,7 +187,7 @@ public class StoriesService extends BaseService { final boolean isBestie = node.optBoolean("has_besties_media", false); StoryModel firstStoryModel = null; if (itemJson != null) { - firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, false, null); + firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, null); } feedStoryModels.add(new FeedStoryModel(id, user, fullyRead, timestamp, firstStoryModel, mediaCount, false, isBestie)); } @@ -364,9 +364,8 @@ public class StoriesService extends BaseService { final ServiceCallback> callback) { final String url = buildUrl(options); final Call userStoryCall = repository.getUserStory(url); - final boolean isLoc = options.getType() == StoryViewerOptions.Type.LOCATION; - final boolean isHashtag = options.getType() == StoryViewerOptions.Type.HASHTAG; - final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT; + final boolean isLocOrHashtag = options.getType() == StoryViewerOptions.Type.LOCATION || options.getType() == StoryViewerOptions.Type.HASHTAG; + final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT || options.getType() == StoryViewerOptions.Type.STORY_ARCHIVE; userStoryCall.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -380,7 +379,7 @@ public class StoriesService extends BaseService { data = new JSONObject(body); if (!isHighlight) { - data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel"); + data = data.optJSONObject((isLocOrHashtag) ? "story" : "reel"); } else { data = data.getJSONObject("reels").optJSONObject(options.getName()); } @@ -388,8 +387,7 @@ public class StoriesService extends BaseService { String username = null; if (data != null // && localUsername == null - && !isLoc - && !isHashtag) { + && !isLocOrHashtag) { username = data.getJSONObject("user").getString("username"); } @@ -397,12 +395,11 @@ public class StoriesService extends BaseService { if (data != null && (media = data.optJSONArray("items")) != null && media.length() > 0 && media.optJSONObject(0) != null) { - final int mediaLen = media.length(); final List models = new ArrayList<>(); for (int i = 0; i < mediaLen; ++i) { data = media.getJSONObject(i); - models.add(ResponseBodyUtils.parseStoryItem(data, isLoc, isHashtag, username)); + models.add(ResponseBodyUtils.parseStoryItem(data, isLocOrHashtag, username)); } callback.onSuccess(models); } else { @@ -543,6 +540,7 @@ public class StoriesService extends BaseService { id = String.valueOf(options.getId()); break; case HIGHLIGHT: + case STORY_ARCHIVE: builder.append("feed/reels_media/?user_ids="); id = options.getName(); break; @@ -550,15 +548,12 @@ public class StoriesService extends BaseService { break; // case FEED_STORY_POSITION: // break; - // case STORY_ARCHIVE: - // break; } if (id == null) { return null; } - final String userId = id.replace(":", "%3A"); - builder.append(userId); - if (type != StoryViewerOptions.Type.HIGHLIGHT) { + builder.append(id); + if (type != StoryViewerOptions.Type.HIGHLIGHT && type != StoryViewerOptions.Type.STORY_ARCHIVE) { builder.append("/story/"); } return builder.toString(); diff --git a/app/src/main/res/layout/fragment_direct_messages_thread.xml b/app/src/main/res/layout/fragment_direct_messages_thread.xml index 007005a1..96b0bc34 100644 --- a/app/src/main/res/layout/fragment_direct_messages_thread.xml +++ b/app/src/main/res/layout/fragment_direct_messages_thread.xml @@ -156,7 +156,7 @@ android:hint="@string/message" android:paddingTop="12dp" android:paddingBottom="12dp" - android:textColor="@color/white" + android:textColor="?dmInputTextColor" android:textColorHint="@color/grey_500" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index a1baa934..7a683ca9 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -50,8 +50,8 @@ @@ -37,93 +36,98 @@ android:layout_gravity="center" android:visibility="gone" /> - + + + + + android:layout_weight="1" + android:text="@string/view_story_post" + android:textColor="@color/btn_green_text_color" + android:visibility="gone" + app:backgroundTint="@color/btn_green_background" /> - + - + - + - + - + + - - - - - - + + diff --git a/app/src/main/res/layout/layout_dm_base.xml b/app/src/main/res/layout/layout_dm_base.xml index 0c389368..fd6c61da 100644 --- a/app/src/main/res/layout/layout_dm_base.xml +++ b/app/src/main/res/layout/layout_dm_base.xml @@ -113,6 +113,7 @@ android:paddingEnd="@dimen/dm_message_card_radius" android:paddingBottom="4dp" android:singleLine="true" + android:textColor="@color/white" app:layout_constraintBottom_toTopOf="@id/chat_message_layout" app:layout_constraintEnd_toEndOf="@id/chat_message_layout" app:layout_constraintHorizontal_bias="0" diff --git a/app/src/main/res/layout/layout_dm_link.xml b/app/src/main/res/layout/layout_dm_link.xml index b5ad42a7..a9b05472 100644 --- a/app/src/main/res/layout/layout_dm_link.xml +++ b/app/src/main/res/layout/layout_dm_link.xml @@ -15,6 +15,7 @@ android:paddingEnd="@dimen/dm_message_card_radius" android:paddingBottom="@dimen/dm_message_card_radius_small" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" + android:textColor="@color/white" tools:text="Some message" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_dm_media_share.xml b/app/src/main/res/layout/layout_dm_media_share.xml index fcc74942..dfe9e45c 100644 --- a/app/src/main/res/layout/layout_dm_media_share.xml +++ b/app/src/main/res/layout/layout_dm_media_share.xml @@ -86,6 +86,7 @@ android:paddingEnd="8dp" android:paddingBottom="0dp" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" + android:textColor="@color/white" android:textStyle="bold" app:layout_constraintBottom_toTopOf="@id/caption" app:layout_constraintEnd_toEndOf="parent" @@ -101,6 +102,7 @@ android:layout_height="wrap_content" android:padding="8dp" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" + android:textColor="@color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/layout_dm_profile.xml b/app/src/main/res/layout/layout_dm_profile.xml index a8e179b5..fc1e11f5 100644 --- a/app/src/main/res/layout/layout_dm_profile.xml +++ b/app/src/main/res/layout/layout_dm_profile.xml @@ -27,7 +27,7 @@ android:ellipsize="end" android:gravity="bottom" android:singleLine="true" - android:textColor="?android:textColorPrimary" + android:textColor="@color/white" android:textSize="16sp" android:textStyle="bold" app:layout_constrainedWidth="true" @@ -62,12 +62,14 @@ android:ellipsize="end" android:singleLine="true" android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" + android:textColor="@color/white" android:visibility="gone" app:layout_constraintBottom_toTopOf="@id/barrier" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/username" app:layout_constraintTop_toBottomOf="@id/username" - tools:text="Full name" /> + tools:text="Full name" + tools:visibility="visible" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_dm_text.xml b/app/src/main/res/layout/layout_dm_text.xml index 7ec388f9..1e1bdca9 100644 --- a/app/src/main/res/layout/layout_dm_text.xml +++ b/app/src/main/res/layout/layout_dm_text.xml @@ -9,4 +9,5 @@ android:paddingEnd="@dimen/dm_message_card_radius" android:paddingBottom="@dimen/dm_message_card_radius_small" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" + android:textColor="@color/white" tools:text="Text message" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_dm_voice_media.xml b/app/src/main/res/layout/layout_dm_voice_media.xml index d2c49818..8c8b5968 100644 --- a/app/src/main/res/layout/layout_dm_voice_media.xml +++ b/app/src/main/res/layout/layout_dm_voice_media.xml @@ -66,10 +66,12 @@ android:layout_height="54dp" android:layout_marginStart="8dp" android:layout_marginEnd="4dp" + app:waveformBackgroundColor="?dmWaveformBgColor" app:layout_constraintBottom_toTopOf="@id/duration" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/play_wrapper" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:waveformProgressColor="?dmWaveformProgressColor" /> @@ -50,6 +51,7 @@ app:chipBackgroundColor="@null" app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintStart_toEndOf="@id/btnFollow" + app:layout_constraintTop_toTopOf="parent" tools:text="omg what do u expect" tools:visibility="visible" /> @@ -66,6 +68,7 @@ app:chipIconTint="@color/blue_700" app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintStart_toEndOf="@id/mainStatus" + app:layout_constraintTop_toTopOf="parent" app:rippleColor="@color/blue_A400" tools:visibility="visible" /> @@ -81,6 +84,7 @@ app:chipIconTint="@color/red_600" app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintStart_toEndOf="@id/btnSaved" + app:layout_constraintTop_toTopOf="parent" app:rippleColor="@color/red_300" tools:visibility="visible" /> @@ -109,6 +113,7 @@ app:chipBackgroundColor="@null" app:chipIcon="@drawable/ic_outline_person_pin_24" app:chipIconTint="@color/deep_orange_800" + app:layout_constraintBottom_toTopOf="@+id/mainFullName" app:layout_constraintStart_toEndOf="@id/mainProfileImage" app:layout_constraintTop_toBottomOf="@id/fav_chip" app:rippleColor="@color/deep_orange_400" @@ -124,6 +129,7 @@ app:chipBackgroundColor="@null" app:chipIcon="@drawable/ic_round_send_24" app:chipIconTint="@color/green" + app:layout_constraintBottom_toTopOf="@+id/mainFullName" app:layout_constraintStart_toEndOf="@id/btnTagged" app:layout_constraintTop_toBottomOf="@id/fav_chip" app:rippleColor="@color/green" @@ -161,6 +167,23 @@ app:srcCompat="@drawable/verified" tools:visibility="visible" /> + + + + + @@ -26,4 +29,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index d2dddd99..fe527b2d 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,7 +1,8 @@ 8dp - @dimen/profile_picture_size + 30dp + 90dp 90dp 40dp diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index b0147225..cd0d78a9 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -20,6 +20,9 @@ @style/ThemeOverlay.MaterialComponents.ActionBar @style/ThemeOverlay.MaterialComponents.MaterialAlertDialog.Light + @color/grey_600 + @color/deep_purple_400 + @color/deep_purple_600