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 4d79357f..6b4a96c1 100755 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ .externalNativeBuild .cxx app/release +/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 e77a6ace..9b39a1a2 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,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 @@ -48,8 +58,41 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } -} + flavorDimensions "repo" + + productFlavors { + github { + dimension "repo" + // versionNameSuffix "-github" // appended in assemble task + buildConfigField("String", "dsn", SENTRY_DSN) + } + + fdroid { + dimension "repo" + 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 { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' @@ -70,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" @@ -81,7 +124,7 @@ dependencies { implementation 'androidx.palette:palette:1.0.0' implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" - implementation 'com.google.guava:guava:27.1-jre' + implementation 'com.google.guava:guava:27.0.1-android' // Room def room_version = "2.2.6" @@ -90,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" @@ -114,8 +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/sentry.gradle b/app/sentry.gradle new file mode 100644 index 00000000..f2b24aa7 --- /dev/null +++ b/app/sentry.gradle @@ -0,0 +1,13 @@ +def dsnKey = 'DSN' +def defaultDsn = '\"\"' + +final Properties properties = new Properties() +File propertiesFile = rootProject.file('sentry.properties') +if (!propertiesFile.exists()) { + propertiesFile.createNewFile() +} +properties.load(new FileInputStream(propertiesFile)) + +ext{ + SENTRY_DSN = properties.getProperty(dsnKey, defaultDsn) +} \ No newline at end of file diff --git a/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java b/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java new file mode 100644 index 00000000..b30e263e --- /dev/null +++ b/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java @@ -0,0 +1,40 @@ +package awais.instagrabber.fragments.settings; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentManager; +import androidx.preference.Preference; + +import java.util.Collections; +import java.util.List; + +import awais.instagrabber.fragments.settings.IFlavorSettings; +import awais.instagrabber.fragments.settings.SettingCategory; + +public final class FlavorSettings implements IFlavorSettings { + + private static FlavorSettings instance; + + private FlavorSettings() { + } + + public static FlavorSettings getInstance() { + if (instance == null) { + instance = new FlavorSettings(); + } + return instance; + } + + @NonNull + @Override + public List getPreferences(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager, + @NonNull final SettingCategory settingCategory) { + // switch (settingCategory) { + // default: + // break; + // } + return Collections.emptyList(); + } +} diff --git a/app/src/fdroid/java/awaisomereport/CrashHandler.java b/app/src/fdroid/java/awaisomereport/CrashHandler.java new file mode 100644 index 00000000..2f27d1c3 --- /dev/null +++ b/app/src/fdroid/java/awaisomereport/CrashHandler.java @@ -0,0 +1,56 @@ +package awaisomereport; + +import android.app.Application; + +import androidx.annotation.NonNull; + +public class CrashHandler implements ICrashHandler { + private static final String TAG = CrashHandler.class.getSimpleName(); + + private final Application application; + + public CrashHandler(@NonNull final Application application) { + this.application = application; + } + + @Override + public void uncaughtException(@NonNull final Thread t, + @NonNull final Throwable exception, + @NonNull final Thread.UncaughtExceptionHandler defaultEH) { + CrashReporterHelper.startErrorReporterActivity(application, exception); + // zipLogs(); + defaultEH.uncaughtException(t, exception); + } + + // public synchronized CrashReporter zipLogs() { + // final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() : + // new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs"); + // + // try (final FileOutputStream fos = new FileOutputStream(crashLogsZip); + // final ZipOutputStream zos = new ZipOutputStream(fos)) { + // + // final File[] files = logDir.listFiles(); + // + // if (files != null) { + // zos.setLevel(5); + // byte[] buffer; + // for (final File file : files) { + // if (file != null && file.length() > 0) { + // buffer = new byte[1024]; + // try (final FileInputStream fis = new FileInputStream(file)) { + // zos.putNextEntry(new ZipEntry(file.getName())); + // int length; + // while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length); + // zos.closeEntry(); + // } + // } + // } + // } + // + // } catch (final Exception e) { + // if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + // } + // + // return this; + // } +} diff --git a/app/src/github/AndroidManifest.xml b/app/src/github/AndroidManifest.xml new file mode 100644 index 00000000..4c6a6fe0 --- /dev/null +++ b/app/src/github/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java b/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java new file mode 100644 index 00000000..85adb8a9 --- /dev/null +++ b/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java @@ -0,0 +1,83 @@ +package awais.instagrabber.fragments.settings; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentManager; +import androidx.preference.Preference; + +import com.google.common.collect.ImmutableList; + +import java.util.Collections; +import java.util.List; + +import awais.instagrabber.R; +import awais.instagrabber.dialogs.ConfirmDialogFragment; + +import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY; +import static awais.instagrabber.utils.Utils.settingsHelper; + +public final class FlavorSettings implements IFlavorSettings { + + private static FlavorSettings instance; + + private FlavorSettings() { + } + + public static FlavorSettings getInstance() { + if (instance == null) { + instance = new FlavorSettings(); + } + return instance; + } + + @NonNull + @Override + public List getPreferences(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager, + @NonNull final SettingCategory settingCategory) { + switch (settingCategory) { + case GENERAL: + return getGeneralPrefs(context, fragmentManager); + default: + break; + } + return Collections.emptyList(); + } + + private List getGeneralPrefs(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager) { + return ImmutableList.of( + getSentryPreference(context, fragmentManager) + ); + } + + private Preference getSentryPreference(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager) { + if (!settingsHelper.hasPreference(PREF_ENABLE_SENTRY)) { + // disabled by default + settingsHelper.putBoolean(PREF_ENABLE_SENTRY, false); + } + return PreferenceHelper.getSwitchPreference( + context, + PREF_ENABLE_SENTRY, + R.string.enable_sentry, + R.string.sentry_summary, + false, + (preference, newValue) -> { + if (!(newValue instanceof Boolean)) return true; + final boolean enabled = (Boolean) newValue; + if (enabled) { + final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance( + 111, + 0, + R.string.sentry_start_next_launch, + R.string.ok, + 0, + 0); + dialogFragment.show(fragmentManager, "sentry_dialog"); + } + return true; + }); + } +} diff --git a/app/src/github/java/awaisomereport/CrashHandler.java b/app/src/github/java/awaisomereport/CrashHandler.java new file mode 100644 index 00000000..cd28eaa2 --- /dev/null +++ b/app/src/github/java/awaisomereport/CrashHandler.java @@ -0,0 +1,59 @@ +package awaisomereport; + +import android.app.Application; + +import androidx.annotation.NonNull; + +import awais.instagrabber.BuildConfig; +import awais.instagrabber.fragments.settings.PreferenceKeys; +import io.sentry.SentryLevel; +import io.sentry.android.core.SentryAndroid; +import io.sentry.protocol.Contexts; +import io.sentry.protocol.Device; + +import static awais.instagrabber.utils.Utils.settingsHelper; + +public class CrashHandler implements ICrashHandler { + private static final String TAG = CrashHandler.class.getSimpleName(); + + private final Application application; + private final boolean enabled; + + public CrashHandler(@NonNull final Application application) { + this.application = application; + if (!settingsHelper.hasPreference(PreferenceKeys.PREF_ENABLE_SENTRY)) { + // disabled by default (change to true if we need enabled by default) + enabled = false; + } else { + enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_SENTRY); + } + if (!enabled) return; + SentryAndroid.init(application, options -> { + options.setDsn(BuildConfig.dsn); + options.setDiagnosticLevel(SentryLevel.ERROR); + options.setBeforeSend((event, hint) -> { + // Removing unneeded info from event + final Contexts contexts = event.getContexts(); + final Device device = contexts.getDevice(); + device.setName(null); + device.setTimezone(null); + device.setCharging(null); + device.setBootTime(null); + device.setFreeStorage(null); + device.setBatteryTemperature(null); + return event; + }); + }); + } + + @Override + public void uncaughtException(@NonNull final Thread t, + @NonNull final Throwable exception, + @NonNull final Thread.UncaughtExceptionHandler defaultEH) { + // When enabled, Sentry auto captures unhandled exceptions + if (!enabled) { + CrashReporterHelper.startErrorReporterActivity(application, exception); + } + defaultEH.uncaughtException(t, exception); + } +} diff --git a/app/src/github/res/values/strings.xml b/app/src/github/res/values/strings.xml new file mode 100644 index 00000000..6bc4c7a4 --- /dev/null +++ b/app/src/github/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Enable Sentry + Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io + Sentry will start on next launch + \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java index 36807a6c..4dcc9dde 100644 --- a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java +++ b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java @@ -35,16 +35,16 @@ public final class InstaGrabberApplication extends Application { @Override public void onCreate() { super.onCreate(); - // final Set requestListeners = new HashSet<>(); - // requestListeners.add(new RequestLoggingListener()); - final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig - .newBuilder(this) - // .setMainDiskCacheConfig(diskCacheConfig) - // .setRequestListeners(requestListeners) - .setDownsampleEnabled(true) - .build(); - Fresco.initialize(this, imagePipelineConfig); - // FLog.setMinimumLoggingLevel(FLog.VERBOSE); + CookieHandler.setDefault(NET_COOKIE_MANAGER); + + if (settingsHelper == null) { + settingsHelper = new SettingsHelper(this); + } + + if (!BuildConfig.DEBUG) { + CrashReporter.get(this).start(); + } + // logCollector = new LogCollector(this); if (BuildConfig.DEBUG) { try { @@ -55,14 +55,17 @@ public final class InstaGrabberApplication extends Application { Log.e(TAG, "Error", e); } } - - if (!BuildConfig.DEBUG) CrashReporter.get(this).start(); - // logCollector = new LogCollector(this); - - CookieHandler.setDefault(NET_COOKIE_MANAGER); - - if (settingsHelper == null) - settingsHelper = new SettingsHelper(this); + + // final Set requestListeners = new HashSet<>(); + // requestListeners.add(new RequestLoggingListener()); + final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig + .newBuilder(this) + // .setMainDiskCacheConfig(diskCacheConfig) + // .setRequestListeners(requestListeners) + .setDownsampleEnabled(true) + .build(); + Fresco.initialize(this, imagePipelineConfig); + // FLog.setMinimumLoggingLevel(FLog.VERBOSE); if (applicationHandler == null) { applicationHandler = new Handler(getApplicationContext().getMainLooper()); diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.java b/app/src/main/java/awais/instagrabber/activities/MainActivity.java index 8f16cd5d..2da9cb24 100644 --- a/app/src/main/java/awais/instagrabber/activities/MainActivity.java +++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.java @@ -11,7 +11,6 @@ import android.content.ServiceConnection; import android.content.res.TypedArray; import android.database.MatrixCursor; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -57,21 +56,21 @@ import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import awais.instagrabber.R; import awais.instagrabber.adapters.SuggestionsAdapter; import awais.instagrabber.asyncs.PostFetcher; -import awais.instagrabber.asyncs.SuggestionsFetcher; import awais.instagrabber.customviews.emoji.EmojiVariantManager; import awais.instagrabber.databinding.ActivityMainBinding; import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections; import awais.instagrabber.fragments.main.FeedFragment; import awais.instagrabber.fragments.settings.PreferenceKeys; -import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.IntentModel; -import awais.instagrabber.models.SuggestionModel; import awais.instagrabber.models.enums.SuggestionType; +import awais.instagrabber.repositories.responses.search.SearchItem; +import awais.instagrabber.repositories.responses.search.SearchResponse; import awais.instagrabber.services.ActivityCheckerService; import awais.instagrabber.services.DMSyncAlarmReceiver; import awais.instagrabber.utils.AppExecutors; @@ -84,6 +83,10 @@ import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.emoji.EmojiParser; import awais.instagrabber.viewmodels.AppStateViewModel; import awais.instagrabber.webservices.RetrofitFactory; +import awais.instagrabber.webservices.SearchService; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -106,6 +109,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage private SuggestionsAdapter suggestionAdapter; private AutoCompleteTextView searchAutoComplete; private SearchView searchView; + private SearchService searchService; private boolean showSearch = true; private Handler suggestionsFetchHandler; private int firstFragmentGraphIndex; @@ -173,10 +177,11 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage getSupportFragmentManager().addOnBackStackChangedListener(this); // Initialise the internal map AppExecutors.getInstance().tasksThread().execute(() -> { - EmojiParser.getInstance(); + EmojiParser.setup(this); EmojiVariantManager.getInstance(); }); initEmojiCompat(); + searchService = SearchService.getInstance(); // initDmService(); } @@ -313,7 +318,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage final Bundle bundle = new Bundle(); switch (type) { case TYPE_LOCATION: - bundle.putString("locationId", query); + bundle.putLong("locationId", Long.valueOf(query)); navController.navigate(R.id.action_global_locationFragment, bundle); break; case TYPE_HASHTAG: @@ -341,51 +346,84 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { private boolean searchUser; private boolean searchHash; - private AsyncTask prevSuggestionAsync; + private Call prevSuggestionAsync; private final String[] COLUMNS = { BaseColumns._ID, Constants.EXTRAS_USERNAME, Constants.EXTRAS_NAME, Constants.EXTRAS_TYPE, + "query", "pfp", "verified" }; private String currentSearchQuery; - private final FetchListener fetchListener = new FetchListener() { + private final Callback cb = new Callback() { @Override - public void doBefore() { - suggestionAdapter.changeCursor(null); - } - - @Override - public void onResult(final SuggestionModel[] result) { + public void onResponse(@NonNull final Call call, + @NonNull final Response response) { final MatrixCursor cursor; - if (result == null) cursor = null; + final SearchResponse body = response.body(); + if (body == null) { + cursor = null; + return; + } + final List result = new ArrayList(); + if (isLoggedIn) { + if (body.getList() != null) result.addAll(searchHash ? body.getList() + .stream() + .filter(i -> i.getUser() == null) + .collect(Collectors.toList()) : body.getList()); + } else { - cursor = new MatrixCursor(COLUMNS, 0); - for (int i = 0; i < result.length; i++) { - final SuggestionModel suggestionModel = result[i]; - if (suggestionModel != null) { - final SuggestionType suggestionType = suggestionModel.getSuggestionType(); - final Object[] objects = { - i, - suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getName() : suggestionModel.getUsername(), - suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getUsername() : suggestionModel.getName(), - suggestionType, - suggestionModel.getProfilePic(), - suggestionModel.isVerified()}; - if (!searchHash && !searchUser) cursor.addRow(objects); - else { - final boolean isCurrHash = suggestionType == SuggestionType.TYPE_HASHTAG; - if (searchHash && isCurrHash || !searchHash && !isCurrHash) - cursor.addRow(objects); - } - } + if (body.getUsers() != null && !searchHash) result.addAll(body.getUsers()); + if (body.getHashtags() != null) result.addAll(body.getHashtags()); + if (body.getPlaces() != null) result.addAll(body.getPlaces()); + } + cursor = new MatrixCursor(COLUMNS, 0); + for (int i = 0; i < result.size(); i++) { + final SearchItem suggestionModel = result.get(i); + if (suggestionModel != null) { + Object[] objects = null; + if (suggestionModel.getUser() != null) + objects = new Object[]{ + suggestionModel.getPosition(), + suggestionModel.getUser().getUsername(), + suggestionModel.getUser().getFullName(), + SuggestionType.TYPE_USER, + suggestionModel.getUser().getUsername(), + suggestionModel.getUser().getProfilePicUrl(), + suggestionModel.getUser().isVerified()}; + else if (suggestionModel.getHashtag() != null) + objects = new Object[]{ + suggestionModel.getPosition(), + suggestionModel.getHashtag().getName(), + suggestionModel.getHashtag().getSubtitle(), + SuggestionType.TYPE_HASHTAG, + suggestionModel.getHashtag().getName(), + "res:/" + R.drawable.ic_hashtag, + false}; + else if (suggestionModel.getPlace() != null) + objects = new Object[]{ + suggestionModel.getPosition(), + suggestionModel.getPlace().getTitle(), + suggestionModel.getPlace().getSubtitle(), + SuggestionType.TYPE_LOCATION, + suggestionModel.getPlace().getLocation().getPk(), + "res:/" + R.drawable.ic_location, + false}; + cursor.addRow(objects); } } suggestionAdapter.changeCursor(cursor); } + + @Override + public void onFailure(@NonNull final Call call, + Throwable t) { + if (!call.isCanceled() && t != null) + Log.e(TAG, "Exception on search:", t); + } }; private final Runnable runnable = () -> { @@ -404,17 +442,19 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage if (searchAutoComplete != null) { searchAutoComplete.setThreshold(1); } - prevSuggestionAsync = new SuggestionsFetcher(fetchListener).executeOnExecutor( - AsyncTask.THREAD_POOL_EXECUTOR, - searchUser || searchHash ? currentSearchQuery.substring(1) - : currentSearchQuery); + prevSuggestionAsync = searchService.search(isLoggedIn, + searchUser || searchHash ? currentSearchQuery.substring(1) + : currentSearchQuery, + searchUser ? "user" : (searchHash ? "hashtag" : "blended")); + suggestionAdapter.changeCursor(null); + prevSuggestionAsync.enqueue(cb); } }; private void cancelSuggestionsAsync() { if (prevSuggestionAsync != null) try { - prevSuggestionAsync.cancel(true); + prevSuggestionAsync.cancel(); } catch (final Exception ignored) {} } @@ -706,7 +746,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage final NavController navController = currentNavControllerLiveData.getValue(); if (navController == null) return; final Bundle bundle = new Bundle(); - bundle.putString("locationId", locationId); + bundle.putLong("locationId", Long.valueOf(locationId)); navController.navigate(R.id.action_global_locationFragment, bundle); } diff --git a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java index 8a5c3a4b..80ab465b 100644 --- a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import awais.instagrabber.adapters.viewholder.NotificationViewHolder; import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.models.enums.NotificationType; -import awais.instagrabber.repositories.responses.Notification; +import awais.instagrabber.repositories.responses.notification.Notification; public final class NotificationsAdapter extends ListAdapter { private final OnNotificationClickListener notificationClickListener; diff --git a/app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java index 154e83f2..6c4fa4c7 100755 --- a/app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java @@ -35,12 +35,12 @@ public final class SuggestionsAdapter extends CursorAdapter { @Override public void bindView(@NonNull final View view, final Context context, @NonNull final Cursor cursor) { - // i, username, fullname, type, picUrl, verified - // 0, 1 , 2 , 3 , 4 , 5 + // i, username, fullname, type, query, picUrl, verified + // 0, 1 , 2 , 3 , 4 , 5 , 6 final String fullName = cursor.getString(2); String username = cursor.getString(1); - String picUrl = cursor.getString(4); - final boolean verified = cursor.getString(5).charAt(0) == 't'; + String picUrl = cursor.getString(5); + final boolean verified = cursor.getString(6).charAt(0) == 't'; final String type = cursor.getString(3); SuggestionType suggestionType = null; @@ -50,22 +50,14 @@ public final class SuggestionsAdapter extends CursorAdapter { Log.e(TAG, "Unknown suggestion type: " + type, e); } if (suggestionType == null) return; - final String query; + String query = cursor.getString(4); switch (suggestionType) { case TYPE_USER: username = '@' + username; - query = username; break; case TYPE_HASHTAG: username = '#' + username; - query = username; break; - case TYPE_LOCATION: - query = fullName; - picUrl = "res:/" + R.drawable.ic_location; - break; - default: - return; // will never come here } if (onSuggestionClickListener != null) { @@ -75,12 +67,8 @@ public final class SuggestionsAdapter extends CursorAdapter { final ItemSuggestionBinding binding = ItemSuggestionBinding.bind(view); binding.isVerified.setVisibility(verified ? View.VISIBLE : View.GONE); binding.tvUsername.setText(username); - if (suggestionType.equals(SuggestionType.TYPE_LOCATION)) { - binding.tvFullName.setVisibility(View.GONE); - } else { - binding.tvFullName.setVisibility(View.VISIBLE); - binding.tvFullName.setText(fullName); - } + binding.tvFullName.setVisibility(View.VISIBLE); + binding.tvFullName.setText(fullName); binding.ivProfilePic.setImageURI(picUrl); } diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java index 1c896770..153837e8 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java @@ -9,8 +9,8 @@ import awais.instagrabber.R; import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener; import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.models.enums.NotificationType; -import awais.instagrabber.repositories.responses.Notification; -import awais.instagrabber.repositories.responses.NotificationArgs; +import awais.instagrabber.repositories.responses.notification.Notification; +import awais.instagrabber.repositories.responses.notification.NotificationArgs; public final class NotificationViewHolder extends RecyclerView.ViewHolder { private final ItemNotificationBinding binding; 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/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java index a8fb8dda..949b2a9a 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java @@ -24,6 +24,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItemVisual import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.ResponseBodyUtils; +import awais.instagrabber.utils.TextUtils; public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { @@ -48,7 +49,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { if (media == null) return; setExpiryInfo(visualMedia); setPreview(visualMedia, messageDirection); - final boolean expired = media.getPk() == null; + final boolean expired = TextUtils.isEmpty(media.getId()); if (expired) return; itemView.setOnClickListener(v -> openMedia(media)); /*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null || @@ -118,7 +119,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { final RavenMediaViewMode viewMode = visualMedia.getViewMode(); if (viewMode != RavenMediaViewMode.PERMANENT) { final MediaItemType mediaType = media.getMediaType(); - final boolean expired = media.getPk() == null; + final boolean expired = TextUtils.isEmpty(media.getId()); final int info; switch (mediaType) { case MEDIA_TYPE_IMAGE: @@ -153,7 +154,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { private void setPreview(final DirectItemVisualMedia visualMedia, final MessageDirection messageDirection) { final Media media = visualMedia.getMedia(); - final boolean expired = media.getPk() == null; + final boolean expired = TextUtils.isEmpty(media.getId()); if (expired) { binding.preview.setVisibility(View.GONE); binding.typeIcon.setVisibility(View.GONE); diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java deleted file mode 100644 index 8aabc107..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java +++ /dev/null @@ -1,81 +0,0 @@ -package awais.instagrabber.asyncs; - -import android.os.AsyncTask; -import android.util.Log; - -import androidx.annotation.Nullable; - -import org.json.JSONObject; - -import java.math.BigDecimal; -import java.net.HttpURLConnection; -import java.net.URL; - -import awais.instagrabber.BuildConfig; -import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.models.LocationModel; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.NetworkUtils; -//import awaisomereport.LogCollector; - -//import static awais.instagrabber.utils.Utils.logCollector; - -public final class LocationFetcher extends AsyncTask { - private static final String TAG = "LocationFetcher"; - - private final FetchListener fetchListener; - private final long id; - - public LocationFetcher(final long id, final FetchListener fetchListener) { - // idSlug = id + "/" + slug UPDATE: slug can be ignored tbh - this.id = id; - this.fetchListener = fetchListener; - } - - @Nullable - @Override - protected LocationModel doInBackground(final Void... voids) { - LocationModel result = null; - - try { - final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/locations/" + id + "/?__a=1") - .openConnection(); - conn.setUseCaches(true); - conn.connect(); - - if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - final JSONObject location = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql") - .getJSONObject(Constants.EXTRAS_LOCATION); - - final JSONObject timelineMedia = location.getJSONObject("edge_location_to_media"); - // if (timelineMedia.has("edges")) { - // final JSONArray edges = timelineMedia.getJSONArray("edges"); - // } - result = new LocationModel( - location.getLong(Constants.EXTRAS_ID), - location.getString("name"), - location.getString("blurb"), - location.getString("website"), - location.getString("profile_pic_url"), - timelineMedia.getLong("count"), - BigDecimal.valueOf(location.optDouble("lat", 0d)).toString(), - BigDecimal.valueOf(location.optDouble("lng", 0d)).toString() - ); - } - - conn.disconnect(); - } catch (final Exception e) { -// if (logCollector != null) -// logCollector.appendException(e, LogCollector.LogFile.ASYNC_LOCATION_FETCHER, "doInBackground"); - if (BuildConfig.DEBUG) { - Log.e(TAG, "", e); - } - } - return result; - } - - @Override - protected void onPostExecute(final LocationModel result) { - if (fetchListener != null) fetchListener.onResult(result); - } -} diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java index 7613c9d2..274b2314 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java @@ -4,7 +4,7 @@ import java.util.List; import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.models.LocationModel; +import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.webservices.GraphQLService; @@ -14,12 +14,12 @@ import awais.instagrabber.webservices.ServiceCallback; public class LocationPostFetchService implements PostFetcher.PostFetchService { private final LocationService locationService; private final GraphQLService graphQLService; - private final LocationModel locationModel; + private final Location locationModel; private String nextMaxId; private boolean moreAvailable; private final boolean isLoggedIn; - public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) { + public LocationPostFetchService(final Location locationModel, final boolean isLoggedIn) { this.locationModel = locationModel; this.isLoggedIn = isLoggedIn; locationService = isLoggedIn ? LocationService.getInstance() : null; @@ -47,8 +47,8 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService { } } }; - if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb); - else graphQLService.fetchLocationPosts(locationModel.getId(), nextMaxId, cb); + if (isLoggedIn) locationService.fetchPosts(locationModel.getPk(), nextMaxId, cb); + else graphQLService.fetchLocationPosts(locationModel.getPk(), nextMaxId, cb); } @Override diff --git a/app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java deleted file mode 100755 index c40d0406..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java +++ /dev/null @@ -1,113 +0,0 @@ -package awais.instagrabber.asyncs; - -import android.os.AsyncTask; -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.InterruptedIOException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; - -import awais.instagrabber.BuildConfig; -import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.models.SuggestionModel; -import awais.instagrabber.models.enums.SuggestionType; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.NetworkUtils; -import awais.instagrabber.utils.UrlEncoder; - -public final class SuggestionsFetcher extends AsyncTask { - private final FetchListener fetchListener; - - public SuggestionsFetcher(final FetchListener fetchListener) { - this.fetchListener = fetchListener; - } - - @Override - protected void onPreExecute() { - if (fetchListener != null) fetchListener.doBefore(); - } - - @Override - protected SuggestionModel[] doInBackground(final String... params) { - SuggestionModel[] result = null; - try { - final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/web/search/topsearch/?context=blended&count=50&query=" - + UrlEncoder.encodeUrl(params[0])).openConnection(); - conn.setUseCaches(false); - conn.connect(); - - if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - final JSONObject jsonObject = new JSONObject(NetworkUtils.readFromConnection(conn)); - conn.disconnect(); - - final JSONArray usersArray = jsonObject.getJSONArray("users"); - final JSONArray hashtagsArray = jsonObject.getJSONArray("hashtags"); - final JSONArray placesArray = jsonObject.getJSONArray("places"); - - final int usersLen = usersArray.length(); - final int hashtagsLen = hashtagsArray.length(); - final int placesLen = placesArray.length(); - - final ArrayList suggestionModels = new ArrayList<>(usersLen + hashtagsLen); - for (int i = 0; i < hashtagsLen; i++) { - final JSONObject hashtagsArrayJSONObject = hashtagsArray.getJSONObject(i); - - final JSONObject hashtag = hashtagsArrayJSONObject.getJSONObject("hashtag"); - - suggestionModels.add(new SuggestionModel(false, - hashtag.getString(Constants.EXTRAS_NAME), - null, - hashtag.optString("profile_pic_url", Constants.DEFAULT_HASH_TAG_PIC), - SuggestionType.TYPE_HASHTAG, - hashtagsArrayJSONObject.optInt("position", suggestionModels.size() - 1))); - } - - for (int i = 0; i < placesLen; i++) { - final JSONObject placesArrayJSONObject = placesArray.getJSONObject(i); - - final JSONObject place = placesArrayJSONObject.getJSONObject("place"); - - // name - suggestionModels.add(new SuggestionModel(false, - place.getJSONObject("location").getString("pk"), // +"/"+place.getString("slug"), - place.getString("title"), - place.optString("profile_pic_url"), - SuggestionType.TYPE_LOCATION, - placesArrayJSONObject.optInt("position", suggestionModels.size() - 1))); - } - - for (int i = 0; i < usersLen; i++) { - final JSONObject usersArrayJSONObject = usersArray.getJSONObject(i); - - final JSONObject user = usersArrayJSONObject.getJSONObject(Constants.EXTRAS_USER); - - suggestionModels.add(new SuggestionModel(user.getBoolean("is_verified"), - user.getString(Constants.EXTRAS_USERNAME), - user.getString("full_name"), - user.getString("profile_pic_url"), - SuggestionType.TYPE_USER, - usersArrayJSONObject.optInt("position", suggestionModels.size() - 1))); - } - - suggestionModels.trimToSize(); - - Collections.sort(suggestionModels); - - result = suggestionModels.toArray(new SuggestionModel[0]); - } - } catch (final Exception e) { - if (BuildConfig.DEBUG && !(e instanceof InterruptedIOException)) Log.e("AWAISKING_APP", "", e); - } - return result; - } - - @Override - protected void onPostExecute(final SuggestionModel[] result) { - if (fetchListener != null) fetchListener.onResult(result); - } -} \ No newline at end of file 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/dialogs/ConfirmDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java index caba0b38..5b8eb3c9 100644 --- a/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java +++ b/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -17,6 +18,9 @@ public class ConfirmDialogFragment extends DialogFragment { private Context context; private ConfirmDialogFragmentCallback callback; + private final int defaultPositiveButtonText = R.string.ok; + // private final int defaultNegativeButtonText = R.string.cancel; + @NonNull public static ConfirmDialogFragment newInstance(final int requestCode, @StringRes final int title, @@ -26,11 +30,21 @@ public class ConfirmDialogFragment extends DialogFragment { @StringRes final int neutralText) { Bundle args = new Bundle(); args.putInt("requestCode", requestCode); - args.putInt("title", title); - args.putInt("message", message); - args.putInt("positive", positiveText); - args.putInt("negative", negativeText); - args.putInt("neutral", neutralText); + if (title != 0) { + args.putInt("title", title); + } + if (message != 0) { + args.putInt("message", message); + } + if (positiveText != 0) { + args.putInt("positive", positiveText); + } + if (negativeText != 0) { + args.putInt("negative", negativeText); + } + if (neutralText != 0) { + args.putInt("neutral", neutralText); + } ConfirmDialogFragment fragment = new ConfirmDialogFragment(); fragment.setArguments(args); return fragment; @@ -41,10 +55,9 @@ public class ConfirmDialogFragment extends DialogFragment { @Override public void onAttach(@NonNull final Context context) { super.onAttach(context); - try { - callback = (ConfirmDialogFragmentCallback) getParentFragment(); - } catch (ClassCastException e) { - throw new ClassCastException("Calling fragment must implement ConfirmDialogFragmentCallback interface"); + final Fragment parentFragment = getParentFragment(); + if (parentFragment instanceof ConfirmDialogFragmentCallback) { + callback = (ConfirmDialogFragmentCallback) parentFragment; } this.context = context; } @@ -53,38 +66,42 @@ public class ConfirmDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { final Bundle arguments = getArguments(); - int title = -1; - int message = -1; - int positiveButtonText = R.string.ok; - int negativeButtonText = R.string.cancel; - int neutralButtonText = -1; + int title = 0; + int message = 0; + int neutralButtonText = 0; + int negativeButtonText = 0; + + final int positiveButtonText; final int requestCode; if (arguments != null) { - title = arguments.getInt("title", -1); - message = arguments.getInt("message", -1); - positiveButtonText = arguments.getInt("positive", R.string.ok); - negativeButtonText = arguments.getInt("negative", R.string.cancel); - neutralButtonText = arguments.getInt("neutral", -1); + title = arguments.getInt("title", 0); + message = arguments.getInt("message", 0); + positiveButtonText = arguments.getInt("positive", defaultPositiveButtonText); + negativeButtonText = arguments.getInt("negative", 0); + neutralButtonText = arguments.getInt("neutral", 0); requestCode = arguments.getInt("requestCode", 0); } else { requestCode = 0; + positiveButtonText = defaultPositiveButtonText; } final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context) .setPositiveButton(positiveButtonText, (d, w) -> { if (callback == null) return; callback.onPositiveButtonClicked(requestCode); - }) - .setNegativeButton(negativeButtonText, (dialog, which) -> { - if (callback == null) return; - callback.onNegativeButtonClicked(requestCode); }); - if (title > 0) { + if (title != 0) { builder.setTitle(title); } - if (message > 0) { + if (message != 0) { builder.setMessage(message); } - if (neutralButtonText > 0) { + if (negativeButtonText != 0) { + builder.setNegativeButton(negativeButtonText, (dialog, which) -> { + if (callback == null) return; + callback.onNegativeButtonClicked(requestCode); + }); + } + if (neutralButtonText != 0) { builder.setNeutralButton(neutralButtonText, (dialog, which) -> { if (callback == null) return; callback.onNeutralButtonClicked(requestCode); 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/FollowViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java index 42fcee57..52b4fed7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java @@ -2,7 +2,6 @@ package awais.instagrabber.fragments; import android.content.Context; import android.content.res.Resources; -import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; @@ -57,7 +56,6 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh private FollowAdapter adapter; private View.OnClickListener clickListener; private FragmentFollowersViewerBinding binding; - private AsyncTask currentlyExecuting; private SwipeRefreshLayout root; private FriendshipService friendshipService; private AppCompatActivity fragmentActivity; diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 75e72949..81530dc5 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -3,7 +3,6 @@ package awais.instagrabber.fragments; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Typeface; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.text.SpannableStringBuilder; @@ -95,7 +94,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private Hashtag hashtagModel = null; private ActionMode actionMode; private StoriesService storiesService; - private AsyncTask currentlyExecuting; private boolean isLoggedIn; private TagsService tagsService; private GraphQLService graphQLService; @@ -474,7 +472,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe hashtag, FavoriteType.HASHTAG, hashtagModel.getName(), - hashtagModel.getProfilePicUrl(), + "res:/" + R.drawable.ic_hashtag, result.getDateAdded() ), new RepositoryCallback() { @Override @@ -518,7 +516,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe hashtag, FavoriteType.HASHTAG, hashtagModel.getName(), - hashtagModel.getProfilePicUrl(), + "res:/" + R.drawable.ic_hashtag, new Date() ), new RepositoryCallback() { @Override @@ -533,7 +531,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe }); } })); - hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getProfilePicUrl()); + hashtagDetailsBinding.mainHashtagImage.setImageURI("res:/" + R.drawable.ic_hashtag); final String postCount = String.valueOf(hashtagModel.getMediaCount()); final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, hashtagModel.getMediaCount() > 2000000000L diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 24fa4b88..65db4826 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -3,14 +3,9 @@ package awais.instagrabber.fragments; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.graphics.Typeface; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; -import android.text.SpannableStringBuilder; -import android.text.style.RelativeSizeSpan; -import android.text.style.StyleSpan; import android.util.Log; import android.view.ActionMode; import android.view.LayoutInflater; @@ -45,7 +40,6 @@ import java.util.Set; import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; -import awais.instagrabber.asyncs.LocationFetcher; import awais.instagrabber.asyncs.LocationPostFetchService; import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; @@ -56,23 +50,24 @@ import awais.instagrabber.db.entities.Favorite; import awais.instagrabber.db.repositories.FavoriteRepository; import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; -import awais.instagrabber.models.LocationModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.repositories.requests.StoryViewerOptions; +import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; +import awais.instagrabber.webservices.GraphQLService; +import awais.instagrabber.webservices.LocationService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; //import awaisomereport.LogCollector; import static androidx.core.content.PermissionChecker.checkSelfPermission; -import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; //import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -89,10 +84,11 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private boolean hasStories = false; private boolean opening = false; private long locationId; - private LocationModel locationModel; + private Location locationModel; private ActionMode actionMode; private StoriesService storiesService; - private AsyncTask currentlyExecuting; + private GraphQLService graphQLService; + private LocationService locationService; private boolean isLoggedIn; private boolean storiesFetching; private Set selectedFeedModels; @@ -265,12 +261,29 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } } }; + private final ServiceCallback cb = new ServiceCallback() { + @Override + public void onSuccess(final Location result) { + locationModel = result; + binding.swipeRefreshLayout.setRefreshing(false); + setupLocationDetails(); + } + + @Override + public void onFailure(final Throwable t) { + setupLocationDetails(); + } + }; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentActivity = (MainActivity) requireActivity(); + final String cookie = settingsHelper.getString(Constants.COOKIE); + isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; + locationService = isLoggedIn ? LocationService.getInstance() : null; storiesService = StoriesService.getInstance(null, 0L, null); + graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); setHasOptionsMenu(true); } @@ -354,8 +367,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private void init() { if (getArguments() == null) return; - final String cookie = settingsHelper.getString(Constants.COOKIE); - isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; final LocationFragmentArgs fragmentArgs = LocationFragmentArgs.fromBundle(getArguments()); locationId = fragmentArgs.getLocationId(); locationDetailsBinding.favChip.setVisibility(View.GONE); @@ -377,42 +388,38 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } private void fetchLocationModel() { - stopCurrentExecutor(); binding.swipeRefreshLayout.setRefreshing(true); - currentlyExecuting = new LocationFetcher(locationId, result -> { - locationModel = result; - binding.swipeRefreshLayout.setRefreshing(false); - if (locationModel == null) { - final Context context = getContext(); - if (context == null) return; - Toast.makeText(context, R.string.error_loading_location, Toast.LENGTH_SHORT).show(); - binding.swipeRefreshLayout.setEnabled(false); - return; - } - setTitle(); - setupLocationDetails(); - setupPosts(); - fetchStories(); - // fetchPosts(); - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if (isLoggedIn) locationService.fetch(locationId, cb); + else graphQLService.fetchLocation(locationId, cb); } private void setupLocationDetails() { - final long locationId = locationModel.getId(); + if (locationModel == null) { + try { + Toast.makeText(getContext(), R.string.error_loading_location, Toast.LENGTH_SHORT).show(); + binding.swipeRefreshLayout.setEnabled(false); + } + catch (Exception ignored) {} + return; + } + setTitle(); + setupPosts(); + fetchStories(); + final long locationId = locationModel.getPk(); // binding.swipeRefreshLayout.setRefreshing(true); - locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic()); - final String postCount = String.valueOf(locationModel.getPostCount()); - final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, - locationModel.getPostCount() > 2000000000L - ? 2000000000 - : locationModel.getPostCount().intValue(), - postCount)); - span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); - span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); - locationDetailsBinding.mainLocPostCount.setText(span); - locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE); + locationDetailsBinding.mainLocationImage.setImageURI("res:/" + R.drawable.ic_location); +// final String postCount = String.valueOf(locationModel.getCount()); +// final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, +// locationModel.getPostCount() > 2000000000L +// ? 2000000000 +// : locationModel.getPostCount().intValue(), +// postCount)); +// span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); +// span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); +// locationDetailsBinding.mainLocPostCount.setText(span); +// locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE); locationDetailsBinding.locationFullName.setText(locationModel.getName()); - CharSequence biography = locationModel.getBio(); + CharSequence biography = locationModel.getAddress() + "\n" + locationModel.getCity(); // binding.locationBiography.setCaptionIsExpandable(true); // binding.locationBiography.setCaptionIsExpanded(true); @@ -423,22 +430,22 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } else { locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE); locationDetailsBinding.locationBiography.setText(biography); - locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> { - final NavController navController = NavHostFragment.findNavController(this); - final Bundle bundle = new Bundle(); - final String originalText = autoLinkItem.getOriginalText().trim(); - bundle.putString(ARG_HASHTAG, originalText); - navController.navigate(R.id.action_global_hashTagFragment, bundle); - }); - locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> { - final String originalText = autoLinkItem.getOriginalText().trim(); - navigateToProfile(originalText); - }); - locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context, - autoLinkItem.getOriginalText() - .trim())); - locationDetailsBinding.locationBiography - .addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim())); +// locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> { +// final NavController navController = NavHostFragment.findNavController(this); +// final Bundle bundle = new Bundle(); +// final String originalText = autoLinkItem.getOriginalText().trim(); +// bundle.putString(ARG_HASHTAG, originalText); +// navController.navigate(R.id.action_global_hashTagFragment, bundle); +// }); +// locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> { +// final String originalText = autoLinkItem.getOriginalText().trim(); +// navigateToProfile(originalText); +// }); +// locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context, +// autoLinkItem.getOriginalText() +// .trim())); +// locationDetailsBinding.locationBiography +// .addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim())); locationDetailsBinding.locationBiography.setOnLongClickListener(v -> { Utils.copyText(context, biography); return true; @@ -457,16 +464,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR locationDetailsBinding.btnMap.setOnClickListener(null); } - final String url = locationModel.getUrl(); - if (TextUtils.isEmpty(url)) { - locationDetailsBinding.locationUrl.setVisibility(View.GONE); - } else if (!url.startsWith("http")) { - locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE); - locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl("http://" + url)); - } else { - locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE); - locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl(url)); - } final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context); final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource); locationDetailsBinding.favChip.setVisibility(View.VISIBLE); @@ -481,7 +478,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR String.valueOf(locationId), FavoriteType.LOCATION, locationModel.getName(), - locationModel.getSdProfilePic(), + "res:/" + R.drawable.ic_location, result.getDateAdded() ), new RepositoryCallback() { @Override @@ -523,7 +520,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR String.valueOf(locationId), FavoriteType.LOCATION, locationModel.getName(), - locationModel.getSdProfilePic(), + "res:/" + R.drawable.ic_location, new Date() ), new RepositoryCallback() { @Override @@ -581,18 +578,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } } - private void stopCurrentExecutor() { - if (currentlyExecuting != null) { - try { - currentlyExecuting.cancel(true); - } catch (final Exception e) { -// if (logCollector != null) logCollector.appendException( -// e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor"); - Log.e(TAG, "", e); - } - } - } - private void setTitle() { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null && locationModel != null) { diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java index cae95e09..d9b56ce7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java @@ -2,7 +2,6 @@ package awais.instagrabber.fragments; import android.content.Context; import android.content.DialogInterface; -import android.os.AsyncTask; import android.os.Bundle; import android.text.SpannableString; import android.text.Spanned; @@ -37,9 +36,9 @@ import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.FriendshipChangeResponse; import awais.instagrabber.repositories.responses.Media; -import awais.instagrabber.repositories.responses.Notification; -import awais.instagrabber.repositories.responses.NotificationArgs; -import awais.instagrabber.repositories.responses.NotificationImage; +import awais.instagrabber.repositories.responses.notification.Notification; +import awais.instagrabber.repositories.responses.notification.NotificationArgs; +import awais.instagrabber.repositories.responses.notification.NotificationImage; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.TextUtils; 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/DirectMessageInboxFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java index 4b8bece2..34d4ed1c 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.IntentFilter; import android.content.res.Configuration; import android.os.Bundle; +import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -104,6 +105,11 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh public void onPause() { super.onPause(); unregisterReceiver(); + isPendingRequestTotalBadgeAttached = false; + if (pendingRequestTotalBadgeDrawable != null) { + BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId()); + pendingRequestTotalBadgeDrawable = null; + } } @Override @@ -124,21 +130,13 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh public void onDestroyView() { super.onDestroyView(); unregisterReceiver(); - isPendingRequestTotalBadgeAttached = false; - if (pendingRequestTotalBadgeDrawable != null) { - BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId()); - pendingRequestTotalBadgeDrawable = null; - } } @Override public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - pendingRequestsMenuItem = menu.add(Menu.NONE, R.id.pending_requests, Menu.NONE, "Pending requests"); - pendingRequestsMenuItem.setIcon(R.drawable.ic_account_clock_24) - .setVisible(false) - .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS); - attachPendingRequestsBadge(viewModel.getPendingRequestsTotal().getValue()); + inflater.inflate(R.menu.dm_inbox_menu, menu); + pendingRequestsMenuItem = menu.findItem(R.id.pending_requests); + pendingRequestsMenuItem.setVisible(isPendingRequestTotalBadgeAttached); } @Override @@ -213,7 +211,16 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh @SuppressLint("UnsafeExperimentalUsageError") private void attachPendingRequestsBadge(@Nullable final Integer count) { - if (pendingRequestsMenuItem == null) return; + if (pendingRequestsMenuItem == null) { + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + attachPendingRequestsBadge(count); + } + }, 500); + return; + } if (pendingRequestTotalBadgeDrawable == null) { final Context context = getContext(); if (context == null) return; 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 bbd174f2..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) { @@ -186,7 +187,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi R.string.admin_approval_required_description, R.string.ok, R.string.cancel, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog"); return; @@ -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) -> { @@ -272,10 +271,10 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance( LEAVE_THREAD_REQUEST_CODE, R.string.dms_action_leave_question, - -1, + 0, R.string.yes, R.string.no, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "leave_thread_confirmation_dialog"); }); @@ -290,7 +289,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi R.string.dms_action_end_description, R.string.yes, R.string.no, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "end_thread_confirmation_dialog"); }); 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 d1e4b5f9..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); } @@ -1328,95 +1331,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact } } - // private void sendText(final String text, final String itemId, final boolean delete) { - // DirectThreadBroadcaster.TextBroadcastOptions textOptions = null; - // DirectThreadBroadcaster.ReactionBroadcastOptions reactionOptions = null; - // if (text != null) { - // try { - // textOptions = new DirectThreadBroadcaster.TextBroadcastOptions(text); - // } catch (UnsupportedEncodingException e) { - // Log.e(TAG, "Error", e); - // return; - // } - // } else { - // reactionOptions = new DirectThreadBroadcaster.ReactionBroadcastOptions(itemId, delete); - // } - // broadcast(text != null ? textOptions : reactionOptions, result -> { - // final Context context = getContext(); - // if (context == null) return; - // if (result == null || result.getResponseCode() != HttpURLConnection.HTTP_OK) { - // Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - // return; - // } - // if (text != null) { - // // binding.commentText.setText(""); - // } else { - // // final View viewWithTag = binding.messageList.findViewWithTag(directItemModel); - // // if (viewWithTag != null) { - // // final ViewParent dim = viewWithTag.getParent(); - // // if (dim instanceof View) { - // // final View dimView = (View) dim; - // // final View likedContainer = dimView.findViewById(R.id.liked_container); - // // if (likedContainer != null) { - // // likedContainer.setVisibility(delete ? View.GONE : View.VISIBLE); - // // } - // // } - // // } - // // directItemModel.setLiked(); - // } - // context.sendBroadcast(new Intent(DMRefreshBroadcastReceiver.ACTION_REFRESH_DM)); - // hasSentSomething = true; - // // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener) - // // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - // }); - // } - - // private void sendImage(final Uri imageUri) { - // final Context context = getContext(); - // if (context == null) return; - // try (InputStream inputStream = context.getContentResolver().openInputStream(imageUri)) { - // final Bitmap bitmap = BitmapFactory.decodeStream(inputStream); - // Toast.makeText(context, R.string.uploading, Toast.LENGTH_SHORT).show(); - // // Upload Image - // final ImageUploader imageUploader = new ImageUploader(); - // imageUploader.setOnTaskCompleteListener(response -> { - // if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) { - // Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - // if (response != null && response.getResponse() != null) { - // Log.e(TAG, response.getResponse().toString()); - // } - // return; - // } - // final JSONObject responseJson = response.getResponse(); - // try { - // final String uploadId = responseJson.getString("upload_id"); - // // Broadcast - // final DirectThreadBroadcaster.ImageBroadcastOptions options = new DirectThreadBroadcaster.ImageBroadcastOptions(true, uploadId); - // hasSentSomething = true; - // broadcast(options, - // broadcastResponse -> { - // // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener) - // // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - // }); - // } catch (JSONException e) { - // Log.e(TAG, "Error parsing json response", e); - // } - // }); - // final ImageUploadOptions options = ImageUploadOptions.builder(bitmap).build(); - // imageUploader.execute(options); - // } catch (IOException e) { - // Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - // Log.e(TAG, "Error opening file", e); - // } - // } - - // private void broadcast(final DirectThreadBroadcaster.BroadcastOptions broadcastOptions, - // final DirectThreadBroadcaster.OnBroadcastCompleteListener listener) { - // final DirectThreadBroadcaster broadcaster = new DirectThreadBroadcaster(threadId); - // broadcaster.setOnTaskCompleteListener(listener); - // broadcaster.execute(broadcastOptions); - // } - @NonNull private User getUser(final long userId) { for (final User user : users) { @@ -1426,58 +1340,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact return null; } - // private void searchUsername(final String text) { - // final Bundle bundle = new Bundle(); - // bundle.putString("username", "@" + text); - // NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle); - // } - - // class ThreadAction extends AsyncTask { - // String action, argument; - // - // protected Void doInBackground(String... rawAction) { - // action = rawAction[0]; - // argument = rawAction[1]; - // final String url = "https://i.instagram.com/api/v1/direct_v2/threads/" + threadId + "/items/" + argument + "/" + action + "/"; - // try { - // String urlParameters = "_csrftoken=" + COOKIE.split("csrftoken=")[1].split(";")[0] - // + "&_uuid=" + Utils.settingsHelper.getString(Constants.DEVICE_UUID); - // final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection(); - // urlConnection.setRequestMethod("POST"); - // urlConnection.setUseCaches(false); - // urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); - // urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - // urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length)); - // urlConnection.setDoOutput(true); - // DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream()); - // wr.writeBytes(urlParameters); - // wr.flush(); - // wr.close(); - // urlConnection.connect(); - // if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { - // if (action.equals("delete")) { - // hasDeletedSomething = true; - // } else if (action.equals("seen")) { - // // context.sendBroadcast(new Intent(DMRefreshBroadcastReceiver.ACTION_REFRESH_DM)); - // } - // } - // urlConnection.disconnect(); - // } catch (Throwable ex) { - // Log.e("austin_debug", action + ": " + ex); - // } - // return null; - // } - // - // @Override - // protected void onPostExecute(Void result) { - // if (hasDeletedSomething) { - // // directItemModel = null; - // // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener) - // // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - // } - // } - // } - private void setupKbHeightProvider() { if (heightProvider != null) return; heightProvider = new HeightProvider(fragmentActivity).init().setHeightListener(height -> { 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 a9b9f722..4b3bfcfd 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -588,10 +588,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; } @@ -683,6 +679,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); @@ -917,11 +914,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); } } @@ -1206,7 +1203,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/fragments/settings/GeneralPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java index 4606fb48..f63c4a7a 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java @@ -9,6 +9,8 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreferenceCompat; +import java.util.List; + import awais.instagrabber.R; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; @@ -29,6 +31,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { } screen.addPreference(getUpdateCheckPreference(context)); screen.addPreference(getFlagSecurePreference(context)); + final List preferences = FlavorSettings.getInstance().getPreferences(context, + getChildFragmentManager(), + SettingCategory.GENERAL); + if (preferences != null) { + for (final Preference preference : preferences) { + screen.addPreference(preference); + } + } } private Preference getDefaultTabPreference(@NonNull final Context context) { diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java b/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java new file mode 100644 index 00000000..4b0f1c5a --- /dev/null +++ b/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java @@ -0,0 +1,14 @@ +package awais.instagrabber.fragments.settings; + +import android.content.Context; + +import androidx.fragment.app.FragmentManager; +import androidx.preference.Preference; + +import java.util.List; + +public interface IFlavorSettings { + List getPreferences(Context context, + FragmentManager childFragmentManager, + SettingCategory settingCategory); +} diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java b/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java index 3f481685..3482cf7f 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java @@ -5,4 +5,5 @@ public final class PreferenceKeys { public static final String PREF_ENABLE_DM_AUTO_REFRESH = "enable_dm_auto_refresh"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER = "enable_dm_auto_refresh_freq_number"; + public static final String PREF_ENABLE_SENTRY = "enable_sentry"; } diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java b/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java new file mode 100644 index 00000000..88ba5e6f --- /dev/null +++ b/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java @@ -0,0 +1,6 @@ +package awais.instagrabber.fragments.settings; + +public enum SettingCategory { + GENERAL, + // add more as and when required +} 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/models/LocationModel.java b/app/src/main/java/awais/instagrabber/models/LocationModel.java deleted file mode 100755 index e0e13730..00000000 --- a/app/src/main/java/awais/instagrabber/models/LocationModel.java +++ /dev/null @@ -1,56 +0,0 @@ -package awais.instagrabber.models; - -import java.io.Serializable; - -public final class LocationModel implements Serializable { - private final long postCount; - private final long id; - private final String name; - private final String bio; - private final String url; - private final String sdProfilePic; - private final String lat; - private final String lng; - - public LocationModel(final long id, - final String name, - final String bio, - final String url, - final String sdProfilePic, - final long postCount, - final String lat, - final String lng) { - this.id = id; - this.name = name; - this.bio = bio; - this.url = url; - this.sdProfilePic = sdProfilePic; - this.postCount = postCount; - this.lat = lat; - this.lng = lng; - } - - public long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getBio() { - return bio; - } - - public String getUrl() { - return url; - } - - public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; } - - public String getSdProfilePic() { - return sdProfilePic; - } - - public Long getPostCount() { return postCount; } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/SuggestionModel.java b/app/src/main/java/awais/instagrabber/models/SuggestionModel.java deleted file mode 100755 index 855b1d4b..00000000 --- a/app/src/main/java/awais/instagrabber/models/SuggestionModel.java +++ /dev/null @@ -1,51 +0,0 @@ -package awais.instagrabber.models; - -import androidx.annotation.NonNull; - -import awais.instagrabber.models.enums.SuggestionType; - -public final class SuggestionModel implements Comparable { - private final int position; - private final boolean isVerified; - private final String username, name, profilePic; - private final SuggestionType suggestionType; - - public SuggestionModel(final boolean isVerified, final String username, final String name, final String profilePic, - final SuggestionType suggestionType, final int position) { - this.isVerified = isVerified; - this.username = username; - this.name = name; - this.profilePic = profilePic; - this.suggestionType = suggestionType; - this.position = position; - } - - public boolean isVerified() { - return isVerified; - } - - public String getUsername() { - return username; - } - - public String getName() { - return name; - } - - public String getProfilePic() { - return profilePic; - } - - public SuggestionType getSuggestionType() { - return suggestionType; - } - - public int getPosition() { - return position; - } - - @Override - public int compareTo(@NonNull final SuggestionModel model) { - return Integer.compare(getPosition(), model.getPosition()); - } -} diff --git a/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java b/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java index bb7d036c..b5273e35 100644 --- a/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java @@ -2,7 +2,7 @@ package awais.instagrabber.repositories; import java.util.Map; -import awais.instagrabber.repositories.responses.FeedFetchResponse; +import awais.instagrabber.repositories.responses.feed.FeedFetchResponse; import retrofit2.Call; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; diff --git a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java index a6246fe0..efbc1c9a 100644 --- a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java @@ -16,4 +16,7 @@ public interface GraphQLRepository { @GET("/explore/tags/{tag}/?__a=1") Call getTag(@Path("tag") String tag); + + @GET("/explore/locations/{locationId}/?__a=1") + Call getLocation(@Path("locationId") long locationId); } diff --git a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java index df999f0b..69e7302e 100644 --- a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java @@ -3,12 +3,15 @@ package awais.instagrabber.repositories; import java.util.Map; import awais.instagrabber.repositories.responses.LocationFeedResponse; +import awais.instagrabber.repositories.responses.Place; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Path; import retrofit2.http.QueryMap; public interface LocationRepository { + @GET("/api/v1/locations/{location}/info/") + Call fetch(@Path("location") final long locationId); @GET("/api/v1/feed/location/{location}/") Call fetchPosts(@Path("location") final long locationId, diff --git a/app/src/main/java/awais/instagrabber/repositories/SearchRepository.java b/app/src/main/java/awais/instagrabber/repositories/SearchRepository.java new file mode 100644 index 00000000..7dda390d --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/SearchRepository.java @@ -0,0 +1,15 @@ +package awais.instagrabber.repositories; + +import java.util.Map; + +import awais.instagrabber.repositories.responses.search.SearchResponse; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.QueryMap; +import retrofit2.http.Url; + +public interface SearchRepository { + @GET + Call search(@Url String url, @QueryMap(encoded = true) Map queryParams); +} 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/Hashtag.java b/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java index aca5daad..2ce08eb5 100755 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java @@ -5,22 +5,22 @@ import java.io.Serializable; import awais.instagrabber.models.enums.FollowingType; public final class Hashtag implements Serializable { - private final FollowingType following; // 0 false 1 true + private final FollowingType following; // 0 false 1 true; not on search results private final long mediaCount; private final String id; private final String name; - private final String profilePicUrl; // on app API this is always null (property exists) + private final String searchResultSubtitle; // shows how many posts there are on search results public Hashtag(final String id, final String name, - final String profilePicUrl, final long mediaCount, - final FollowingType following) { + final FollowingType following, + final String searchResultSubtitle) { this.id = id; this.name = name; - this.profilePicUrl = profilePicUrl; this.mediaCount = mediaCount; this.following = following; + this.searchResultSubtitle = searchResultSubtitle; } public String getId() { @@ -31,10 +31,6 @@ public final class Hashtag implements Serializable { return name; } - public String getProfilePicUrl() { - return profilePicUrl; - } - public Long getMediaCount() { return mediaCount; } @@ -42,4 +38,8 @@ public final class Hashtag implements Serializable { public FollowingType getFollowing() { return following; } + + public String getSubtitle() { + return searchResultSubtitle; + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Location.java b/app/src/main/java/awais/instagrabber/repositories/responses/Location.java index ea1e66fe..c6a3d950 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Location.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Location.java @@ -9,16 +9,16 @@ public class Location implements Serializable { private final String name; private final String address; private final String city; - private final float lng; - private final float lat; + private final double lng; + private final double lat; public Location(final long pk, final String shortName, final String name, final String address, final String city, - final float lng, - final float lat) { + final double lng, + final double lat) { this.pk = pk; this.shortName = shortName; this.name = name; @@ -48,22 +48,24 @@ public class Location implements Serializable { return city; } - public float getLng() { + public double getLng() { return lng; } - public float getLat() { + public double getLat() { return lat; } + public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; } + @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Location location = (Location) o; return pk == location.pk && - Float.compare(location.lng, lng) == 0 && - Float.compare(location.lat, lat) == 0 && + Double.compare(location.lng, lng) == 0 && + Double.compare(location.lat, lat) == 0 && Objects.equals(shortName, location.shortName) && Objects.equals(name, location.name) && Objects.equals(address, location.address) && diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Media.java b/app/src/main/java/awais/instagrabber/repositories/responses/Media.java index 1a149a66..c11669a8 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Media.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Media.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Objects; import awais.instagrabber.models.enums.MediaItemType; +import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator; import awais.instagrabber.utils.Utils; public class Media implements Serializable { diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java index 5dcdb487..a6339e20 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java @@ -2,6 +2,9 @@ package awais.instagrabber.repositories.responses; import java.util.List; +import awais.instagrabber.repositories.responses.notification.Notification; +import awais.instagrabber.repositories.responses.notification.NotificationCounts; + public class NewsInboxResponse { private final NotificationCounts counts; private final List newStories; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Place.java b/app/src/main/java/awais/instagrabber/repositories/responses/Place.java new file mode 100644 index 00000000..c72e1c41 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Place.java @@ -0,0 +1,43 @@ +package awais.instagrabber.repositories.responses; + +public class Place { + private final Location location; + // for search + private final String title; // those are repeated within location + private final String subtitle; // address + private final String slug; // browser only; for end of address + // for location info + private final String status; + + public Place(final Location location, + final String title, + final String subtitle, + final String slug, + final String status) { + this.location = location; + this.title = title; + this.subtitle = subtitle; + this.slug = slug; + this.status = status; + } + + public Location getLocation() { + return location; + } + + public String getTitle() { + return title; + } + + public String getSubtitle() { + return subtitle; + } + + public String getSlug() { + return slug; + } + + public String getStatus() { + return status; + } +} 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/repositories/responses/EndOfFeedDemarcator.java b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java similarity index 94% rename from app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedDemarcator.java rename to app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java index 44b9936d..2408bc89 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedDemarcator.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.feed; import java.io.Serializable; import java.util.Objects; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroup.java b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java similarity index 92% rename from app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroup.java rename to app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java index 43384a41..e675691e 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroup.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java @@ -1,9 +1,11 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.feed; import java.io.Serializable; import java.util.List; import java.util.Objects; +import awais.instagrabber.repositories.responses.Media; + public class EndOfFeedGroup implements Serializable { private final String id; private final String title; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroupSet.java b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java similarity index 97% rename from app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroupSet.java rename to app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java index 3da29e03..cfda0f39 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroupSet.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.feed; import java.io.Serializable; import java.util.List; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/FeedFetchResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java similarity index 90% rename from app/src/main/java/awais/instagrabber/repositories/responses/FeedFetchResponse.java rename to app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java index 2d91a9f7..9d8659af 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/FeedFetchResponse.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java @@ -1,7 +1,9 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.feed; import java.util.List; +import awais.instagrabber.repositories.responses.Media; + public class FeedFetchResponse { private final List items; private final int numResults; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Notification.java b/app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java similarity index 91% rename from app/src/main/java/awais/instagrabber/repositories/responses/Notification.java rename to app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java index c784d52b..6da18866 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Notification.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.notification; import awais.instagrabber.models.enums.NotificationType; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java similarity index 97% rename from app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java rename to app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java index 87116896..27fde081 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.notification; import androidx.annotation.NonNull; @@ -7,8 +7,6 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import android.util.Log; - import awais.instagrabber.utils.Utils; public class NotificationArgs { diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java similarity index 95% rename from app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java rename to app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java index 38fb70f9..10074f70 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.notification; import androidx.annotation.NonNull; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java similarity index 84% rename from app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java rename to app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java index 3ba92ce2..e3d31bc9 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.notification; public class NotificationImage { private final String id; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java b/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java new file mode 100644 index 00000000..4aca79da --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java @@ -0,0 +1,38 @@ +package awais.instagrabber.repositories.responses.search; + +import awais.instagrabber.repositories.responses.Hashtag; +import awais.instagrabber.repositories.responses.Place; +import awais.instagrabber.repositories.responses.User; + +public class SearchItem { + private final User user; + private final Place place; + private final Hashtag hashtag; + private final int position; + + public SearchItem(final User user, + final Place place, + final Hashtag hashtag, + final int position) { + this.user = user; + this.place = place; + this.hashtag = hashtag; + this.position = position; + } + + public User getUser() { + return user; + } + + public Place getPlace() { + return place; + } + + public Hashtag getHashtag() { + return hashtag; + } + + public int getPosition() { + return position; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java new file mode 100644 index 00000000..04db0286 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java @@ -0,0 +1,48 @@ +package awais.instagrabber.repositories.responses.search; + +import java.util.List; + +import awais.instagrabber.repositories.responses.User; + +public class SearchResponse { + // app + private final List list; + // browser + private final List users; + private final List places; + private final List hashtags; + // universal + private final String status; + + public SearchResponse(final List list, + final List users, + final List places, + final List hashtags, + final String status) { + this.list = list; + this.users = users; + this.places = places; + this.hashtags = hashtags; + this.status = status; + } + + public List getList() { + return list; + } + + public List getUsers() { + return users; + } + + public List getPlaces() { + return places; + } + + public List getHashtags() { + return hashtags; + } + + public String getStatus() { + return status; + } +} diff --git a/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java b/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java index 5160f9aa..1845188e 100644 --- a/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java +++ b/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java @@ -18,13 +18,11 @@ import java.util.List; import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; -import awais.instagrabber.repositories.responses.NotificationCounts; +import awais.instagrabber.repositories.responses.notification.NotificationCounts; import awais.instagrabber.utils.Constants; import awais.instagrabber.webservices.NewsService; import awais.instagrabber.webservices.ServiceCallback; -import static awais.instagrabber.utils.Utils.settingsHelper; - public class ActivityCheckerService extends Service { private static final String TAG = "ActivityCheckerService"; private static final int INITIAL_DELAY_MILLIS = 200; diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index 4c064e81..219185bf 100644 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -1,6 +1,8 @@ package awais.instagrabber.utils; public final class Constants { + public static final String CRASH_REPORT_EMAIL = "barinsta@austinhuang.me"; + // string prefs public static final String FOLDER_PATH = "custom_path"; public static final String DATE_TIME_FORMAT = "date_time_format"; 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..2722d11e 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -729,15 +729,19 @@ public final class ResponseBodyUtils { width = dimensions.optInt("width"); } String thumbnailUrl = null; - final JSONArray displayResources = feedItem.getJSONArray("display_resources"); final List candidates = new ArrayList(); - for (int i = 0; i < displayResources.length(); i++) { - final JSONObject displayResource = displayResources.getJSONObject(i); - candidates.add(new MediaCandidate( - displayResource.getInt("config_width"), - displayResource.getInt("config_height"), - displayResource.getString("src") - )); + if (feedItem.has("display_resources") || feedItem.has("thumbnail_resources")) { + final JSONArray displayResources = feedItem.has("display_resources") + ? feedItem.getJSONArray("display_resources") + : feedItem.getJSONArray("thumbnail_resources"); + for (int i = 0; i < displayResources.length(); i++) { + final JSONObject displayResource = displayResources.getJSONObject(i); + candidates.add(new MediaCandidate( + displayResource.getInt("config_width"), + displayResource.getInt("config_height"), + displayResource.getString("src") + )); + } } final ImageVersions2 imageVersions2 = new ImageVersions2(candidates); @@ -943,8 +947,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 +955,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/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java index e48a96cd..1ff49767 100755 --- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java +++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java @@ -15,6 +15,7 @@ import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_D import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS; +import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY; import static awais.instagrabber.utils.Constants.APP_LANGUAGE; import static awais.instagrabber.utils.Constants.APP_THEME; import static awais.instagrabber.utils.Constants.APP_UA; @@ -145,6 +146,10 @@ public final class SettingsHelper { if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply(); } + public boolean hasPreference(final String key) { + return sharedPreferences != null && sharedPreferences.contains(key); + } + @StringDef( {APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, @@ -156,7 +161,7 @@ public final class SettingsHelper { @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY, CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH, - FLAG_SECURE, TOGGLE_KEYWORD_FILTER}) + FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY}) public @interface BooleanSettings {} @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER}) diff --git a/app/src/main/java/awais/instagrabber/utils/TextUtils.java b/app/src/main/java/awais/instagrabber/utils/TextUtils.java index e26bd8cf..64eddd22 100644 --- a/app/src/main/java/awais/instagrabber/utils/TextUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/TextUtils.java @@ -69,7 +69,7 @@ public final class TextUtils { str = str.trim(); return "".equals(str) || "null".equals(str) || str.isEmpty(); } - return "null".contentEquals(charSequence) || "".contentEquals(charSequence) || charSequence.length() < 1; + return "null".contentEquals(charSequence) || "".contentEquals(charSequence); } public static String millisToTimeString(final long millis) { diff --git a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java index 17c8327c..ac584c34 100644 --- a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java +++ b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java @@ -1,7 +1,10 @@ package awais.instagrabber.utils.emoji; +import android.content.Context; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.common.collect.ImmutableList; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; @@ -18,6 +21,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import awais.instagrabber.R; import awais.instagrabber.customviews.emoji.Emoji; import awais.instagrabber.customviews.emoji.EmojiCategory; import awais.instagrabber.customviews.emoji.EmojiCategoryType; @@ -33,25 +37,25 @@ public final class EmojiParser { private Map categoryMap = Collections.emptyMap(); private ImmutableList categories; - public static EmojiParser getInstance() { + public static void setup(@NonNull final Context context) { if (instance == null) { synchronized (LOCK) { if (instance == null) { - instance = new EmojiParser(); + instance = new EmojiParser(context); } } } + } + + public static EmojiParser getInstance() { + if (instance == null) { + throw new RuntimeException("Setup not done!"); + } return instance; } - private EmojiParser() { - final String file = "res/raw/emojis.json"; - final ClassLoader classLoader = getClass().getClassLoader(); - if (classLoader == null) { - Log.e(TAG, "Emoji: classLoader is null"); - return; - } - try (final InputStream in = classLoader.getResourceAsStream(file)) { + private EmojiParser(final Context context) { + try (final InputStream in = context.getResources().openRawResource(R.raw.emojis)) { final String json = NetworkUtils.readFromInputStream(in); final Gson gson = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 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/viewmodels/NotificationViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java index 59a8bb64..1e3e9845 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java @@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel; import java.util.List; -import awais.instagrabber.repositories.responses.Notification; +import awais.instagrabber.repositories.responses.notification.Notification; public class NotificationViewModel extends ViewModel { private MutableLiveData> list; diff --git a/app/src/main/java/awais/instagrabber/webservices/FeedService.java b/app/src/main/java/awais/instagrabber/webservices/FeedService.java index 8b1626ed..e636ac22 100644 --- a/app/src/main/java/awais/instagrabber/webservices/FeedService.java +++ b/app/src/main/java/awais/instagrabber/webservices/FeedService.java @@ -12,10 +12,10 @@ import java.util.Map; import java.util.UUID; import awais.instagrabber.repositories.FeedRepository; -import awais.instagrabber.repositories.responses.EndOfFeedDemarcator; -import awais.instagrabber.repositories.responses.EndOfFeedGroup; -import awais.instagrabber.repositories.responses.EndOfFeedGroupSet; -import awais.instagrabber.repositories.responses.FeedFetchResponse; +import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator; +import awais.instagrabber.repositories.responses.feed.EndOfFeedGroup; +import awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet; +import awais.instagrabber.repositories.responses.feed.FeedFetchResponse; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.utils.TextUtils; diff --git a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java index e56f5581..6246cd67 100644 --- a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java +++ b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java @@ -19,6 +19,7 @@ import awais.instagrabber.repositories.GraphQLRepository; import awais.instagrabber.repositories.responses.FriendshipStatus; import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse; import awais.instagrabber.repositories.responses.Hashtag; +import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.User; @@ -392,9 +393,53 @@ public class GraphQLService extends BaseService { callback.onSuccess(new Hashtag( body.getString(Constants.EXTRAS_ID), body.getString("name"), - body.getString("profile_pic_url"), timelineMedia.getLong("count"), - body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING)); + body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING, + null)); + } catch (JSONException e) { + Log.e(TAG, "onResponse", e); + if (callback != null) { + callback.onFailure(e); + } + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + if (callback != null) { + callback.onFailure(t); + } + } + }); + } + + public void fetchLocation(final long locationId, + final ServiceCallback callback) { + final Call request = repository.getLocation(locationId); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String rawBody = response.body(); + if (rawBody == null) { + Log.e(TAG, "Error occurred while fetching gql location of " + locationId); + callback.onSuccess(null); + return; + } + try { + final JSONObject body = new JSONObject(rawBody) + .getJSONObject("graphql") + .getJSONObject(Constants.EXTRAS_LOCATION); + final JSONObject timelineMedia = body.getJSONObject("edge_location_to_media"); + final JSONObject address = new JSONObject(body.getString("address_json")); + callback.onSuccess(new Location( + body.getLong(Constants.EXTRAS_ID), + body.getString("slug"), + body.getString("name"), + address.optString("street_address"), + address.optString("city_name"), + body.optDouble("lng", 0d), + body.optDouble("lat", 0d) + )); } catch (JSONException e) { Log.e(TAG, "onResponse", e); if (callback != null) { diff --git a/app/src/main/java/awais/instagrabber/webservices/LocationService.java b/app/src/main/java/awais/instagrabber/webservices/LocationService.java index 2f8036a3..aca6ef47 100644 --- a/app/src/main/java/awais/instagrabber/webservices/LocationService.java +++ b/app/src/main/java/awais/instagrabber/webservices/LocationService.java @@ -5,7 +5,9 @@ import androidx.annotation.NonNull; import com.google.common.collect.ImmutableMap; import awais.instagrabber.repositories.LocationRepository; +import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.LocationFeedResponse; +import awais.instagrabber.repositories.responses.Place; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.utils.TextUtils; import retrofit2.Call; @@ -67,34 +69,24 @@ public class LocationService extends BaseService { }); } - // private PostsFetchResponse parseResponse(@NonNull final String body) throws JSONException { - // final JSONObject root = new JSONObject(body); - // final boolean moreAvailable = root.optBoolean("more_available"); - // final String nextMaxId = root.optString("next_max_id"); - // final JSONArray itemsJson = root.optJSONArray("items"); - // final List items = parseItems(itemsJson); - // return new PostsFetchResponse( - // items, - // moreAvailable, - // nextMaxId - // ); - // } + public void fetch(@NonNull final long locationId, + final ServiceCallback callback) { + final Call request = repository.fetch(locationId); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + if (callback == null) { + return; + } + callback.onSuccess(response.body() == null ? null : response.body().getLocation()); + } - // private List parseItems(final JSONArray items) throws JSONException { - // if (items == null) { - // return Collections.emptyList(); - // } - // final List feedModels = new ArrayList<>(); - // for (int i = 0; i < items.length(); i++) { - // final JSONObject itemJson = items.optJSONObject(i); - // if (itemJson == null) { - // continue; - // } - // final FeedModel feedModel = ResponseBodyUtils.parseItem(itemJson); - // if (feedModel != null) { - // feedModels.add(feedModel); - // } - // } - // return feedModels; - // } + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + if (callback != null) { + callback.onFailure(t); + } + } + }); + } } diff --git a/app/src/main/java/awais/instagrabber/webservices/NewsService.java b/app/src/main/java/awais/instagrabber/webservices/NewsService.java index 667fb647..a7664a8a 100644 --- a/app/src/main/java/awais/instagrabber/webservices/NewsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/NewsService.java @@ -16,6 +16,10 @@ import awais.instagrabber.repositories.responses.NewsInboxResponse; import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.NotificationArgs; import awais.instagrabber.repositories.responses.NotificationCounts; +import awais.instagrabber.repositories.responses.NewsInboxResponse; +import awais.instagrabber.repositories.responses.notification.Notification; +import awais.instagrabber.repositories.responses.notification.NotificationArgs; +import awais.instagrabber.repositories.responses.notification.NotificationCounts; import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.UserSearchResponse; import awais.instagrabber.utils.Constants; diff --git a/app/src/main/java/awais/instagrabber/webservices/SearchService.java b/app/src/main/java/awais/instagrabber/webservices/SearchService.java new file mode 100644 index 00000000..39b5bd65 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/webservices/SearchService.java @@ -0,0 +1,50 @@ +package awais.instagrabber.webservices; + +import androidx.annotation.NonNull; + +import com.google.common.collect.ImmutableMap; + +import awais.instagrabber.repositories.SearchRepository; +import awais.instagrabber.repositories.responses.search.SearchResponse; +import awais.instagrabber.utils.TextUtils; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; + +public class SearchService extends BaseService { + private static final String TAG = "LocationService"; + + private final SearchRepository repository; + + private static SearchService instance; + + private SearchService() { + final Retrofit retrofit = getRetrofitBuilder() + .baseUrl("https://www.instagram.com") + .build(); + repository = retrofit.create(SearchRepository.class); + } + + public static SearchService getInstance() { + if (instance == null) { + instance = new SearchService(); + } + return instance; + } + + public Call search(final boolean isLoggedIn, + final String query, + final String context) { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put("query", query); + // context is one of: "blended", "user", "place", "hashtag" + // note that "place" and "hashtag" can contain ONE user result, who knows why + builder.put("context", context); + builder.put("count", "50"); + return repository.search(isLoggedIn + ? "https://i.instagram.com/api/v1/fbsearch/topsearch_flat/" + : "https://www.instagram.com/web/search/topsearch/", + builder.build()); + } +} diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java index cc19b086..059a8c7b 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java @@ -93,7 +93,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); } @@ -185,7 +185,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)); } catch (Exception e) {} // to cover promotional reels with non-long user pk's @@ -361,9 +361,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) { @@ -377,7 +376,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()); } @@ -385,8 +384,7 @@ public class StoriesService extends BaseService { String username = null; if (data != null // && localUsername == null - && !isLoc - && !isHashtag) { + && !isLocOrHashtag) { username = data.getJSONObject("user").getString("username"); } @@ -394,12 +392,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 { @@ -540,6 +537,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; @@ -547,15 +545,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/java/awaisomereport/CrashReporter.java b/app/src/main/java/awaisomereport/CrashReporter.java index f5f22143..8b61e952 100755 --- a/app/src/main/java/awaisomereport/CrashReporter.java +++ b/app/src/main/java/awaisomereport/CrashReporter.java @@ -1,50 +1,35 @@ package awaisomereport; import android.app.Application; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Process; -import android.util.Log; import androidx.annotation.NonNull; -import androidx.core.content.FileProvider; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Date; -//import java.util.zip.ZipEntry; -//import java.util.zip.ZipOutputStream; - -import awais.instagrabber.BuildConfig; -import awais.instagrabber.utils.Utils; public final class CrashReporter implements Thread.UncaughtExceptionHandler { + private static final String TAG = CrashReporter.class.getSimpleName(); + private static CrashReporter reporterInstance; - private final Application application; - private final String email; -// private final File crashLogsZip; + + // private final File crashLogsZip; + private final CrashHandler crashHandler; + private boolean startAttempted = false; + private Thread.UncaughtExceptionHandler defaultEH; public static CrashReporter get(final Application application) { - if (reporterInstance == null) reporterInstance = new CrashReporter(application); + if (reporterInstance == null) { + reporterInstance = new CrashReporter(application); + } return reporterInstance; } private CrashReporter(@NonNull final Application application) { - this.application = application; - this.email = "barinsta@austinhuang.me"; -// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip"); + crashHandler = new CrashHandler(application); + // this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip"); } public void start() { if (!startAttempted) { + defaultEH = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); startAttempted = true; } @@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) { - final StringBuilder reportBuilder = new StringBuilder(); - reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues."); - reportBuilder.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation."); - reportBuilder.append("\r\n\r\nError report collected on: ").append(new Date().toString()); - - reportBuilder - .append("\r\n\r\nInformation:\r\n==============") - .append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME) - .append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE) - .append("\r\nPHONE-MODEL : ").append(Build.MODEL) - .append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE) - .append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT) - .append("\r\nBRAND : ").append(Build.BRAND) - .append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER) - .append("\r\nBOARD : ").append(Build.BOARD) - .append("\r\nDEVICE : ").append(Build.DEVICE) - .append("\r\nPRODUCT : ").append(Build.PRODUCT) - .append("\r\nHOST : ").append(Build.HOST) - .append("\r\nTAGS : ").append(Build.TAGS); - - reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n"); - final Writer result = new StringWriter(); - try (final PrintWriter printWriter = new PrintWriter(result)) { - exception.printStackTrace(printWriter); - reportBuilder.append(result.toString()); - - reportBuilder.append("\r\nCause:\r\n=============="); - - // for AsyncTask crashes - Throwable cause = exception.getCause(); - while (cause != null) { - cause.printStackTrace(printWriter); - reportBuilder.append(result.toString()); - cause = cause.getCause(); - } - } - reportBuilder.append("\r\n\r\n**** End of current Report ***"); - - final String errorContent = reportBuilder.toString(); - try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) { - trace.write(errorContent.getBytes()); - } catch (final Exception ex) { - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", ex); - } - - application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - -// zipLogs(); - - Process.killProcess(Process.myPid()); - System.exit(10); - } - -// public synchronized CrashReporter zipLogs() { -// final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() : -// new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs"); -// -// try (final FileOutputStream fos = new FileOutputStream(crashLogsZip); -// final ZipOutputStream zos = new ZipOutputStream(fos)) { -// -// final File[] files = logDir.listFiles(); -// -// if (files != null) { -// zos.setLevel(5); -// byte[] buffer; -// for (final File file : files) { -// if (file != null && file.length() > 0) { -// buffer = new byte[1024]; -// try (final FileInputStream fis = new FileInputStream(file)) { -// zos.putNextEntry(new ZipEntry(file.getName())); -// int length; -// while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length); -// zos.closeEntry(); -// } -// } -// } -// } -// -// } catch (final Exception e) { -// if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); -// } -// -// return this; -// } - - @SuppressWarnings("ResultOfMethodCallIgnored") - public void startCrashEmailIntent(final Context context) { - try { - final String filePath = context.getFilesDir().getAbsolutePath(); - - String[] errorFileList; - - try { - final File dir = new File(filePath); - if (dir.exists() && !dir.isDirectory()) dir.delete(); - dir.mkdir(); - errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace")); - } catch (final Exception e) { - errorFileList = null; - } - - if (errorFileList != null && errorFileList.length > 0) { - final StringBuilder errorStringBuilder; - - errorStringBuilder = new StringBuilder("\r\n\r\n"); - final int maxSendMail = 5; - - int curIndex = 0; - for (final String curString : errorFileList) { - final File file = new File(filePath + '/' + curString); - - if (curIndex++ <= maxSendMail) { - errorStringBuilder.append("New Trace collected:\r\n=====================\r\n"); - try (final BufferedReader input = new BufferedReader(new FileReader(file))) { - String line; - while ((line = input.readLine()) != null) - errorStringBuilder.append(line).append("\r\n"); - } - } - - file.delete(); - } - - errorStringBuilder.append("\r\n\r\n"); - - context.startActivity(Intent.createChooser(new Intent(Intent.ACTION_SEND).setType("message/rfc822") - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - .putExtra(Intent.EXTRA_EMAIL, new String[]{email}) -// .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip)) - .putExtra(Intent.EXTRA_SUBJECT, "Barinsta Crash Report") - .putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()), "Select an email app to send crash logs")); - } - } catch (final Exception e) { - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + if (crashHandler == null) { + defaultEH.uncaughtException(t, exception); + return; } + crashHandler.uncaughtException(t, exception, defaultEH); } } diff --git a/app/src/main/java/awaisomereport/CrashReporterHelper.java b/app/src/main/java/awaisomereport/CrashReporterHelper.java new file mode 100644 index 00000000..83f17f3e --- /dev/null +++ b/app/src/main/java/awaisomereport/CrashReporterHelper.java @@ -0,0 +1,134 @@ +package awaisomereport; + +import android.app.Application; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Date; + +import awais.instagrabber.BuildConfig; +import awais.instagrabber.R; +import awais.instagrabber.utils.Constants; + +public final class CrashReporterHelper { + private static final String TAG = CrashReporterHelper.class.getSimpleName(); + + public static void startErrorReporterActivity(@NonNull final Application application, + @NonNull final Throwable exception) { + final StringBuilder reportBuilder = new StringBuilder(); + reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues.") + .append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation.") + .append("\r\n\r\nError report collected on: ").append(new Date().toString()) + .append("\r\n\r\nInformation:\r\n==============") + .append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME) + .append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE) + .append("\r\nPHONE-MODEL : ").append(Build.MODEL) + .append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE) + .append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT) + .append("\r\nBRAND : ").append(Build.BRAND) + .append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER) + .append("\r\nBOARD : ").append(Build.BOARD) + .append("\r\nDEVICE : ").append(Build.DEVICE) + .append("\r\nPRODUCT : ").append(Build.PRODUCT) + .append("\r\nHOST : ").append(Build.HOST) + .append("\r\nTAGS : ").append(Build.TAGS); + + reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n"); + final Writer result = new StringWriter(); + try (final PrintWriter printWriter = new PrintWriter(result)) { + exception.printStackTrace(printWriter); + reportBuilder.append(result.toString()); + reportBuilder.append("\r\nCause:\r\n=============="); + // for AsyncTask crashes + Throwable cause = exception.getCause(); + while (cause != null) { + cause.printStackTrace(printWriter); + reportBuilder.append(result.toString()); + cause = cause.getCause(); + } + } + reportBuilder.append("\r\n\r\n**** End of current Report ***"); + + final String errorContent = reportBuilder.toString(); + try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) { + trace.write(errorContent.getBytes()); + } catch (final Exception ex) { + if (BuildConfig.DEBUG) Log.e(TAG, "", ex); + } + + application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } + + public static void startCrashEmailIntent(final Context context) { + try { + final String filePath = context.getFilesDir().getAbsolutePath(); + + String[] errorFileList; + + try { + final File dir = new File(filePath); + if (dir.exists() && !dir.isDirectory()) { + //noinspection ResultOfMethodCallIgnored + dir.delete(); + } + //noinspection ResultOfMethodCallIgnored + dir.mkdirs(); + errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace")); + } catch (final Exception e) { + errorFileList = null; + } + + if (errorFileList == null || errorFileList.length <= 0) { + return; + } + final StringBuilder errorStringBuilder; + + errorStringBuilder = new StringBuilder("\r\n\r\n"); + final int maxSendMail = 5; + int curIndex = 0; + for (final String curString : errorFileList) { + final File file = new File(filePath + '/' + curString); + + if (curIndex++ <= maxSendMail) { + errorStringBuilder.append("New Trace collected:\r\n=====================\r\n"); + try (final BufferedReader input = new BufferedReader(new FileReader(file))) { + String line; + while ((line = input.readLine()) != null) + errorStringBuilder.append(line).append("\r\n"); + } + } + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + + errorStringBuilder.append("\r\n\r\n"); + final Resources resources = context.getResources(); + context.startActivity(Intent.createChooser( + new Intent(Intent.ACTION_SEND) + .setType("message/rfc822") + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + .putExtra(Intent.EXTRA_EMAIL, new String[]{Constants.CRASH_REPORT_EMAIL}) + // .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip)) + .putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.crash_report_subject)) + .putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()), + context.getResources().getString(R.string.crash_report_title)) + ); + } catch (final Exception e) { + Log.e(TAG, "", e); + } + } +} diff --git a/app/src/main/java/awaisomereport/ErrorReporterActivity.java b/app/src/main/java/awaisomereport/ErrorReporterActivity.java index 9fb1ce40..31dc3817 100755 --- a/app/src/main/java/awaisomereport/ErrorReporterActivity.java +++ b/app/src/main/java/awaisomereport/ErrorReporterActivity.java @@ -43,8 +43,9 @@ public final class ErrorReporterActivity extends Activity implements View.OnClic @Override public void onClick(@NonNull final View v) { - if (v == btnReport) - CrashReporter.get(getApplication()).startCrashEmailIntent(this); + if (v == btnReport) { + CrashReporterHelper.startCrashEmailIntent(this); + } finish(); System.exit(10); } diff --git a/app/src/main/java/awaisomereport/ICrashHandler.java b/app/src/main/java/awaisomereport/ICrashHandler.java new file mode 100644 index 00000000..103987d9 --- /dev/null +++ b/app/src/main/java/awaisomereport/ICrashHandler.java @@ -0,0 +1,7 @@ +package awaisomereport; + +public interface ICrashHandler { + void uncaughtException(Thread t, + Throwable exception, + Thread.UncaughtExceptionHandler defaultEH); +} diff --git a/app/src/main/res/drawable/ic_hashtag.png b/app/src/main/res/drawable/ic_hashtag.png new file mode 100644 index 00000000..ce135fc0 Binary files /dev/null and b/app/src/main/res/drawable/ic_hashtag.png differ 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" /> - + + + + + + + + + + + + + + @@ -60,8 +59,8 @@ app:chipIcon="@drawable/ic_outline_star_plus_24" app:chipIconTint="@color/yellow_800" app:layout_constraintBottom_toBottomOf="@id/mainLocationImage" - app:layout_constraintStart_toEndOf="@id/btnMap" - app:layout_constraintTop_toBottomOf="@id/mainLocPostCount" + app:layout_constraintStart_toEndOf="@id/mainLocationImage" + app:layout_constraintTop_toBottomOf="@id/btnMap" app:rippleColor="@color/yellow_400" /> - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_profile_details.xml b/app/src/main/res/layout/layout_profile_details.xml index 1d747107..035a04b0 100644 --- a/app/src/main/res/layout/layout_profile_details.xml +++ b/app/src/main/res/layout/layout_profile_details.xml @@ -36,6 +36,7 @@ app:chipIconTint="@color/deep_purple_200" app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintStart_toEndOf="@id/mainProfileImage" + app:layout_constraintTop_toTopOf="parent" app:rippleColor="@color/purple_200" tools:visibility="visible" /> @@ -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" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 95557279..18b0557d 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -21,6 +21,7 @@ Comentaris Activitat Cerca actualitzacions a l\'inici + Block screenshots & app preview Descarrega les publicacions a carpetes de nom d\'usuari Marca les històries com a vistes després de visualitzar-es L\'autor de la història sabrà que l\'has vista @@ -451,4 +452,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 56cd43da..0bb9e0ac 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -21,6 +21,7 @@ Komentáře Aktivita Zkontrolovat aktualizace při spuštění + Block screenshots & app preview Stáhnout příspěvky do složek s uživatelským jménem Označit příběhy po zhlédnutí jako zobrazené Autor příběhu bude vědět, že jsi si ho zobrazili @@ -467,4 +468,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 09593f9b..1f541fcd 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -21,6 +21,7 @@ Kommentare Aktivität Beim Start auf Aktualisierungen prüfen + Block screenshots & app preview Beiträge in Benutzernamen-Ordner herunterladen Stories nach dem Ansehen als gesehen markieren Die Person wird wissen, dass du dir die Story angesehen hast @@ -451,4 +452,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-el/arrays.xml b/app/src/main/res/values-el/arrays.xml index fcb037ce..0dd3a31e 100644 --- a/app/src/main/res/values-el/arrays.xml +++ b/app/src/main/res/values-el/arrays.xml @@ -45,7 +45,7 @@ - - secs - mins + δεύτερα + λεπτά diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 6ae4285c..f9817a0a 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -21,6 +21,7 @@ Σχόλια Δραστηριότητα Έλεγχος για ενημερώσεις στο ξεκίνημα + Block screenshots & app preview Λήψη δημοσίευσης στους φακέλους με ονόματα χρηστών Επισήμανση ιστοριών ως προβληθέντων μετά την προβολή Ο συντάκτης της ιστορίας θα ξέρει ότι την προβάλατε @@ -114,10 +115,10 @@ Άρση αποκλεισμού Περιορισμός Άρση Περιορισμού - Mute stories - Mute posts - Unmute stories - Unmute posts + Σίγαση ιστοριών + Σίγαση δημοσιεύσεων + Κατάργηση σίγασης ιστοριών + Κατάργηση σίγασης δημοσιεύσεων Αντιγραφή βιογραφικού Μετάφραση βιογραφικού Κοινά @@ -147,7 +148,7 @@ Προβολή εικόνας προφίλ Μη υποστηριζόμενος τύπος μηνύματος Κατάργηση αποστολής μηνύματος - View on GIPHY + Προβολή στο GIPHY %s κοινοποίησε μια δημοσίευση από @%s %s κοινοποίησε μια εικόνα %s κοινοποίησε ένα βίντεο @@ -186,7 +187,7 @@ Αποχώρηση από αυτήν τη συνομιλία; Διώξιμο Χρήστες που αποχώρησαν - Invalid user + Μη έγκυρος χρήστης Instagram does not allow uploading videos longer than 60 secs for DM. Instagram does not allow uploading audio longer than 60 secs. Απευθείας λήψη @@ -245,8 +246,8 @@ %d επισημάνσεις \'Μου Αρέσει\' σε σχόλια %d ετικέτες χρήστη %d επισημάνσεις \"Μου αρέσει\" - %d photos of you - %d follow requests + %d φωτογραφίες σας + %d αιτήματα ακολούθησης Αποσυνδεθήκατε πριν κάνετε κλικ σε αυτή την ειδοποίηση;! Ροή Προφίλ @@ -281,13 +282,13 @@ Σκοτεινό θέμα Barista Σκοτεινό Material - Added to Favorites! - Add to Favorites + Προστέθηκε στα Αγαπημένα! + Προσθήκη στα αγαπημένα Λογαριασμοί Hashtags Τοποθεσίες Άγνωστο - Removed from Favourites! + Αφαίρεση από τα αγαπημένα! Αντίγραφα ασφαλείας & Επαναφορά Αντίγραφο ασφαλείας ρυθμίσεων εφαρμογής Barinsta, δεδομένα σύνδεσης λογαριασμού και/ή αγαπημένα σε ένα απλό κείμενο ή κρυπτογραφημένο αρχείο αντιγράφου ασφαλείας για μεταγενέστερη επαναφορά. Αν δημιουργείτε αντίγραφα ασφαλείας των δεδομένων σύνδεσης λογαριασμού, αντιμετωπίζετε το αρχείο ως απόρρητο και κρατήστε το κάπου που είναι ασφαλές! @@ -437,18 +438,25 @@ Αποδοχή Εσείς No pending requests - Checking for new messages - Stories + Γίνεται έλεγχος για νέες μηνύματα + Ιστορίες DM - Notifications - Post + Ειδοποιήσεις + Δημοσίευση Enable DM notifications Auto refresh messages - Auto refresh every - secs - mins - Search GIPHY + Αυτόματη ανανέωση κάθε + δεύτερα + λεπτά + Αναζήτηση Στο GIPHY Response is null! Response status is not ok! Request failed! + Λέξη-κλειδί + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Σήμανθηκε ως αναγνωσμένο + Η διαγραφή απέτυχε diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1bc5ee91..f392ec86 100755 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -21,6 +21,7 @@ Comentarios Actividad Buscar actualizaciones al inicio + Bloquea capturas de pantalla & vista previa de aplicaciones Usar subcarpetas con el nombre de usuario Marcar historias como vistas después de verlas El autor de la historia sabrá que lo has visto @@ -451,4 +452,11 @@ ¡La respuesta es nula! ¡El estado de la respuesta no es correcto! ¡Solicitud fallida! + Palabra clave + Activar filtro de palabras clave + Editar filtros de palabras clave + Palabra clave añadida: %s a la lista de filtros + Se eliminó la palabra clave: %s de la lista de filtros + Marcado como visto + Eliminación fallida diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 570d4ec3..dd0321e2 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -21,6 +21,7 @@ Iruzkinak Jarduera Bilatu eguneratzeak abioan + Block screenshots & app preview Deskargatu bidalketak erabiltzaile-izena duten karpetetara Markatu istorioak ikusita gisa ikusi ondoren Istorioaren egileak ikusi duzula jakingo du @@ -179,14 +180,14 @@ Pantaila-argazkia aterata Ez da iritsi Unseen count response is null! - Message... + Mezua... Press and hold to record audio Updating... - Leave chat + Utzi txata Utzi txat hau? Bota Joan diren erabiltzaileak - Invalid user + Erabiltzaile baliogabea Instagram does not allow uploading videos longer than 60 secs for DM. Instagram does not allow uploading audio longer than 60 secs. Deskargatu zuzenean @@ -281,13 +282,13 @@ Gai iluna Akeita Beltz materiala - Added to Favorites! - Add to Favorites + Gogokoetara gehituta! + Gehitu gogokoetara Kontuak Traolak Kokalekuak Ezezaguna - Removed from Favourites! + Gogokoetatik kenduta! Backup & Restore Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration. If you\'re backing up account login data, treat the file as confidential and keep it somewhere safe! @@ -299,7 +300,7 @@ Ezarri Gorde Argazki-oina - Edit caption + Editatu argazki-oina Translate caption Video player timeline Atsegiten… @@ -333,19 +334,19 @@ Erabiltzaile kopuruaren arabera, honek kargatzen tardatu dezake. Itxaron. Bidalketa ez da aurkitu! URLak irekiko dituen aplikaziorik ez da aurkitu - Gallery - Camera - All Photos - All Media - All Videos - Brightness - Contrast + Galeria + Kamera + Argazki guztiak + Multimedia guztia + Bideo guztiak + Distira + Kontrastea Vibrance - Saturation + Saturazioa Sharpen - Exposure - Center - Color + Esposizioa + Erdiratu + Kolorea Start End Bilateral Blur @@ -355,9 +356,9 @@ Clarendon 1977 Aden - Reset - Crop - Normal + Berrezarri + Ebaki + Normala atsegite %d %d atsegite @@ -376,9 +377,9 @@ Biltegiratze-baimenik ez da eman! Details - Title - Members - Admin + Izenburua + Kideak + Administratzailea Inviter Mute messages Mute mentions @@ -451,4 +452,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 451ba8b7..c6da89cf 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -21,6 +21,7 @@ دیدگاه‌ها فعالیت بررسی بروزرسانی هنگام آغاز برنامه + Block screenshots & app preview بارگیری پست ها در پوشه های به نام کاربر نشان کرد استوری ها به عنوان دیده شده بعد از دیدن نویسنده استوری می داند که شما آن را دیده اید @@ -452,4 +453,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 09d7db8d..15f8a5cc 100755 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -21,6 +21,7 @@ Commentaires Activité Rechercher les mises à jours au démarrage + Bloquer les captures d\'écran & l\'aperçu de l\'application Télécharger les messages dans les dossiers des noms d\'utilisateurs Marquer les stories comme vues après consultation L\'auteur de la story saura que vous l\'avez vue @@ -178,7 +179,7 @@ Suggéré Capture d\'écran effectuée Impossible de livrer - Unseen count response is null! + La réponse du comptage invisible est nulle ! Message... Appuyer de manière prolongée pour enregistrer un audio Mise à jour en cours... @@ -186,9 +187,9 @@ Quitter cette conversation ? Éjecter Utilisateurs restants - Invalid user - Instagram does not allow uploading videos longer than 60 secs for DM. - Instagram does not allow uploading audio longer than 60 secs. + Utilisateur non valide + Instagram n\'autorise pas le téléchargement de vidéos de plus de 60 secondes pour les DM. + Instagram ne permet pas de télécharger des fichiers audio de plus de 60 secondes. Télécharger directement Téléchargez les posts directement sur le téléphone ! Téléchargement de(s) post(s) @@ -448,7 +449,14 @@ secondes minutes Rechercher GIPHY - Response is null! - Response status is not ok! - Request failed! + La réponse est nulle ! + L\'état de la réponse n\'est pas correct ! + La demande a échoué ! + Mot-clé + Activer le filtre par mot-clé + Modifier les filtres de mots-clés + Mot-clé ajouté : %s à la liste de filtres + Mot-clé supprimé : %s de la liste de filtres + Marqué comme vu + Suppression non réussie diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 86201c55..bc923135 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -21,6 +21,7 @@ टिप्पणियाँ कार्यकलाप खुलने पर अपडेट के लिए जाँच करें + Block screenshots & app preview पोस्ट को ब्यबहारकारी के नाम पर किये फोल्डरस में रखें स्टोरि को दिखने के बाद \"दिखा गया\" दिखादें सटोरि के लेखक जानेगा कि तुम देखे हो इसको @@ -452,4 +453,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index c34cac16..462c1a2e 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -21,6 +21,7 @@ Komentar Aktivitas Cek pembaruan saat memulai + Block screenshots & app preview Unduh kiriman ke folder nama pengguna Tandai cerita dibaca setelah melihat Pembuat cerita akan tahu Anda melihatnya @@ -443,4 +444,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 62250a37..d8202389 100755 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -21,6 +21,7 @@ Commenti Attività Verifica per aggiornamenti all\'avvio + Blocca screenshot & anteprima app Scarica i post nelle cartelle del nome utente Segna le storie come viste dopo la visualizzazione L\'autore della storia saprà che l\'hai visualizzata @@ -178,7 +179,7 @@ Suggerito Catturato Impossibile consegnare - Unseen count response is null! + Il responso del conteggio non visto è nullo! Messaggio... Tieni premuto per registrare l\'audio In aggiornamento... @@ -186,9 +187,9 @@ Lasciare questa chat? Espelli Utenti rimanenti - Invalid user - Instagram does not allow uploading videos longer than 60 secs for DM. - Instagram does not allow uploading audio longer than 60 secs. + Utente non valido + Instagram non permette di caricare video più lunghi di 60 secondi per DM. + Instagram non permette di caricare audio più lunghi di 60 secondi. Scarica direttamente Scarica i post direttamente sul telefono! Recuperando i post @@ -448,7 +449,14 @@ secondi minuti Cerca GIPHY - Response is null! - Response status is not ok! - Request failed! + La risposta è nulla! + Lo stato di risposta non è ok! + Richiesta fallita! + Parola Chiave + Abilita filtro parole chiave + Modifica filtri di parole chiave + Parola chiave aggiunta: %s alla lista filtri + Parola chiave rimossa: %s dalla lista filtri + Segnato come visto + Eliminazione non riuscita diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 41440092..5a11dc06 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -21,6 +21,7 @@ コメント アクティビティ 起動時にアップデートを確認 + Block screenshots & app preview ユーザ名のフォルダに投稿をダウンロード ストーリーズを表示後に既読にする ストーリーの作成者は、あなたが閲覧したことを知ることができます。 @@ -443,4 +444,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 185e4ddc..80d8b915 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -21,6 +21,7 @@ Comments Activity Check for updates at startup + Block screenshots & app preview Download posts to username folders Mark stories as seen after viewing Story author will know you viewed it @@ -451,4 +452,5 @@ Response is null! Response status is not ok! Request failed! + Marked as seen diff --git a/app/src/main/res/values-mk/arrays.xml b/app/src/main/res/values-mk/arrays.xml index b56358b7..974c0995 100644 --- a/app/src/main/res/values-mk/arrays.xml +++ b/app/src/main/res/values-mk/arrays.xml @@ -45,7 +45,7 @@ - - secs - mins + секунди + минути diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index c1d8631a..abb89551 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -9,7 +9,7 @@ Грешка при копирање текст Копрано на clipboard! Пријави - Protect file with password + Заштити податоци со лозинка Лозинка ОК Да @@ -21,6 +21,7 @@ Коментари Активности Провери за ажурирање + Block screenshots & app preview Превземи објави во папката со кориснички имиња Означи ги приказните како видени Авторот на приказната ќе знае дека сте ја погледнале приказната @@ -28,10 +29,10 @@ Другите членови ќе знаат дека сте ја виделе содржината на пораката Овозможи нотификации Сортирање на објави - Error loading profile! Is the username valid? If so, you may be ratelimited. - Error loading profile! Is the username valid? Or did they block you? - Error loading hashtag! Is the name valid? - Error loading location! Is the URL valid? + Грешка при вчитување на профилот! Проверете дали корисничкото име е валидно? + Грешка при вчитување на профилот! Проверете дали корисничкото име е валидно? + Грешка при вчитувањето на хаштагот! Дали е валиден хаштагот? + Грешка при вчитување на локација! Дали URL адресата е валидна? Фатална Грешка при креирање на папка(и). Зачувај во друга папка Одбери папка @@ -40,15 +41,15 @@ Влијае на анонимните корисници: Јазик - %s\nPost - %s\nPosts + %s\nОбјава + %s\nОбјави %s Објава %s Објави - %s\nFollower + %s\nСледбеник %s\nСледбеници %s\nСледбеници @@ -92,13 +93,13 @@ Отстрани ги сите кориснички сметки Ова ќе ги отстране сите кориснички сметки од апликацијата!\n За да отстраните само една корисничка сметка држете со прстот на сметката\n Дали сакате да продолжите? Формат на датум - Create new collection - Edit collection name - Delete collection - Are you sure you want to delete this collection? - All contained media will remain in other collections. - Add to collection... - Remove from collection + Креирај нова колекција + Уреди го името на колекцијата + Избриши колекција + Дали сте сигурни дека сакате да ја избришите оваа колекција? + Сите медиуми ќе останат во други колекции. + Додади во колекција... + Одстрани од колекција Лајкнато Зачувано Означен @@ -114,10 +115,10 @@ Одблокни Ограничи Одограничи - Mute stories - Mute posts - Unmute stories - Unmute posts + Игнорирај Приказни + Игнорирај Објави + Одигнорирај приказни + Одигнорирај Објави Копирај био Преведи био Блиски @@ -145,28 +146,28 @@ Отвори Профил Погледни приказна Прегладај профилната слика - Unsupported message type + Овој вид на порака е неподржан Избриши порака - View on GIPHY - %s shared a post by @%s - %s shared an image - %s shared a video - %s sent a message - %s shared a gif - %s shared a sticker - %s shared a profile: @%s - %s shared a location: %s - %s shared a story highlight by @%s - %s shared a story by @%s - %s sent a voice message - %s shared a clip by @%s - %s shared an IGTV video by @%s - You replied to their story: %s - %s replied to your story: %s - You reacted to their story: %s - %s reacted to your story: %s - You mentioned @%s in your story - %s mentioned you in their story + Погледни на GIPHY + %s сподели објава од @%s + %s сподели слика + %s сподели видео + %s прати порака + %s сподели gif + %s сподели лепенка + %s сподели профил: @%s + %s сподели локација:%s + %s сподели приказна од @%s + %s сподели приказна од @%s + %s прати гласовна порака + %s сподели клип од @%s + %s сподели IGTV видео од @%s + Ти изреагира на нивната приказна: %s + %s ти одгорвори на твојата приказна:%s + Ти изреагира на нивната приказна: %s + %s ти одгорвори на твојата приказна:%s + Ги спомна @%s на твојата приказна + %s те спомна на нивната приказна Непознат вид на порака Истече времето на медиумот! Пратено @@ -178,17 +179,17 @@ Предложено Скриншотнато Неможе да се прати - Unseen count response is null! - Message... - Press and hold to record audio - Updating... - Leave chat + Одговорот беше null! + Одговори... + Притисни и задржи за да снимиш аудио порака + Се ажурира... + Напушти чат Дали сакате да го напуштите чатот? Кикни Поранешни корисници - Invalid user - Instagram does not allow uploading videos longer than 60 secs for DM. - Instagram does not allow uploading audio longer than 60 secs. + Невалиден корисник + Инстаграм не дозволува видеа подолги од 60 секунди во пораки. + Инстаграм не дозволува аудио пораки подолги од 60 секунди. Превземи директно Превземи објави во твојот телефон! Се превземаат објава(ви) @@ -245,8 +246,8 @@ %d лајкови на коментари %d корисничи тагови %d лајкови - %d photos of you - %d follow requests + %d фотографии од тебе + %d барања за следење Се одјавивте пред да притисните на нотификацијата?! Објави Профил @@ -281,16 +282,16 @@ Темна тема Barista Материјално Црна - Added to Favorites! - Add to Favorites + Додадено во Омилени! + Додади во Омилени Акаунти Хаштагови Локации Непознато - Removed from Favourites! - Backup & Restore - Backup Barinsta app settings, account login data, and/or favorites to a plain text or encrypted backup file for later restoration. - If you\'re backing up account login data, treat the file as confidential and keep it somewhere safe! + Одстрането од Омилени! + Направете Резервна Копија & Враќање + Зачувај опции од апликацијата, кориснички профил, и/или информации од твоите омилени профили во обичен текст или со енкрипција за подоцна да можеш да ги вратиш. + Ако зачувуваш информации за најавување, третирај го генералниот фајл како многу важен, чувај го на безбедно место! Креирај новa резервна копија Поврати информации од резервна копија Фајл: @@ -299,8 +300,8 @@ Примени Зачувај Наслов - Edit caption - Translate caption + Промени наслов + Преведи наслов Лента на Видеоплеер Се лајкнува… Лајкнувањето беше неуспешно @@ -312,7 +313,7 @@ Зачувувањето неуспешно Отстранувањето беше неуспешно Се превзема… - Download item %1$d of %2$d + Превземање податок %1$d од %2$d Избриши Коментирај Изглед @@ -333,31 +334,31 @@ Во зависност од бројот на корисници, ова може да потрае, Ве молиме бидете трпеливи. Објавата не беше пронајдена! Но беше пронајдена апликација за отварање линкови - Gallery - Camera - All Photos - All Media - All Videos - Brightness - Contrast - Vibrance - Saturation - Sharpen - Exposure - Center - Color - Start - End - Bilateral Blur + Галерија + Камера + Сите слики + Сите Медиуми + Сите видеа + Осветленост + Контраст + Јачина на боја + Заситеност + Заостри + Изложеност + Центар + Боја + Почеток + Крај + Билатерална заматеност Vignette - Box blur - Sepia - Clarendon + Заматување + Сепија + Кларендон 1977 - Aden - Reset - Crop - Normal + Аден + Ресетирај + Скрати + Нормално %d лајк %d лајкови @@ -367,88 +368,95 @@ %d коментари - %d view - %d views + %d преглед + %d прегледи %s приказна %s приказни Нема дозвола за пристап до меморија! - Details - Title - Members - Admin - Inviter - Mute messages - Mute mentions - Add members - Search - Done - Make Admin - Remove as Admin - Edit was unsuccessful - Message - Reply - Tap to remove - Forward - You forwarded a message - Forwarded a message - Add - Send - Replying to yourself - Replying to %s - You replied to yourself - You replied - You replied to %s - Replied to %s - Replied to you - Replied to themself - You reacted to their story - Reacted to your story - You mentioned them in your story - Mentioned you in their story - You replied to their story - Replied to your story - Image has expired - Image will expire when seen - Video has expired - Video will expire when seen - Message has expired - Message will expire when seen - \@%s\'s story - \@%s\'s story highlight - Photo - Video - Voice message - Post - Approval required to join - Requests - Admins only - Added by %s - Admin approval required - An admin approval will be required to add new members to the group - End chat - End chat? - All members will be removed from the group. They will still be able to view the chat history. - Pending Requests - Accept request from %1s (%2s)? - Decline - Accept - You - No pending requests - Checking for new messages - Stories - DM - Notifications - Post - Enable DM notifications - Auto refresh messages - Auto refresh every - secs - mins - Search GIPHY - Response is null! - Response status is not ok! - Request failed! + Детали + Наслов + Членови + Админ + Поканувач + Игнорирај пораки + Игнорирај спомнувања + Додади учесници + Пребарај + Готово + Поставете Админ + Одстрани од Администратор + Уредувањето беше неуспешно + Порака + Одговори + Допри за да одтсраниш + Препрати + Ти препрати порака + Препрати порака + Додади + Прати + Одговорара на себе + Одговара на %s + Си одговори на себе + Ти одговори + Ти одговори на %s + Одговори на %s + Тебе ти одговори + Си одговори себеси + Ти реагира на приказната + Изреагира на твојата приказна + Ги спомна нив на твојата приказна + Те спомна тебе на нивната приказна + Ти одговори на нивната приказна + Одговори на твојата приказна + Му помина рокот на сликата + Сликата ќе исчезне кога ќе биде погледната + Истече рокот на видеото + Видеото ќе исчезне кога ќе биде погледнато + Истече рокот на пораката + Ќе истече рокот на пораката кога ќе ја погледните + Приказна на @%s + Нагласена приказна на @%s + Слика + Видео + Гласовна порака + Објава + Потребно е одобрување за да се придружиш + Барања + Само за Админи + Додадено од %s + Одобрување од админ е потребно + Потребно е одобрување од админ за додавање на нови членови во групата + Заврши разговор + Заврши разговор? + Сите членови ќе бидат остранети од групата. Тие ќе имаат сеуште пристап до историјата на разговорот. + Барања во очекување + Прифатете го барањето од %1s (%2s)? + Одбиј + Прифати + Ти + Нема барања во очекување + Се проверува за нови пораки + Приказни + Пораки + Нотификации + Објави + Овозможи Нотификации за пораки + Автоматски освежи пораки + Автоматски освежи на секои + секунди + минути + Пребарај на GIPHY + Одбоворот е null! + Одговорот на статусот не е ок! + Барањето неуспешно! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index e3542615..fda4c863 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -21,6 +21,7 @@ Opmerkingen Activiteit Controleer op updates bij het opstarten + Block screenshots & app preview Download berichten naar gebruikersnaam mappen Markeer verhalen als gelezen na bekijken Verhaalmaker zal het weten als je het bekeken hebt @@ -451,4 +452,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index 843021c6..ce4427ec 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -21,6 +21,7 @@ ଟିପ୍ପଣୀ କାର୍ଯ୍ୟକଳାପ ଖୋଲିବା ସମୟରେ ଅପଡେଟ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ + Block screenshots & app preview ଡାଉନଲୋଡ ପୋଷ୍ଟକୁ ବ୍ୟବହାରକାରୀଙ୍କ ନାମରେ ହୋଇଥିବା ସ୍ଥାନ ରେ ରଖ କାହାଣୀଗୁଡିକ ଦେଖିବା ପରେ \'ଦେଖାଗଲା\' ଚିହ୍ନିତ କରନ୍ତୁ | କାହାଣୀ ପ୍ରେରକ ଜାଣିବେ ତୁମେ ଏହାକୁ ଦେଖିଛ @@ -451,4 +452,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-pl/arrays.xml b/app/src/main/res/values-pl/arrays.xml index 14ac60e3..1147bc83 100644 --- a/app/src/main/res/values-pl/arrays.xml +++ b/app/src/main/res/values-pl/arrays.xml @@ -45,7 +45,7 @@ - - secs - mins + s. + min. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f1dd058f..93fb3ff8 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -21,6 +21,7 @@ Komentarze Aktywność Sprawdź aktualizacje przy starcie + Blokuj zrzuty ekranu & podgląd aplikacji Pobierz posty do folderów o nazwie użytkownika Oznacz relacje jako widoczne po wyświetleniu Autor relacji będzie widział, że to wyświetliłeś @@ -155,13 +156,13 @@ Wyświetl zdjęcie profilowe Nieobsługiwany typ wiadomości Cofnij wysłanie wiadomości - View on GIPHY + Zobacz na GIPHY %s udostępnił post @%s %s udostępnił obraz %s udostępnił film %s wysłał wiadomość %s udostępnił gif - %s shared a sticker + %s udostępnił naklejkę %s udostępnił profil: @%s %s udostępnił lokalizację: %s %s udostępnił story @%s @@ -194,9 +195,9 @@ Opuścić ten czat? Wyrzuć Pozostali użytkownicy - Invalid user - Instagram does not allow uploading videos longer than 60 secs for DM. - Instagram does not allow uploading audio longer than 60 secs. + Nieprawidłowy użytkownik + Instagram nie pozwala na przesyłanie filmów dłuższych niż 60 sekund w wiadomości prywatnej. + Instagram nie pozwala na przesyłanie dźwięku dłuższego niż 60 sekund. Pobierz bezpośrednio Pobiera posty bezpośrednio na telefon! Pobieranie post(ów) @@ -254,7 +255,7 @@ %d tagów użytkowników %d polubień %d photos of you - %d follow requests + %d próśb o o obserwowanie Wylogowałeś się przed kliknięciem tego powiadomienia?! Aktualności Profil @@ -452,19 +453,26 @@ Odrzuć Akceptuj Ty - No pending requests - Checking for new messages - Stories - DM - Notifications + Brak oczekujących żądań + Sprawdzanie nowych wiadomości + Relacje + PW + Powiadomienia Post - Enable DM notifications - Auto refresh messages - Auto refresh every - secs - mins - Search GIPHY - Response is null! - Response status is not ok! - Request failed! + Włącz powiadomienia PW + Automatyczne odświeżanie wiadomości + Automatycznie odświeżaj co + s. + min. + Szukaj GIPHY + Odpowiedź jest pusta! + Status odpowiedzi nie jest OK! + Żądanie nie powiodło się! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Oznacz jako przeczytane + Usuwanie nie powiodło się diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 1f819c27..4c7bbbc4 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -21,6 +21,7 @@ Comentários Atividade Verificar se há atualizações ao iniciar + Block screenshots & app preview Baixar publicações para pastas com o nome de usuário Marcar stories como vistos após a visualização O autor do story saberá que você viu @@ -109,7 +110,7 @@ Remover Seguir Deixar de seguir - Favoritar + Favorito Bloquear Desbloquear Restringir @@ -178,7 +179,7 @@ Sugerido Capturado Não pode ser entregue - Unseen count response is null! + Resposta do contador de não lidas é nula! Mensagem... Aperte e segure para gravar um áudio Atualizando... @@ -186,9 +187,9 @@ Sair deste chat? Expulsar Usuários que saíram - Invalid user - Instagram does not allow uploading videos longer than 60 secs for DM. - Instagram does not allow uploading audio longer than 60 secs. + Usuário inválido + O Instagram não permite o upload de vídeos com mais de 60 segundos para DM. + O Instagram não permite o upload de áudios com mais de 60 segundos. Baixar diretamente Baixar as publicações diretamente no telefone! Buscando publicação(ões) @@ -391,7 +392,7 @@ Mensagem Responder Toque para remover - Avançar + Encaminhar Você encaminhou uma mensagem Encaminhou uma mensagem Adicionar @@ -448,7 +449,14 @@ segs mins Buscar GIPHY - Response is null! - Response status is not ok! - Request failed! + A resposta é nula! + O estado da resposta não está correto! + A solicitação falhou! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-ru/arrays.xml b/app/src/main/res/values-ru/arrays.xml index ef526afa..8fb04bad 100644 --- a/app/src/main/res/values-ru/arrays.xml +++ b/app/src/main/res/values-ru/arrays.xml @@ -45,7 +45,7 @@ - - secs - mins + секунды + минуты diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 90a02afa..b491a1c5 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -21,6 +21,7 @@ Комментарии Активность Проверять наличие обновлений при запуске + Block screenshots & app preview Скачать публикации в папки с именем пользователя Отметить истории как увиденные после просмотра Автор истории узнает, что вы просмотрели её @@ -467,4 +468,11 @@ Ответ пуст! Ответный статус не в порядке! Запрос не удался! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-sk/arrays.xml b/app/src/main/res/values-sk/arrays.xml index 71b6bb4e..9e9535a2 100644 --- a/app/src/main/res/values-sk/arrays.xml +++ b/app/src/main/res/values-sk/arrays.xml @@ -45,7 +45,7 @@ - - secs - mins + sekúnd + minút diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index eab6b11a..f12178ec 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -21,6 +21,7 @@ Komentáre Aktivita Kontrolovať aktualizácie pri štarte + Block screenshots & app preview Ukľadať do priečinkov podľa mena Označiť príbehy po videní ako videné Autor príbehu bude vedieť že ste ho videli @@ -420,11 +421,11 @@ Replied to %s Replied to you Replied to themself - You reacted to their story - Reacted to your story - You mentioned them in your story - Mentioned you in their story - You replied to their story + Reagovali ste na ich príbeh + Reagoval/a na tvoj príbeh + Spomenuli ste ich vo vašom príbehu + Spomenul/a ťa v príbehu + Odpovedal/a si na ich príbeh Replied to your story Image has expired Image will expire when seen @@ -456,15 +457,22 @@ Checking for new messages Stories DM - Notifications - Post - Enable DM notifications - Auto refresh messages - Auto refresh every - secs - mins - Search GIPHY - Response is null! - Response status is not ok! - Request failed! + Oznámenia + Príspevok + Zapnúť DM oznámenia + Auto obnovenie správ + Automatické obnovenie každých + sekúnd + minút + Hľadať GIPHY + Odpoveď je null! + Stav odpovede nie je ok! + Požiadavka zlyhala! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 62a8d873..4ca6836d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -21,6 +21,7 @@ Yorumlar Hareketler Güncellemeleri başlangıçta kontrol et + Block screenshots & app preview İndirmeleri kullanıcı adından oluşan bir alt klasörün içine yap Hikayeleri gördükten sonra görüldü olarak işaretle Hikayeyi paylaşan gördüğünüzü bilecek @@ -451,4 +452,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index fcea4f24..debb92fc 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -21,6 +21,7 @@ Bình luận Hoạt động Kiểm tra cập nhật khi khởi động + Block screenshots & app preview Tải bài viết xuống theo thư mục tên người dùng trong Downloads Đánh dấu story là đã xem sau khi xem Người đăng story sẽ biết bạn đã xem nó @@ -443,4 +444,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 8b78ce74..a6fe9c77 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -21,6 +21,7 @@ 留言 动态 启动时检查更新 + 屏蔽截图及应用预览 下载帖子到用户名文件夹 查看快拍后将其标记为已读 快拍作者会知道您已看过 @@ -443,4 +444,11 @@ 回应为空! 回应状态错误! 请求失败 + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + 已标记为已读 + 删除失败 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 2070321a..26371e94 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -21,6 +21,7 @@ 評論 動態 啟動時檢查更新 + Block screenshots & app preview 將貼文下載到用戶名資料夾 檢視完限時動態後標記為已讀 限時動態的作者會知道您已查看了此限時動態 @@ -443,4 +444,11 @@ Response is null! Response status is not ok! Request failed! + Keyword + Enable keyword filter + Edit keyword filters + Added keyword: %s to filter list + Removed keyword: %s from filter list + Marked as seen + Delete unsuccessful diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 17b84a01..18f963e7 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -16,6 +16,9 @@ + + + @@ -26,4 +29,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/color.xml b/app/src/main/res/values/color.xml index 3a2893aa..fd773071 100755 --- a/app/src/main/res/values/color.xml +++ b/app/src/main/res/values/color.xml @@ -162,7 +162,7 @@ #ffddac #a17c4f #616161 - @color/white + #d63f44 @@ -172,5 +172,5 @@ #fff4e8 #8e2929 #000000 - @color/white + \ 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/ids.xml b/app/src/main/res/values/ids.xml index ecb381cf..7319700f 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -4,5 +4,4 @@ - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index af62d980..ce22f77f 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -479,4 +479,6 @@ Login required! Sentry block. User is inactive! + Barinsta Crash Report + Select an email app to send crash logs diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index b0147225..a0e7a3b8 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 @@ -89,6 +100,9 @@ false @style/ThemeOverlay.MaterialComponents.Dark.ActionBar + @color/white + @color/blue_800 + @color/white