commit
a9003b43ba
6
.idea/compiler.xml
Normal file
6
.idea/compiler.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="1.8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -15,6 +15,7 @@
|
|||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
<option name="useQualifiedModuleNames" value="true" />
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
@ -31,5 +31,10 @@
|
|||||||
<option name="name" value="Google" />
|
<option name="name" value="Google" />
|
||||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||||
</remote-repository>
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="maven" />
|
||||||
|
<option name="name" value="maven" />
|
||||||
|
<option name="url" value="http://maven.geotoolkit.org/" />
|
||||||
|
</remote-repository>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -40,7 +40,7 @@
|
|||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="app" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="app" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
|
||||||
<module name="app" />
|
<module name="InstaGrabber.app" />
|
||||||
<option name="DEPLOY" value="true" />
|
<option name="DEPLOY" value="true" />
|
||||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
||||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
<option name="DEPLOY_AS_INSTANT" value="false" />
|
||||||
<option name="ARTIFACT_NAME" value="" />
|
<option name="ARTIFACT_NAME" value="" />
|
||||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||||
|
<option name="ALL_USERS" value="false" />
|
||||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
||||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||||
<option name="MODE" value="default_activity" />
|
<option name="MODE" value="default_activity" />
|
||||||
@ -41,11 +42,16 @@
|
|||||||
</Native>
|
</Native>
|
||||||
<Profilers>
|
<Profilers>
|
||||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
|
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
|
||||||
|
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||||
</Profilers>
|
</Profilers>
|
||||||
<option name="DEEP_LINK" value="" />
|
<option name="DEEP_LINK" value="" />
|
||||||
<option name="ACTIVITY_CLASS" value="awais.instagrabber.activities.MainActivity" />
|
<option name="ACTIVITY_CLASS" value="awais.instagrabber.activities.MainActivity" />
|
||||||
|
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
||||||
|
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
@ -10,8 +10,8 @@ android {
|
|||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
|
|
||||||
versionCode 51
|
versionCode 52
|
||||||
versionName '19.0-a2'
|
versionName '19.0'
|
||||||
|
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
@ -20,6 +20,9 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
// Flag to enable support for the new language APIs
|
||||||
|
coreLibraryDesugaringEnabled true
|
||||||
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
@ -36,32 +39,51 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
def appcompat_version = "1.2.0"
|
|
||||||
def nav_version = "2.3.0"
|
|
||||||
def preference_version = "1.1.1"
|
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha02'
|
configurations.all {
|
||||||
implementation 'com.google.android.exoplayer:exoplayer:2.11.1'
|
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
|
||||||
|
|
||||||
|
def appcompat_version = "1.2.0"
|
||||||
|
def nav_version = '2.3.1'
|
||||||
|
|
||||||
|
implementation 'com.google.android.material:material:1.3.0-alpha03'
|
||||||
|
|
||||||
|
implementation 'com.google.android.exoplayer:exoplayer-core:2.12.0'
|
||||||
|
implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.0'
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||||
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
|
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05"
|
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha06"
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
||||||
implementation "androidx.navigation:navigation-fragment:$nav_version"
|
implementation "androidx.navigation:navigation-fragment:$nav_version"
|
||||||
implementation "androidx.navigation:navigation-ui:$nav_version"
|
implementation "androidx.navigation:navigation-ui:$nav_version"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.0.1"
|
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||||
implementation "androidx.preference:preference:$preference_version"
|
implementation "androidx.preference:preference:1.1.1"
|
||||||
|
implementation "androidx.work:work-runtime:2.4.0"
|
||||||
|
implementation 'androidx.palette:palette:1.0.0'
|
||||||
|
|
||||||
// implementation 'com.github.hendrawd:StorageUtil:1.1.0'
|
implementation 'com.google.guava:guava:27.0.1-android'
|
||||||
|
|
||||||
|
// implementation 'com.github.hendrawd:StorageUtil:1.1.0'
|
||||||
|
implementation 'com.github.ammargitham:AutoLinkTextViewV2:master-SNAPSHOT'
|
||||||
|
|
||||||
implementation 'org.jsoup:jsoup:1.13.1'
|
implementation 'org.jsoup:jsoup:1.13.1'
|
||||||
implementation 'com.facebook.fresco:fresco:2.3.0'
|
implementation 'com.facebook.fresco:fresco:2.3.0'
|
||||||
|
implementation 'com.facebook.fresco:animated-gif:2.3.0'
|
||||||
|
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
|
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
|
implementation 'com.github.dragon66:icafe:master-SNAPSHOT'
|
||||||
|
implementation 'javax.media:jai_imageio:1.1.1'
|
||||||
|
|
||||||
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
|
||||||
|
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0'
|
||||||
}
|
}
|
||||||
|
@ -71,22 +71,7 @@
|
|||||||
android:taskAffinity="awais.instagrabber.errorreport"
|
android:taskAffinity="awais.instagrabber.errorreport"
|
||||||
android:theme="@android:style/Theme.DeviceDefault.Dialog" />
|
android:theme="@android:style/Theme.DeviceDefault.Dialog" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".directdownload.MultiDirectDialog"
|
android:name=".activities.DirectDownload"
|
||||||
android:allowEmbedded="false"
|
|
||||||
android:allowTaskReparenting="false"
|
|
||||||
android:autoRemoveFromRecents="true"
|
|
||||||
android:clearTaskOnLaunch="true"
|
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
|
||||||
android:documentLaunchMode="never"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:finishOnTaskLaunch="true"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:lockTaskMode="never"
|
|
||||||
android:noHistory="false"
|
|
||||||
android:taskAffinity="awais.instagrabber.multidialog"
|
|
||||||
android:theme="@style/FlyingGayDialog" />
|
|
||||||
<activity
|
|
||||||
android:name=".directdownload.DirectDownload"
|
|
||||||
android:allowEmbedded="false"
|
android:allowEmbedded="false"
|
||||||
android:allowTaskReparenting="false"
|
android:allowTaskReparenting="false"
|
||||||
android:autoRemoveFromRecents="true"
|
android:autoRemoveFromRecents="true"
|
||||||
@ -100,7 +85,7 @@
|
|||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:lockTaskMode="never"
|
android:lockTaskMode="never"
|
||||||
android:noHistory="false"
|
android:noHistory="false"
|
||||||
android:theme="@style/CompletelyTransparent">
|
android:theme="@style/Theme.AppCompat.Translucent">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
<action android:name="android.intent.action.SEARCH" />
|
||||||
@ -138,6 +123,12 @@
|
|||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/provider_paths" />
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
<service android:name=".services.ActivityCheckerService" />
|
|
||||||
|
<service
|
||||||
|
android:name=".services.ActivityCheckerService"
|
||||||
|
android:exported="false" />
|
||||||
|
<service
|
||||||
|
android:name=".services.DeleteImageIntentService"
|
||||||
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
@ -32,12 +32,16 @@ public final class InstaGrabberApplication extends Application {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
// final Set<RequestListener> requestListeners = new HashSet<>();
|
||||||
|
// requestListeners.add(new RequestLoggingListener());
|
||||||
final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
|
final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
|
||||||
.newBuilder(this)
|
.newBuilder(this)
|
||||||
// .setMainDiskCacheConfig(diskCacheConfig)
|
// .setMainDiskCacheConfig(diskCacheConfig)
|
||||||
|
// .setRequestListeners(requestListeners)
|
||||||
.setDownsampleEnabled(true)
|
.setDownsampleEnabled(true)
|
||||||
.build();
|
.build();
|
||||||
Fresco.initialize(this, imagePipelineConfig);
|
Fresco.initialize(this, imagePipelineConfig);
|
||||||
|
// FLog.setMinimumLoggingLevel(FLog.VERBOSE);
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,149 @@
|
|||||||
|
package awais.instagrabber.activities;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.asyncs.PostFetcher;
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.IntentModel;
|
||||||
|
import awais.instagrabber.models.enums.IntentModelType;
|
||||||
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
|
import awais.instagrabber.utils.IntentUtils;
|
||||||
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
|
||||||
|
public final class DirectDownload extends AppCompatActivity {
|
||||||
|
private static final int NOTIFICATION_ID = 1900000000;
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
|
|
||||||
|
private boolean contextFound = false;
|
||||||
|
private Intent intent;
|
||||||
|
private Context context;
|
||||||
|
private NotificationManagerCompat notificationManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_direct);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWindowAttributesChanged(final WindowManager.LayoutParams params) {
|
||||||
|
super.onWindowAttributesChanged(params);
|
||||||
|
if (!contextFound) {
|
||||||
|
intent = getIntent();
|
||||||
|
context = getApplicationContext();
|
||||||
|
if (intent != null && context != null) {
|
||||||
|
contextFound = true;
|
||||||
|
checkPermissions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resources getResources() {
|
||||||
|
if (!contextFound) {
|
||||||
|
intent = getIntent();
|
||||||
|
context = getApplicationContext();
|
||||||
|
if (intent != null && context != null) {
|
||||||
|
contextFound = true;
|
||||||
|
checkPermissions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.getResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void checkPermissions() {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
doDownload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ActivityCompat.requestPermissions(this, DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||||
|
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||||
|
doDownload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void doDownload() {
|
||||||
|
notificationManager = NotificationManagerCompat.from(getApplicationContext());
|
||||||
|
final Intent intent = getIntent();
|
||||||
|
final String action = intent.getAction();
|
||||||
|
if (TextUtils.isEmpty(action) || Intent.ACTION_MAIN.equals(action)) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean error = true;
|
||||||
|
|
||||||
|
String data = null;
|
||||||
|
final Bundle extras = intent.getExtras();
|
||||||
|
if (extras != null) {
|
||||||
|
final Object extraData = extras.get(Intent.EXTRA_TEXT);
|
||||||
|
if (extraData != null) {
|
||||||
|
error = false;
|
||||||
|
data = extraData.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
final Uri intentData = intent.getData();
|
||||||
|
if (intentData != null) data = intentData.toString();
|
||||||
|
}
|
||||||
|
if (data == null || TextUtils.isEmpty(data)) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final IntentModel model = IntentUtils.parseUrl(data);
|
||||||
|
if (model == null || model.getType() != IntentModelType.POST) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String text = model.getText();
|
||||||
|
new PostFetcher(text, new FetchListener<FeedModel>() {
|
||||||
|
@Override
|
||||||
|
public void doBefore() {
|
||||||
|
if (notificationManager == null) return;
|
||||||
|
final Notification fetchingPostNotification = new NotificationCompat.Builder(getApplicationContext(), Constants.DOWNLOAD_CHANNEL_ID)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||||
|
.setSmallIcon(R.drawable.ic_download)
|
||||||
|
.setAutoCancel(false)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||||
|
.setContentText(getString(R.string.direct_download_loading))
|
||||||
|
.build();
|
||||||
|
notificationManager.notify(NOTIFICATION_ID, fetchingPostNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResult(final FeedModel result) {
|
||||||
|
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
||||||
|
if (result == null) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DownloadUtils.download(getApplicationContext(), result);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import android.content.Intent;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
import android.webkit.CookieSyncManager;
|
import android.webkit.CookieSyncManager;
|
||||||
@ -59,7 +60,7 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
loginBinding = ActivityLoginBinding.inflate(getLayoutInflater());
|
loginBinding = ActivityLoginBinding.inflate(LayoutInflater.from(getApplicationContext()));
|
||||||
setContentView(loginBinding.getRoot());
|
setContentView(loginBinding.getRoot());
|
||||||
|
|
||||||
initWebView();
|
initWebView();
|
||||||
@ -117,12 +118,9 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
|
|||||||
webSettings.setDisplayZoomControls(false);
|
webSettings.setDisplayZoomControls(false);
|
||||||
webSettings.setLoadWithOverviewMode(true);
|
webSettings.setLoadWithOverviewMode(true);
|
||||||
webSettings.setUseWideViewPort(true);
|
webSettings.setUseWideViewPort(true);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
webSettings.setAllowFileAccessFromFileURLs(true);
|
||||||
webSettings.setAllowFileAccessFromFileURLs(true);
|
webSettings.setAllowUniversalAccessFromFileURLs(true);
|
||||||
webSettings.setAllowUniversalAccessFromFileURLs(true);
|
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
||||||
}
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||||
|
@ -24,10 +24,12 @@ import android.widget.AutoCompleteTextView;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.navigation.NavBackStackEntry;
|
import androidx.navigation.NavBackStackEntry;
|
||||||
@ -39,10 +41,10 @@ import androidx.navigation.ui.NavigationUI;
|
|||||||
import com.google.android.material.appbar.AppBarLayout;
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -50,9 +52,12 @@ import java.util.Map;
|
|||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.SuggestionsAdapter;
|
import awais.instagrabber.adapters.SuggestionsAdapter;
|
||||||
|
import awais.instagrabber.asyncs.PostFetcher;
|
||||||
import awais.instagrabber.asyncs.SuggestionsFetcher;
|
import awais.instagrabber.asyncs.SuggestionsFetcher;
|
||||||
import awais.instagrabber.customviews.helpers.CustomHideBottomViewOnScrollBehavior;
|
import awais.instagrabber.customviews.helpers.CustomHideBottomViewOnScrollBehavior;
|
||||||
import awais.instagrabber.databinding.ActivityMainBinding;
|
import awais.instagrabber.databinding.ActivityMainBinding;
|
||||||
|
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||||
|
import awais.instagrabber.fragments.main.FeedFragment;
|
||||||
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
|
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
import awais.instagrabber.models.IntentModel;
|
import awais.instagrabber.models.IntentModel;
|
||||||
@ -94,9 +99,12 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
R.id.notificationsViewer,
|
R.id.notificationsViewer,
|
||||||
R.id.themePreferencesFragment,
|
R.id.themePreferencesFragment,
|
||||||
R.id.favoritesFragment,
|
R.id.favoritesFragment,
|
||||||
R.id.backupPreferencesFragment);
|
R.id.backupPreferencesFragment,
|
||||||
|
R.id.directMessagesThreadFragment
|
||||||
|
);
|
||||||
private static final Map<Integer, Integer> NAV_TO_MENU_ID_MAP = new HashMap<>();
|
private static final Map<Integer, Integer> NAV_TO_MENU_ID_MAP = new HashMap<>();
|
||||||
private static final List<Integer> REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = Collections.singletonList(R.id.commentsViewerFragment);
|
private static final List<Integer> REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = ImmutableList.of(R.id.commentsViewerFragment,
|
||||||
|
R.id.directMessagesThreadFragment);
|
||||||
private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex";
|
private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex";
|
||||||
|
|
||||||
private ActivityMainBinding binding;
|
private ActivityMainBinding binding;
|
||||||
@ -157,10 +165,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
bindActivityCheckerService();
|
bindActivityCheckerService();
|
||||||
}
|
}
|
||||||
getSupportFragmentManager().addOnBackStackChangedListener(this);
|
getSupportFragmentManager().addOnBackStackChangedListener(this);
|
||||||
|
|
||||||
// Log.d("austin_debug", "dir: "+Arrays.toString(StorageUtil.getStorageDirectories(getApplicationContext())));
|
|
||||||
// final File sdcard = new File(StorageUtil.getStorageDirectories(getApplicationContext())[0]);
|
|
||||||
// Log.d("austin_debug", "files: "+Arrays.toString(sdcard.listFiles()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -222,7 +226,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isTaskRoot() && isBackStackEmpty) {
|
if (isTaskRoot() && isBackStackEmpty) {
|
||||||
finishAfterTransition();
|
finishAfterTransition();
|
||||||
} else {
|
} else {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
@ -403,11 +407,14 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
}
|
}
|
||||||
final List<Integer> mainNavList = getMainNavList(main_nav_ids);
|
final List<Integer> mainNavList = getMainNavList(main_nav_ids);
|
||||||
if (setDefaultFromSettings) {
|
if (setDefaultFromSettings) {
|
||||||
final String defaultTabIdString = settingsHelper.getString(Constants.DEFAULT_TAB);
|
final String defaultTabResNameString = settingsHelper.getString(Constants.DEFAULT_TAB);
|
||||||
try {
|
try {
|
||||||
final int defaultNavId = TextUtils.isEmpty(defaultTabIdString)
|
int navId = 0;
|
||||||
? R.navigation.profile_nav_graph
|
if (!TextUtils.isEmpty(defaultTabResNameString)) {
|
||||||
: Integer.parseInt(defaultTabIdString);
|
navId = getResources().getIdentifier(defaultTabResNameString, "navigation", getPackageName());
|
||||||
|
}
|
||||||
|
final int defaultNavId = navId <= 0 ? R.navigation.profile_nav_graph
|
||||||
|
: navId;
|
||||||
final int index = mainNavList.indexOf(defaultNavId);
|
final int index = mainNavList.indexOf(defaultNavId);
|
||||||
if (index >= 0) firstFragmentGraphIndex = index;
|
if (index >= 0) firstFragmentGraphIndex = index;
|
||||||
setBottomNavSelectedItem(defaultNavId);
|
setBottomNavSelectedItem(defaultNavId);
|
||||||
@ -422,8 +429,18 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
R.id.main_nav_host,
|
R.id.main_nav_host,
|
||||||
getIntent(),
|
getIntent(),
|
||||||
firstFragmentGraphIndex);
|
firstFragmentGraphIndex);
|
||||||
navControllerLiveData.observe(this, this::setupNavigation);
|
navControllerLiveData.observe(this, navController -> setupNavigation(binding.toolbar, navController));
|
||||||
currentNavControllerLiveData = navControllerLiveData;
|
currentNavControllerLiveData = navControllerLiveData;
|
||||||
|
binding.bottomNavView.setOnNavigationItemReselectedListener(item -> {
|
||||||
|
// Log.d(TAG, "setupBottomNavigationBar: item: " + item);
|
||||||
|
final Fragment navHostFragment = getSupportFragmentManager().findFragmentById(R.id.main_nav_host);
|
||||||
|
if (navHostFragment != null) {
|
||||||
|
final Fragment fragment = navHostFragment.getChildFragmentManager().getPrimaryNavigationFragment();
|
||||||
|
if (fragment instanceof FeedFragment) {
|
||||||
|
((FeedFragment) fragment).scrollToTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBottomNavSelectedItem(final int navId) {
|
private void setBottomNavSelectedItem(final int navId) {
|
||||||
@ -447,8 +464,11 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
return mainNavList;
|
return mainNavList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupNavigation(final NavController navController) {
|
private void setupNavigation(final Toolbar toolbar, final NavController navController) {
|
||||||
NavigationUI.setupWithNavController(binding.toolbar, navController);
|
if (navController == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NavigationUI.setupWithNavController(toolbar, navController);
|
||||||
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
|
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
|
||||||
// below is a hack to check if we are at the end of the current stack, to setup the search view
|
// below is a hack to check if we are at the end of the current stack, to setup the search view
|
||||||
binding.appBarLayout.setExpanded(true, true);
|
binding.appBarLayout.setExpanded(true, true);
|
||||||
@ -572,13 +592,18 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
private void showPostView(@NonNull final IntentModel intentModel) {
|
private void showPostView(@NonNull final IntentModel intentModel) {
|
||||||
final String shortCode = intentModel.getText();
|
final String shortCode = intentModel.getText();
|
||||||
// Log.d(TAG, "shortCode: " + shortCode);
|
// Log.d(TAG, "shortCode: " + shortCode);
|
||||||
final NavController navController = currentNavControllerLiveData.getValue();
|
final AlertDialog alertDialog = new AlertDialog.Builder(this)
|
||||||
if (currentNavControllerLiveData == null || navController == null) return;
|
.setCancelable(false)
|
||||||
final Bundle bundle = new Bundle();
|
.setView(R.layout.dialog_opening_post)
|
||||||
bundle.putStringArray("idOrCodeArray", new String[]{shortCode});
|
.create();
|
||||||
bundle.putInt("index", 0);
|
alertDialog.show();
|
||||||
bundle.putBoolean("isId", false);
|
new PostFetcher(shortCode, feedModel -> {
|
||||||
navController.navigate(R.id.action_global_postViewFragment, bundle);
|
final PostViewV2Fragment fragment = PostViewV2Fragment
|
||||||
|
.builder(feedModel)
|
||||||
|
.build();
|
||||||
|
fragment.setOnShowListener(dialog -> alertDialog.dismiss());
|
||||||
|
fragment.show(getSupportFragmentManager(), "post_view");
|
||||||
|
}).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showLocationView(@NonNull final IntentModel intentModel) {
|
private void showLocationView(@NonNull final IntentModel intentModel) {
|
||||||
@ -626,4 +651,31 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
public BottomNavigationView getBottomNavView() {
|
public BottomNavigationView getBottomNavView() {
|
||||||
return binding.bottomNavView;
|
return binding.bottomNavView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// public void fitSystemWindows(final boolean fit) {
|
||||||
|
// binding.appBarLayout.setBackground(null);
|
||||||
|
// binding.appBarLayout.setFitsSystemWindows(fit);
|
||||||
|
// binding.collapsingToolbarLayout.setBackground(null);
|
||||||
|
// binding.collapsingToolbarLayout.setFitsSystemWindows(fit);
|
||||||
|
// final Drawable toolbarBackground = binding.toolbar.getBackground();
|
||||||
|
// binding.toolbar.setFitsSystemWindows(fit);
|
||||||
|
// binding.toolbar.setBackground(null);
|
||||||
|
// binding.toolbar.setClickable(false);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public int getNavHostContainerId() {
|
||||||
|
// return binding.mainNavHost.getId();
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void setToolbar(final Toolbar toolbar) {
|
||||||
|
binding.appBarLayout.setVisibility(View.GONE);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
setupNavigation(toolbar, currentNavControllerLiveData.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetToolbar() {
|
||||||
|
binding.appBarLayout.setVisibility(View.VISIBLE);
|
||||||
|
setSupportActionBar(binding.toolbar);
|
||||||
|
setupNavigation(binding.toolbar, currentNavControllerLiveData.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,137 +2,191 @@ package awais.instagrabber.adapters;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Filter;
|
|
||||||
import android.widget.Filterable;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.adapters.viewholder.comments.ChildCommentViewHolder;
|
||||||
import awais.instagrabber.adapters.viewholder.CommentViewHolder;
|
import awais.instagrabber.adapters.viewholder.comments.ParentCommentViewHolder;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
import awais.instagrabber.databinding.ItemCommentBinding;
|
||||||
|
import awais.instagrabber.databinding.ItemCommentSmallBinding;
|
||||||
import awais.instagrabber.models.CommentModel;
|
import awais.instagrabber.models.CommentModel;
|
||||||
import awais.instagrabber.models.ProfileModel;
|
|
||||||
import awais.instagrabber.utils.LocaleUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
|
|
||||||
public final class CommentsAdapter extends RecyclerView.Adapter<CommentViewHolder> implements Filterable {
|
public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerView.ViewHolder> {
|
||||||
|
private static final int TYPE_PARENT = 1;
|
||||||
|
private static final int TYPE_CHILD = 2;
|
||||||
|
|
||||||
private CommentModel[] filteredCommentModels;
|
private final Map<Integer, Integer> positionTypeMap = new HashMap<>();
|
||||||
private LayoutInflater layoutInflater;
|
|
||||||
|
|
||||||
private final boolean isParent;
|
// private final Filter filter = new Filter() {
|
||||||
private final View.OnClickListener onClickListener;
|
// @NonNull
|
||||||
private final MentionClickListener mentionClickListener;
|
// @Override
|
||||||
private final CommentModel[] commentModels;
|
// protected FilterResults performFiltering(final CharSequence filter) {
|
||||||
private final String[] quantityStrings = new String[2];
|
// final FilterResults results = new FilterResults();
|
||||||
private final Filter filter = new Filter() {
|
// results.values = commentModels;
|
||||||
@NonNull
|
//
|
||||||
|
// final int commentsLen = commentModels == null ? 0 : commentModels.size();
|
||||||
|
// if (commentModels != null && commentsLen > 0 && !TextUtils.isEmpty(filter)) {
|
||||||
|
// final String query = filter.toString().toLowerCase();
|
||||||
|
// final ArrayList<CommentModel> filterList = new ArrayList<>(commentsLen);
|
||||||
|
//
|
||||||
|
// for (final CommentModel commentModel : commentModels) {
|
||||||
|
// final String commentText = commentModel.getText().toString().toLowerCase();
|
||||||
|
//
|
||||||
|
// if (commentText.contains(query)) filterList.add(commentModel);
|
||||||
|
// else {
|
||||||
|
// final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
|
||||||
|
// if (childCommentModels != null) {
|
||||||
|
// for (final CommentModel childCommentModel : childCommentModels) {
|
||||||
|
// final String childCommentText = childCommentModel.getText().toString().toLowerCase();
|
||||||
|
// if (childCommentText.contains(query)) filterList.add(commentModel);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// filterList.trimToSize();
|
||||||
|
// results.values = filterList.toArray(new CommentModel[0]);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return results;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// protected void publishResults(final CharSequence constraint, @NonNull final FilterResults results) {
|
||||||
|
// if (results.values instanceof List) {
|
||||||
|
// //noinspection unchecked
|
||||||
|
// filteredCommentModels = (List<CommentModel>) results.values;
|
||||||
|
// notifyDataSetChanged();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
private static final DiffUtil.ItemCallback<CommentModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<CommentModel>() {
|
||||||
@Override
|
@Override
|
||||||
protected FilterResults performFiltering(final CharSequence filter) {
|
public boolean areItemsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
|
||||||
final FilterResults results = new FilterResults();
|
return oldItem.getId().equals(newItem.getId());
|
||||||
results.values = commentModels;
|
|
||||||
|
|
||||||
final int commentsLen = commentModels == null ? 0 : commentModels.length;
|
|
||||||
if (commentModels != null && commentsLen > 0 && !TextUtils.isEmpty(filter)) {
|
|
||||||
final String query = filter.toString().toLowerCase();
|
|
||||||
final ArrayList<CommentModel> filterList = new ArrayList<>(commentsLen);
|
|
||||||
|
|
||||||
for (final CommentModel commentModel : commentModels) {
|
|
||||||
final String commentText = commentModel.getText().toString().toLowerCase();
|
|
||||||
|
|
||||||
if (commentText.contains(query)) filterList.add(commentModel);
|
|
||||||
else {
|
|
||||||
final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
|
|
||||||
if (childCommentModels != null) {
|
|
||||||
for (final CommentModel childCommentModel : childCommentModels) {
|
|
||||||
final String childCommentText = childCommentModel.getText().toString().toLowerCase();
|
|
||||||
if (childCommentText.contains(query)) filterList.add(commentModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
filterList.trimToSize();
|
|
||||||
results.values = filterList.toArray(new CommentModel[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void publishResults(final CharSequence constraint, @NonNull final FilterResults results) {
|
public boolean areContentsTheSame(@NonNull final CommentModel oldItem, @NonNull final CommentModel newItem) {
|
||||||
if (results.values instanceof CommentModel[]) {
|
return oldItem.getId().equals(newItem.getId());
|
||||||
filteredCommentModels = (CommentModel[]) results.values;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private final CommentCallback commentCallback;
|
||||||
|
private CommentModel selected;
|
||||||
|
private int selectedIndex;
|
||||||
|
|
||||||
public CommentsAdapter(final CommentModel[] commentModels,
|
public CommentsAdapter(final CommentCallback commentCallback) {
|
||||||
final boolean isParent,
|
super(DIFF_CALLBACK);
|
||||||
final View.OnClickListener onClickListener,
|
this.commentCallback = commentCallback;
|
||||||
final MentionClickListener mentionClickListener) {
|
|
||||||
super();
|
|
||||||
this.commentModels = this.filteredCommentModels = commentModels;
|
|
||||||
this.isParent = isParent;
|
|
||||||
this.onClickListener = onClickListener;
|
|
||||||
this.mentionClickListener = mentionClickListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Filter getFilter() {
|
|
||||||
return filter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public CommentViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
|
||||||
final Context context = parent.getContext();
|
final Context context = parent.getContext();
|
||||||
if (quantityStrings[0] == null) quantityStrings[0] = context.getString(R.string.single_like);
|
final LayoutInflater layoutInflater = LayoutInflater.from(context);
|
||||||
if (quantityStrings[1] == null) quantityStrings[1] = context.getString(R.string.multiple_likes);
|
if (type == TYPE_PARENT) {
|
||||||
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context);
|
final ItemCommentBinding binding = ItemCommentBinding.inflate(layoutInflater, parent, false);
|
||||||
final View view = layoutInflater.inflate(isParent ? R.layout.item_comment
|
return new ParentCommentViewHolder(binding);
|
||||||
: R.layout.item_comment_small,
|
}
|
||||||
parent,
|
final ItemCommentSmallBinding binding = ItemCommentSmallBinding.inflate(layoutInflater, parent, false);
|
||||||
false);
|
return new ChildCommentViewHolder(binding);
|
||||||
return new CommentViewHolder(view,
|
|
||||||
onClickListener,
|
|
||||||
mentionClickListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull final CommentViewHolder holder, final int position) {
|
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
|
||||||
final CommentModel commentModel = filteredCommentModels[position];
|
final CommentModel commentModel = getItem(position);
|
||||||
if (commentModel != null) {
|
if (commentModel == null) return;
|
||||||
holder.setCommentModel(commentModel);
|
final int type = getItemViewType(position);
|
||||||
|
final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId());
|
||||||
|
if (type == TYPE_PARENT) {
|
||||||
|
final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder;
|
||||||
|
viewHolder.bind(commentModel, selected, commentCallback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ChildCommentViewHolder viewHolder = (ChildCommentViewHolder) holder;
|
||||||
|
viewHolder.bind(commentModel, selected, commentCallback);
|
||||||
|
}
|
||||||
|
|
||||||
holder.setComment(commentModel.getText());
|
@Override
|
||||||
holder.setDate(commentModel.getDateTime());
|
public void submitList(@Nullable final List<CommentModel> list) {
|
||||||
holder.setLiked(commentModel.getLiked());
|
final List<CommentModel> flatList = flattenList(list);
|
||||||
|
super.submitList(flatList);
|
||||||
|
}
|
||||||
|
|
||||||
final long likes = commentModel.getLikes();
|
@Override
|
||||||
holder.setLikes(String.format(LocaleUtils.getCurrentLocale(), "%d %s", likes, quantityStrings[likes == 1 ? 0 : 1]));
|
public void submitList(@Nullable final List<CommentModel> list, @Nullable final Runnable commitCallback) {
|
||||||
|
final List<CommentModel> flatList = flattenList(list);
|
||||||
|
super.submitList(flatList, commitCallback);
|
||||||
|
}
|
||||||
|
|
||||||
final ProfileModel profileModel = commentModel.getProfileModel();
|
private List<CommentModel> flattenList(final List<CommentModel> list) {
|
||||||
if (profileModel != null) {
|
if (list == null) {
|
||||||
holder.setUsername(profileModel.getUsername());
|
return Collections.emptyList();
|
||||||
holder.getProfilePicView().setImageURI(profileModel.getSdProfilePic());
|
}
|
||||||
}
|
final List<CommentModel> flatList = new ArrayList<>();
|
||||||
if (holder.isParent()) {
|
int lastCommentIndex = -1;
|
||||||
final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
|
for (final CommentModel parent : list) {
|
||||||
if (childCommentModels != null && childCommentModels.length > 0)
|
lastCommentIndex++;
|
||||||
holder.setChildAdapter(new CommentsAdapter(childCommentModels, false, onClickListener, mentionClickListener));
|
flatList.add(parent);
|
||||||
else holder.hideChildComments();
|
positionTypeMap.put(lastCommentIndex, TYPE_PARENT);
|
||||||
|
final List<CommentModel> children = parent.getChildCommentModels();
|
||||||
|
if (children != null) {
|
||||||
|
for (final CommentModel child : children) {
|
||||||
|
lastCommentIndex++;
|
||||||
|
flatList.add(child);
|
||||||
|
positionTypeMap.put(lastCommentIndex, TYPE_CHILD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return flatList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemViewType(final int position) {
|
||||||
return filteredCommentModels == null ? 0 : filteredCommentModels.length;
|
final Integer type = positionTypeMap.get(position);
|
||||||
|
if (type == null) {
|
||||||
|
return TYPE_PARENT;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelected(final CommentModel commentModel) {
|
||||||
|
this.selected = commentModel;
|
||||||
|
selectedIndex = getCurrentList().indexOf(commentModel);
|
||||||
|
notifyItemChanged(selectedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearSelection() {
|
||||||
|
this.selected = null;
|
||||||
|
notifyItemChanged(selectedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommentModel getSelected() {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CommentCallback {
|
||||||
|
void onClick(final CommentModel comment);
|
||||||
|
|
||||||
|
void onHashtagClick(final String hashtag);
|
||||||
|
|
||||||
|
void onMentionClick(final String mention);
|
||||||
|
|
||||||
|
void onURLClick(final String url);
|
||||||
|
|
||||||
|
void onEmailClick(final String emailAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -107,6 +107,7 @@ public final class DirectMessageItemsAdapter extends ListAdapter<DirectItemModel
|
|||||||
return new DirectMessageReelShareViewHolder(baseBinding, binding, onClickListener, mentionClickListener);
|
return new DirectMessageReelShareViewHolder(baseBinding, binding, onClickListener, mentionClickListener);
|
||||||
}
|
}
|
||||||
case MEDIA_SHARE:
|
case MEDIA_SHARE:
|
||||||
|
case FELIX_SHARE:
|
||||||
case CLIP: {
|
case CLIP: {
|
||||||
final LayoutDmMediaShareBinding binding = LayoutDmMediaShareBinding.inflate(layoutInflater, itemViewParent, false);
|
final LayoutDmMediaShareBinding binding = LayoutDmMediaShareBinding.inflate(layoutInflater, itemViewParent, false);
|
||||||
return new DirectMessageMediaShareViewHolder(baseBinding, binding, onClickListener);
|
return new DirectMessageMediaShareViewHolder(baseBinding, binding, onClickListener);
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
package awais.instagrabber.adapters;
|
|
||||||
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
import awais.instagrabber.adapters.viewholder.DiscoverViewHolder;
|
|
||||||
import awais.instagrabber.models.DiscoverItemModel;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
|
|
||||||
public final class DiscoverAdapter extends MultiSelectListAdapter<DiscoverItemModel, DiscoverViewHolder> {
|
|
||||||
|
|
||||||
private static final DiffUtil.ItemCallback<DiscoverItemModel> diffCallback = new DiffUtil.ItemCallback<DiscoverItemModel>() {
|
|
||||||
@Override
|
|
||||||
public boolean areItemsTheSame(@NonNull final DiscoverItemModel oldItem, @NonNull final DiscoverItemModel newItem) {
|
|
||||||
return oldItem.getPostId().equals(newItem.getPostId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areContentsTheSame(@NonNull final DiscoverItemModel oldItem, @NonNull final DiscoverItemModel newItem) {
|
|
||||||
return oldItem.getPostId().equals(newItem.getPostId());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public DiscoverAdapter(final OnItemClickListener<DiscoverItemModel> clickListener,
|
|
||||||
final OnItemLongClickListener<DiscoverItemModel> longClickListener) {
|
|
||||||
super(diffCallback, clickListener, longClickListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public DiscoverViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
|
||||||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
|
||||||
return new DiscoverViewHolder(layoutInflater.inflate(R.layout.item_post, parent, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull final DiscoverViewHolder holder, final int position) {
|
|
||||||
final DiscoverItemModel itemModel = getItem(position);
|
|
||||||
if (itemModel != null) {
|
|
||||||
itemModel.setPosition(position);
|
|
||||||
holder.itemView.setTag(itemModel);
|
|
||||||
holder.itemView.setOnClickListener(v -> getInternalOnItemClickListener().onItemClick(itemModel, position));
|
|
||||||
holder.itemView.setOnLongClickListener(v -> getInternalOnLongItemClickListener().onItemLongClick(itemModel, position));
|
|
||||||
final MediaItemType mediaType = itemModel.getItemType();
|
|
||||||
holder.typeIcon.setVisibility(
|
|
||||||
mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
|
|
||||||
holder.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? R.drawable.ic_slider_24 : R.drawable.ic_video_24);
|
|
||||||
holder.selectedView.setVisibility(itemModel.isSelected() ? View.VISIBLE : View.GONE);
|
|
||||||
holder.postImage.setImageURI(itemModel.getDisplayUrl());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,53 @@
|
|||||||
|
package awais.instagrabber.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.viewholder.TopicClusterViewHolder;
|
||||||
|
import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
|
||||||
|
import awais.instagrabber.models.TopicCluster;
|
||||||
|
|
||||||
|
public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicClusterViewHolder> {
|
||||||
|
private static final DiffUtil.ItemCallback<TopicCluster> DIFF_CALLBACK = new DiffUtil.ItemCallback<TopicCluster>() {
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {
|
||||||
|
return oldItem.getId().equals(newItem.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {
|
||||||
|
return oldItem.getCoverMedia().getDisplayUrl().equals(newItem.getCoverMedia().getDisplayUrl())
|
||||||
|
&& oldItem.getTitle().equals(newItem.getTitle());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final OnTopicClickListener onTopicClickListener;
|
||||||
|
|
||||||
|
public DiscoverTopicsAdapter(final OnTopicClickListener onTopicClickListener) {
|
||||||
|
super(DIFF_CALLBACK);
|
||||||
|
this.onTopicClickListener = onTopicClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||||
|
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||||
|
final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false);
|
||||||
|
return new TopicClusterViewHolder(binding, onTopicClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull final TopicClusterViewHolder holder, final int position) {
|
||||||
|
final TopicCluster topicCluster = getItem(position);
|
||||||
|
holder.bind(topicCluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnTopicClickListener {
|
||||||
|
void onTopicClick(TopicCluster topicCluster, View root, View cover, View title, int titleColor, int backgroundColor);
|
||||||
|
}
|
||||||
|
}
|
@ -1,111 +1,111 @@
|
|||||||
package awais.instagrabber.adapters;
|
// package awais.instagrabber.adapters;
|
||||||
|
//
|
||||||
import android.content.Context;
|
// import android.content.Context;
|
||||||
import android.view.LayoutInflater;
|
// import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
// import android.view.View;
|
||||||
import android.view.ViewGroup;
|
// import android.view.ViewGroup;
|
||||||
|
//
|
||||||
import androidx.annotation.NonNull;
|
// import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
// import androidx.recyclerview.widget.DiffUtil;
|
||||||
import androidx.recyclerview.widget.ListAdapter;
|
// import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
//
|
||||||
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
|
// import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
|
||||||
import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
|
// import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
|
||||||
import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
|
// import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
|
||||||
import awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;
|
// import awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;
|
||||||
import awais.instagrabber.customviews.RamboTextView;
|
// import awais.instagrabber.customviews.RamboTextView;
|
||||||
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
|
// import awais.instagrabber.databinding.ItemFeedPhotoBinding;
|
||||||
import awais.instagrabber.databinding.ItemFeedSliderBinding;
|
// import awais.instagrabber.databinding.ItemFeedSliderBinding;
|
||||||
import awais.instagrabber.databinding.ItemFeedVideoBinding;
|
// import awais.instagrabber.databinding.ItemFeedVideoBinding;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
// import awais.instagrabber.interfaces.MentionClickListener;
|
||||||
import awais.instagrabber.models.FeedModel;
|
// import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
// import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.utils.Utils;
|
// import awais.instagrabber.utils.Utils;
|
||||||
|
//
|
||||||
public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder> {
|
// public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder> {
|
||||||
private static final String TAG = "FeedAdapter";
|
// private static final String TAG = "FeedAdapter";
|
||||||
private final View.OnClickListener clickListener;
|
// private final View.OnClickListener clickListener;
|
||||||
private final MentionClickListener mentionClickListener;
|
// private final MentionClickListener mentionClickListener;
|
||||||
private final View.OnLongClickListener longClickListener = v -> {
|
// private final View.OnLongClickListener longClickListener = v -> {
|
||||||
final Object tag;
|
// final Object tag;
|
||||||
if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
|
// if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
|
||||||
Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
|
// Utils.copyText(v.getContext(), ((FeedModel) tag).getPostCaption());
|
||||||
return true;
|
// return true;
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
private static final DiffUtil.ItemCallback<FeedModel> diffCallback = new DiffUtil.ItemCallback<FeedModel>() {
|
// private static final DiffUtil.ItemCallback<FeedModel> diffCallback = new DiffUtil.ItemCallback<FeedModel>() {
|
||||||
@Override
|
// @Override
|
||||||
public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
// public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
||||||
return oldItem.getPostId().equals(newItem.getPostId());
|
// return oldItem.getPostId().equals(newItem.getPostId());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
// public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
||||||
return oldItem.getPostId().equals(newItem.getPostId());
|
// return oldItem.getPostId().equals(newItem.getPostId());
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
public FeedAdapter(final View.OnClickListener clickListener,
|
// public FeedAdapter(final View.OnClickListener clickListener,
|
||||||
final MentionClickListener mentionClickListener) {
|
// final MentionClickListener mentionClickListener) {
|
||||||
super(diffCallback);
|
// super(diffCallback);
|
||||||
// private final static String ellipsize = "… more";
|
// // private final static String ellipsize = "… more";
|
||||||
this.clickListener = clickListener;
|
// this.clickListener = clickListener;
|
||||||
this.mentionClickListener = mentionClickListener;
|
// this.mentionClickListener = mentionClickListener;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@NonNull
|
// @NonNull
|
||||||
@Override
|
// @Override
|
||||||
public FeedItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
// public FeedItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||||
final Context context = parent.getContext();
|
// final Context context = parent.getContext();
|
||||||
final LayoutInflater layoutInflater = LayoutInflater.from(context);
|
// final LayoutInflater layoutInflater = LayoutInflater.from(context);
|
||||||
final MediaItemType type = MediaItemType.valueOf(viewType);
|
// final MediaItemType type = MediaItemType.valueOf(viewType);
|
||||||
switch (type) {
|
// switch (type) {
|
||||||
case MEDIA_TYPE_VIDEO: {
|
// case MEDIA_TYPE_VIDEO: {
|
||||||
final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
|
// final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
|
||||||
return new FeedVideoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
// return new FeedVideoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
||||||
}
|
// }
|
||||||
case MEDIA_TYPE_SLIDER: {
|
// case MEDIA_TYPE_SLIDER: {
|
||||||
final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
|
// final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
|
||||||
return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
// return new FeedSliderViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
||||||
}
|
// }
|
||||||
case MEDIA_TYPE_IMAGE:
|
// case MEDIA_TYPE_IMAGE:
|
||||||
default: {
|
// default: {
|
||||||
final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
|
// final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
|
||||||
return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
// return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public void onBindViewHolder(@NonNull final FeedItemViewHolder viewHolder, final int position) {
|
// public void onBindViewHolder(@NonNull final FeedItemViewHolder viewHolder, final int position) {
|
||||||
final FeedModel feedModel = getItem(position);
|
// final FeedModel feedModel = getItem(position);
|
||||||
if (feedModel == null) {
|
// if (feedModel == null) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
feedModel.setPosition(position);
|
// feedModel.setPosition(position);
|
||||||
viewHolder.bind(feedModel);
|
// viewHolder.bind(feedModel, (feedModel1, view, postImage) -> {});
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public int getItemViewType(final int position) {
|
// public int getItemViewType(final int position) {
|
||||||
return getItem(position).getItemType().getId();
|
// return getItem(position).getItemType().getId();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public void onViewAttachedToWindow(@NonNull final FeedItemViewHolder holder) {
|
// public void onViewAttachedToWindow(@NonNull final FeedItemViewHolder holder) {
|
||||||
super.onViewAttachedToWindow(holder);
|
// super.onViewAttachedToWindow(holder);
|
||||||
// Log.d(TAG, "attached holder: " + holder);
|
// // Log.d(TAG, "attached holder: " + holder);
|
||||||
if (!(holder instanceof FeedSliderViewHolder)) return;
|
// if (!(holder instanceof FeedSliderViewHolder)) return;
|
||||||
final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
|
// final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
|
||||||
feedSliderViewHolder.startPlayingVideo();
|
// feedSliderViewHolder.startPlayingVideo();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public void onViewDetachedFromWindow(@NonNull final FeedItemViewHolder holder) {
|
// public void onViewDetachedFromWindow(@NonNull final FeedItemViewHolder holder) {
|
||||||
super.onViewDetachedFromWindow(holder);
|
// super.onViewDetachedFromWindow(holder);
|
||||||
// Log.d(TAG, "detached holder: " + holder);
|
// // Log.d(TAG, "detached holder: " + holder);
|
||||||
if (!(holder instanceof FeedSliderViewHolder)) return;
|
// if (!(holder instanceof FeedSliderViewHolder)) return;
|
||||||
final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
|
// final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
|
||||||
feedSliderViewHolder.stopPlayingVideo();
|
// feedSliderViewHolder.stopPlayingVideo();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
241
app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
Normal file
241
app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
package awais.instagrabber.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.viewholder.FeedGridItemViewHolder;
|
||||||
|
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
|
||||||
|
import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
|
||||||
|
import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
|
||||||
|
import awais.instagrabber.adapters.viewholder.feed.FeedVideoViewHolder;
|
||||||
|
import awais.instagrabber.databinding.ItemFeedGridBinding;
|
||||||
|
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
|
||||||
|
import awais.instagrabber.databinding.ItemFeedSliderBinding;
|
||||||
|
import awais.instagrabber.databinding.ItemFeedVideoBinding;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
|
|
||||||
|
public final class FeedAdapterV2 extends ListAdapter<FeedModel, RecyclerView.ViewHolder> {
|
||||||
|
private static final String TAG = "FeedAdapterV2";
|
||||||
|
|
||||||
|
private final FeedItemCallback feedItemCallback;
|
||||||
|
private final SelectionModeCallback selectionModeCallback;
|
||||||
|
private final Set<Integer> selectedPositions = new HashSet<>();
|
||||||
|
private final Set<FeedModel> selectedFeedModels = new HashSet<>();
|
||||||
|
|
||||||
|
private PostsLayoutPreferences layoutPreferences;
|
||||||
|
private boolean selectionModeActive = false;
|
||||||
|
|
||||||
|
|
||||||
|
private static final DiffUtil.ItemCallback<FeedModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<FeedModel>() {
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
||||||
|
return oldItem.getPostId().equals(newItem.getPostId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(@NonNull final FeedModel oldItem, @NonNull final FeedModel newItem) {
|
||||||
|
return oldItem.getPostId().equals(newItem.getPostId());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final AdapterSelectionCallback adapterSelectionCallback = new AdapterSelectionCallback() {
|
||||||
|
@Override
|
||||||
|
public boolean onPostLongClick(final int position, final FeedModel feedModel) {
|
||||||
|
if (!selectionModeActive) {
|
||||||
|
selectionModeActive = true;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
if (selectionModeCallback != null) {
|
||||||
|
selectionModeCallback.onSelectionStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedPositions.add(position);
|
||||||
|
selectedFeedModels.add(feedModel);
|
||||||
|
notifyItemChanged(position);
|
||||||
|
if (selectionModeCallback != null) {
|
||||||
|
selectionModeCallback.onSelectionChange(selectedFeedModels);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostClick(final int position, final FeedModel feedModel) {
|
||||||
|
if (!selectionModeActive) return;
|
||||||
|
if (selectedPositions.contains(position)) {
|
||||||
|
selectedPositions.remove(position);
|
||||||
|
selectedFeedModels.remove(feedModel);
|
||||||
|
} else {
|
||||||
|
selectedPositions.add(position);
|
||||||
|
selectedFeedModels.add(feedModel);
|
||||||
|
}
|
||||||
|
notifyItemChanged(position);
|
||||||
|
if (selectionModeCallback != null) {
|
||||||
|
selectionModeCallback.onSelectionChange(selectedFeedModels);
|
||||||
|
}
|
||||||
|
if (selectedPositions.isEmpty()) {
|
||||||
|
selectionModeActive = false;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
if (selectionModeCallback != null) {
|
||||||
|
selectionModeCallback.onSelectionEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public FeedAdapterV2(@NonNull final PostsLayoutPreferences layoutPreferences,
|
||||||
|
final FeedItemCallback feedItemCallback,
|
||||||
|
final SelectionModeCallback selectionModeCallback) {
|
||||||
|
super(DIFF_CALLBACK);
|
||||||
|
this.layoutPreferences = layoutPreferences;
|
||||||
|
this.feedItemCallback = feedItemCallback;
|
||||||
|
this.selectionModeCallback = selectionModeCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||||
|
final Context context = parent.getContext();
|
||||||
|
final LayoutInflater layoutInflater = LayoutInflater.from(context);
|
||||||
|
switch (layoutPreferences.getType()) {
|
||||||
|
case LINEAR:
|
||||||
|
return getLinearViewHolder(parent, layoutInflater, viewType);
|
||||||
|
case GRID:
|
||||||
|
case STAGGERED_GRID:
|
||||||
|
default:
|
||||||
|
final ItemFeedGridBinding binding = ItemFeedGridBinding.inflate(layoutInflater, parent, false);
|
||||||
|
return new FeedGridItemViewHolder(binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private RecyclerView.ViewHolder getLinearViewHolder(@NonNull final ViewGroup parent,
|
||||||
|
final LayoutInflater layoutInflater,
|
||||||
|
final int viewType) {
|
||||||
|
switch (MediaItemType.valueOf(viewType)) {
|
||||||
|
case MEDIA_TYPE_VIDEO: {
|
||||||
|
final ItemFeedVideoBinding binding = ItemFeedVideoBinding.inflate(layoutInflater, parent, false);
|
||||||
|
return new FeedVideoViewHolder(binding, feedItemCallback);
|
||||||
|
}
|
||||||
|
case MEDIA_TYPE_SLIDER: {
|
||||||
|
final ItemFeedSliderBinding binding = ItemFeedSliderBinding.inflate(layoutInflater, parent, false);
|
||||||
|
return new FeedSliderViewHolder(binding, feedItemCallback);
|
||||||
|
}
|
||||||
|
case MEDIA_TYPE_IMAGE:
|
||||||
|
default: {
|
||||||
|
final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
|
||||||
|
return new FeedPhotoViewHolder(binding, feedItemCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, final int position) {
|
||||||
|
final FeedModel feedModel = getItem(position);
|
||||||
|
if (feedModel == null) return;
|
||||||
|
switch (layoutPreferences.getType()) {
|
||||||
|
case LINEAR:
|
||||||
|
((FeedItemViewHolder) viewHolder).bind(feedModel);
|
||||||
|
break;
|
||||||
|
case GRID:
|
||||||
|
case STAGGERED_GRID:
|
||||||
|
default:
|
||||||
|
((FeedGridItemViewHolder) viewHolder).bind(position,
|
||||||
|
feedModel,
|
||||||
|
layoutPreferences,
|
||||||
|
feedItemCallback,
|
||||||
|
adapterSelectionCallback,
|
||||||
|
selectionModeActive,
|
||||||
|
selectedPositions.contains(position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(final int position) {
|
||||||
|
return getItem(position).getItemType().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLayoutPreferences(@NonNull final PostsLayoutPreferences layoutPreferences) {
|
||||||
|
this.layoutPreferences = layoutPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endSelection() {
|
||||||
|
if (!selectionModeActive) return;
|
||||||
|
selectionModeActive = false;
|
||||||
|
selectedPositions.clear();
|
||||||
|
selectedFeedModels.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
if (selectionModeCallback != null) {
|
||||||
|
selectionModeCallback.onSelectionEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public void onViewAttachedToWindow(@NonNull final FeedItemViewHolder holder) {
|
||||||
|
// super.onViewAttachedToWindow(holder);
|
||||||
|
// // Log.d(TAG, "attached holder: " + holder);
|
||||||
|
// if (!(holder instanceof FeedSliderViewHolder)) return;
|
||||||
|
// final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
|
||||||
|
// feedSliderViewHolder.startPlayingVideo();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onViewDetachedFromWindow(@NonNull final FeedItemViewHolder holder) {
|
||||||
|
// super.onViewDetachedFromWindow(holder);
|
||||||
|
// // Log.d(TAG, "detached holder: " + holder);
|
||||||
|
// if (!(holder instanceof FeedSliderViewHolder)) return;
|
||||||
|
// final FeedSliderViewHolder feedSliderViewHolder = (FeedSliderViewHolder) holder;
|
||||||
|
// feedSliderViewHolder.stopPlayingVideo();
|
||||||
|
// }
|
||||||
|
|
||||||
|
public interface FeedItemCallback {
|
||||||
|
void onPostClick(final FeedModel feedModel,
|
||||||
|
final View profilePicView,
|
||||||
|
final View mainPostImage);
|
||||||
|
|
||||||
|
void onProfilePicClick(final FeedModel feedModel,
|
||||||
|
final View profilePicView);
|
||||||
|
|
||||||
|
void onNameClick(final FeedModel feedModel,
|
||||||
|
final View profilePicView);
|
||||||
|
|
||||||
|
void onLocationClick(final FeedModel feedModel);
|
||||||
|
|
||||||
|
void onMentionClick(final String mention);
|
||||||
|
|
||||||
|
void onHashtagClick(final String hashtag);
|
||||||
|
|
||||||
|
void onCommentsClick(final FeedModel feedModel);
|
||||||
|
|
||||||
|
void onDownloadClick(final FeedModel feedModel, final int childPosition);
|
||||||
|
|
||||||
|
void onEmailClick(final String emailId);
|
||||||
|
|
||||||
|
void onURLClick(final String url);
|
||||||
|
|
||||||
|
void onSliderClick(FeedModel feedModel, int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface AdapterSelectionCallback {
|
||||||
|
boolean onPostLongClick(final int position, FeedModel feedModel);
|
||||||
|
|
||||||
|
void onPostClick(final int position, FeedModel feedModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SelectionModeCallback {
|
||||||
|
void onSelectionStart();
|
||||||
|
|
||||||
|
void onSelectionChange(final Set<FeedModel> selectedFeedModels);
|
||||||
|
|
||||||
|
void onSelectionEnd();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package awais.instagrabber.adapters;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
|
||||||
|
public class FeedItemCallbackAdapter implements FeedAdapterV2.FeedItemCallback {
|
||||||
|
@Override
|
||||||
|
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNameClick(final FeedModel feedModel, final View profilePicView) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationClick(final FeedModel feedModel) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMentionClick(final String mention) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHashtagClick(final String hashtag) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommentsClick(final FeedModel feedModel) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmailClick(final String emailId) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onURLClick(final String url) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSliderClick(final FeedModel feedModel, final int position) {}
|
||||||
|
}
|
@ -12,8 +12,8 @@ public abstract class MultiSelectListAdapter<T extends MultiSelectListAdapter.Se
|
|||||||
ListAdapter<T, VH> {
|
ListAdapter<T, VH> {
|
||||||
|
|
||||||
private boolean isSelecting;
|
private boolean isSelecting;
|
||||||
private final OnItemClickListener<T> internalOnItemClickListener;
|
private OnItemClickListener<T> internalOnItemClickListener;
|
||||||
private final OnItemLongClickListener<T> internalOnLongItemClickListener;
|
private OnItemLongClickListener<T> internalOnLongItemClickListener;
|
||||||
|
|
||||||
private final List<T> selectedItems = new ArrayList<>();
|
private final List<T> selectedItems = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -1,77 +1,75 @@
|
|||||||
package awais.instagrabber.adapters;
|
// package awais.instagrabber.adapters;
|
||||||
|
//
|
||||||
import android.view.LayoutInflater;
|
// import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
// import android.view.View;
|
||||||
import android.view.ViewGroup;
|
// import android.view.ViewGroup;
|
||||||
|
//
|
||||||
import androidx.annotation.NonNull;
|
// import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
// import androidx.recyclerview.widget.DiffUtil;
|
||||||
import androidx.recyclerview.widget.ListAdapter;
|
// import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
//
|
||||||
import java.util.Arrays;
|
// import awais.instagrabber.adapters.viewholder.PostViewerViewHolder;
|
||||||
|
// import awais.instagrabber.databinding.ItemFullPostViewBinding;
|
||||||
import awais.instagrabber.adapters.viewholder.PostViewerViewHolder;
|
// import awais.instagrabber.interfaces.MentionClickListener;
|
||||||
import awais.instagrabber.databinding.ItemFullPostViewBinding;
|
// import awais.instagrabber.models.ViewerPostModelWrapper;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
//
|
||||||
import awais.instagrabber.models.ViewerPostModelWrapper;
|
// public class PostViewAdapter extends ListAdapter<ViewerPostModelWrapper, PostViewerViewHolder> {
|
||||||
|
// private final OnPostViewChildViewClickListener clickListener;
|
||||||
public class PostViewAdapter extends ListAdapter<ViewerPostModelWrapper, PostViewerViewHolder> {
|
// private final OnPostCaptionLongClickListener longClickListener;
|
||||||
private final OnPostViewChildViewClickListener clickListener;
|
// private final MentionClickListener mentionClickListener;
|
||||||
private final OnPostCaptionLongClickListener longClickListener;
|
//
|
||||||
private final MentionClickListener mentionClickListener;
|
// private static final DiffUtil.ItemCallback<ViewerPostModelWrapper> diffCallback = new DiffUtil.ItemCallback<ViewerPostModelWrapper>() {
|
||||||
|
// @Override
|
||||||
private static final DiffUtil.ItemCallback<ViewerPostModelWrapper> diffCallback = new DiffUtil.ItemCallback<ViewerPostModelWrapper>() {
|
// public boolean areItemsTheSame(@NonNull final ViewerPostModelWrapper oldItem,
|
||||||
@Override
|
// @NonNull final ViewerPostModelWrapper newItem) {
|
||||||
public boolean areItemsTheSame(@NonNull final ViewerPostModelWrapper oldItem,
|
// return oldItem.getPosition() == newItem.getPosition();
|
||||||
@NonNull final ViewerPostModelWrapper newItem) {
|
// }
|
||||||
return oldItem.getPosition() == newItem.getPosition();
|
//
|
||||||
}
|
// @Override
|
||||||
|
// public boolean areContentsTheSame(@NonNull final ViewerPostModelWrapper oldItem,
|
||||||
@Override
|
// @NonNull final ViewerPostModelWrapper newItem) {
|
||||||
public boolean areContentsTheSame(@NonNull final ViewerPostModelWrapper oldItem,
|
// return oldItem.getViewerPostModels().equals(newItem.getViewerPostModels());
|
||||||
@NonNull final ViewerPostModelWrapper newItem) {
|
// }
|
||||||
return Arrays.equals(oldItem.getViewerPostModels(), newItem.getViewerPostModels());
|
// };
|
||||||
}
|
//
|
||||||
};
|
// public PostViewAdapter(final OnPostViewChildViewClickListener clickListener,
|
||||||
|
// final OnPostCaptionLongClickListener longClickListener,
|
||||||
public PostViewAdapter(final OnPostViewChildViewClickListener clickListener,
|
// final MentionClickListener mentionClickListener) {
|
||||||
final OnPostCaptionLongClickListener longClickListener,
|
// super(diffCallback);
|
||||||
final MentionClickListener mentionClickListener) {
|
// this.clickListener = clickListener;
|
||||||
super(diffCallback);
|
// this.longClickListener = longClickListener;
|
||||||
this.clickListener = clickListener;
|
// this.mentionClickListener = mentionClickListener;
|
||||||
this.longClickListener = longClickListener;
|
// }
|
||||||
this.mentionClickListener = mentionClickListener;
|
//
|
||||||
}
|
// @Override
|
||||||
|
// public void onViewDetachedFromWindow(@NonNull final PostViewerViewHolder holder) {
|
||||||
@Override
|
// holder.stopPlayingVideo();
|
||||||
public void onViewDetachedFromWindow(@NonNull final PostViewerViewHolder holder) {
|
// }
|
||||||
holder.stopPlayingVideo();
|
//
|
||||||
}
|
// @NonNull
|
||||||
|
// @Override
|
||||||
@NonNull
|
// public PostViewerViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
|
||||||
@Override
|
// final int viewType) {
|
||||||
public PostViewerViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
|
// final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||||
final int viewType) {
|
// final ItemFullPostViewBinding binding = ItemFullPostViewBinding
|
||||||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
// .inflate(layoutInflater, parent, false);
|
||||||
final ItemFullPostViewBinding binding = ItemFullPostViewBinding
|
// return new PostViewerViewHolder(binding);
|
||||||
.inflate(layoutInflater, parent, false);
|
// }
|
||||||
return new PostViewerViewHolder(binding);
|
//
|
||||||
}
|
// @Override
|
||||||
|
// public void onBindViewHolder(@NonNull final PostViewerViewHolder holder, final int position) {
|
||||||
@Override
|
// final ViewerPostModelWrapper item = getItem(position);
|
||||||
public void onBindViewHolder(@NonNull final PostViewerViewHolder holder, final int position) {
|
// holder.bind(item, position, clickListener, longClickListener, mentionClickListener);
|
||||||
final ViewerPostModelWrapper item = getItem(position);
|
// }
|
||||||
holder.bind(item, position, clickListener, longClickListener, mentionClickListener);
|
//
|
||||||
}
|
// public interface OnPostViewChildViewClickListener {
|
||||||
|
// void onClick(View v,
|
||||||
public interface OnPostViewChildViewClickListener {
|
// ViewerPostModelWrapper viewerPostModelWrapper,
|
||||||
void onClick(View v,
|
// int postPosition,
|
||||||
ViewerPostModelWrapper viewerPostModelWrapper,
|
// int childPosition);
|
||||||
int postPosition,
|
// }
|
||||||
int childPosition);
|
//
|
||||||
}
|
// public interface OnPostCaptionLongClickListener {
|
||||||
|
// void onLongClick(String text);
|
||||||
public interface OnPostCaptionLongClickListener {
|
// }
|
||||||
void onLongClick(String text);
|
// }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -15,6 +15,7 @@ import com.facebook.drawee.backends.pipeline.Fresco;
|
|||||||
import com.facebook.drawee.interfaces.DraweeController;
|
import com.facebook.drawee.interfaces.DraweeController;
|
||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||||
@ -171,8 +172,9 @@ public class PostViewerChildAdapter extends ListAdapter<ViewerPostModel, PostVie
|
|||||||
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
|
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
|
||||||
player.setVolume(vol);
|
player.setVolume(vol);
|
||||||
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
|
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
|
||||||
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(itemView.getContext(), "instagram"))
|
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(
|
||||||
.createMediaSource(Uri.parse(item.getDisplayUrl()));
|
new DefaultDataSourceFactory(itemView.getContext(), "instagram")
|
||||||
|
).createMediaSource(MediaItem.fromUri(item.getDisplayUrl()));
|
||||||
// mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
|
// mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
|
||||||
// @Override
|
// @Override
|
||||||
// public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
|
// public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
|
||||||
@ -194,7 +196,8 @@ public class PostViewerChildAdapter extends ListAdapter<ViewerPostModel, PostVie
|
|||||||
// viewerBinding.progressView.setVisibility(View.GONE);
|
// viewerBinding.progressView.setVisibility(View.GONE);
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
player.prepare(mediaSource);
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
player.setVolume(vol);
|
player.setVolume(vol);
|
||||||
// viewerBinding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
|
// viewerBinding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
|
||||||
// viewerBinding.bottomPanel.btnMute.setOnClickListener(onClickListener);
|
// viewerBinding.bottomPanel.btnMute.setOnClickListener(onClickListener);
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package awais.instagrabber.adapters;
|
||||||
|
|
||||||
|
public class SliderCallbackAdapter implements SliderItemsAdapter.SliderCallback {
|
||||||
|
@Override
|
||||||
|
public void onThumbnailLoaded(final int position) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClicked(final int position) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerPlay(final int position) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerPause(final int position) {}
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
package awais.instagrabber.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.viewholder.SliderItemViewHolder;
|
||||||
|
import awais.instagrabber.adapters.viewholder.SliderPhotoViewHolder;
|
||||||
|
import awais.instagrabber.adapters.viewholder.SliderVideoViewHolder;
|
||||||
|
import awais.instagrabber.customviews.VerticalDragHelper;
|
||||||
|
import awais.instagrabber.databinding.ItemSliderPhotoBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
|
|
||||||
|
public final class SliderItemsAdapter extends ListAdapter<PostChild, SliderItemViewHolder> {
|
||||||
|
|
||||||
|
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener;
|
||||||
|
private final boolean loadVideoOnItemClick;
|
||||||
|
private final SliderCallback sliderCallback;
|
||||||
|
private final awais.instagrabber.databinding.LayoutExoCustomControlsBinding controlsBinding;
|
||||||
|
|
||||||
|
private static final DiffUtil.ItemCallback<PostChild> DIFF_CALLBACK = new DiffUtil.ItemCallback<PostChild>() {
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) {
|
||||||
|
return oldItem.getPostId().equals(newItem.getPostId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(@NonNull final PostChild oldItem, @NonNull final PostChild newItem) {
|
||||||
|
return oldItem.getPostId().equals(newItem.getPostId());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public SliderItemsAdapter(final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener,
|
||||||
|
final LayoutExoCustomControlsBinding controlsBinding,
|
||||||
|
final boolean loadVideoOnItemClick,
|
||||||
|
final SliderCallback sliderCallback) {
|
||||||
|
super(DIFF_CALLBACK);
|
||||||
|
this.onVerticalDragListener = onVerticalDragListener;
|
||||||
|
this.loadVideoOnItemClick = loadVideoOnItemClick;
|
||||||
|
this.sliderCallback = sliderCallback;
|
||||||
|
this.controlsBinding = controlsBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public SliderItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||||
|
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||||
|
final MediaItemType mediaItemType = MediaItemType.valueOf(viewType);
|
||||||
|
switch (mediaItemType) {
|
||||||
|
case MEDIA_TYPE_VIDEO: {
|
||||||
|
final LayoutVideoPlayerWithThumbnailBinding binding = LayoutVideoPlayerWithThumbnailBinding.inflate(inflater, parent, false);
|
||||||
|
return new SliderVideoViewHolder(binding, onVerticalDragListener, controlsBinding, loadVideoOnItemClick);
|
||||||
|
}
|
||||||
|
case MEDIA_TYPE_IMAGE:
|
||||||
|
default:
|
||||||
|
final ItemSliderPhotoBinding binding = ItemSliderPhotoBinding.inflate(inflater, parent, false);
|
||||||
|
return new SliderPhotoViewHolder(binding, onVerticalDragListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull final SliderItemViewHolder holder, final int position) {
|
||||||
|
final PostChild model = getItem(position);
|
||||||
|
holder.bind(model, position, sliderCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(final int position) {
|
||||||
|
final PostChild viewerPostModel = getItem(position);
|
||||||
|
return viewerPostModel.getItemType().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// @NonNull
|
||||||
|
// @Override
|
||||||
|
// public Object instantiateItem(@NonNull final ViewGroup container, final int position) {
|
||||||
|
// final Context context = container.getContext();
|
||||||
|
// final ViewerPostModel sliderItem = sliderItems.get(position);
|
||||||
|
//
|
||||||
|
// if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
|
||||||
|
// final ViewSwitcher viewSwitcher = createViewSwitcher(context, position, sliderItem.getThumbnailUrl(), sliderItem.getDisplayUrl());
|
||||||
|
// container.addView(viewSwitcher);
|
||||||
|
// return viewSwitcher;
|
||||||
|
// }
|
||||||
|
// final GenericDraweeHierarchy hierarchy = GenericDraweeHierarchyBuilder.newInstance(container.getResources())
|
||||||
|
// .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||||
|
// .build();
|
||||||
|
// final SimpleDraweeView photoView = new SimpleDraweeView(context, hierarchy);
|
||||||
|
// photoView.setLayoutParams(layoutParams);
|
||||||
|
// final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(sliderItem.getDisplayUrl()))
|
||||||
|
// .setLocalThumbnailPreviewsEnabled(true)
|
||||||
|
// .setProgressiveRenderingEnabled(true)
|
||||||
|
// .build();
|
||||||
|
// photoView.setImageRequest(imageRequest);
|
||||||
|
// container.addView(photoView);
|
||||||
|
// return photoView;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @NonNull
|
||||||
|
// private ViewSwitcher createViewSwitcher(final Context context,
|
||||||
|
// final int position,
|
||||||
|
// final String thumbnailUrl,
|
||||||
|
// final String displayUrl) {
|
||||||
|
//
|
||||||
|
// final ViewSwitcher viewSwitcher = new ViewSwitcher(context);
|
||||||
|
// viewSwitcher.setLayoutParams(layoutParams);
|
||||||
|
//
|
||||||
|
// final FrameLayout frameLayout = new FrameLayout(context);
|
||||||
|
// frameLayout.setLayoutParams(layoutParams);
|
||||||
|
//
|
||||||
|
// final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(context.getResources())
|
||||||
|
// .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||||
|
// .build();
|
||||||
|
// final SimpleDraweeView simpleDraweeView = new SimpleDraweeView(context, hierarchy);
|
||||||
|
// simpleDraweeView.setLayoutParams(layoutParams);
|
||||||
|
// simpleDraweeView.setImageURI(thumbnailUrl);
|
||||||
|
// frameLayout.addView(simpleDraweeView);
|
||||||
|
//
|
||||||
|
// final AppCompatImageView imageView = new AppCompatImageView(context);
|
||||||
|
// final int px = Utils.convertDpToPx(50);
|
||||||
|
// final FrameLayout.LayoutParams playButtonLayoutParams = new FrameLayout.LayoutParams(px, px);
|
||||||
|
// playButtonLayoutParams.gravity = Gravity.CENTER;
|
||||||
|
// imageView.setLayoutParams(playButtonLayoutParams);
|
||||||
|
// imageView.setImageResource(R.drawable.exo_icon_play);
|
||||||
|
// frameLayout.addView(imageView);
|
||||||
|
//
|
||||||
|
// viewSwitcher.addView(frameLayout);
|
||||||
|
//
|
||||||
|
// final PlayerView playerView = new PlayerView(context);
|
||||||
|
// viewSwitcher.addView(playerView);
|
||||||
|
// if (shouldAutoPlay && position == 0) {
|
||||||
|
// loadPlayer(context, position, displayUrl, viewSwitcher, factory, playerChangeListener);
|
||||||
|
// } else
|
||||||
|
// frameLayout.setOnClickListener(v -> loadPlayer(context, position, displayUrl, viewSwitcher, factory, playerChangeListener));
|
||||||
|
// return viewSwitcher;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public interface SliderCallback {
|
||||||
|
void onThumbnailLoaded(int position);
|
||||||
|
|
||||||
|
void onItemClicked(int position);
|
||||||
|
|
||||||
|
void onPlayerPlay(int position);
|
||||||
|
|
||||||
|
void onPlayerPause(int position);
|
||||||
|
}
|
||||||
|
}
|
@ -1,95 +0,0 @@
|
|||||||
package awais.instagrabber.adapters.viewholder;
|
|
||||||
|
|
||||||
import android.text.Spannable;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
import awais.instagrabber.adapters.CommentsAdapter;
|
|
||||||
import awais.instagrabber.customviews.RamboTextView;
|
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
|
||||||
import awais.instagrabber.models.CommentModel;
|
|
||||||
|
|
||||||
public final class CommentViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
private final MentionClickListener mentionClickListener;
|
|
||||||
private final RecyclerView rvChildComments;
|
|
||||||
private final SimpleDraweeView ivProfilePic;
|
|
||||||
private final TextView tvUsername;
|
|
||||||
private final TextView tvDate;
|
|
||||||
private final TextView tvComment;
|
|
||||||
private final TextView tvLikes;
|
|
||||||
private final View container;
|
|
||||||
|
|
||||||
public CommentViewHolder(@NonNull final View itemView,
|
|
||||||
final View.OnClickListener onClickListener,
|
|
||||||
final MentionClickListener mentionClickListener) {
|
|
||||||
super(itemView);
|
|
||||||
|
|
||||||
container = itemView.findViewById(R.id.container);
|
|
||||||
if (onClickListener != null) container.setOnClickListener(onClickListener);
|
|
||||||
|
|
||||||
this.mentionClickListener = mentionClickListener;
|
|
||||||
|
|
||||||
ivProfilePic = itemView.findViewById(R.id.ivProfilePic);
|
|
||||||
tvUsername = itemView.findViewById(R.id.tvUsername);
|
|
||||||
tvDate = itemView.findViewById(R.id.tvDate);
|
|
||||||
tvLikes = itemView.findViewById(R.id.tvLikes);
|
|
||||||
tvComment = itemView.findViewById(R.id.tvComment);
|
|
||||||
|
|
||||||
tvUsername.setSelected(true);
|
|
||||||
tvDate.setSelected(true);
|
|
||||||
|
|
||||||
rvChildComments = itemView.findViewById(R.id.rvChildComments);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final SimpleDraweeView getProfilePicView() {
|
|
||||||
return ivProfilePic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean isParent() {
|
|
||||||
return rvChildComments != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setCommentModel(final CommentModel commentModel) {
|
|
||||||
if (container != null) container.setTag(commentModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setUsername(final String username) {
|
|
||||||
if (tvUsername != null) tvUsername.setText(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setDate(final String date) {
|
|
||||||
if (tvDate != null) tvDate.setText(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setLikes(final String likes) {
|
|
||||||
if (tvLikes != null) tvLikes.setText(likes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setLiked(final boolean liked) {
|
|
||||||
if (liked) container.setBackgroundColor(0x40FF69B4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setComment(final CharSequence comment) {
|
|
||||||
if (tvComment != null) {
|
|
||||||
tvComment.setText(comment, comment instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
|
|
||||||
((RamboTextView) tvComment).setMentionClickListener(mentionClickListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setChildAdapter(final CommentsAdapter adapter) {
|
|
||||||
if (isParent()) {
|
|
||||||
rvChildComments.setAdapter(adapter);
|
|
||||||
rvChildComments.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void hideChildComments() {
|
|
||||||
if (isParent()) rvChildComments.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
@ -97,6 +97,7 @@ public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHol
|
|||||||
case MEDIA_SHARE:
|
case MEDIA_SHARE:
|
||||||
case RAVEN_MEDIA:
|
case RAVEN_MEDIA:
|
||||||
case CLIP:
|
case CLIP:
|
||||||
|
case FELIX_SHARE:
|
||||||
messageText = context.getString(R.string.direct_messages_sent_media);
|
messageText = context.getString(R.string.direct_messages_sent_media);
|
||||||
break;
|
break;
|
||||||
case ACTION_LOG:
|
case ACTION_LOG:
|
||||||
|
@ -0,0 +1,161 @@
|
|||||||
|
package awais.instagrabber.adapters.viewholder;
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.DimenRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
|
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
|
import awais.instagrabber.asyncs.DownloadedCheckerAsyncTask;
|
||||||
|
import awais.instagrabber.databinding.ItemFeedGridBinding;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
|
||||||
|
import static awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID;
|
||||||
|
|
||||||
|
public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private final ItemFeedGridBinding binding;
|
||||||
|
|
||||||
|
public FeedGridItemViewHolder(@NonNull final ItemFeedGridBinding binding) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(final int position,
|
||||||
|
@NonNull final FeedModel feedModel,
|
||||||
|
@NonNull final PostsLayoutPreferences layoutPreferences,
|
||||||
|
final FeedAdapterV2.FeedItemCallback feedItemCallback,
|
||||||
|
final FeedAdapterV2.AdapterSelectionCallback adapterSelectionCallback,
|
||||||
|
final boolean selectionModeActive,
|
||||||
|
final boolean selected) {
|
||||||
|
itemView.setOnClickListener(v -> {
|
||||||
|
if (!selectionModeActive && feedItemCallback != null) {
|
||||||
|
feedItemCallback.onPostClick(feedModel, binding.profilePic, binding.postImage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectionModeActive && adapterSelectionCallback != null) {
|
||||||
|
adapterSelectionCallback.onPostClick(position, feedModel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (adapterSelectionCallback != null) {
|
||||||
|
itemView.setOnLongClickListener(v -> adapterSelectionCallback.onPostLongClick(position, feedModel));
|
||||||
|
}
|
||||||
|
binding.selectedView.setVisibility(selected ? View.VISIBLE : View.GONE);
|
||||||
|
// for rounded borders (clip view to background shape)
|
||||||
|
itemView.setClipToOutline(layoutPreferences.getHasRoundedCorners());
|
||||||
|
if (layoutPreferences.getType() == STAGGERED_GRID) {
|
||||||
|
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
|
||||||
|
binding.postImage.setAspectRatio(aspectRatio);
|
||||||
|
} else {
|
||||||
|
binding.postImage.setAspectRatio(1);
|
||||||
|
}
|
||||||
|
if (layoutPreferences.isAvatarVisible()) {
|
||||||
|
binding.profilePic.setVisibility(View.VISIBLE);
|
||||||
|
binding.profilePic.setImageURI(feedModel.getProfileModel().getSdProfilePic());
|
||||||
|
final ViewGroup.LayoutParams layoutParams = binding.profilePic.getLayoutParams();
|
||||||
|
@DimenRes final int dimenRes;
|
||||||
|
switch (layoutPreferences.getProfilePicSize()) {
|
||||||
|
case SMALL:
|
||||||
|
dimenRes = R.dimen.profile_pic_size_small;
|
||||||
|
break;
|
||||||
|
case TINY:
|
||||||
|
dimenRes = R.dimen.profile_pic_size_tiny;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case REGULAR:
|
||||||
|
dimenRes = R.dimen.profile_pic_size_regular;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
final int dimensionPixelSize = itemView.getResources().getDimensionPixelSize(dimenRes);
|
||||||
|
layoutParams.width = dimensionPixelSize;
|
||||||
|
layoutParams.height = dimensionPixelSize;
|
||||||
|
binding.profilePic.requestLayout();
|
||||||
|
} else {
|
||||||
|
binding.profilePic.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if (layoutPreferences.isNameVisible()) {
|
||||||
|
binding.name.setVisibility(View.VISIBLE);
|
||||||
|
binding.name.setText(feedModel.getProfileModel().getUsername());
|
||||||
|
} else {
|
||||||
|
binding.name.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
String thumbnailUrl = null;
|
||||||
|
final int typeIconRes;
|
||||||
|
switch (feedModel.getItemType()) {
|
||||||
|
case MEDIA_TYPE_IMAGE:
|
||||||
|
typeIconRes = -1;
|
||||||
|
thumbnailUrl = feedModel.getThumbnailUrl();
|
||||||
|
break;
|
||||||
|
case MEDIA_TYPE_VIDEO:
|
||||||
|
thumbnailUrl = feedModel.getThumbnailUrl();
|
||||||
|
typeIconRes = R.drawable.exo_icon_play;
|
||||||
|
break;
|
||||||
|
case MEDIA_TYPE_SLIDER:
|
||||||
|
final List<PostChild> sliderItems = feedModel.getSliderItems();
|
||||||
|
if (sliderItems != null) {
|
||||||
|
thumbnailUrl = sliderItems.get(0).getThumbnailUrl();
|
||||||
|
}
|
||||||
|
typeIconRes = R.drawable.ic_checkbox_multiple_blank_stroke;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
typeIconRes = -1;
|
||||||
|
thumbnailUrl = null;
|
||||||
|
}
|
||||||
|
if (TextUtils.isEmpty(thumbnailUrl)) {
|
||||||
|
binding.postImage.setController(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeIconRes <= 0) {
|
||||||
|
binding.typeIcon.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
binding.typeIcon.setVisibility(View.VISIBLE);
|
||||||
|
binding.typeIcon.setImageResource(typeIconRes);
|
||||||
|
}
|
||||||
|
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl))
|
||||||
|
.setLocalThumbnailPreviewsEnabled(true)
|
||||||
|
.setProgressiveRenderingEnabled(true)
|
||||||
|
.build();
|
||||||
|
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
|
||||||
|
.setImageRequest(requestBuilder)
|
||||||
|
.setOldController(binding.postImage.getController());
|
||||||
|
binding.postImage.setController(builder.build());
|
||||||
|
final DownloadedCheckerAsyncTask task = new DownloadedCheckerAsyncTask(result -> {
|
||||||
|
final List<Boolean> checkList = result.get(feedModel.getPostId());
|
||||||
|
if (checkList == null || checkList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (feedModel.getItemType()) {
|
||||||
|
case MEDIA_TYPE_IMAGE:
|
||||||
|
case MEDIA_TYPE_VIDEO:
|
||||||
|
binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);
|
||||||
|
binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(R.color.green_A400)));
|
||||||
|
break;
|
||||||
|
case MEDIA_TYPE_SLIDER:
|
||||||
|
binding.downloaded.setVisibility(checkList.get(0) ? View.VISIBLE : View.GONE);
|
||||||
|
boolean allDownloaded = checkList.size() == feedModel.getSliderItems().size();
|
||||||
|
if (allDownloaded) {
|
||||||
|
allDownloaded = checkList.stream().allMatch(downloaded -> downloaded);
|
||||||
|
}
|
||||||
|
binding.downloaded.setImageTintList(ColorStateList.valueOf(itemView.getResources().getColor(
|
||||||
|
allDownloaded ? R.color.green_A400 : R.color.yellow_400)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.execute(feedModel);
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,9 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
|
|||||||
text = R.string.mention_notif;
|
text = R.string.mention_notif;
|
||||||
subtext = model.getText();
|
subtext = model.getText();
|
||||||
break;
|
break;
|
||||||
|
case TAGGED:
|
||||||
|
text = R.string.tagged_notif;
|
||||||
|
break;
|
||||||
case FOLLOW:
|
case FOLLOW:
|
||||||
text = R.string.follow_notif;
|
text = R.string.follow_notif;
|
||||||
break;
|
break;
|
||||||
|
@ -19,11 +19,11 @@ public final class PostMediaViewHolder extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
public void bind(final ViewerPostModel model, final int position, final View.OnClickListener clickListener) {
|
public void bind(final ViewerPostModel model, final int position, final View.OnClickListener clickListener) {
|
||||||
if (model == null) return;
|
if (model == null) return;
|
||||||
model.setPosition(position);
|
// model.setPosition(position);
|
||||||
itemView.setTag(model);
|
itemView.setTag(model);
|
||||||
itemView.setOnClickListener(clickListener);
|
itemView.setOnClickListener(clickListener);
|
||||||
binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);
|
binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);
|
||||||
binding.isDownloaded.setVisibility(model.isDownloaded() ? View.VISIBLE : View.GONE);
|
binding.isDownloaded.setVisibility(model.isDownloaded() ? View.VISIBLE : View.GONE);
|
||||||
binding.icon.setImageURI(model.getSliderDisplayUrl());
|
binding.icon.setImageURI(model.getDisplayUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ public final class PostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
final OnItemClickListener<PostModel> clickListener,
|
final OnItemClickListener<PostModel> clickListener,
|
||||||
final OnItemLongClickListener<PostModel> longClickListener) {
|
final OnItemLongClickListener<PostModel> longClickListener) {
|
||||||
if (postModel == null) return;
|
if (postModel == null) return;
|
||||||
postModel.setPosition(position);
|
// postModel.setPosition(position);
|
||||||
itemView.setOnClickListener(v -> clickListener.onItemClick(postModel, position));
|
itemView.setOnClickListener(v -> clickListener.onItemClick(postModel, position));
|
||||||
itemView.setOnLongClickListener(v -> longClickListener.onItemLongClick(postModel, position));
|
itemView.setOnLongClickListener(v -> longClickListener.onItemLongClick(postModel, position));
|
||||||
|
|
||||||
|
@ -1,239 +1,240 @@
|
|||||||
package awais.instagrabber.adapters.viewholder;
|
// package awais.instagrabber.adapters.viewholder;
|
||||||
|
//
|
||||||
import android.content.res.ColorStateList;
|
// import android.content.res.ColorStateList;
|
||||||
import android.content.res.Resources;
|
// import android.content.res.Resources;
|
||||||
import android.util.Log;
|
// import android.util.Log;
|
||||||
import android.view.View;
|
// import android.view.View;
|
||||||
import android.widget.TextView;
|
// import android.widget.TextView;
|
||||||
|
//
|
||||||
import androidx.annotation.NonNull;
|
// import androidx.annotation.NonNull;
|
||||||
import androidx.core.content.ContextCompat;
|
// import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.view.ViewCompat;
|
// import androidx.core.view.ViewCompat;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
// import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
// import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
//
|
||||||
import com.google.android.exoplayer2.Player;
|
// import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
// import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
import com.google.android.exoplayer2.ui.PlayerView;
|
// import com.google.android.exoplayer2.ui.PlayerView;
|
||||||
|
//
|
||||||
import java.util.ArrayList;
|
// import java.util.ArrayList;
|
||||||
import java.util.List;
|
// import java.util.List;
|
||||||
|
//
|
||||||
import awais.instagrabber.R;
|
// import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.PostViewAdapter.OnPostCaptionLongClickListener;
|
// import awais.instagrabber.adapters.PostViewAdapter.OnPostCaptionLongClickListener;
|
||||||
import awais.instagrabber.adapters.PostViewAdapter.OnPostViewChildViewClickListener;
|
// import awais.instagrabber.adapters.PostViewAdapter.OnPostViewChildViewClickListener;
|
||||||
import awais.instagrabber.adapters.PostViewerChildAdapter;
|
// import awais.instagrabber.adapters.PostViewerChildAdapter;
|
||||||
import awais.instagrabber.databinding.ItemFullPostViewBinding;
|
// import awais.instagrabber.databinding.ItemFullPostViewBinding;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
// import awais.instagrabber.interfaces.MentionClickListener;
|
||||||
import awais.instagrabber.models.ProfileModel;
|
// import awais.instagrabber.models.PostChild;
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
// import awais.instagrabber.models.ProfileModel;
|
||||||
import awais.instagrabber.models.ViewerPostModelWrapper;
|
// import awais.instagrabber.models.ViewerPostModel;
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
// import awais.instagrabber.models.ViewerPostModelWrapper;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
// import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.utils.Utils;
|
// import awais.instagrabber.utils.TextUtils;
|
||||||
|
// import awais.instagrabber.utils.Utils;
|
||||||
public class PostViewerViewHolder extends RecyclerView.ViewHolder {
|
//
|
||||||
private static final String TAG = "PostViewerViewHolder";
|
// public class PostViewerViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
// private static final String TAG = "PostViewerViewHolder";
|
||||||
private final ItemFullPostViewBinding binding;
|
//
|
||||||
private int currentChildPosition;
|
// private final ItemFullPostViewBinding binding;
|
||||||
|
// private int currentChildPosition;
|
||||||
public PostViewerViewHolder(@NonNull final ItemFullPostViewBinding binding) {
|
//
|
||||||
super(binding.getRoot());
|
// public PostViewerViewHolder(@NonNull final ItemFullPostViewBinding binding) {
|
||||||
this.binding = binding;
|
// super(binding.getRoot());
|
||||||
binding.topPanel.viewStoryPost.setVisibility(View.GONE);
|
// this.binding = binding;
|
||||||
}
|
// binding.topPanel.viewStoryPost.setVisibility(View.GONE);
|
||||||
|
// }
|
||||||
public void bind(final ViewerPostModelWrapper wrapper,
|
//
|
||||||
final int position,
|
// public void bind(final ViewerPostModelWrapper wrapper,
|
||||||
final OnPostViewChildViewClickListener clickListener,
|
// final int position,
|
||||||
final OnPostCaptionLongClickListener longClickListener,
|
// final OnPostViewChildViewClickListener clickListener,
|
||||||
final MentionClickListener mentionClickListener) {
|
// final OnPostCaptionLongClickListener longClickListener,
|
||||||
if (wrapper == null) return;
|
// final MentionClickListener mentionClickListener) {
|
||||||
final ViewerPostModel[] items = wrapper.getViewerPostModels();
|
// if (wrapper == null) return;
|
||||||
if (items == null || items.length <= 0) return;
|
// final List<PostChild> items = wrapper.getViewerPostModels();
|
||||||
if (items[0] == null) return;
|
// if (items == null || items.size() == 0) return;
|
||||||
final PostViewerChildAdapter adapter = new PostViewerChildAdapter();
|
// if (items.get(0) == null) return;
|
||||||
binding.mediaViewPager.setAdapter(adapter);
|
// final PostViewerChildAdapter adapter = new PostViewerChildAdapter();
|
||||||
final ViewerPostModel firstPost = items[0];
|
// binding.mediaViewPager.setAdapter(adapter);
|
||||||
setPostInfo(firstPost, mentionClickListener);
|
// final PostChild firstPost = items.get(0);
|
||||||
setMediaItems(items, adapter);
|
// setPostInfo(firstPost, mentionClickListener);
|
||||||
setupListeners(wrapper,
|
// setMediaItems(items, adapter);
|
||||||
position,
|
// setupListeners(wrapper,
|
||||||
clickListener,
|
// position,
|
||||||
longClickListener,
|
// clickListener,
|
||||||
mentionClickListener,
|
// longClickListener,
|
||||||
firstPost.getLocation());
|
// mentionClickListener,
|
||||||
}
|
// firstPost.getLocation());
|
||||||
|
// }
|
||||||
private void setPostInfo(final ViewerPostModel firstPost,
|
//
|
||||||
final MentionClickListener mentionClickListener) {
|
// private void setPostInfo(final PostChild firstPost,
|
||||||
final ProfileModel profileModel = firstPost.getProfileModel();
|
// final MentionClickListener mentionClickListener) {
|
||||||
if (profileModel == null) return;
|
// final ProfileModel profileModel = firstPost.getProfileModel();
|
||||||
binding.topPanel.title.setText(profileModel.getUsername());
|
// if (profileModel == null) return;
|
||||||
final String locationName = firstPost.getLocationName();
|
// binding.topPanel.title.setText(profileModel.getUsername());
|
||||||
if (!TextUtils.isEmpty(locationName)) {
|
// final String locationName = firstPost.getLocationName();
|
||||||
binding.topPanel.location.setVisibility(View.VISIBLE);
|
// if (!TextUtils.isEmpty(locationName)) {
|
||||||
binding.topPanel.location.setText(locationName);
|
// binding.topPanel.location.setVisibility(View.VISIBLE);
|
||||||
} else binding.topPanel.location.setVisibility(View.GONE);
|
// binding.topPanel.location.setText(locationName);
|
||||||
binding.topPanel.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
|
// } else binding.topPanel.location.setVisibility(View.GONE);
|
||||||
binding.bottomPanel.commentsCount.setText(String.valueOf(firstPost.getCommentsCount()));
|
// binding.topPanel.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
|
||||||
final CharSequence postCaption = firstPost.getPostCaption();
|
// binding.bottomPanel.commentsCount.setText(String.valueOf(firstPost.getCommentsCount()));
|
||||||
if (TextUtils.hasMentions(postCaption)) {
|
// final CharSequence postCaption = firstPost.getPostCaption();
|
||||||
binding.bottomPanel.viewerCaption.setMentionClickListener(mentionClickListener);
|
// if (TextUtils.hasMentions(postCaption)) {
|
||||||
binding.bottomPanel.viewerCaption
|
// binding.bottomPanel.viewerCaption.setMentionClickListener(mentionClickListener);
|
||||||
.setText(TextUtils.getMentionText(postCaption), TextView.BufferType.SPANNABLE);
|
// binding.bottomPanel.viewerCaption
|
||||||
} else {
|
// .setText(TextUtils.getMentionText(postCaption), TextView.BufferType.SPANNABLE);
|
||||||
binding.bottomPanel.viewerCaption.setMentionClickListener(null);
|
// } else {
|
||||||
binding.bottomPanel.viewerCaption.setText(postCaption);
|
// binding.bottomPanel.viewerCaption.setMentionClickListener(null);
|
||||||
}
|
// binding.bottomPanel.viewerCaption.setText(postCaption);
|
||||||
binding.bottomPanel.tvPostDate.setText(firstPost.getPostDate());
|
// }
|
||||||
setupLikes(firstPost);
|
// binding.bottomPanel.tvPostDate.setText(firstPost.getPostDate());
|
||||||
setupSave(firstPost);
|
// setupLikes(firstPost);
|
||||||
}
|
// setupSave(firstPost);
|
||||||
|
// }
|
||||||
private void setupLikes(final ViewerPostModel firstPost) {
|
//
|
||||||
final boolean liked = firstPost.getLike();
|
// private void setupLikes(final ViewerPostModel firstPost) {
|
||||||
final long likeCount = firstPost.getLikes();
|
// final boolean liked = firstPost.getLike();
|
||||||
final Resources resources = itemView.getContext().getResources();
|
// final long likeCount = firstPost.getLikes();
|
||||||
if (liked) {
|
// final Resources resources = itemView.getContext().getResources();
|
||||||
final String unlikeString = resources.getString(R.string.unlike, String.valueOf(likeCount));
|
// if (liked) {
|
||||||
binding.btnLike.setText(unlikeString);
|
// final String unlikeString = resources.getString(R.string.unlike, String.valueOf(likeCount));
|
||||||
ViewCompat.setBackgroundTintList(binding.btnLike,
|
// binding.btnLike.setText(unlikeString);
|
||||||
ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_pink_background)));
|
// ViewCompat.setBackgroundTintList(binding.btnLike,
|
||||||
} else {
|
// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_pink_background)));
|
||||||
final String likeString = resources.getString(R.string.like, String.valueOf(likeCount));
|
// } else {
|
||||||
binding.btnLike.setText(likeString);
|
// final String likeString = resources.getString(R.string.like, String.valueOf(likeCount));
|
||||||
ViewCompat.setBackgroundTintList(binding.btnLike,
|
// binding.btnLike.setText(likeString);
|
||||||
ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightpink_background)));
|
// ViewCompat.setBackgroundTintList(binding.btnLike,
|
||||||
}
|
// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightpink_background)));
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
private void setupSave(final ViewerPostModel firstPost) {
|
//
|
||||||
final boolean saved = firstPost.getBookmark();
|
// private void setupSave(final ViewerPostModel firstPost) {
|
||||||
if (saved) {
|
// final boolean saved = firstPost.isSaved();
|
||||||
binding.btnBookmark.setText(R.string.unbookmark);
|
// if (saved) {
|
||||||
ViewCompat.setBackgroundTintList(binding.btnBookmark,
|
// binding.btnBookmark.setText(R.string.unbookmark);
|
||||||
ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_orange_background)));
|
// ViewCompat.setBackgroundTintList(binding.btnBookmark,
|
||||||
} else {
|
// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_orange_background)));
|
||||||
binding.btnBookmark.setText(R.string.bookmark);
|
// } else {
|
||||||
ViewCompat.setBackgroundTintList(
|
// binding.btnBookmark.setText(R.string.bookmark);
|
||||||
binding.btnBookmark,
|
// ViewCompat.setBackgroundTintList(
|
||||||
ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightorange_background)));
|
// binding.btnBookmark,
|
||||||
}
|
// ColorStateList.valueOf(ContextCompat.getColor(itemView.getContext(), R.color.btn_lightorange_background)));
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
private void setupListeners(final ViewerPostModelWrapper wrapper,
|
//
|
||||||
final int position,
|
// private void setupListeners(final ViewerPostModelWrapper wrapper,
|
||||||
final OnPostViewChildViewClickListener clickListener,
|
// final int position,
|
||||||
final OnPostCaptionLongClickListener longClickListener,
|
// final OnPostViewChildViewClickListener clickListener,
|
||||||
final MentionClickListener mentionClickListener,
|
// final OnPostCaptionLongClickListener longClickListener,
|
||||||
final String location) {
|
// final MentionClickListener mentionClickListener,
|
||||||
final View.OnClickListener onClickListener = v -> clickListener
|
// final String location) {
|
||||||
.onClick(v, wrapper, position, currentChildPosition);
|
// final View.OnClickListener onClickListener = v -> clickListener
|
||||||
binding.bottomPanel.btnComments.setOnClickListener(onClickListener);
|
// .onClick(v, wrapper, position, currentChildPosition);
|
||||||
binding.topPanel.title.setOnClickListener(onClickListener);
|
// binding.bottomPanel.btnComments.setOnClickListener(onClickListener);
|
||||||
binding.topPanel.ivProfilePic.setOnClickListener(onClickListener);
|
// binding.topPanel.title.setOnClickListener(onClickListener);
|
||||||
binding.bottomPanel.btnDownload.setOnClickListener(onClickListener);
|
// binding.topPanel.ivProfilePic.setOnClickListener(onClickListener);
|
||||||
binding.bottomPanel.viewerCaption.setOnClickListener(onClickListener);
|
// binding.bottomPanel.btnDownload.setOnClickListener(onClickListener);
|
||||||
binding.btnLike.setOnClickListener(onClickListener);
|
// binding.bottomPanel.viewerCaption.setOnClickListener(onClickListener);
|
||||||
binding.btnBookmark.setOnClickListener(onClickListener);
|
// binding.btnLike.setOnClickListener(onClickListener);
|
||||||
binding.bottomPanel.viewerCaption.setOnLongClickListener(v -> {
|
// binding.btnBookmark.setOnClickListener(onClickListener);
|
||||||
longClickListener.onLongClick(binding.bottomPanel.viewerCaption.getText().toString());
|
// binding.bottomPanel.viewerCaption.setOnLongClickListener(v -> {
|
||||||
return true;
|
// longClickListener.onLongClick(binding.bottomPanel.viewerCaption.getText().toString());
|
||||||
});
|
// return true;
|
||||||
if (!TextUtils.isEmpty(location)) {
|
// });
|
||||||
binding.topPanel.location.setOnClickListener(v -> mentionClickListener
|
// if (!TextUtils.isEmpty(location)) {
|
||||||
.onClick(binding.topPanel.location, location, false, true));
|
// binding.topPanel.location.setOnClickListener(v -> mentionClickListener
|
||||||
}
|
// .onClick(binding.topPanel.location, location, false, true));
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
private void setMediaItems(final ViewerPostModel[] items,
|
//
|
||||||
final PostViewerChildAdapter adapter) {
|
// private void setMediaItems(final List<ViewerPostModel> items,
|
||||||
final List<ViewerPostModel> filteredList = new ArrayList<>();
|
// final PostViewerChildAdapter adapter) {
|
||||||
for (final ViewerPostModel model : items) {
|
// final List<ViewerPostModel> filteredList = new ArrayList<>();
|
||||||
final MediaItemType itemType = model.getItemType();
|
// for (final ViewerPostModel model : items) {
|
||||||
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO || itemType == MediaItemType.MEDIA_TYPE_IMAGE) {
|
// final MediaItemType itemType = model.getItemType();
|
||||||
filteredList.add(model);
|
// if (itemType == MediaItemType.MEDIA_TYPE_VIDEO || itemType == MediaItemType.MEDIA_TYPE_IMAGE) {
|
||||||
}
|
// filteredList.add(model);
|
||||||
}
|
// }
|
||||||
binding.mediaCounter.setVisibility(filteredList.size() > 1 ? View.VISIBLE : View.GONE);
|
// }
|
||||||
final String counter = "1/" + filteredList.size();
|
// binding.mediaCounter.setVisibility(filteredList.size() > 1 ? View.VISIBLE : View.GONE);
|
||||||
binding.mediaCounter.setText(counter);
|
// final String counter = "1/" + filteredList.size();
|
||||||
adapter.submitList(filteredList);
|
// binding.mediaCounter.setText(counter);
|
||||||
binding.mediaViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
// adapter.submitList(filteredList);
|
||||||
@Override
|
// binding.mediaViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||||
public void onPageSelected(final int position) {
|
// @Override
|
||||||
if (filteredList.size() <= 0 || position >= filteredList.size()) return;
|
// public void onPageSelected(final int position) {
|
||||||
currentChildPosition = position;
|
// if (filteredList.size() <= 0 || position >= filteredList.size()) return;
|
||||||
final String counter = (position + 1) + "/" + filteredList.size();
|
// currentChildPosition = position;
|
||||||
binding.mediaCounter.setText(counter);
|
// final String counter = (position + 1) + "/" + filteredList.size();
|
||||||
final ViewerPostModel viewerPostModel = filteredList.get(position);
|
// binding.mediaCounter.setText(counter);
|
||||||
if (viewerPostModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
|
// final ViewerPostModel viewerPostModel = filteredList.get(position);
|
||||||
setVideoDetails(viewerPostModel);
|
// if (viewerPostModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
|
||||||
setVolumeListener(position);
|
// setVideoDetails(viewerPostModel);
|
||||||
return;
|
// setVolumeListener(position);
|
||||||
}
|
// return;
|
||||||
setImageDetails();
|
// }
|
||||||
}
|
// setImageDetails();
|
||||||
});
|
// }
|
||||||
}
|
// });
|
||||||
|
// }
|
||||||
private void setVolumeListener(final int position) {
|
//
|
||||||
binding.bottomPanel.btnMute.setOnClickListener(v -> {
|
// private void setVolumeListener(final int position) {
|
||||||
try {
|
// binding.bottomPanel.btnMute.setOnClickListener(v -> {
|
||||||
final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager
|
// try {
|
||||||
.getChildAt(0)).findViewHolderForAdapterPosition(position);
|
// final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager
|
||||||
if (viewHolder != null) {
|
// .getChildAt(0)).findViewHolderForAdapterPosition(position);
|
||||||
final View itemView = viewHolder.itemView;
|
// if (viewHolder != null) {
|
||||||
if (itemView instanceof PlayerView) {
|
// final View itemView = viewHolder.itemView;
|
||||||
final SimpleExoPlayer player = (SimpleExoPlayer) ((PlayerView) itemView)
|
// if (itemView instanceof PlayerView) {
|
||||||
.getPlayer();
|
// final SimpleExoPlayer player = (SimpleExoPlayer) ((PlayerView) itemView)
|
||||||
if (player == null) return;
|
// .getPlayer();
|
||||||
final float vol = player.getVolume() == 0f ? 1f : 0f;
|
// if (player == null) return;
|
||||||
player.setVolume(vol);
|
// final float vol = player.getVolume() == 0f ? 1f : 0f;
|
||||||
binding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24
|
// player.setVolume(vol);
|
||||||
: R.drawable.ic_volume_off_24);
|
// binding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24
|
||||||
Utils.sessionVolumeFull = vol == 1f;
|
// : R.drawable.ic_volume_off_24);
|
||||||
}
|
// Utils.sessionVolumeFull = vol == 1f;
|
||||||
}
|
// }
|
||||||
} catch (Exception e) {
|
// }
|
||||||
Log.e(TAG, "Error", e);
|
// } catch (Exception e) {
|
||||||
}
|
// Log.e(TAG, "Error", e);
|
||||||
});
|
// }
|
||||||
}
|
// });
|
||||||
|
// }
|
||||||
private void setImageDetails() {
|
//
|
||||||
binding.bottomPanel.btnMute.setVisibility(View.GONE);
|
// private void setImageDetails() {
|
||||||
binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE);
|
// binding.bottomPanel.btnMute.setVisibility(View.GONE);
|
||||||
}
|
// binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE);
|
||||||
|
// }
|
||||||
private void setVideoDetails(final ViewerPostModel viewerPostModel) {
|
//
|
||||||
binding.bottomPanel.btnMute.setVisibility(View.VISIBLE);
|
// private void setVideoDetails(final ViewerPostModel viewerPostModel) {
|
||||||
final long videoViews = viewerPostModel.getVideoViews();
|
// binding.bottomPanel.btnMute.setVisibility(View.VISIBLE);
|
||||||
if (videoViews < 0) {
|
// final long videoViews = viewerPostModel.getVideoViews();
|
||||||
binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE);
|
// if (videoViews < 0) {
|
||||||
return;
|
// binding.bottomPanel.videoViewsContainer.setVisibility(View.GONE);
|
||||||
}
|
// return;
|
||||||
binding.bottomPanel.tvVideoViews.setText(String.valueOf(videoViews));
|
// }
|
||||||
binding.bottomPanel.videoViewsContainer.setVisibility(View.VISIBLE);
|
// binding.bottomPanel.tvVideoViews.setText(String.valueOf(videoViews));
|
||||||
}
|
// binding.bottomPanel.videoViewsContainer.setVisibility(View.VISIBLE);
|
||||||
|
// }
|
||||||
public void stopPlayingVideo() {
|
//
|
||||||
try {
|
// public void stopPlayingVideo() {
|
||||||
final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager
|
// try {
|
||||||
.getChildAt(0)).findViewHolderForAdapterPosition(currentChildPosition);
|
// final RecyclerView.ViewHolder viewHolder = ((RecyclerView) binding.mediaViewPager
|
||||||
if (viewHolder != null) {
|
// .getChildAt(0)).findViewHolderForAdapterPosition(currentChildPosition);
|
||||||
final View itemView = viewHolder.itemView;
|
// if (viewHolder != null) {
|
||||||
if (itemView instanceof PlayerView) {
|
// final View itemView = viewHolder.itemView;
|
||||||
final Player player = ((PlayerView) itemView).getPlayer();
|
// if (itemView instanceof PlayerView) {
|
||||||
if (player != null) {
|
// final Player player = ((PlayerView) itemView).getPlayer();
|
||||||
player.setPlayWhenReady(false);
|
// if (player != null) {
|
||||||
}
|
// player.setPlayWhenReady(false);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} catch (Exception e) {
|
// }
|
||||||
Log.e(TAG, "Error", e);
|
// } catch (Exception e) {
|
||||||
}
|
// Log.e(TAG, "Error", e);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package awais.instagrabber.adapters.viewholder;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.SliderItemsAdapter;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
|
|
||||||
|
public abstract class SliderItemViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private static final String TAG = "FeedSliderItemViewHolder";
|
||||||
|
|
||||||
|
public SliderItemViewHolder(@NonNull final View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void bind(final PostChild model,
|
||||||
|
final int position,
|
||||||
|
final SliderItemsAdapter.SliderCallback sliderCallback);
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
package awais.instagrabber.adapters.viewholder;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
|
import com.facebook.drawee.controller.BaseControllerListener;
|
||||||
|
import com.facebook.imagepipeline.image.ImageInfo;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.SliderItemsAdapter;
|
||||||
|
import awais.instagrabber.customviews.VerticalDragHelper;
|
||||||
|
import awais.instagrabber.customviews.drawee.AnimatedZoomableController;
|
||||||
|
import awais.instagrabber.databinding.ItemSliderPhotoBinding;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
|
|
||||||
|
public class SliderPhotoViewHolder extends SliderItemViewHolder {
|
||||||
|
private static final String TAG = "FeedSliderPhotoViewHolder";
|
||||||
|
|
||||||
|
private final ItemSliderPhotoBinding binding;
|
||||||
|
private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener;
|
||||||
|
|
||||||
|
public SliderPhotoViewHolder(@NonNull final ItemSliderPhotoBinding binding,
|
||||||
|
final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
this.onVerticalDragListener = onVerticalDragListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(@NonNull final PostChild model,
|
||||||
|
final int position,
|
||||||
|
final SliderItemsAdapter.SliderCallback sliderCallback) {
|
||||||
|
final ImageRequest requestBuilder = ImageRequestBuilder
|
||||||
|
.newBuilderWithSource(Uri.parse(model.getDisplayUrl()))
|
||||||
|
.setLocalThumbnailPreviewsEnabled(true)
|
||||||
|
.build();
|
||||||
|
binding.getRoot()
|
||||||
|
.setController(Fresco.newDraweeControllerBuilder()
|
||||||
|
.setImageRequest(requestBuilder)
|
||||||
|
.setControllerListener(new BaseControllerListener<ImageInfo>() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(final String id, final Throwable throwable) {
|
||||||
|
if (sliderCallback != null) {
|
||||||
|
sliderCallback.onThumbnailLoaded(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinalImageSet(final String id,
|
||||||
|
final ImageInfo imageInfo,
|
||||||
|
final Animatable animatable) {
|
||||||
|
if (sliderCallback != null) {
|
||||||
|
sliderCallback.onThumbnailLoaded(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setLowResImageRequest(ImageRequest.fromUri(model.getThumbnailUrl()))
|
||||||
|
.build());
|
||||||
|
binding.getRoot().setOnClickListener(v -> {
|
||||||
|
if (sliderCallback != null) {
|
||||||
|
sliderCallback.onItemClicked(position);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
final AnimatedZoomableController zoomableController = AnimatedZoomableController.newInstance();
|
||||||
|
zoomableController.setMaxScaleFactor(3f);
|
||||||
|
binding.getRoot().setZoomableController(zoomableController);
|
||||||
|
if (onVerticalDragListener != null) {
|
||||||
|
binding.getRoot().setOnVerticalDragListener(onVerticalDragListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void setDimensions(final FeedModel feedModel, final int spanCount, final boolean animate) {
|
||||||
|
// final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams();
|
||||||
|
// final int deviceWidth = Utils.displayMetrics.widthPixels;
|
||||||
|
// final int spanWidth = deviceWidth / spanCount;
|
||||||
|
// final int spanHeight = NumberUtils.getResultingHeight(spanWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
|
||||||
|
// final int width = spanWidth == 0 ? deviceWidth : spanWidth;
|
||||||
|
// final int height = spanHeight == 0 ? deviceWidth + 1 : spanHeight;
|
||||||
|
// if (animate) {
|
||||||
|
// Animation animation = AnimationUtils.expand(
|
||||||
|
// binding.imageViewer,
|
||||||
|
// layoutParams.width,
|
||||||
|
// layoutParams.height,
|
||||||
|
// width,
|
||||||
|
// height,
|
||||||
|
// new Animation.AnimationListener() {
|
||||||
|
// @Override
|
||||||
|
// public void onAnimationStart(final Animation animation) {
|
||||||
|
// showOrHideDetails(spanCount);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onAnimationEnd(final Animation animation) {
|
||||||
|
// // showOrHideDetails(spanCount);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onAnimationRepeat(final Animation animation) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// binding.imageViewer.startAnimation(animation);
|
||||||
|
// } else {
|
||||||
|
// layoutParams.width = width;
|
||||||
|
// layoutParams.height = height;
|
||||||
|
// binding.imageViewer.requestLayout();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void showOrHideDetails(final int spanCount) {
|
||||||
|
// if (spanCount == 1) {
|
||||||
|
// binding.itemFeedTop.getRoot().setVisibility(View.VISIBLE);
|
||||||
|
// binding.itemFeedBottom.getRoot().setVisibility(View.VISIBLE);
|
||||||
|
// } else {
|
||||||
|
// binding.itemFeedTop.getRoot().setVisibility(View.GONE);
|
||||||
|
// binding.itemFeedBottom.getRoot().setVisibility(View.GONE);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,185 @@
|
|||||||
|
package awais.instagrabber.adapters.viewholder;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.SliderItemsAdapter;
|
||||||
|
import awais.instagrabber.customviews.VerticalDragHelper;
|
||||||
|
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
|
||||||
|
import awais.instagrabber.customviews.VideoPlayerViewHelper;
|
||||||
|
import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.NumberUtils;
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
|
public class SliderVideoViewHolder extends SliderItemViewHolder {
|
||||||
|
private static final String TAG = "SliderVideoViewHolder";
|
||||||
|
|
||||||
|
private final LayoutVideoPlayerWithThumbnailBinding binding;
|
||||||
|
private final LayoutExoCustomControlsBinding controlsBinding;
|
||||||
|
private final boolean loadVideoOnItemClick;
|
||||||
|
private VideoPlayerViewHelper videoPlayerViewHelper;
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
public SliderVideoViewHolder(@NonNull final LayoutVideoPlayerWithThumbnailBinding binding,
|
||||||
|
final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener,
|
||||||
|
final LayoutExoCustomControlsBinding controlsBinding,
|
||||||
|
final boolean loadVideoOnItemClick) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
this.controlsBinding = controlsBinding;
|
||||||
|
this.loadVideoOnItemClick = loadVideoOnItemClick;
|
||||||
|
if (onVerticalDragListener != null) {
|
||||||
|
final VerticalDragHelper thumbnailVerticalDragHelper = new VerticalDragHelper(binding.thumbnailParent);
|
||||||
|
final VerticalDragHelper playerVerticalDragHelper = new VerticalDragHelper(binding.playerView);
|
||||||
|
thumbnailVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
|
||||||
|
playerVerticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
|
||||||
|
binding.thumbnailParent.setOnTouchListener((v, event) -> {
|
||||||
|
final boolean onDragTouch = thumbnailVerticalDragHelper.onDragTouch(event);
|
||||||
|
if (onDragTouch) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return thumbnailVerticalDragHelper.onGestureTouchEvent(event);
|
||||||
|
});
|
||||||
|
binding.playerView.setOnTouchListener((v, event) -> {
|
||||||
|
final boolean onDragTouch = playerVerticalDragHelper.onDragTouch(event);
|
||||||
|
if (onDragTouch) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return playerVerticalDragHelper.onGestureTouchEvent(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(@NonNull final PostChild model,
|
||||||
|
final int position,
|
||||||
|
final SliderItemsAdapter.SliderCallback sliderCallback) {
|
||||||
|
final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
|
||||||
|
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThumbnailClick() {
|
||||||
|
if (sliderCallback != null) {
|
||||||
|
sliderCallback.onItemClicked(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThumbnailLoaded() {
|
||||||
|
if (sliderCallback != null) {
|
||||||
|
sliderCallback.onThumbnailLoaded(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerViewLoaded() {
|
||||||
|
// binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
|
||||||
|
final ViewGroup.LayoutParams layoutParams = binding.playerView.getLayoutParams();
|
||||||
|
final int requiredWidth = Utils.displayMetrics.widthPixels;
|
||||||
|
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, model.getHeight(), model.getWidth());
|
||||||
|
layoutParams.width = requiredWidth;
|
||||||
|
layoutParams.height = resultingHeight;
|
||||||
|
binding.playerView.requestLayout();
|
||||||
|
// setMuteIcon(vol == 0f && Utils.sessionVolumeFull ? 1f : vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay() {
|
||||||
|
if (sliderCallback != null) {
|
||||||
|
sliderCallback.onPlayerPlay(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
if (sliderCallback != null) {
|
||||||
|
sliderCallback.onPlayerPause(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final float aspectRatio = (float) model.getWidth() / model.getHeight();
|
||||||
|
videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
|
||||||
|
binding,
|
||||||
|
model.getDisplayUrl(),
|
||||||
|
vol,
|
||||||
|
aspectRatio,
|
||||||
|
model.getThumbnailUrl(),
|
||||||
|
loadVideoOnItemClick,
|
||||||
|
controlsBinding,
|
||||||
|
videoPlayerCallback);
|
||||||
|
// binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
|
||||||
|
// final float newVol = videoPlayerViewHelper.toggleMute();
|
||||||
|
// setMuteIcon(newVol);
|
||||||
|
// Utils.sessionVolumeFull = newVol == 1f;
|
||||||
|
// });
|
||||||
|
binding.playerView.setOnClickListener(v -> {
|
||||||
|
if (sliderCallback != null) {
|
||||||
|
sliderCallback.onItemClicked(position);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
if (videoPlayerViewHelper == null) return;
|
||||||
|
videoPlayerViewHelper.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releasePlayer() {
|
||||||
|
if (videoPlayerViewHelper == null) return;
|
||||||
|
videoPlayerViewHelper.releasePlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void setDimensions(final FeedModel feedModel, final int spanCount, final boolean animate) {
|
||||||
|
// final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams();
|
||||||
|
// final int deviceWidth = Utils.displayMetrics.widthPixels;
|
||||||
|
// final int spanWidth = deviceWidth / spanCount;
|
||||||
|
// final int spanHeight = NumberUtils.getResultingHeight(spanWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
|
||||||
|
// final int width = spanWidth == 0 ? deviceWidth : spanWidth;
|
||||||
|
// final int height = spanHeight == 0 ? deviceWidth + 1 : spanHeight;
|
||||||
|
// if (animate) {
|
||||||
|
// Animation animation = AnimationUtils.expand(
|
||||||
|
// binding.imageViewer,
|
||||||
|
// layoutParams.width,
|
||||||
|
// layoutParams.height,
|
||||||
|
// width,
|
||||||
|
// height,
|
||||||
|
// new Animation.AnimationListener() {
|
||||||
|
// @Override
|
||||||
|
// public void onAnimationStart(final Animation animation) {
|
||||||
|
// showOrHideDetails(spanCount);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onAnimationEnd(final Animation animation) {
|
||||||
|
// // showOrHideDetails(spanCount);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onAnimationRepeat(final Animation animation) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// binding.imageViewer.startAnimation(animation);
|
||||||
|
// } else {
|
||||||
|
// layoutParams.width = width;
|
||||||
|
// layoutParams.height = height;
|
||||||
|
// binding.imageViewer.requestLayout();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void showOrHideDetails(final int spanCount) {
|
||||||
|
// if (spanCount == 1) {
|
||||||
|
// binding.itemFeedTop.getRoot().setVisibility(View.VISIBLE);
|
||||||
|
// binding.itemFeedBottom.getRoot().setVisibility(View.VISIBLE);
|
||||||
|
// } else {
|
||||||
|
// binding.itemFeedTop.getRoot().setVisibility(View.GONE);
|
||||||
|
// binding.itemFeedBottom.getRoot().setVisibility(View.GONE);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package awais.instagrabber.adapters.viewholder;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.palette.graphics.Palette;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.facebook.common.executors.CallerThreadExecutor;
|
||||||
|
import com.facebook.common.references.CloseableReference;
|
||||||
|
import com.facebook.datasource.DataSource;
|
||||||
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
|
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||||
|
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
||||||
|
import com.facebook.imagepipeline.image.CloseableImage;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.adapters.DiscoverTopicsAdapter;
|
||||||
|
import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
|
||||||
|
import awais.instagrabber.models.TopicCluster;
|
||||||
|
|
||||||
|
public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private final ItemDiscoverTopicBinding binding;
|
||||||
|
private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener;
|
||||||
|
|
||||||
|
public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding,
|
||||||
|
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
this.onTopicClickListener = onTopicClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(final TopicCluster topicCluster) {
|
||||||
|
if (topicCluster == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final AtomicInteger titleColor = new AtomicInteger(-1);
|
||||||
|
final AtomicInteger backgroundColor = new AtomicInteger(-1);
|
||||||
|
if (onTopicClickListener != null) {
|
||||||
|
itemView.setOnClickListener(v -> onTopicClickListener.onTopicClick(
|
||||||
|
topicCluster,
|
||||||
|
binding.getRoot(),
|
||||||
|
binding.cover,
|
||||||
|
binding.title,
|
||||||
|
titleColor.get(),
|
||||||
|
backgroundColor.get()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// binding.title.setTransitionName("title-" + topicCluster.getId());
|
||||||
|
binding.cover.setTransitionName("cover-" + topicCluster.getId());
|
||||||
|
final ImageRequest imageRequest = ImageRequestBuilder
|
||||||
|
.newBuilderWithSource(Uri.parse(topicCluster.getCoverMedia().getDisplayUrl()))
|
||||||
|
.build();
|
||||||
|
final ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||||
|
final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline
|
||||||
|
.fetchDecodedImage(imageRequest, CallerThreadExecutor.getInstance());
|
||||||
|
dataSource.subscribe(new BaseBitmapDataSubscriber() {
|
||||||
|
@Override
|
||||||
|
public void onNewResultImpl(@Nullable Bitmap bitmap) {
|
||||||
|
if (dataSource.isFinished()) {
|
||||||
|
dataSource.close();
|
||||||
|
}
|
||||||
|
if (bitmap != null) {
|
||||||
|
Palette.from(bitmap).generate(p -> {
|
||||||
|
final Palette.Swatch swatch = p.getDominantSwatch();
|
||||||
|
final Resources resources = itemView.getResources();
|
||||||
|
int titleTextColor = resources.getColor(R.color.white);
|
||||||
|
if (swatch != null) {
|
||||||
|
backgroundColor.set(swatch.getRgb());
|
||||||
|
GradientDrawable gd = new GradientDrawable(
|
||||||
|
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||||
|
new int[]{Color.TRANSPARENT, backgroundColor.get()});
|
||||||
|
titleTextColor = swatch.getTitleTextColor();
|
||||||
|
binding.background.setBackground(gd);
|
||||||
|
}
|
||||||
|
titleColor.set(titleTextColor);
|
||||||
|
binding.title.setTextColor(titleTextColor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailureImpl(@NonNull DataSource dataSource) {
|
||||||
|
dataSource.close();
|
||||||
|
}
|
||||||
|
}, CallerThreadExecutor.getInstance());
|
||||||
|
binding.cover.setImageRequest(imageRequest);
|
||||||
|
binding.title.setText(topicCluster.getTitle());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
package awais.instagrabber.adapters.viewholder.comments;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
|
||||||
|
import awais.instagrabber.databinding.ItemCommentSmallBinding;
|
||||||
|
import awais.instagrabber.models.CommentModel;
|
||||||
|
import awais.instagrabber.models.ProfileModel;
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
|
public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final ItemCommentSmallBinding binding;
|
||||||
|
|
||||||
|
public ChildCommentViewHolder(@NonNull final ItemCommentSmallBinding binding) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(final CommentModel comment,
|
||||||
|
final boolean selected,
|
||||||
|
final CommentCallback commentCallback) {
|
||||||
|
if (comment == null) return;
|
||||||
|
if (commentCallback != null) {
|
||||||
|
itemView.setOnClickListener(v -> commentCallback.onClick(comment));
|
||||||
|
}
|
||||||
|
if (selected) {
|
||||||
|
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_selected));
|
||||||
|
} else {
|
||||||
|
itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
|
||||||
|
}
|
||||||
|
setupCommentText(comment, commentCallback);
|
||||||
|
binding.tvDate.setText(comment.getDateTime());
|
||||||
|
setLiked(comment.getLiked());
|
||||||
|
setLikes((int) comment.getLikes());
|
||||||
|
setUser(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCommentText(final CommentModel comment, final CommentCallback commentCallback) {
|
||||||
|
binding.tvComment.clearOnURLClickListeners();
|
||||||
|
binding.tvComment.clearOnHashtagClickListeners();
|
||||||
|
binding.tvComment.clearOnMentionClickListeners();
|
||||||
|
binding.tvComment.clearOnEmailClickListeners();
|
||||||
|
binding.tvComment.setText(comment.getText());
|
||||||
|
binding.tvComment.addOnHashtagListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText();
|
||||||
|
if (commentCallback == null) return;
|
||||||
|
commentCallback.onHashtagClick(originalText);
|
||||||
|
});
|
||||||
|
binding.tvComment.addOnMentionClickListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText();
|
||||||
|
if (commentCallback == null) return;
|
||||||
|
commentCallback.onMentionClick(originalText);
|
||||||
|
|
||||||
|
});
|
||||||
|
binding.tvComment.addOnEmailClickListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText();
|
||||||
|
if (commentCallback == null) return;
|
||||||
|
commentCallback.onEmailClick(originalText);
|
||||||
|
});
|
||||||
|
binding.tvComment.addOnURLClickListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText();
|
||||||
|
if (commentCallback == null) return;
|
||||||
|
commentCallback.onURLClick(originalText);
|
||||||
|
});
|
||||||
|
binding.tvComment.setOnLongClickListener(v -> {
|
||||||
|
Utils.copyText(itemView.getContext(), comment.getText());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
binding.tvComment.setOnClickListener(v -> commentCallback.onClick(comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUser(final CommentModel comment) {
|
||||||
|
final ProfileModel profileModel = comment.getProfileModel();
|
||||||
|
if (profileModel == null) return;
|
||||||
|
binding.tvUsername.setText(profileModel.getUsername());
|
||||||
|
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
|
||||||
|
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLikes(final int likes) {
|
||||||
|
final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes);
|
||||||
|
binding.tvLikes.setText(likesString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setLiked(final boolean liked) {
|
||||||
|
if (liked) {
|
||||||
|
// container.setBackgroundColor(0x40FF69B4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
package awais.instagrabber.adapters.viewholder.comments;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.adapters.CommentsAdapter.CommentCallback;
|
||||||
|
import awais.instagrabber.databinding.ItemCommentBinding;
|
||||||
|
import awais.instagrabber.models.CommentModel;
|
||||||
|
import awais.instagrabber.models.ProfileModel;
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
|
public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final ItemCommentBinding binding;
|
||||||
|
|
||||||
|
public ParentCommentViewHolder(@NonNull final ItemCommentBinding binding) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(final CommentModel comment,
|
||||||
|
final boolean selected,
|
||||||
|
final CommentCallback commentCallback) {
|
||||||
|
if (comment == null) return;
|
||||||
|
if (commentCallback != null) {
|
||||||
|
itemView.setOnClickListener(v -> commentCallback.onClick(comment));
|
||||||
|
}
|
||||||
|
if (selected) {
|
||||||
|
itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_selected));
|
||||||
|
} else {
|
||||||
|
itemView.setBackgroundColor(itemView.getResources().getColor(android.R.color.transparent));
|
||||||
|
}
|
||||||
|
setupCommentText(comment, commentCallback);
|
||||||
|
binding.tvDate.setText(comment.getDateTime());
|
||||||
|
setLiked(comment.getLiked());
|
||||||
|
setLikes((int) comment.getLikes());
|
||||||
|
setUser(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCommentText(final CommentModel comment, final CommentCallback commentCallback) {
|
||||||
|
binding.tvComment.clearOnURLClickListeners();
|
||||||
|
binding.tvComment.clearOnHashtagClickListeners();
|
||||||
|
binding.tvComment.clearOnMentionClickListeners();
|
||||||
|
binding.tvComment.clearOnEmailClickListeners();
|
||||||
|
binding.tvComment.setText(comment.getText());
|
||||||
|
binding.tvComment.addOnHashtagListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText();
|
||||||
|
if (commentCallback == null) return;
|
||||||
|
commentCallback.onHashtagClick(originalText);
|
||||||
|
});
|
||||||
|
binding.tvComment.addOnMentionClickListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText();
|
||||||
|
if (commentCallback == null) return;
|
||||||
|
commentCallback.onMentionClick(originalText);
|
||||||
|
|
||||||
|
});
|
||||||
|
binding.tvComment.addOnEmailClickListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText();
|
||||||
|
if (commentCallback == null) return;
|
||||||
|
commentCallback.onEmailClick(originalText);
|
||||||
|
});
|
||||||
|
binding.tvComment.addOnURLClickListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText();
|
||||||
|
if (commentCallback == null) return;
|
||||||
|
commentCallback.onURLClick(originalText);
|
||||||
|
});
|
||||||
|
binding.tvComment.setOnLongClickListener(v -> {
|
||||||
|
Utils.copyText(itemView.getContext(), comment.getText());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
binding.tvComment.setOnClickListener(v -> commentCallback.onClick(comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUser(final CommentModel comment) {
|
||||||
|
final ProfileModel profileModel = comment.getProfileModel();
|
||||||
|
if (profileModel == null) return;
|
||||||
|
binding.tvUsername.setText(profileModel.getUsername());
|
||||||
|
binding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
|
||||||
|
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLikes(final int likes) {
|
||||||
|
final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes);
|
||||||
|
binding.tvLikes.setText(likesString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setLiked(final boolean liked) {
|
||||||
|
if (liked) {
|
||||||
|
// container.setBackgroundColor(0x40FF69B4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,64 @@
|
|||||||
package awais.instagrabber.adapters.viewholder.directmessages;
|
package awais.instagrabber.adapters.viewholder.directmessages;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.util.Pair;
|
||||||
|
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
|
import com.facebook.imagepipeline.common.ResizeOptions;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
|
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
|
||||||
import awais.instagrabber.databinding.LayoutDmBaseBinding;
|
import awais.instagrabber.databinding.LayoutDmBaseBinding;
|
||||||
import awais.instagrabber.models.direct_messages.DirectItemModel;
|
import awais.instagrabber.models.direct_messages.DirectItemModel;
|
||||||
|
import awais.instagrabber.utils.NumberUtils;
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
public class DirectMessageAnimatedMediaViewHolder extends DirectMessageItemViewHolder {
|
public class DirectMessageAnimatedMediaViewHolder extends DirectMessageItemViewHolder {
|
||||||
|
|
||||||
private final LayoutDmAnimatedMediaBinding binding;
|
private final LayoutDmAnimatedMediaBinding binding;
|
||||||
|
private final int maxHeight;
|
||||||
|
private final int maxWidth;
|
||||||
|
|
||||||
public DirectMessageAnimatedMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
|
public DirectMessageAnimatedMediaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
|
||||||
@NonNull final LayoutDmAnimatedMediaBinding binding,
|
@NonNull final LayoutDmAnimatedMediaBinding binding,
|
||||||
final View.OnClickListener onClickListener) {
|
final View.OnClickListener onClickListener) {
|
||||||
super(baseBinding, onClickListener);
|
super(baseBinding, onClickListener);
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
|
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
|
||||||
|
maxWidth = (int) (Utils.displayMetrics.widthPixels - Utils.convertDpToPx(64) - getItemMargin());
|
||||||
setItemView(binding.getRoot());
|
setItemView(binding.getRoot());
|
||||||
|
removeElevation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindItem(final DirectItemModel directItemModel) {
|
public void bindItem(final DirectItemModel directItemModel) {
|
||||||
|
final DirectItemModel.DirectItemAnimatedMediaModel animatedMediaModel = directItemModel.getAnimatedMediaModel();
|
||||||
|
final String url = animatedMediaModel.getGifUrl();
|
||||||
|
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
|
||||||
|
animatedMediaModel.getHeight(),
|
||||||
|
animatedMediaModel.getWidth(),
|
||||||
|
maxHeight,
|
||||||
|
maxWidth
|
||||||
|
);
|
||||||
|
binding.ivAnimatedMessage.setVisibility(View.VISIBLE);
|
||||||
|
final ViewGroup.LayoutParams layoutParams = binding.ivAnimatedMessage.getLayoutParams();
|
||||||
|
final int width = widthHeight.first != null ? widthHeight.first : 0;
|
||||||
|
final int height = widthHeight.second != null ? widthHeight.second : 0;
|
||||||
|
layoutParams.width = width;
|
||||||
|
layoutParams.height = height;
|
||||||
|
binding.ivAnimatedMessage.requestLayout();
|
||||||
|
final ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
|
||||||
|
.setResizeOptions(ResizeOptions.forDimensions(width, height))
|
||||||
|
.build();
|
||||||
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
|
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
|
||||||
.setUri(directItemModel.getAnimatedMediaModel().getGifUrl())
|
.setImageRequest(request)
|
||||||
.setAutoPlayAnimations(true)
|
.setAutoPlayAnimations(true)
|
||||||
.build());
|
.build());
|
||||||
binding.ivAnimatedMessage.setVisibility(View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,10 @@ public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolde
|
|||||||
this.binding.messageCard.addView(view);
|
this.binding.messageCard.addView(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getItemMargin() {
|
||||||
|
return itemMargin;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void bindItem(final DirectItemModel directItemModel);
|
public abstract void bindItem(final DirectItemModel directItemModel);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -88,4 +92,8 @@ public abstract class DirectMessageItemViewHolder extends RecyclerView.ViewHolde
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void removeElevation() {
|
||||||
|
binding.messageCard.setCardElevation(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ public class DirectMessageMediaViewHolder extends DirectMessageItemViewHolder {
|
|||||||
super(baseBinding, onClickListener);
|
super(baseBinding, onClickListener);
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
|
maxHeight = itemView.getResources().getDimensionPixelSize(R.dimen.dm_media_img_max_height);
|
||||||
maxWidth = (int) (Utils.displayMetrics.widthPixels * 0.8);
|
maxWidth = (int) (Utils.displayMetrics.widthPixels - Utils.convertDpToPx(64) - getItemMargin());
|
||||||
setItemView(binding.getRoot());
|
setItemView(binding.getRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,92 +1,110 @@
|
|||||||
package awais.instagrabber.adapters.viewholder.feed;
|
package awais.instagrabber.adapters.viewholder.feed;
|
||||||
|
|
||||||
import android.text.SpannableString;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.text.Spanned;
|
import android.transition.TransitionManager;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import awais.instagrabber.customviews.CommentMentionClickSpan;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.customviews.RamboTextView;
|
|
||||||
import awais.instagrabber.databinding.ItemFeedBottomBinding;
|
import awais.instagrabber.databinding.ItemFeedBottomBinding;
|
||||||
import awais.instagrabber.databinding.ItemFeedTopBinding;
|
import awais.instagrabber.databinding.ItemFeedTopBinding;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.models.ProfileModel;
|
import awais.instagrabber.models.ProfileModel;
|
||||||
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
|
||||||
|
import static android.text.TextUtils.TruncateAt.END;
|
||||||
|
|
||||||
public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
|
public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
|
||||||
public static final int MAX_CHARS = 255;
|
public static final int MAX_LINES_COLLAPSED = 5;
|
||||||
private final ItemFeedTopBinding topBinding;
|
private final ItemFeedTopBinding topBinding;
|
||||||
private final ItemFeedBottomBinding bottomBinding;
|
private final ItemFeedBottomBinding bottomBinding;
|
||||||
private final MentionClickListener mentionClickListener;
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
|
||||||
|
|
||||||
public FeedItemViewHolder(@NonNull final View root,
|
public FeedItemViewHolder(@NonNull final View root,
|
||||||
final ItemFeedTopBinding topBinding,
|
final ItemFeedTopBinding topBinding,
|
||||||
final ItemFeedBottomBinding bottomBinding,
|
final ItemFeedBottomBinding bottomBinding,
|
||||||
final MentionClickListener mentionClickListener,
|
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
final View.OnClickListener clickListener,
|
|
||||||
final View.OnLongClickListener longClickListener) {
|
|
||||||
super(root);
|
super(root);
|
||||||
this.topBinding = topBinding;
|
this.topBinding = topBinding;
|
||||||
this.bottomBinding = bottomBinding;
|
this.bottomBinding = bottomBinding;
|
||||||
this.mentionClickListener = mentionClickListener;
|
topBinding.title.setMovementMethod(new LinkMovementMethod());
|
||||||
// topBinding.title.setMovementMethod(new LinkMovementMethod());
|
this.feedItemCallback = feedItemCallback;
|
||||||
bottomBinding.btnComments.setOnClickListener(clickListener);
|
|
||||||
topBinding.viewStoryPost.setOnClickListener(clickListener);
|
|
||||||
topBinding.ivProfilePic.setOnClickListener(clickListener);
|
|
||||||
bottomBinding.btnDownload.setOnClickListener(clickListener);
|
|
||||||
bottomBinding.viewerCaption.setOnClickListener(clickListener);
|
|
||||||
bottomBinding.viewerCaption.setOnLongClickListener(longClickListener);
|
|
||||||
bottomBinding.viewerCaption.setMentionClickListener(mentionClickListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind(final FeedModel feedModel) {
|
public void bind(final FeedModel feedModel) {
|
||||||
if (feedModel == null) {
|
if (feedModel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
topBinding.viewStoryPost.setTag(feedModel);
|
setupProfilePic(feedModel);
|
||||||
topBinding.ivProfilePic.setTag(feedModel);
|
setupLocation(feedModel);
|
||||||
bottomBinding.btnDownload.setTag(feedModel);
|
|
||||||
bottomBinding.viewerCaption.setTag(feedModel);
|
|
||||||
bottomBinding.btnComments.setTag(feedModel);
|
|
||||||
final ProfileModel profileModel = feedModel.getProfileModel();
|
|
||||||
if (profileModel != null) {
|
|
||||||
topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
|
|
||||||
final int titleLen = profileModel.getUsername().length() + 1;
|
|
||||||
final SpannableString spannableString = new SpannableString("@" + profileModel.getUsername());
|
|
||||||
spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
|
|
||||||
topBinding.title.setText(spannableString);
|
|
||||||
topBinding.title.setMentionClickListener(
|
|
||||||
(view, text, isHashtag, isLocation) -> mentionClickListener.onClick(null, profileModel.getUsername(), false, false));
|
|
||||||
}
|
|
||||||
bottomBinding.tvPostDate.setText(feedModel.getPostDate());
|
bottomBinding.tvPostDate.setText(feedModel.getPostDate());
|
||||||
final long commentsCount = feedModel.getCommentsCount();
|
setupComments(feedModel);
|
||||||
bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
|
setupCaption(feedModel);
|
||||||
|
if (feedModel.getItemType() != MediaItemType.MEDIA_TYPE_SLIDER) {
|
||||||
final String locationName = feedModel.getLocationName();
|
bottomBinding.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, -1));
|
||||||
final String locationId = feedModel.getLocationId();
|
|
||||||
setLocation(locationName, locationId);
|
|
||||||
CharSequence postCaption = feedModel.getPostCaption();
|
|
||||||
final boolean captionEmpty = TextUtils.isEmpty(postCaption);
|
|
||||||
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
|
|
||||||
if (!captionEmpty) {
|
|
||||||
if (TextUtils.hasMentions(postCaption)) {
|
|
||||||
postCaption = TextUtils.getMentionText(postCaption);
|
|
||||||
feedModel.setPostCaption(postCaption);
|
|
||||||
bottomBinding.viewerCaption.setText(postCaption, TextView.BufferType.SPANNABLE);
|
|
||||||
} else {
|
|
||||||
bottomBinding.viewerCaption.setText(postCaption);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
expandCollapseTextView(bottomBinding.viewerCaption, feedModel.getPostCaption());
|
|
||||||
bindItem(feedModel);
|
bindItem(feedModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLocation(final String locationName, final String locationId) {
|
private void setupComments(final FeedModel feedModel) {
|
||||||
|
final long commentsCount = feedModel.getCommentsCount();
|
||||||
|
bottomBinding.commentsCount.setText(String.valueOf(commentsCount));
|
||||||
|
bottomBinding.commentsCount.setOnClickListener(v -> feedItemCallback.onCommentsClick(feedModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupProfilePic(final FeedModel feedModel) {
|
||||||
|
final ProfileModel profileModel = feedModel.getProfileModel();
|
||||||
|
if (profileModel != null) {
|
||||||
|
topBinding.ivProfilePic.setOnClickListener(v -> feedItemCallback.onProfilePicClick(feedModel, topBinding.ivProfilePic));
|
||||||
|
topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
|
||||||
|
setupTitle(feedModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTitle(final FeedModel feedModel) {
|
||||||
|
// final int titleLen = profileModel.getUsername().length() + 1;
|
||||||
|
// final SpannableString spannableString = new SpannableString();
|
||||||
|
// spannableString.setSpan(new CommentMentionClickSpan(), 0, titleLen, 0);
|
||||||
|
final ProfileModel profileModel = feedModel.getProfileModel();
|
||||||
|
final String title = "@" + profileModel.getUsername();
|
||||||
|
topBinding.title.setText(title);
|
||||||
|
topBinding.title.setOnClickListener(v -> feedItemCallback.onNameClick(feedModel, topBinding.ivProfilePic));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCaption(final FeedModel feedModel) {
|
||||||
|
bottomBinding.viewerCaption.clearOnMentionClickListeners();
|
||||||
|
bottomBinding.viewerCaption.clearOnHashtagClickListeners();
|
||||||
|
bottomBinding.viewerCaption.clearOnURLClickListeners();
|
||||||
|
bottomBinding.viewerCaption.clearOnEmailClickListeners();
|
||||||
|
final CharSequence postCaption = feedModel.getPostCaption();
|
||||||
|
final boolean captionEmpty = TextUtils.isEmpty(postCaption);
|
||||||
|
bottomBinding.viewerCaption.setVisibility(captionEmpty ? View.GONE : View.VISIBLE);
|
||||||
|
if (captionEmpty) return;
|
||||||
|
bottomBinding.viewerCaption.setText(postCaption);
|
||||||
|
bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
|
||||||
|
bottomBinding.viewerCaption.setEllipsize(END);
|
||||||
|
bottomBinding.viewerCaption.setOnClickListener(v -> bottomBinding.getRoot().post(() -> {
|
||||||
|
TransitionManager.beginDelayedTransition(bottomBinding.getRoot());
|
||||||
|
if (bottomBinding.viewerCaption.getMaxLines() == MAX_LINES_COLLAPSED) {
|
||||||
|
bottomBinding.viewerCaption.setMaxLines(Integer.MAX_VALUE);
|
||||||
|
bottomBinding.viewerCaption.setEllipsize(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bottomBinding.viewerCaption.setMaxLines(MAX_LINES_COLLAPSED);
|
||||||
|
bottomBinding.viewerCaption.setEllipsize(END);
|
||||||
|
}));
|
||||||
|
bottomBinding.viewerCaption.addOnMentionClickListener(autoLinkItem -> feedItemCallback.onMentionClick(autoLinkItem.getOriginalText()));
|
||||||
|
bottomBinding.viewerCaption.addOnHashtagListener(autoLinkItem -> feedItemCallback.onHashtagClick(autoLinkItem.getOriginalText()));
|
||||||
|
bottomBinding.viewerCaption.addOnEmailClickListener(autoLinkItem -> feedItemCallback.onEmailClick(autoLinkItem.getOriginalText()));
|
||||||
|
bottomBinding.viewerCaption.addOnURLClickListener(autoLinkItem -> feedItemCallback.onURLClick(autoLinkItem.getOriginalText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupLocation(final FeedModel feedModel) {
|
||||||
|
final String locationName = feedModel.getLocationName();
|
||||||
if (TextUtils.isEmpty(locationName)) {
|
if (TextUtils.isEmpty(locationName)) {
|
||||||
topBinding.location.setVisibility(View.GONE);
|
topBinding.location.setVisibility(View.GONE);
|
||||||
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
|
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
|
||||||
@ -98,41 +116,9 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
|
topBinding.title.setLayoutParams(new RelativeLayout.LayoutParams(
|
||||||
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
|
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT
|
||||||
));
|
));
|
||||||
topBinding.location.setOnClickListener(v -> mentionClickListener.onClick(topBinding.location, locationId, false, true));
|
topBinding.location.setOnClickListener(v -> feedItemCallback.onLocationClick(feedModel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* expands or collapses {@link RamboTextView} [stg idek why i wrote this documentation]
|
|
||||||
*
|
|
||||||
* @param textView the {@link RamboTextView} view, to expand and collapse
|
|
||||||
* @param caption caption
|
|
||||||
* @return isExpanded
|
|
||||||
*/
|
|
||||||
public static boolean expandCollapseTextView(@NonNull final RamboTextView textView, final CharSequence caption) {
|
|
||||||
if (TextUtils.isEmpty(caption)) return false;
|
|
||||||
|
|
||||||
final TextView.BufferType bufferType = caption instanceof Spanned ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL;
|
|
||||||
|
|
||||||
if (textView.isCaptionExpanded()) {
|
|
||||||
textView.setText(caption, bufferType);
|
|
||||||
textView.setCaptionIsExpanded(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int i = TextUtils.indexOfChar(caption, '\r', 0);
|
|
||||||
if (i == -1) i = TextUtils.indexOfChar(caption, '\n', 0);
|
|
||||||
if (i == -1) i = MAX_CHARS;
|
|
||||||
|
|
||||||
final int captionLen = caption.length();
|
|
||||||
final int minTrim = Math.min(MAX_CHARS, i);
|
|
||||||
if (captionLen <= minTrim) return false;
|
|
||||||
|
|
||||||
if (TextUtils.hasMentions(caption))
|
|
||||||
textView.setText(TextUtils.getMentionText(caption), TextView.BufferType.SPANNABLE);
|
|
||||||
textView.setCaptionIsExpandable(true);
|
|
||||||
textView.setCaptionIsExpanded(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void bindItem(final FeedModel feedModel);
|
public abstract void bindItem(final FeedModel feedModel);
|
||||||
}
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package awais.instagrabber.adapters.viewholder.feed;
|
package awais.instagrabber.adapters.viewholder.feed;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.view.GestureDetector;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
@ -13,25 +14,24 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
|||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
|
import awais.instagrabber.databinding.ItemFeedPhotoBinding;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
|
|
||||||
public class FeedPhotoViewHolder extends FeedItemViewHolder {
|
public class FeedPhotoViewHolder extends FeedItemViewHolder {
|
||||||
private static final String TAG = "FeedPhotoViewHolder";
|
private static final String TAG = "FeedPhotoViewHolder";
|
||||||
|
|
||||||
private final ItemFeedPhotoBinding binding;
|
private final ItemFeedPhotoBinding binding;
|
||||||
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
|
||||||
|
|
||||||
public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,
|
public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,
|
||||||
final MentionClickListener mentionClickListener,
|
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
final View.OnClickListener clickListener,
|
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
|
||||||
final View.OnLongClickListener longClickListener) {
|
|
||||||
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
|
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
|
this.feedItemCallback = feedItemCallback;
|
||||||
binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
|
binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE);
|
||||||
|
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
|
||||||
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
|
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
|
||||||
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
|
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
|
||||||
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||||
@ -44,33 +44,35 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
|
|||||||
if (feedModel == null) {
|
if (feedModel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams();
|
binding.getRoot().post(() -> {
|
||||||
final int requiredWidth = Utils.displayMetrics.widthPixels;
|
setDimensions(feedModel);
|
||||||
layoutParams.width = feedModel.getImageWidth() == 0 ? requiredWidth : feedModel.getImageWidth();
|
final String thumbnailUrl = feedModel.getThumbnailUrl();
|
||||||
layoutParams.height = feedModel.getImageHeight() == 0 ? requiredWidth + 1 : feedModel.getImageHeight();
|
String url = feedModel.getDisplayUrl();
|
||||||
binding.imageViewer.requestLayout();
|
if (TextUtils.isEmpty(url)) url = thumbnailUrl;
|
||||||
final String thumbnailUrl = feedModel.getThumbnailUrl();
|
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
|
||||||
String url = feedModel.getDisplayUrl();
|
// .setLocalThumbnailPreviewsEnabled(true)
|
||||||
if (TextUtils.isEmpty(url)) url = thumbnailUrl;
|
// .setProgressiveRenderingEnabled(true)
|
||||||
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
|
.build();
|
||||||
.setLocalThumbnailPreviewsEnabled(true)
|
binding.imageViewer.setController(Fresco.newDraweeControllerBuilder()
|
||||||
.setProgressiveRenderingEnabled(true)
|
.setImageRequest(requestBuilder)
|
||||||
.build();
|
.setOldController(binding.imageViewer.getController())
|
||||||
binding.imageViewer.setController(Fresco.newDraweeControllerBuilder()
|
.setLowResImageRequest(ImageRequest.fromUri(thumbnailUrl))
|
||||||
.setImageRequest(requestBuilder)
|
.build());
|
||||||
.setOldController(binding.imageViewer.getController())
|
binding.imageViewer.setTapListener(new GestureDetector.SimpleOnGestureListener() {
|
||||||
.setLowResImageRequest(ImageRequest.fromUri(thumbnailUrl))
|
@Override
|
||||||
.build());
|
public boolean onSingleTapConfirmed(final MotionEvent e) {
|
||||||
// binding.imageViewer.setImageURI(url);
|
if (feedItemCallback != null) {
|
||||||
// final RequestBuilder<Bitmap> thumbnailRequestBuilder = glide
|
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.imageViewer);
|
||||||
// .asBitmap()
|
return true;
|
||||||
// .load(thumbnailUrl)
|
}
|
||||||
// .diskCacheStrategy(DiskCacheStrategy.ALL);
|
return false;
|
||||||
// glide.asBitmap()
|
}
|
||||||
// .load(url)
|
});
|
||||||
// .thumbnail(thumbnailRequestBuilder)
|
});
|
||||||
// .diskCacheStrategy(DiskCacheStrategy.ALL)
|
}
|
||||||
// .into(customTarget);
|
|
||||||
|
|
||||||
|
private void setDimensions(final FeedModel feedModel) {
|
||||||
|
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
|
||||||
|
binding.imageViewer.setAspectRatio(aspectRatio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,15 @@
|
|||||||
package awais.instagrabber.adapters.viewholder.feed;
|
package awais.instagrabber.adapters.viewholder.feed;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.ViewSwitcher;
|
import android.widget.ViewSwitcher;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
import androidx.viewpager.widget.PagerAdapter;
|
|
||||||
import androidx.viewpager.widget.ViewPager;
|
|
||||||
|
|
||||||
import com.facebook.drawee.drawable.ScalingUtils;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchy;
|
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
|
||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
|
||||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||||
@ -28,13 +19,16 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
|||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
|
import awais.instagrabber.adapters.SliderCallbackAdapter;
|
||||||
|
import awais.instagrabber.adapters.SliderItemsAdapter;
|
||||||
import awais.instagrabber.databinding.ItemFeedSliderBinding;
|
import awais.instagrabber.databinding.ItemFeedSliderBinding;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
import awais.instagrabber.models.PostChild;
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.NumberUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
@ -44,25 +38,20 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
|
|||||||
private static final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
|
private static final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
|
||||||
|
|
||||||
private final ItemFeedSliderBinding binding;
|
private final ItemFeedSliderBinding binding;
|
||||||
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
|
||||||
private final DefaultDataSourceFactory dataSourceFactory;
|
private final DefaultDataSourceFactory dataSourceFactory;
|
||||||
|
|
||||||
private final PlayerChangeListener playerChangeListener = (position, player) -> {
|
|
||||||
pagerPlayer = player;
|
|
||||||
playerPosition = position;
|
|
||||||
};
|
|
||||||
|
|
||||||
private CacheDataSourceFactory cacheDataSourceFactory;
|
private CacheDataSourceFactory cacheDataSourceFactory;
|
||||||
private SimpleExoPlayer pagerPlayer;
|
private SimpleExoPlayer pagerPlayer;
|
||||||
private int playerPosition = 0;
|
private int playerPosition = 0;
|
||||||
|
|
||||||
public FeedSliderViewHolder(@NonNull final ItemFeedSliderBinding binding,
|
public FeedSliderViewHolder(@NonNull final ItemFeedSliderBinding binding,
|
||||||
final MentionClickListener mentionClickListener,
|
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
final View.OnClickListener clickListener,
|
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
|
||||||
final View.OnLongClickListener longClickListener) {
|
|
||||||
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
|
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
|
this.feedItemCallback = feedItemCallback;
|
||||||
binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
|
binding.itemFeedBottom.tvVideoViews.setVisibility(View.GONE);
|
||||||
|
// binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
|
||||||
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
|
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
|
||||||
layoutParams.height = Utils.displayMetrics.widthPixels + 1;
|
layoutParams.height = Utils.displayMetrics.widthPixels + 1;
|
||||||
binding.mediaList.setLayoutParams(layoutParams);
|
binding.mediaList.setLayoutParams(layoutParams);
|
||||||
@ -76,112 +65,108 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindItem(final FeedModel feedModel) {
|
public void bindItem(final FeedModel feedModel) {
|
||||||
final ViewerPostModel[] sliderItems = feedModel.getSliderItems();
|
final List<PostChild> sliderItems = feedModel.getSliderItems();
|
||||||
final int sliderItemLen = sliderItems != null ? sliderItems.length : 0;
|
final int sliderItemLen = sliderItems != null ? sliderItems.size() : 0;
|
||||||
if (sliderItemLen <= 0) {
|
if (sliderItemLen <= 0) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String text = "1/" + sliderItemLen;
|
final String text = "1/" + sliderItemLen;
|
||||||
binding.mediaCounter.setText(text);
|
binding.mediaCounter.setText(text);
|
||||||
binding.mediaList.setOffscreenPageLimit(Math.min(5, sliderItemLen));
|
binding.mediaList.setOffscreenPageLimit(1);
|
||||||
|
final SliderItemsAdapter adapter = new SliderItemsAdapter(null, null, false, new SliderCallbackAdapter() {
|
||||||
final PagerAdapter adapter = binding.mediaList.getAdapter();
|
|
||||||
if (adapter != null) {
|
|
||||||
final int count = adapter.getCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
adapter.destroyItem(binding.mediaList, i, binding.mediaList.getChildAt(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final ChildMediaItemsAdapter itemsAdapter = new ChildMediaItemsAdapter(sliderItems,
|
|
||||||
cacheDataSourceFactory != null
|
|
||||||
? cacheDataSourceFactory
|
|
||||||
: dataSourceFactory,
|
|
||||||
playerChangeListener);
|
|
||||||
binding.mediaList.setAdapter(itemsAdapter);
|
|
||||||
|
|
||||||
//noinspection deprecation
|
|
||||||
binding.mediaList.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
|
|
||||||
private int prevPos = 0;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(final int position) {
|
public void onItemClicked(final int position) {
|
||||||
ViewerPostModel sliderItem = sliderItems[prevPos];
|
feedItemCallback.onSliderClick(feedModel, position);
|
||||||
if (sliderItem != null) {
|
|
||||||
sliderItem.setSelected(false);
|
|
||||||
if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
|
|
||||||
// stop playing prev video
|
|
||||||
final ViewSwitcher prevChild = (ViewSwitcher) binding.mediaList.getChildAt(prevPos);
|
|
||||||
if (prevChild == null || prevChild.getTag() == null || !(prevChild.getTag() instanceof SimpleExoPlayer)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
((SimpleExoPlayer) prevChild.getTag()).setPlayWhenReady(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sliderItem = sliderItems[position];
|
|
||||||
if (sliderItem == null) return;
|
|
||||||
sliderItem.setSelected(true);
|
|
||||||
final String text = (position + 1) + "/" + sliderItemLen;
|
|
||||||
binding.mediaCounter.setText(text);
|
|
||||||
prevPos = position;
|
|
||||||
if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
|
|
||||||
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
|
|
||||||
if (shouldAutoPlay) {
|
|
||||||
autoPlay(position);
|
|
||||||
}
|
|
||||||
} else binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
binding.mediaList.setAdapter(adapter);
|
||||||
|
binding.mediaList.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(final int position) {
|
||||||
|
if (position >= sliderItemLen) return;
|
||||||
|
final String text = (position + 1) + "/" + sliderItemLen;
|
||||||
|
binding.mediaCounter.setText(text);
|
||||||
|
setDimensions(binding.mediaList, sliderItems.get(position));
|
||||||
|
binding.itemFeedBottom.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, position));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setDimensions(binding.mediaList, sliderItems.get(0));
|
||||||
|
binding.itemFeedBottom.btnDownload.setOnClickListener(v -> feedItemCallback.onDownloadClick(feedModel, 0));
|
||||||
|
adapter.submitList(sliderItems);
|
||||||
|
// final View.OnClickListener muteClickListener = v -> {
|
||||||
|
// final int currentItem = binding.mediaList.getCurrentItem();
|
||||||
|
// if (currentItem < 0 || currentItem >= binding.mediaList.getChildCount()) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// final PostChild sliderItem = sliderItems.get(currentItem);
|
||||||
|
// if (sliderItem.getItemType() != MediaItemType.MEDIA_TYPE_VIDEO) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// final View currentView = binding.mediaList.getChildAt(currentItem);
|
||||||
|
// if (!(currentView instanceof ViewSwitcher)) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// final ViewSwitcher viewSwitcher = (ViewSwitcher) currentView;
|
||||||
|
// final Object tag = viewSwitcher.getTag();
|
||||||
|
// if (!(tag instanceof SimpleExoPlayer)) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// final SimpleExoPlayer player = (SimpleExoPlayer) tag;
|
||||||
|
// final float intVol = player.getVolume() == 0f ? 1f : 0f;
|
||||||
|
// player.setVolume(intVol);
|
||||||
|
// // binding.itemFeedBottom.btnMute.setImageResource(intVol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
|
||||||
|
// // Utils.sessionVolumeFull = intVol == 1f;
|
||||||
|
// };
|
||||||
|
// final PostChild firstItem = sliderItems.get(0);
|
||||||
|
// if (firstItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
|
||||||
|
// binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
|
||||||
|
// }
|
||||||
|
// binding.itemFeedBottom.btnMute.setImageResource(Utils.sessionVolumeFull ? R.drawable.ic_volume_off_24 : R.drawable.ic_volume_up_24);
|
||||||
|
// binding.itemFeedBottom.btnMute.setOnClickListener(muteClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
final View.OnClickListener muteClickListener = v -> {
|
private void setDimensions(final View view, final PostChild model) {
|
||||||
final int currentItem = binding.mediaList.getCurrentItem();
|
final ViewGroup.LayoutParams layoutParams = binding.mediaList.getLayoutParams();
|
||||||
if (currentItem < 0 || currentItem >= binding.mediaList.getChildCount()) {
|
int requiredWidth = layoutParams.width;
|
||||||
return;
|
if (requiredWidth <= 0) {
|
||||||
}
|
final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
|
||||||
final ViewerPostModel sliderItem = sliderItems[currentItem];
|
@Override
|
||||||
if (sliderItem.getItemType() != MediaItemType.MEDIA_TYPE_VIDEO) {
|
public boolean onPreDraw() {
|
||||||
return;
|
view.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||||
}
|
setLayoutParamDimens(binding.mediaList, model);
|
||||||
final View currentView = binding.mediaList.getChildAt(currentItem);
|
return true;
|
||||||
if (!(currentView instanceof ViewSwitcher)) {
|
}
|
||||||
return;
|
};
|
||||||
}
|
view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
|
||||||
final ViewSwitcher viewSwitcher = (ViewSwitcher) currentView;
|
return;
|
||||||
final Object tag = viewSwitcher.getTag();
|
|
||||||
if (!(tag instanceof SimpleExoPlayer)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final SimpleExoPlayer player = (SimpleExoPlayer) tag;
|
|
||||||
final float intVol = player.getVolume() == 0f ? 1f : 0f;
|
|
||||||
player.setVolume(intVol);
|
|
||||||
binding.itemFeedBottom.btnMute.setImageResource(intVol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
|
|
||||||
Utils.sessionVolumeFull = intVol == 1f;
|
|
||||||
};
|
|
||||||
final ViewerPostModel firstItem = sliderItems[0];
|
|
||||||
if (firstItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
|
|
||||||
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
binding.itemFeedBottom.btnMute.setImageResource(Utils.sessionVolumeFull ? R.drawable.ic_volume_off_24 : R.drawable.ic_volume_up_24);
|
setLayoutParamDimens(binding.mediaList, model);
|
||||||
binding.itemFeedBottom.btnMute.setOnClickListener(muteClickListener);
|
}
|
||||||
|
|
||||||
|
private void setLayoutParamDimens(final View view, final PostChild model) {
|
||||||
|
final int requiredWidth = view.getMeasuredWidth();
|
||||||
|
final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||||
|
final int spanHeight = NumberUtils.getResultingHeight(requiredWidth, model.getHeight(), model.getWidth());
|
||||||
|
layoutParams.height = spanHeight == 0 ? requiredWidth + 1 : spanHeight;
|
||||||
|
view.requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void autoPlay(final int position) {
|
private void autoPlay(final int position) {
|
||||||
if (!shouldAutoPlay) {
|
// if (!shouldAutoPlay) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
final ChildMediaItemsAdapter adapter = (ChildMediaItemsAdapter) binding.mediaList.getAdapter();
|
// final ChildMediaItemsAdapter adapter = (ChildMediaItemsAdapter) binding.mediaList.getAdapter();
|
||||||
if (adapter == null) {
|
// if (adapter == null) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
final ViewerPostModel sliderItem = adapter.getItemAtPosition(position);
|
// final ViewerPostModel sliderItem = adapter.getItemAtPosition(position);
|
||||||
if (sliderItem.getItemType() != MediaItemType.MEDIA_TYPE_VIDEO) {
|
// if (sliderItem.getItemType() != MediaItemType.MEDIA_TYPE_VIDEO) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
final ViewSwitcher viewSwitcher = (ViewSwitcher) binding.mediaList.getChildAt(position);
|
// final ViewSwitcher viewSwitcher = (ViewSwitcher) binding.mediaList.getChildAt(position);
|
||||||
loadPlayer(binding.getRoot().getContext(),
|
// loadPlayer(binding.getRoot().getContext(),
|
||||||
position, sliderItem.getDisplayUrl(),
|
// position, sliderItem.getDisplayUrl(),
|
||||||
viewSwitcher,
|
// viewSwitcher,
|
||||||
cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory,
|
// cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory,
|
||||||
playerChangeListener);
|
// playerChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startPlayingVideo() {
|
public void startPlayingVideo() {
|
||||||
@ -200,7 +185,8 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void loadPlayer(final Context context,
|
private static void loadPlayer(final Context context,
|
||||||
final int position, final String displayUrl,
|
final int position,
|
||||||
|
final String displayUrl,
|
||||||
final ViewSwitcher viewSwitcher,
|
final ViewSwitcher viewSwitcher,
|
||||||
final DataSource.Factory factory,
|
final DataSource.Factory factory,
|
||||||
final PlayerChangeListener playerChangeListener) {
|
final PlayerChangeListener playerChangeListener) {
|
||||||
@ -223,119 +209,14 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
|
|||||||
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
|
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
|
||||||
player.setVolume(vol);
|
player.setVolume(vol);
|
||||||
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
|
player.setPlayWhenReady(Utils.settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
|
||||||
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(displayUrl));
|
final MediaItem mediaItem = MediaItem.fromUri(displayUrl);
|
||||||
|
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem);
|
||||||
player.setRepeatMode(Player.REPEAT_MODE_ALL);
|
player.setRepeatMode(Player.REPEAT_MODE_ALL);
|
||||||
player.prepare(mediaSource);
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
player.setVolume(vol);
|
player.setVolume(vol);
|
||||||
playerChangeListener.playerChanged(position, player);
|
playerChangeListener.playerChanged(position, player);
|
||||||
viewSwitcher.setTag(player);
|
viewSwitcher.setTag(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ChildMediaItemsAdapter extends PagerAdapter {
|
|
||||||
// private static final String TAG = "ChildMediaItemsAdapter";
|
|
||||||
|
|
||||||
private final ViewerPostModel[] sliderItems;
|
|
||||||
private final DataSource.Factory factory;
|
|
||||||
private final PlayerChangeListener playerChangeListener;
|
|
||||||
private final ViewGroup.LayoutParams layoutParams;
|
|
||||||
|
|
||||||
private ChildMediaItemsAdapter(final ViewerPostModel[] sliderItems,
|
|
||||||
final DataSource.Factory factory,
|
|
||||||
final PlayerChangeListener playerChangeListener) {
|
|
||||||
this.sliderItems = sliderItems;
|
|
||||||
this.factory = factory;
|
|
||||||
this.playerChangeListener = playerChangeListener;
|
|
||||||
layoutParams = new ViewGroup.LayoutParams(Utils.displayMetrics.widthPixels, Utils.displayMetrics.widthPixels + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Object instantiateItem(@NonNull final ViewGroup container, final int position) {
|
|
||||||
final Context context = container.getContext();
|
|
||||||
final ViewerPostModel sliderItem = sliderItems[position];
|
|
||||||
|
|
||||||
final String displayUrl = sliderItem.getDisplayUrl();
|
|
||||||
if (sliderItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
|
|
||||||
final ViewSwitcher viewSwitcher = createViewSwitcher(context, position, sliderItem.getSliderDisplayUrl(), displayUrl);
|
|
||||||
container.addView(viewSwitcher);
|
|
||||||
return viewSwitcher;
|
|
||||||
}
|
|
||||||
final GenericDraweeHierarchy hierarchy = GenericDraweeHierarchyBuilder.newInstance(container.getResources())
|
|
||||||
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
|
||||||
.build();
|
|
||||||
final SimpleDraweeView photoView = new SimpleDraweeView(context, hierarchy);
|
|
||||||
photoView.setLayoutParams(layoutParams);
|
|
||||||
final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(displayUrl))
|
|
||||||
.setLocalThumbnailPreviewsEnabled(true)
|
|
||||||
.setProgressiveRenderingEnabled(true)
|
|
||||||
.build();
|
|
||||||
photoView.setImageRequest(imageRequest);
|
|
||||||
container.addView(photoView);
|
|
||||||
return photoView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private ViewSwitcher createViewSwitcher(final Context context, final int position, final String sliderDisplayUrl, final String displayUrl) {
|
|
||||||
|
|
||||||
final ViewSwitcher viewSwitcher = new ViewSwitcher(context);
|
|
||||||
viewSwitcher.setLayoutParams(layoutParams);
|
|
||||||
|
|
||||||
final FrameLayout frameLayout = new FrameLayout(context);
|
|
||||||
frameLayout.setLayoutParams(layoutParams);
|
|
||||||
|
|
||||||
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(context.getResources())
|
|
||||||
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
|
||||||
.build();
|
|
||||||
final SimpleDraweeView simpleDraweeView = new SimpleDraweeView(context, hierarchy);
|
|
||||||
simpleDraweeView.setLayoutParams(layoutParams);
|
|
||||||
simpleDraweeView.setImageURI(sliderDisplayUrl);
|
|
||||||
frameLayout.addView(simpleDraweeView);
|
|
||||||
|
|
||||||
final AppCompatImageView imageView = new AppCompatImageView(context);
|
|
||||||
final int px = Utils.convertDpToPx(50);
|
|
||||||
final FrameLayout.LayoutParams playButtonLayoutParams = new FrameLayout.LayoutParams(px, px);
|
|
||||||
playButtonLayoutParams.gravity = Gravity.CENTER;
|
|
||||||
imageView.setLayoutParams(playButtonLayoutParams);
|
|
||||||
imageView.setImageResource(R.drawable.exo_icon_play);
|
|
||||||
frameLayout.addView(imageView);
|
|
||||||
|
|
||||||
viewSwitcher.addView(frameLayout);
|
|
||||||
|
|
||||||
final PlayerView playerView = new PlayerView(context);
|
|
||||||
viewSwitcher.addView(playerView);
|
|
||||||
if (shouldAutoPlay && position == 0) {
|
|
||||||
loadPlayer(context, position, displayUrl, viewSwitcher, factory, playerChangeListener);
|
|
||||||
} else
|
|
||||||
frameLayout.setOnClickListener(v -> loadPlayer(context, position, displayUrl, viewSwitcher, factory, playerChangeListener));
|
|
||||||
return viewSwitcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroyItem(@NonNull final ViewGroup container, final int position, @NonNull final Object object) {
|
|
||||||
final View view = container.getChildAt(position);
|
|
||||||
// Log.d(TAG, "destroy position: " + position + ", view: " + view);
|
|
||||||
if (view instanceof ViewSwitcher) {
|
|
||||||
final Object tag = view.getTag();
|
|
||||||
if (tag instanceof SimpleExoPlayer) {
|
|
||||||
final SimpleExoPlayer player = (SimpleExoPlayer) tag;
|
|
||||||
player.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
container.removeView((View) object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return sliderItems != null ? sliderItems.length : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isViewFromObject(@NonNull final View view, @NonNull final Object object) {
|
|
||||||
return view.equals(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ViewerPostModel getItemAtPosition(final int position) {
|
|
||||||
return sliderItems[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package awais.instagrabber.adapters.viewholder.feed;
|
package awais.instagrabber.adapters.viewholder.feed;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -9,21 +8,14 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
|
||||||
import com.facebook.drawee.interfaces.DraweeController;
|
|
||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
|
||||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
|
||||||
import com.google.android.exoplayer2.Player;
|
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
|
import awais.instagrabber.customviews.VideoPlayerCallbackAdapter;
|
||||||
|
import awais.instagrabber.customviews.VideoPlayerViewHelper;
|
||||||
import awais.instagrabber.databinding.ItemFeedVideoBinding;
|
import awais.instagrabber.databinding.ItemFeedVideoBinding;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.NumberUtils;
|
import awais.instagrabber.utils.NumberUtils;
|
||||||
@ -35,27 +27,26 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
|
|||||||
private static final String TAG = "FeedVideoViewHolder";
|
private static final String TAG = "FeedVideoViewHolder";
|
||||||
|
|
||||||
private final ItemFeedVideoBinding binding;
|
private final ItemFeedVideoBinding binding;
|
||||||
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback;
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
private final DefaultDataSourceFactory dataSourceFactory;
|
private final DefaultDataSourceFactory dataSourceFactory;
|
||||||
|
|
||||||
private CacheDataSourceFactory cacheDataSourceFactory;
|
private CacheDataSourceFactory cacheDataSourceFactory;
|
||||||
private FeedModel feedModel;
|
private FeedModel feedModel;
|
||||||
private SimpleExoPlayer player;
|
|
||||||
|
|
||||||
private final Runnable loadRunnable = new Runnable() {
|
// private final Runnable loadRunnable = new Runnable() {
|
||||||
@Override
|
// @Override
|
||||||
public void run() {
|
// public void run() {
|
||||||
loadPlayer(feedModel);
|
// // loadPlayer(feedModel);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
public FeedVideoViewHolder(@NonNull final ItemFeedVideoBinding binding,
|
public FeedVideoViewHolder(@NonNull final ItemFeedVideoBinding binding,
|
||||||
final MentionClickListener mentionClickListener,
|
final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
final View.OnClickListener clickListener,
|
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, feedItemCallback);
|
||||||
final View.OnLongClickListener longClickListener) {
|
|
||||||
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
|
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.VISIBLE);
|
this.feedItemCallback = feedItemCallback;
|
||||||
|
binding.itemFeedBottom.tvVideoViews.setVisibility(View.VISIBLE);
|
||||||
handler = new Handler(Looper.getMainLooper());
|
handler = new Handler(Looper.getMainLooper());
|
||||||
final Context context = binding.getRoot().getContext();
|
final Context context = binding.getRoot().getContext();
|
||||||
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
|
dataSourceFactory = new DefaultDataSourceFactory(context, "instagram");
|
||||||
@ -69,90 +60,80 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
|
|||||||
public void bindItem(final FeedModel feedModel) {
|
public void bindItem(final FeedModel feedModel) {
|
||||||
// Log.d(TAG, "Binding post: " + feedModel.getPostId());
|
// Log.d(TAG, "Binding post: " + feedModel.getPostId());
|
||||||
this.feedModel = feedModel;
|
this.feedModel = feedModel;
|
||||||
setThumbnail(feedModel);
|
|
||||||
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(feedModel.getViewCount()));
|
binding.itemFeedBottom.tvVideoViews.setText(String.valueOf(feedModel.getViewCount()));
|
||||||
}
|
// showOrHideDetails(false);
|
||||||
|
final float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
|
||||||
|
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
|
||||||
|
|
||||||
private void setThumbnail(final FeedModel feedModel) {
|
@Override
|
||||||
final ViewGroup.LayoutParams layoutParams = binding.thumbnailParent.getLayoutParams();
|
public void onThumbnailClick() {
|
||||||
final int requiredWidth = Utils.displayMetrics.widthPixels;
|
feedItemCallback.onPostClick(feedModel, binding.itemFeedTop.ivProfilePic, binding.videoPost.thumbnail);
|
||||||
layoutParams.width = feedModel.getImageWidth() == 0 ? requiredWidth : feedModel.getImageWidth();
|
}
|
||||||
layoutParams.height = feedModel.getImageHeight() == 0 ? requiredWidth + 1 : feedModel.getImageHeight();
|
|
||||||
binding.thumbnailParent.requestLayout();
|
|
||||||
final ImageRequest thumbnailRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(feedModel.getThumbnailUrl()))
|
|
||||||
.setProgressiveRenderingEnabled(true)
|
|
||||||
.build();
|
|
||||||
final DraweeController controller = Fresco.newDraweeControllerBuilder()
|
|
||||||
.setImageRequest(thumbnailRequest)
|
|
||||||
.build();
|
|
||||||
binding.thumbnail.setController(controller);
|
|
||||||
binding.thumbnailParent.setOnClickListener(v -> loadPlayer(feedModel));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadPlayer(final FeedModel feedModel) {
|
@Override
|
||||||
if (feedModel == null) {
|
public void onPlayerViewLoaded() {
|
||||||
return;
|
// binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
|
||||||
}
|
final ViewGroup.LayoutParams layoutParams = binding.videoPost.playerView.getLayoutParams();
|
||||||
// Log.d(TAG, "playing post:" + feedModel.getPostId());
|
final int requiredWidth = Utils.displayMetrics.widthPixels;
|
||||||
if (binding.viewSwitcher.getDisplayedChild() == 0) {
|
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
|
||||||
binding.viewSwitcher.showNext();
|
layoutParams.width = requiredWidth;
|
||||||
}
|
layoutParams.height = resultingHeight;
|
||||||
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
|
binding.videoPost.playerView.requestLayout();
|
||||||
final ViewGroup.LayoutParams layoutParams = binding.playerView.getLayoutParams();
|
setMuteIcon(vol == 0f && Utils.sessionVolumeFull ? 1f : vol);
|
||||||
final int requiredWidth = Utils.displayMetrics.widthPixels;
|
}
|
||||||
final int resultingHeight = NumberUtils.getResultingHeight(requiredWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
|
};
|
||||||
layoutParams.width = requiredWidth;
|
// final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory;
|
||||||
layoutParams.height = resultingHeight;
|
// final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory);
|
||||||
binding.playerView.requestLayout();
|
// final Uri uri = Uri.parse(feedModel.getDisplayUrl());
|
||||||
float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
|
// final MediaItem mediaItem = MediaItem.fromUri(uri);
|
||||||
if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
|
// final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);
|
||||||
setMuteIcon(vol);
|
final float aspectRatio = (float) feedModel.getImageWidth() / feedModel.getImageHeight();
|
||||||
player = (SimpleExoPlayer) binding.playerView.getPlayer();
|
final VideoPlayerViewHelper videoPlayerViewHelper = new VideoPlayerViewHelper(binding.getRoot().getContext(),
|
||||||
if (player != null) {
|
binding.videoPost,
|
||||||
player.release();
|
feedModel.getDisplayUrl(),
|
||||||
}
|
vol,
|
||||||
player = new SimpleExoPlayer.Builder(itemView.getContext())
|
aspectRatio,
|
||||||
.setLooper(Looper.getMainLooper())
|
feedModel.getThumbnailUrl(),
|
||||||
.build();
|
false,
|
||||||
player.setVolume(vol);
|
null,
|
||||||
player.setPlayWhenReady(true);
|
videoPlayerCallback);
|
||||||
final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory;
|
binding.videoPost.thumbnail.post(() -> {
|
||||||
final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory);
|
if (feedModel.getImageHeight() > 0.8 * Utils.displayMetrics.heightPixels) {
|
||||||
final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(Uri.parse(feedModel.getDisplayUrl()));
|
final ViewGroup.LayoutParams layoutParams = binding.videoPost.thumbnail.getLayoutParams();
|
||||||
player.setRepeatMode(Player.REPEAT_MODE_ALL);
|
layoutParams.height = (int) (0.8 * Utils.displayMetrics.heightPixels);
|
||||||
player.prepare(mediaSource);
|
binding.videoPost.thumbnail.requestLayout();
|
||||||
binding.playerView.setPlayer(player);
|
}
|
||||||
final SimpleExoPlayer finalPlayer = player;
|
|
||||||
binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
|
|
||||||
final float intVol = finalPlayer.getVolume() == 0f ? 1f : 0f;
|
|
||||||
finalPlayer.setVolume(intVol);
|
|
||||||
setMuteIcon(intVol);
|
|
||||||
Utils.sessionVolumeFull = intVol == 1f;
|
|
||||||
});
|
});
|
||||||
binding.playerView.setOnClickListener(v -> finalPlayer.setPlayWhenReady(!finalPlayer.getPlayWhenReady()));
|
// binding.itemFeedBottom.btnMute.setOnClickListener(v -> {
|
||||||
|
// final float newVol = videoPlayerViewHelper.toggleMute();
|
||||||
|
// setMuteIcon(newVol);
|
||||||
|
// Utils.sessionVolumeFull = newVol == 1f;
|
||||||
|
// });
|
||||||
|
// binding.videoPost.playerView.setOnClickListener(v -> videoPlayerViewHelper.togglePlayback());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void setMuteIcon(final float vol) {
|
private void setMuteIcon(final float vol) {
|
||||||
binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
|
// binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FeedModel getCurrentFeedModel() {
|
public FeedModel getCurrentFeedModel() {
|
||||||
return feedModel;
|
return feedModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopPlaying() {
|
// public void stopPlaying() {
|
||||||
// Log.d(TAG, "Stopping post: " + feedModel.getPostId() + ", player: " + player + ", player.isPlaying: " + (player != null && player.isPlaying()));
|
// // Log.d(TAG, "Stopping post: " + feedModel.getPostId() + ", player: " + player + ", player.isPlaying: " + (player != null && player.isPlaying()));
|
||||||
handler.removeCallbacks(loadRunnable);
|
// handler.removeCallbacks(loadRunnable);
|
||||||
if (player != null) {
|
// if (player != null) {
|
||||||
player.release();
|
// player.release();
|
||||||
}
|
// }
|
||||||
if (binding.viewSwitcher.getDisplayedChild() == 1) {
|
// if (binding.videoPost.root.getDisplayedChild() == 1) {
|
||||||
binding.viewSwitcher.showPrevious();
|
// binding.videoPost.root.showPrevious();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public void startPlaying() {
|
// public void startPlaying() {
|
||||||
handler.removeCallbacks(loadRunnable);
|
// handler.removeCallbacks(loadRunnable);
|
||||||
handler.postDelayed(loadRunnable, 800);
|
// handler.postDelayed(loadRunnable, 800);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package awais.instagrabber.animations;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.Transformation;
|
||||||
|
|
||||||
|
public class ResizeAnimation extends Animation {
|
||||||
|
private static final String TAG = "ResizeAnimation";
|
||||||
|
|
||||||
|
final View view;
|
||||||
|
final int startHeight;
|
||||||
|
final int targetHeight;
|
||||||
|
final int startWidth;
|
||||||
|
final int targetWidth;
|
||||||
|
|
||||||
|
public ResizeAnimation(final View view,
|
||||||
|
final int startHeight,
|
||||||
|
final int startWidth,
|
||||||
|
final int targetHeight,
|
||||||
|
final int targetWidth) {
|
||||||
|
this.view = view;
|
||||||
|
this.startHeight = startHeight;
|
||||||
|
this.targetHeight = targetHeight;
|
||||||
|
this.startWidth = startWidth;
|
||||||
|
this.targetWidth = targetWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyTransformation(final float interpolatedTime, final Transformation t) {
|
||||||
|
// Log.d(TAG, "applyTransformation: interpolatedTime: " + interpolatedTime);
|
||||||
|
view.getLayoutParams().height = (int) (startHeight + (targetHeight - startHeight) * interpolatedTime);
|
||||||
|
view.getLayoutParams().width = (int) (startWidth + (targetWidth - startWidth) * interpolatedTime);
|
||||||
|
view.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(final int width, final int height, final int parentWidth, final int parentHeight) {
|
||||||
|
super.initialize(width, height, parentWidth, parentHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean willChangeBounds() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ import org.json.JSONObject;
|
|||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.List;
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
import awais.instagrabber.BuildConfig;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
@ -25,23 +25,20 @@ import awaisomereport.LogCollector;
|
|||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
import static awais.instagrabber.utils.Utils.logCollector;
|
||||||
|
|
||||||
public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]> {
|
public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> {
|
||||||
private final String shortCode;
|
private static final String TAG = "CommentsFetcher";
|
||||||
private final FetchListener<CommentModel[]> fetchListener;
|
|
||||||
|
|
||||||
/*
|
private final String shortCode;
|
||||||
* i fucking spent the whole day on this and fixing all the fucking problems in this class.
|
private final FetchListener<List<CommentModel>> fetchListener;
|
||||||
* DO NO FUCK WITH THIS CODE!
|
|
||||||
* -AWAiS (The Badak) @the.badak
|
public CommentsFetcher(final String shortCode, final FetchListener<List<CommentModel>> fetchListener) {
|
||||||
*/
|
|
||||||
public CommentsFetcher(final String shortCode, final FetchListener<CommentModel[]> fetchListener) {
|
|
||||||
this.shortCode = shortCode;
|
this.shortCode = shortCode;
|
||||||
this.fetchListener = fetchListener;
|
this.fetchListener = fetchListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected CommentModel[] doInBackground(final Void... voids) {
|
protected List<CommentModel> doInBackground(final Void... voids) {
|
||||||
/*
|
/*
|
||||||
"https://www.instagram.com/graphql/query/?query_hash=97b41c52301f77ce508f55e66d17620e&variables=" + "{\"shortcode\":\"" + shortcode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
|
"https://www.instagram.com/graphql/query/?query_hash=97b41c52301f77ce508f55e66d17620e&variables=" + "{\"shortcode\":\"" + shortcode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}";
|
||||||
|
|
||||||
@ -50,23 +47,20 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
|
|||||||
|
|
||||||
https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""}
|
https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""}
|
||||||
*/
|
*/
|
||||||
final ArrayList<CommentModel> commentModels = getParentComments();
|
final List<CommentModel> commentModels = getParentComments();
|
||||||
|
|
||||||
for (final CommentModel commentModel : commentModels) {
|
for (final CommentModel commentModel : commentModels) {
|
||||||
final CommentModel[] childCommentModels = commentModel.getChildCommentModels();
|
final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
|
||||||
if (childCommentModels != null) {
|
if (childCommentModels != null) {
|
||||||
final int childCommentsLen = childCommentModels.length;
|
final int childCommentsLen = childCommentModels.size();
|
||||||
|
final CommentModel lastChild = childCommentModels.get(childCommentsLen - 1);
|
||||||
final CommentModel lastChild = childCommentModels[childCommentsLen - 1];
|
|
||||||
if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
|
if (lastChild != null && lastChild.hasNextPage() && !TextUtils.isEmpty(lastChild.getEndCursor())) {
|
||||||
final CommentModel[] remoteChildComments = getChildComments(commentModel.getId());
|
final List<CommentModel> remoteChildComments = getChildComments(commentModel.getId());
|
||||||
commentModel.setChildCommentModels(remoteChildComments);
|
commentModel.setChildCommentModels(remoteChildComments);
|
||||||
lastChild.setPageCursor(false, null);
|
lastChild.setPageCursor(false, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return commentModels;
|
||||||
return commentModels.toArray(new CommentModel[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -75,14 +69,13 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(final CommentModel[] result) {
|
protected void onPostExecute(final List<CommentModel> result) {
|
||||||
if (fetchListener != null) fetchListener.onResult(result);
|
if (fetchListener != null) fetchListener.onResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private synchronized CommentModel[] getChildComments(final String commentId) {
|
private synchronized List<CommentModel> getChildComments(final String commentId) {
|
||||||
final ArrayList<CommentModel> commentModels = new ArrayList<>();
|
final List<CommentModel> commentModels = new ArrayList<>();
|
||||||
|
|
||||||
String endCursor = "";
|
String endCursor = "";
|
||||||
while (endCursor != null) {
|
while (endCursor != null) {
|
||||||
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
|
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
|
||||||
@ -96,7 +89,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
|
|||||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
|
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
|
||||||
else {
|
else {
|
||||||
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
|
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
|
||||||
.getJSONObject("comment").getJSONObject("edge_threaded_comments");
|
.getJSONObject("comment")
|
||||||
|
.getJSONObject("edge_threaded_comments");
|
||||||
|
|
||||||
final JSONObject pageInfo = data.getJSONObject("page_info");
|
final JSONObject pageInfo = data.getJSONObject("page_info");
|
||||||
endCursor = pageInfo.getString("end_cursor");
|
endCursor = pageInfo.getString("end_cursor");
|
||||||
@ -110,43 +104,54 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
|
|||||||
|
|
||||||
if (childComment != null) {
|
if (childComment != null) {
|
||||||
final JSONObject owner = childComment.getJSONObject("owner");
|
final JSONObject owner = childComment.getJSONObject("owner");
|
||||||
final ProfileModel profileModel = new ProfileModel(false, false, false,
|
final ProfileModel profileModel = new ProfileModel(false,
|
||||||
owner.getString(Constants.EXTRAS_ID),
|
false,
|
||||||
owner.getString(Constants.EXTRAS_USERNAME),
|
false,
|
||||||
null, null, null,
|
owner.getString(Constants.EXTRAS_ID),
|
||||||
owner.getString("profile_pic_url"),
|
owner.getString(Constants.EXTRAS_USERNAME),
|
||||||
null, 0, 0, 0, false, false, false, false);
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
owner.getString("profile_pic_url"),
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
|
||||||
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
|
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
|
||||||
|
|
||||||
commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
|
commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
|
||||||
childComment.getString("text"),
|
childComment.getString("text"),
|
||||||
childComment.getLong("created_at"),
|
childComment.getLong("created_at"),
|
||||||
likedBy != null ? likedBy.optLong("count", 0) : 0,
|
likedBy != null ? likedBy.optLong("count", 0) : 0,
|
||||||
childComment.getBoolean("viewer_has_liked"),
|
childComment.getBoolean("viewer_has_liked"),
|
||||||
profileModel));
|
profileModel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
if (logCollector != null)
|
if (logCollector != null)
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getChildComments",
|
logCollector.appendException(e,
|
||||||
new Pair<>("commentModels.size", commentModels.size()));
|
LogCollector.LogFile.ASYNC_COMMENTS_FETCHER,
|
||||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
"getChildComments",
|
||||||
|
new Pair<>("commentModels.size", commentModels.size()));
|
||||||
|
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return commentModels.toArray(new CommentModel[0]);
|
return commentModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private synchronized ArrayList<CommentModel> getParentComments() {
|
private synchronized List<CommentModel> getParentComments() {
|
||||||
final ArrayList<CommentModel> commentModelsList = new ArrayList<>();
|
final List<CommentModel> commentModels = new ArrayList<>();
|
||||||
|
|
||||||
String endCursor = "";
|
String endCursor = "";
|
||||||
while (endCursor != null) {
|
while (endCursor != null) {
|
||||||
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
|
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
|
||||||
@ -160,7 +165,9 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
|
|||||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
|
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break;
|
||||||
else {
|
else {
|
||||||
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
|
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
|
||||||
.getJSONObject("shortcode_media").getJSONObject("edge_media_to_parent_comment");
|
.getJSONObject("shortcode_media")
|
||||||
|
.getJSONObject(
|
||||||
|
"edge_media_to_parent_comment");
|
||||||
|
|
||||||
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
|
final JSONObject pageInfo = parentComments.getJSONObject("page_info");
|
||||||
endCursor = pageInfo.optString("end_cursor");
|
endCursor = pageInfo.optString("end_cursor");
|
||||||
@ -182,31 +189,37 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
|
|||||||
|
|
||||||
final JSONArray comments = parentComments.getJSONArray("edges");
|
final JSONArray comments = parentComments.getJSONArray("edges");
|
||||||
final int commentsLen = comments.length();
|
final int commentsLen = comments.length();
|
||||||
final CommentModel[] commentModels = new CommentModel[commentsLen];
|
|
||||||
|
|
||||||
for (int i = 0; i < commentsLen; ++i) {
|
for (int i = 0; i < commentsLen; ++i) {
|
||||||
final JSONObject comment = comments.getJSONObject(i).getJSONObject("node");
|
final JSONObject comment = comments.getJSONObject(i).getJSONObject("node");
|
||||||
|
|
||||||
final JSONObject owner = comment.getJSONObject("owner");
|
final JSONObject owner = comment.getJSONObject("owner");
|
||||||
final ProfileModel profileModel = new ProfileModel(false, false,
|
final ProfileModel profileModel = new ProfileModel(false,
|
||||||
owner.optBoolean("is_verified"),
|
false,
|
||||||
owner.getString(Constants.EXTRAS_ID),
|
owner.optBoolean("is_verified"),
|
||||||
owner.getString(Constants.EXTRAS_USERNAME),
|
owner.getString(Constants.EXTRAS_ID),
|
||||||
null, null, null,
|
owner.getString(Constants.EXTRAS_USERNAME),
|
||||||
owner.getString("profile_pic_url"),
|
null,
|
||||||
null, 0, 0, 0, false, false, false, false);
|
null,
|
||||||
|
null,
|
||||||
|
owner.getString("profile_pic_url"),
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
|
||||||
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
|
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
|
||||||
final String commentId = comment.getString(Constants.EXTRAS_ID);
|
final String commentId = comment.getString(Constants.EXTRAS_ID);
|
||||||
commentModels[i] = new CommentModel(commentId,
|
final CommentModel commentModel = new CommentModel(commentId,
|
||||||
comment.getString("text"),
|
comment.getString("text"),
|
||||||
comment.getLong("created_at"),
|
comment.getLong("created_at"),
|
||||||
likedBy != null ? likedBy.optLong("count", 0) : 0,
|
likedBy != null ? likedBy.optLong("count", 0) : 0,
|
||||||
comment.getBoolean("viewer_has_liked"),
|
comment.getBoolean("viewer_has_liked"),
|
||||||
profileModel);
|
profileModel);
|
||||||
|
|
||||||
JSONObject tempJsonObject;
|
JSONObject tempJsonObject;
|
||||||
|
|
||||||
final JSONArray childCommentsArray;
|
final JSONArray childCommentsArray;
|
||||||
final int childCommentsLen;
|
final int childCommentsLen;
|
||||||
if ((tempJsonObject = comment.optJSONObject("edge_threaded_comments")) != null &&
|
if ((tempJsonObject = comment.optJSONObject("edge_threaded_comments")) != null &&
|
||||||
@ -223,47 +236,53 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, CommentModel[]>
|
|||||||
hasNextPage = false;
|
hasNextPage = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final CommentModel[] childCommentModels = new CommentModel[childCommentsLen];
|
final List<CommentModel> childCommentModels = new ArrayList<>();
|
||||||
for (int j = 0; j < childCommentsLen; ++j) {
|
for (int j = 0; j < childCommentsLen; ++j) {
|
||||||
final JSONObject childComment = childCommentsArray.getJSONObject(j).getJSONObject("node");
|
final JSONObject childComment = childCommentsArray.getJSONObject(j).getJSONObject("node");
|
||||||
|
|
||||||
tempJsonObject = childComment.getJSONObject("owner");
|
tempJsonObject = childComment.getJSONObject("owner");
|
||||||
final ProfileModel childProfileModel = new ProfileModel(false, false,
|
final ProfileModel childProfileModel = new ProfileModel(false,
|
||||||
tempJsonObject.optBoolean("is_verified"),
|
false,
|
||||||
tempJsonObject.getString(Constants.EXTRAS_ID),
|
tempJsonObject.optBoolean("is_verified"),
|
||||||
tempJsonObject.getString(Constants.EXTRAS_USERNAME),
|
tempJsonObject.getString(Constants.EXTRAS_ID),
|
||||||
null, null, null,
|
tempJsonObject.getString(Constants.EXTRAS_USERNAME),
|
||||||
tempJsonObject.getString("profile_pic_url"),
|
null,
|
||||||
null, 0, 0, 0, false, false, false, false);
|
null,
|
||||||
|
null,
|
||||||
|
tempJsonObject.getString("profile_pic_url"),
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
|
||||||
tempJsonObject = childComment.optJSONObject("edge_liked_by");
|
tempJsonObject = childComment.optJSONObject("edge_liked_by");
|
||||||
childCommentModels[j] = new CommentModel(childComment.getString(Constants.EXTRAS_ID),
|
childCommentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
|
||||||
childComment.getString("text"),
|
childComment.getString("text"),
|
||||||
childComment.getLong("created_at"),
|
childComment.getLong("created_at"),
|
||||||
tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
|
tempJsonObject != null ? tempJsonObject.optLong("count", 0) : 0,
|
||||||
childComment.getBoolean("viewer_has_liked"),
|
childComment.getBoolean("viewer_has_liked"),
|
||||||
childProfileModel);
|
childProfileModel));
|
||||||
}
|
}
|
||||||
|
childCommentModels.get(childCommentsLen - 1).setPageCursor(hasNextPage, childEndCursor);
|
||||||
childCommentModels[childCommentsLen - 1].setPageCursor(hasNextPage, childEndCursor);
|
commentModel.setChildCommentModels(childCommentModels);
|
||||||
|
|
||||||
commentModels[i].setChildCommentModels(childCommentModels);
|
|
||||||
}
|
}
|
||||||
|
commentModels.add(commentModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.addAll(commentModelsList, commentModels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
if (logCollector != null)
|
if (logCollector != null)
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
|
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
|
||||||
new Pair<>("commentModelsList.size", commentModelsList.size()));
|
new Pair<>("commentModelsList.size", commentModels.size()));
|
||||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return commentModels;
|
||||||
return commentModelsList;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,202 +0,0 @@
|
|||||||
package awais.instagrabber.asyncs;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.DiscoverItemModel;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.NetworkUtils;
|
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
|
||||||
|
|
||||||
public final class DiscoverFetcher extends AsyncTask<Void, Void, DiscoverItemModel[]> {
|
|
||||||
private final String cluster, maxId, rankToken;
|
|
||||||
private final FetchListener<DiscoverItemModel[]> fetchListener;
|
|
||||||
private int lastId = 0;
|
|
||||||
private boolean isFirst, moreAvailable;
|
|
||||||
private String nextMaxId;
|
|
||||||
|
|
||||||
public DiscoverFetcher(final String cluster, final String maxId, final String rankToken,
|
|
||||||
final FetchListener<DiscoverItemModel[]> fetchListener, final boolean isFirst) {
|
|
||||||
this.cluster = cluster == null ? "explore_all%3A0" : cluster.replace(":", "%3A");
|
|
||||||
this.maxId = maxId == null ? "" : "&max_id=" + maxId;
|
|
||||||
this.rankToken = rankToken;
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
this.isFirst = isFirst;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected final DiscoverItemModel[] doInBackground(final Void... voids) {
|
|
||||||
|
|
||||||
DiscoverItemModel[] result = null;
|
|
||||||
|
|
||||||
final ArrayList<DiscoverItemModel> discoverItemModels = fetchItems(null, maxId);
|
|
||||||
if (discoverItemModels != null) {
|
|
||||||
result = discoverItemModels.toArray(new DiscoverItemModel[0]);
|
|
||||||
if (result.length > 0) {
|
|
||||||
final DiscoverItemModel lastModel = result[result.length - 1];
|
|
||||||
if (lastModel != null && nextMaxId != null) lastModel.setMore(moreAvailable, nextMaxId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList<DiscoverItemModel> fetchItems(ArrayList<DiscoverItemModel> discoverItemModels, final String maxId) {
|
|
||||||
try {
|
|
||||||
final String url = "https://www.instagram.com/explore/grid/?is_prefetch=false&omit_cover_media=true&module=explore_popular" +
|
|
||||||
"&use_sectional_payload=false&cluster_id=" + cluster + "&include_fixed_destinations=true&session_id=" + rankToken + maxId;
|
|
||||||
|
|
||||||
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
|
|
||||||
|
|
||||||
urlConnection.setUseCaches(false);
|
|
||||||
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
|
|
||||||
|
|
||||||
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
|
||||||
final JSONObject discoverResponse = new JSONObject(NetworkUtils.readFromConnection(urlConnection));
|
|
||||||
|
|
||||||
moreAvailable = discoverResponse.getBoolean("more_available");
|
|
||||||
nextMaxId = discoverResponse.optString("next_max_id");
|
|
||||||
|
|
||||||
final JSONArray sectionalItems = discoverResponse.getJSONArray("sectional_items");
|
|
||||||
if (discoverItemModels == null) discoverItemModels = new ArrayList<>(sectionalItems.length() * 2);
|
|
||||||
|
|
||||||
for (int i = 0; i < sectionalItems.length(); ++i) {
|
|
||||||
final JSONObject sectionItem = sectionalItems.getJSONObject(i);
|
|
||||||
|
|
||||||
final String feedType = sectionItem.getString("feed_type");
|
|
||||||
final String layoutType = sectionItem.getString("layout_type");
|
|
||||||
|
|
||||||
if (sectionItem.has("layout_content") && feedType.equals("media")) {
|
|
||||||
final JSONObject layoutContent = sectionItem.getJSONObject("layout_content");
|
|
||||||
|
|
||||||
if ("media_grid".equals(layoutType)) {
|
|
||||||
final JSONArray medias = layoutContent.getJSONArray("medias");
|
|
||||||
for (int j = 0; j < medias.length(); ++j)
|
|
||||||
discoverItemModels.add(makeDiscoverModel(medias.getJSONObject(j).getJSONObject("media")));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
final boolean isOneSide = "one_by_two_left".equals(layoutType);
|
|
||||||
if (isOneSide || "two_by_two_right".equals(layoutType)) {
|
|
||||||
|
|
||||||
final JSONObject layoutItem = layoutContent.getJSONObject(isOneSide ? "one_by_two_item" : "two_by_two_item");
|
|
||||||
if (layoutItem.has("media"))
|
|
||||||
discoverItemModels.add(makeDiscoverModel(layoutItem.getJSONObject("media")));
|
|
||||||
|
|
||||||
if (layoutContent.has("fill_items")) {
|
|
||||||
final JSONArray fillItems = layoutContent.getJSONArray("fill_items");
|
|
||||||
for (int j = 0; j < fillItems.length(); ++j)
|
|
||||||
discoverItemModels.add(makeDiscoverModel(fillItems.getJSONObject(j).getJSONObject("media")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
discoverItemModels.trimToSize();
|
|
||||||
urlConnection.disconnect();
|
|
||||||
|
|
||||||
// hack to fetch 50+ items
|
|
||||||
if (this.isFirst) {
|
|
||||||
final int size = discoverItemModels.size();
|
|
||||||
if (size > 50) this.isFirst = false;
|
|
||||||
discoverItemModels = fetchItems(discoverItemModels, "&max_id=" + (lastId++));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
urlConnection.disconnect();
|
|
||||||
}
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_DISCOVER_FETCHER, "fetchItems",
|
|
||||||
new Pair<>("maxId", maxId),
|
|
||||||
new Pair<>("lastId", lastId),
|
|
||||||
new Pair<>("isFirst", isFirst),
|
|
||||||
new Pair<>("nextMaxId", nextMaxId));
|
|
||||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return discoverItemModels;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private DiscoverItemModel makeDiscoverModel(@NonNull final JSONObject media) throws Exception {
|
|
||||||
final JSONObject user = media.getJSONObject(Constants.EXTRAS_USER);
|
|
||||||
final String username = user.getString(Constants.EXTRAS_USERNAME);
|
|
||||||
// final ProfileModel userProfileModel = new ProfileModel(user.getBoolean("is_private"),
|
|
||||||
// user.getBoolean("is_verified"),
|
|
||||||
// String.valueOf(user.get("pk")),
|
|
||||||
// username,
|
|
||||||
// user.getString("full_name"),
|
|
||||||
// null,
|
|
||||||
// user.getString("profile_pic_url"), null,
|
|
||||||
// 0, 0, 0);
|
|
||||||
|
|
||||||
// final String comment;
|
|
||||||
// if (!media.has("caption")) comment = null;
|
|
||||||
// else {
|
|
||||||
// final Object caption = media.get("caption");
|
|
||||||
// comment = caption instanceof JSONObject ? ((JSONObject) caption).getString("text") : null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
final MediaItemType mediaType = ResponseBodyUtils.getMediaItemType(media.getInt("media_type"));
|
|
||||||
|
|
||||||
final ResponseBodyUtils.ThumbnailDetails thumbnailUrl = ResponseBodyUtils.getThumbnailUrl(media, mediaType);
|
|
||||||
final DiscoverItemModel model = new DiscoverItemModel(mediaType,
|
|
||||||
media.getString("pk"),
|
|
||||||
media.getString("code"),
|
|
||||||
thumbnailUrl != null ? thumbnailUrl.url : null);
|
|
||||||
|
|
||||||
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
|
|
||||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
|
|
||||||
|
|
||||||
// to check if file exists
|
|
||||||
File customDir = null;
|
|
||||||
if (settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
|
|
||||||
final String customPath = settingsHelper.getString(FOLDER_PATH);
|
|
||||||
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath +
|
|
||||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
|
|
||||||
? "/" + username
|
|
||||||
: ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadUtils.checkExistence(downloadDir, customDir, mediaType == MediaItemType.MEDIA_TYPE_SLIDER, model);
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
if (fetchListener != null) fetchListener.doBefore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(final DiscoverItemModel[] discoverItemModels) {
|
|
||||||
if (fetchListener != null) fetchListener.onResult(discoverItemModels);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,56 @@
|
|||||||
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.webservices.DiscoverService;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
|
public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
|
||||||
|
private static final String TAG = "DiscoverPostFetchService";
|
||||||
|
private final DiscoverService discoverService;
|
||||||
|
private final DiscoverService.TopicalExploreRequest topicalExploreRequest;
|
||||||
|
private boolean moreAvailable = false;
|
||||||
|
|
||||||
|
public DiscoverPostFetchService(final DiscoverService.TopicalExploreRequest topicalExploreRequest) {
|
||||||
|
this.topicalExploreRequest = topicalExploreRequest;
|
||||||
|
discoverService = DiscoverService.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
|
discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<DiscoverService.TopicalExploreResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final DiscoverService.TopicalExploreResponse result) {
|
||||||
|
if (result == null) {
|
||||||
|
onFailure(new RuntimeException("result is null"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moreAvailable = result.isMoreAvailable();
|
||||||
|
topicalExploreRequest.setMaxId(result.getNextMaxId());
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onResult(result.getItems());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
topicalExploreRequest.setMaxId(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNextPage() {
|
||||||
|
return moreAvailable;
|
||||||
|
}
|
||||||
|
}
|
@ -1,246 +0,0 @@
|
|||||||
package awais.instagrabber.asyncs;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.media.MediaMetadataRetriever;
|
|
||||||
import android.media.MediaScannerConnection;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
|
||||||
import androidx.core.content.FileProvider;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Constants.NOTIF_GROUP_NAME;
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
|
||||||
import static awaisomereport.LogCollector.LogFile;
|
|
||||||
|
|
||||||
public final class DownloadAsync extends AsyncTask<Void, Float, File> {
|
|
||||||
private static final String TAG = "DownloadAsync";
|
|
||||||
|
|
||||||
private static int lastNotifId = 1;
|
|
||||||
private final int currentNotifId;
|
|
||||||
private final AtomicReference<Context> context;
|
|
||||||
private final File outFile;
|
|
||||||
private final String url;
|
|
||||||
private final FetchListener<File> fetchListener;
|
|
||||||
private final Resources resources;
|
|
||||||
private final NotificationCompat.Builder downloadNotif;
|
|
||||||
private String shortCode, username;
|
|
||||||
private final NotificationManagerCompat notificationManager;
|
|
||||||
|
|
||||||
public DownloadAsync(@NonNull final Context context,
|
|
||||||
final String url,
|
|
||||||
final File outFile,
|
|
||||||
final FetchListener<File> fetchListener) {
|
|
||||||
this.context = new AtomicReference<>(context);
|
|
||||||
this.resources = context.getResources();
|
|
||||||
this.url = url;
|
|
||||||
this.outFile = outFile;
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
this.shortCode = this.username = resources.getString(R.string.downloader_started);
|
|
||||||
this.currentNotifId = ++lastNotifId;
|
|
||||||
if (++lastNotifId + 1 == Integer.MAX_VALUE) lastNotifId = 1;
|
|
||||||
|
|
||||||
@StringRes final int titleRes = R.string.downloader_downloading_post;
|
|
||||||
downloadNotif = new NotificationCompat.Builder(context, Constants.DOWNLOAD_CHANNEL_ID)
|
|
||||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
|
||||||
.setSmallIcon(R.mipmap.ic_launcher)
|
|
||||||
.setContentText(shortCode == null ? username : shortCode)
|
|
||||||
.setOngoing(true)
|
|
||||||
.setProgress(100, 0, false)
|
|
||||||
.setAutoCancel(false)
|
|
||||||
.setOnlyAlertOnce(true)
|
|
||||||
.setContentTitle(resources.getString(titleRes));
|
|
||||||
|
|
||||||
notificationManager = NotificationManagerCompat.from(context.getApplicationContext());
|
|
||||||
notificationManager.notify(currentNotifId, downloadNotif.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DownloadAsync setItems(final String shortCode, final String username) {
|
|
||||||
this.shortCode = shortCode;
|
|
||||||
this.username = username;
|
|
||||||
if (downloadNotif != null) downloadNotif.setContentText(this.shortCode == null ? this.username : this.shortCode);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected File doInBackground(final Void... voids) {
|
|
||||||
try {
|
|
||||||
final URLConnection urlConnection = new URL(url).openConnection();
|
|
||||||
final long fileSize = Build.VERSION.SDK_INT >= 24 ? urlConnection.getContentLengthLong() :
|
|
||||||
urlConnection.getContentLength();
|
|
||||||
float totalRead = 0;
|
|
||||||
|
|
||||||
try (final BufferedInputStream bis = new BufferedInputStream(urlConnection.getInputStream());
|
|
||||||
final FileOutputStream fos = new FileOutputStream(outFile)) {
|
|
||||||
final byte[] buffer = new byte[0x2000];
|
|
||||||
|
|
||||||
int count;
|
|
||||||
boolean deletedIPTC = false;
|
|
||||||
while ((count = bis.read(buffer, 0, 0x2000)) != -1) {
|
|
||||||
totalRead = totalRead + count;
|
|
||||||
|
|
||||||
if (!deletedIPTC) {
|
|
||||||
int iptcStart = -1;
|
|
||||||
int fbmdStart = -1;
|
|
||||||
int fbmdBytesLen = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < buffer.length; ++i) {
|
|
||||||
if (buffer[i] == (byte) 0xFF && buffer[i + 1] == (byte) 0xED)
|
|
||||||
iptcStart = i;
|
|
||||||
else if (buffer[i] == (byte) 'F' && buffer[i + 1] == (byte) 'B'
|
|
||||||
&& buffer[i + 2] == (byte) 'M' && buffer[i + 3] == (byte) 'D') {
|
|
||||||
fbmdStart = i;
|
|
||||||
fbmdBytesLen = buffer[i - 10] << 24 | (buffer[i - 9] & 0xFF) << 16 |
|
|
||||||
(buffer[i - 8] & 0xFF) << 8 | (buffer[i - 7] & 0xFF) |
|
|
||||||
(buffer[i - 6] & 0xFF);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iptcStart != -1 && fbmdStart != -1 && fbmdBytesLen != -1) {
|
|
||||||
final int fbmdDataLen = (iptcStart + (fbmdStart - iptcStart) + (fbmdBytesLen - iptcStart)) - 4;
|
|
||||||
|
|
||||||
fos.write(buffer, 0, iptcStart);
|
|
||||||
fos.write(buffer, fbmdDataLen + iptcStart, count - fbmdDataLen - iptcStart);
|
|
||||||
|
|
||||||
publishProgress(totalRead * 100f / fileSize);
|
|
||||||
|
|
||||||
deletedIPTC = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fos.write(buffer, 0, count);
|
|
||||||
publishProgress(totalRead * 100f / fileSize);
|
|
||||||
}
|
|
||||||
fos.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
return outFile;
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogFile.ASYNC_DOWNLOADER, "doInBackground",
|
|
||||||
new Pair<>("context", context.get()),
|
|
||||||
new Pair<>("resources", resources),
|
|
||||||
new Pair<>("lastNotifId", lastNotifId),
|
|
||||||
new Pair<>("downloadNotif", downloadNotif),
|
|
||||||
new Pair<>("currentNotifId", currentNotifId),
|
|
||||||
new Pair<>("notificationManager", notificationManager));
|
|
||||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
if (fetchListener != null) fetchListener.doBefore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(@NonNull final Float... values) {
|
|
||||||
if (downloadNotif != null) {
|
|
||||||
downloadNotif.setProgress(100, values[0].intValue(), false);
|
|
||||||
notificationManager.notify(currentNotifId, downloadNotif.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(final File result) {
|
|
||||||
if (result != null) {
|
|
||||||
final Context context = this.context.get();
|
|
||||||
|
|
||||||
context.sendBroadcast(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT ?
|
|
||||||
new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(Environment.getExternalStorageDirectory())) :
|
|
||||||
new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(result.getAbsoluteFile()))
|
|
||||||
);
|
|
||||||
MediaScannerConnection.scanFile(context, new String[]{result.getAbsolutePath()}, null, null);
|
|
||||||
|
|
||||||
if (notificationManager != null) {
|
|
||||||
final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", result);
|
|
||||||
|
|
||||||
final ContentResolver contentResolver = context.getContentResolver();
|
|
||||||
Bitmap bitmap = null;
|
|
||||||
if (Utils.isImage(uri, contentResolver)) {
|
|
||||||
try (final InputStream inputStream = contentResolver.openInputStream(uri)) {
|
|
||||||
bitmap = BitmapFactory.decodeStream(inputStream);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_1");
|
|
||||||
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitmap == null) {
|
|
||||||
final MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
retriever.setDataSource(context, uri);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
retriever.setDataSource(result.getAbsolutePath());
|
|
||||||
}
|
|
||||||
bitmap = retriever.getFrameAtTime();
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
|
||||||
try {
|
|
||||||
retriever.close();
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_2");
|
|
||||||
}
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogFile.ASYNC_DOWNLOADER, "onPostExecute::bitmap_3");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final String downloadComplete = resources.getString(R.string.downloader_complete);
|
|
||||||
|
|
||||||
downloadNotif.setContentText(null).setContentTitle(downloadComplete).setProgress(0, 0, false)
|
|
||||||
.setWhen(System.currentTimeMillis()).setOngoing(false).setOnlyAlertOnce(false).setAutoCancel(true)
|
|
||||||
.setGroup(NOTIF_GROUP_NAME).setGroupSummary(true).setContentIntent(
|
|
||||||
PendingIntent.getActivity(context, 2020, new Intent(Intent.ACTION_VIEW, uri)
|
|
||||||
.addFlags(
|
|
||||||
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_FROM_BACKGROUND | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
.putExtra(Intent.EXTRA_STREAM, uri), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT));
|
|
||||||
|
|
||||||
if (bitmap != null)
|
|
||||||
downloadNotif.setStyle(new NotificationCompat.BigPictureStyle().setBigContentTitle(downloadComplete).bigPicture(bitmap))
|
|
||||||
.setLargeIcon(bitmap).setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL);
|
|
||||||
|
|
||||||
notificationManager.cancel(currentNotifId);
|
|
||||||
notificationManager.notify(currentNotifId + 1, downloadNotif.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fetchListener != null) fetchListener.onResult(result);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,42 @@
|
|||||||
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
|
|
||||||
|
public final class DownloadedCheckerAsyncTask extends AsyncTask<FeedModel, Void, Map<String, List<Boolean>>> {
|
||||||
|
private static final String TAG = "DownloadedCheckerAsyncTask";
|
||||||
|
|
||||||
|
private final OnCheckResultListener listener;
|
||||||
|
|
||||||
|
public DownloadedCheckerAsyncTask(final OnCheckResultListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, List<Boolean>> doInBackground(final FeedModel... feedModels) {
|
||||||
|
if (feedModels == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Map<String, List<Boolean>> map = new HashMap<>();
|
||||||
|
for (final FeedModel feedModel : feedModels) {
|
||||||
|
map.put(feedModel.getPostId(), DownloadUtils.checkDownloaded(feedModel));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(final Map<String, List<Boolean>> result) {
|
||||||
|
if (listener == null) return;
|
||||||
|
listener.onResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnCheckResultListener {
|
||||||
|
void onResult(final Map<String, List<Boolean>> result);
|
||||||
|
}
|
||||||
|
}
|
@ -1,243 +0,0 @@
|
|||||||
package awais.instagrabber.asyncs;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.FeedModel;
|
|
||||||
import awais.instagrabber.models.ProfileModel;
|
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.NetworkUtils;
|
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
|
||||||
|
|
||||||
public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
|
|
||||||
private static final String TAG = "FeedFetcher";
|
|
||||||
|
|
||||||
private static final int maxItemsToLoad = 25; // max is 50, but that's too many posts, setting more than 30 is gay
|
|
||||||
private final String endCursor;
|
|
||||||
private final FetchListener<FeedModel[]> fetchListener;
|
|
||||||
|
|
||||||
public FeedFetcher(final FetchListener<FeedModel[]> fetchListener) {
|
|
||||||
this.endCursor = "";
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FeedFetcher(final String endCursor, final FetchListener<FeedModel[]> fetchListener) {
|
|
||||||
this.endCursor = endCursor == null ? "" : endCursor;
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected final FeedModel[] doInBackground(final Void... voids) {
|
|
||||||
FeedModel[] result = null;
|
|
||||||
try {
|
|
||||||
//
|
|
||||||
// stories: 04334405dbdef91f2c4e207b84c204d7 && https://i.instagram.com/api/v1/feed/reels_tray/
|
|
||||||
// https://www.instagram.com/graphql/query/?query_hash=04334405dbdef91f2c4e207b84c204d7&variables={"only_stories":true,"stories_prefetch":false,"stories_video_dash_manifest":false}
|
|
||||||
// ///////////////////////////////////////////////
|
|
||||||
// feed:
|
|
||||||
// https://www.instagram.com/graphql/query/?query_hash=6b838488258d7a4820e48d209ef79eb1&variables=
|
|
||||||
// {"cached_feed_item_ids":[],"fetch_media_item_count":12,"fetch_media_item_cursor":"<end_cursor>","fetch_comment_count":4,"fetch_like":3,"has_stories":false,"has_threaded_comments":true}
|
|
||||||
// only used: fetch_media_item_cursor, fetch_media_item_count: 100 (max 50), has_threaded_comments = true
|
|
||||||
// //////////////////////////////////////////////
|
|
||||||
// more unknowns: https://github.com/qsniyg/rssit/blob/master/rssit/generators/instagram.py
|
|
||||||
//
|
|
||||||
|
|
||||||
final String url = "https://www.instagram.com/graphql/query/?query_hash=6b838488258d7a4820e48d209ef79eb1&variables=" +
|
|
||||||
"{\"fetch_media_item_count\":" + maxItemsToLoad + ",\"has_threaded_comments\":true,\"fetch_media_item_cursor\":\"" + endCursor + "\"}";
|
|
||||||
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
|
|
||||||
|
|
||||||
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
|
||||||
final String json = NetworkUtils.readFromConnection(urlConnection);
|
|
||||||
// Log.d(TAG, json);
|
|
||||||
final JSONObject timelineFeed = new JSONObject(json).getJSONObject("data")
|
|
||||||
.getJSONObject(Constants.EXTRAS_USER).getJSONObject("edge_web_feed_timeline");
|
|
||||||
|
|
||||||
final String endCursor;
|
|
||||||
final boolean hasNextPage;
|
|
||||||
|
|
||||||
final JSONObject pageInfo = timelineFeed.getJSONObject("page_info");
|
|
||||||
if (pageInfo.has("has_next_page")) {
|
|
||||||
hasNextPage = pageInfo.getBoolean("has_next_page");
|
|
||||||
endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null;
|
|
||||||
} else {
|
|
||||||
hasNextPage = false;
|
|
||||||
endCursor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final JSONArray feedItems = timelineFeed.getJSONArray("edges");
|
|
||||||
|
|
||||||
final int feedLen = feedItems.length();
|
|
||||||
final ArrayList<FeedModel> feedModelsList = new ArrayList<>(feedLen);
|
|
||||||
for (int i = 0; i < feedLen; ++i) {
|
|
||||||
final JSONObject feedItem = feedItems.getJSONObject(i).getJSONObject("node");
|
|
||||||
final String mediaType = feedItem.optString("__typename");
|
|
||||||
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
final boolean isVideo = feedItem.optBoolean("is_video");
|
|
||||||
final long videoViews = feedItem.optLong("video_view_count", 0);
|
|
||||||
|
|
||||||
final String displayUrl = feedItem.optString("display_url");
|
|
||||||
if (TextUtils.isEmpty(displayUrl)) continue;
|
|
||||||
final String resourceUrl;
|
|
||||||
|
|
||||||
if (isVideo) resourceUrl = feedItem.getString("video_url");
|
|
||||||
else
|
|
||||||
resourceUrl = feedItem.has("display_resources") ? ResponseBodyUtils.getHighQualityImage(feedItem) : displayUrl;
|
|
||||||
|
|
||||||
ProfileModel profileModel = null;
|
|
||||||
if (feedItem.has("owner")) {
|
|
||||||
final JSONObject owner = feedItem.getJSONObject("owner");
|
|
||||||
profileModel = new ProfileModel(
|
|
||||||
owner.optBoolean("is_private"),
|
|
||||||
false, // if you can see it then you def follow
|
|
||||||
owner.optBoolean("is_verified"),
|
|
||||||
owner.getString(Constants.EXTRAS_ID),
|
|
||||||
owner.getString(Constants.EXTRAS_USERNAME),
|
|
||||||
owner.optString("full_name"),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
owner.getString("profile_pic_url"),
|
|
||||||
null,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject tempJsonObject = feedItem.optJSONObject("edge_media_preview_comment");
|
|
||||||
final long commentsCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
|
|
||||||
|
|
||||||
tempJsonObject = feedItem.optJSONObject("edge_media_to_caption");
|
|
||||||
final JSONArray captions = tempJsonObject != null ? tempJsonObject.getJSONArray("edges") : null;
|
|
||||||
|
|
||||||
String captionText = null;
|
|
||||||
if (captions != null && captions.length() > 0) {
|
|
||||||
if ((tempJsonObject = captions.optJSONObject(0)) != null &&
|
|
||||||
(tempJsonObject = tempJsonObject.optJSONObject("node")) != null)
|
|
||||||
captionText = tempJsonObject.getString("text");
|
|
||||||
}
|
|
||||||
|
|
||||||
final JSONObject location = feedItem.optJSONObject("location");
|
|
||||||
// Log.d(TAG, "location: " + (location == null ? null : location.toString()));
|
|
||||||
String locationId = null;
|
|
||||||
String locationName = null;
|
|
||||||
if (location != null) {
|
|
||||||
locationName = location.optString("name");
|
|
||||||
if (location.has("id")) {
|
|
||||||
locationId = location.getString("id");
|
|
||||||
} else if (location.has("pk")) {
|
|
||||||
locationId = location.getString("pk");
|
|
||||||
}
|
|
||||||
// Log.d(TAG, "locationId: " + locationId);
|
|
||||||
}
|
|
||||||
final FeedModel feedModel = new FeedModel(
|
|
||||||
profileModel,
|
|
||||||
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
|
|
||||||
videoViews,
|
|
||||||
feedItem.getString(Constants.EXTRAS_ID),
|
|
||||||
resourceUrl,
|
|
||||||
displayUrl,
|
|
||||||
feedItem.getString(Constants.EXTRAS_SHORTCODE),
|
|
||||||
captionText,
|
|
||||||
commentsCount,
|
|
||||||
feedItem.optLong("taken_at_timestamp", -1),
|
|
||||||
feedItem.getBoolean("viewer_has_liked"),
|
|
||||||
feedItem.getBoolean("viewer_has_saved"),
|
|
||||||
feedItem.getJSONObject("edge_media_preview_like").getLong("count"),
|
|
||||||
locationName,
|
|
||||||
locationId);
|
|
||||||
|
|
||||||
final boolean isSlider = "GraphSidecar".equals(mediaType) && feedItem.has("edge_sidecar_to_children");
|
|
||||||
|
|
||||||
if (isSlider) {
|
|
||||||
final JSONObject sidecar = feedItem.optJSONObject("edge_sidecar_to_children");
|
|
||||||
if (sidecar != null) {
|
|
||||||
final JSONArray children = sidecar.optJSONArray("edges");
|
|
||||||
|
|
||||||
if (children != null) {
|
|
||||||
final ViewerPostModel[] sliderItems = new ViewerPostModel[children.length()];
|
|
||||||
|
|
||||||
for (int j = 0; j < sliderItems.length; ++j) {
|
|
||||||
final JSONObject node = children.optJSONObject(j).getJSONObject("node");
|
|
||||||
final boolean isChildVideo = node.optBoolean("is_video");
|
|
||||||
|
|
||||||
sliderItems[j] = new ViewerPostModel(
|
|
||||||
isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
|
|
||||||
node.getString(Constants.EXTRAS_ID),
|
|
||||||
isChildVideo ? node.getString("video_url") : ResponseBodyUtils.getHighQualityImage(node),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
node.optLong("video_view_count", -1),
|
|
||||||
-1,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
feedItem.getJSONObject("edge_media_preview_like").getLong("count"),
|
|
||||||
locationName,
|
|
||||||
locationId);
|
|
||||||
|
|
||||||
sliderItems[j].setSliderDisplayUrl(node.getString("display_url"));
|
|
||||||
}
|
|
||||||
|
|
||||||
feedModel.setSliderItems(sliderItems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
feedModelsList.add(feedModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
feedModelsList.trimToSize();
|
|
||||||
|
|
||||||
final FeedModel[] feedModels = feedModelsList.toArray(new FeedModel[0]);
|
|
||||||
final int length = feedModels.length;
|
|
||||||
if (length >= 1 && feedModels[length - 1] != null) {
|
|
||||||
feedModels[length - 1].setPageCursor(hasNextPage, endCursor);
|
|
||||||
}
|
|
||||||
result = feedModels;
|
|
||||||
}
|
|
||||||
|
|
||||||
urlConnection.disconnect();
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_FEED_FETCHER, "doInBackground");
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.e(TAG, "", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
if (fetchListener != null) fetchListener.doBefore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(final FeedModel[] postModels) {
|
|
||||||
if (fetchListener != null) fetchListener.onResult(postModels);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,54 @@
|
|||||||
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||||
|
import awais.instagrabber.webservices.FeedService;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
|
public class FeedPostFetchService implements PostFetcher.PostFetchService {
|
||||||
|
private static final String TAG = "FeedPostFetchService";
|
||||||
|
private final FeedService feedService;
|
||||||
|
private String nextCursor;
|
||||||
|
private boolean hasNextPage;
|
||||||
|
|
||||||
|
public FeedPostFetchService() {
|
||||||
|
feedService = FeedService.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
|
feedService.fetch(25, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final PostsFetchResponse result) {
|
||||||
|
if (result == null) return;
|
||||||
|
nextCursor = result.getNextCursor();
|
||||||
|
hasNextPage = result.hasNextPage();
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onResult(result.getFeedModels());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
// Log.e(TAG, "onFailure: ", t);
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
nextCursor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNextPage() {
|
||||||
|
return hasNextPage;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.HashtagModel;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
import awais.instagrabber.webservices.TagsService;
|
||||||
|
import awais.instagrabber.webservices.TagsService.TagPostsFetchResponse;
|
||||||
|
|
||||||
|
public class HashtagPostFetchService implements PostFetcher.PostFetchService {
|
||||||
|
private final TagsService tagsService;
|
||||||
|
private final HashtagModel hashtagModel;
|
||||||
|
private String nextMaxId;
|
||||||
|
private boolean moreAvailable;
|
||||||
|
|
||||||
|
public HashtagPostFetchService(final HashtagModel hashtagModel) {
|
||||||
|
this.hashtagModel = hashtagModel;
|
||||||
|
tagsService = TagsService.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
|
tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, new ServiceCallback<TagPostsFetchResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final TagPostsFetchResponse result) {
|
||||||
|
if (result == null) return;
|
||||||
|
nextMaxId = result.getNextMaxId();
|
||||||
|
moreAvailable = result.isMoreAvailable();
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onResult(result.getItems());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
// Log.e(TAG, "onFailure: ", t);
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
nextMaxId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNextPage() {
|
||||||
|
return moreAvailable;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.LocationModel;
|
||||||
|
import awais.instagrabber.webservices.LocationService;
|
||||||
|
import awais.instagrabber.webservices.LocationService.LocationPostsFetchResponse;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
|
public class LocationPostFetchService implements PostFetcher.PostFetchService {
|
||||||
|
private final LocationService locationService;
|
||||||
|
private final LocationModel locationModel;
|
||||||
|
private String nextMaxId;
|
||||||
|
private boolean moreAvailable;
|
||||||
|
|
||||||
|
public LocationPostFetchService(final LocationModel locationModel) {
|
||||||
|
this.locationModel = locationModel;
|
||||||
|
locationService = LocationService.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
|
locationService.fetchPosts(locationModel.getId(), nextMaxId, new ServiceCallback<LocationPostsFetchResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final LocationPostsFetchResponse result) {
|
||||||
|
if (result == null) return;
|
||||||
|
nextMaxId = result.getNextMaxId();
|
||||||
|
moreAvailable = result.isMoreAvailable();
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onResult(result.getItems());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
// Log.e(TAG, "onFailure: ", t);
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
nextMaxId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNextPage() {
|
||||||
|
return moreAvailable;
|
||||||
|
}
|
||||||
|
}
|
@ -1,51 +1,48 @@
|
|||||||
package awais.instagrabber.asyncs;
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
import awais.instagrabber.models.ProfileModel;
|
import awais.instagrabber.models.ProfileModel;
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.NetworkUtils;
|
import awais.instagrabber.utils.NetworkUtils;
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awaisomereport.LogCollector;
|
import awaisomereport.LogCollector;
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
import static awais.instagrabber.utils.Utils.logCollector;
|
||||||
|
|
||||||
public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]> {
|
public final class PostFetcher extends AsyncTask<Void, Void, FeedModel> {
|
||||||
private static final String TAG = "PostFetcher";
|
private static final String TAG = "PostFetcher";
|
||||||
|
|
||||||
private final String shortCode;
|
private final String shortCode;
|
||||||
private final FetchListener<ViewerPostModel[]> fetchListener;
|
private final FetchListener<FeedModel> fetchListener;
|
||||||
|
|
||||||
public PostFetcher(final String shortCode, final FetchListener<ViewerPostModel[]> fetchListener) {
|
public PostFetcher(final String shortCode, final FetchListener<FeedModel> fetchListener) {
|
||||||
this.shortCode = shortCode;
|
this.shortCode = shortCode;
|
||||||
this.fetchListener = fetchListener;
|
this.fetchListener = fetchListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ViewerPostModel[] doInBackground(final Void... voids) {
|
protected FeedModel doInBackground(final Void... voids) {
|
||||||
ViewerPostModel[] result = null;
|
|
||||||
CookieUtils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE)); // <- direct download
|
CookieUtils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE)); // <- direct download
|
||||||
|
HttpURLConnection conn = null;
|
||||||
try {
|
try {
|
||||||
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/p/" + shortCode + "/?__a=1").openConnection();
|
conn = (HttpURLConnection) new URL("https://www.instagram.com/p/" + shortCode + "/?__a=1").openConnection();
|
||||||
conn.setUseCaches(false);
|
conn.setUseCaches(false);
|
||||||
conn.connect();
|
conn.connect();
|
||||||
|
|
||||||
@ -53,7 +50,6 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
|
|||||||
|
|
||||||
final JSONObject media = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql")
|
final JSONObject media = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql")
|
||||||
.getJSONObject("shortcode_media");
|
.getJSONObject("shortcode_media");
|
||||||
|
|
||||||
ProfileModel profileModel = null;
|
ProfileModel profileModel = null;
|
||||||
if (media.has("owner")) {
|
if (media.has("owner")) {
|
||||||
final JSONObject owner = media.getJSONObject("owner");
|
final JSONObject owner = media.getJSONObject("owner");
|
||||||
@ -77,19 +73,6 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
|
|||||||
owner.optBoolean("requested_by_viewer")
|
owner.optBoolean("requested_by_viewer")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final String username = profileModel == null ? "" : profileModel.getUsername();
|
|
||||||
// to check if file exists
|
|
||||||
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
|
|
||||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
|
|
||||||
File customDir = null;
|
|
||||||
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
|
|
||||||
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
|
|
||||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
|
|
||||||
? ("/" + username)
|
|
||||||
: ""));
|
|
||||||
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
final long timestamp = media.getLong("taken_at_timestamp");
|
final long timestamp = media.getLong("taken_at_timestamp");
|
||||||
|
|
||||||
final boolean isVideo = media.has("is_video") && media.optBoolean("is_video");
|
final boolean isVideo = media.has("is_video") && media.optBoolean("is_video");
|
||||||
@ -111,75 +94,67 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
|
|||||||
|
|
||||||
JSONObject commentObject = media.optJSONObject("edge_media_to_parent_comment");
|
JSONObject commentObject = media.optJSONObject("edge_media_to_parent_comment");
|
||||||
final long commentsCount = commentObject != null ? commentObject.optLong("count") : 0;
|
final long commentsCount = commentObject != null ? commentObject.optLong("count") : 0;
|
||||||
|
final FeedModel.Builder feedModelBuilder = new FeedModel.Builder()
|
||||||
String endCursor = null;
|
.setItemType(mediaItemType)
|
||||||
if (commentObject != null && (commentObject = commentObject.optJSONObject("page_info")) != null) {
|
.setPostId(media.getString(Constants.EXTRAS_ID))
|
||||||
endCursor = commentObject.optString("end_cursor");
|
.setDisplayUrl(isVideo ? media.getString("video_url")
|
||||||
}
|
: ResponseBodyUtils.getHighQualityImage(media))
|
||||||
|
.setThumbnailUrl(media.getString("display_url"))
|
||||||
if (mediaItemType != MediaItemType.MEDIA_TYPE_SLIDER) {
|
.setImageHeight(media.getJSONObject("dimensions").getInt("height"))
|
||||||
final ViewerPostModel postModel = new ViewerPostModel(
|
.setImageWidth(media.getJSONObject("dimensions").getInt("width"))
|
||||||
mediaItemType,
|
.setShortCode(shortCode)
|
||||||
media.getString(Constants.EXTRAS_ID),
|
.setPostCaption(TextUtils.isEmpty(postCaption) ? null : postCaption)
|
||||||
isVideo ? media.getString("video_url") : ResponseBodyUtils.getHighQualityImage(media),
|
.setProfileModel(profileModel)
|
||||||
shortCode,
|
.setViewCount(isVideo && media.has("video_view_count")
|
||||||
TextUtils.isEmpty(postCaption) ? null : postCaption,
|
? media.getLong("video_view_count")
|
||||||
profileModel,
|
: -1)
|
||||||
isVideo && media.has("video_view_count") ? media.getLong("video_view_count") : -1,
|
.setTimestamp(timestamp)
|
||||||
timestamp, media.getBoolean("viewer_has_liked"), media.getBoolean("viewer_has_saved"),
|
.setLiked(media.getBoolean("viewer_has_liked"))
|
||||||
media.getJSONObject("edge_media_preview_like").getLong("count"),
|
.setBookmarked(media.getBoolean("viewer_has_saved"))
|
||||||
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
|
.setLikesCount(media.getJSONObject("edge_media_preview_like")
|
||||||
media.isNull("location") ? null :
|
.getLong("count"))
|
||||||
(media.getJSONObject("location").optString("id") + "/" +
|
.setLocationName(media.isNull("location")
|
||||||
media.getJSONObject("location").optString("slug")));
|
? null
|
||||||
|
: media.getJSONObject("location").optString("name"))
|
||||||
postModel.setCommentsCount(commentsCount);
|
.setLocationId(media.isNull("location")
|
||||||
|
? null
|
||||||
DownloadUtils.checkExistence(downloadDir, customDir, false, postModel);
|
: media.getJSONObject("location").optString("id"))
|
||||||
|
.setCommentsCount(commentsCount);
|
||||||
result = new ViewerPostModel[]{postModel};
|
if (isSlider) {
|
||||||
|
|
||||||
} else {
|
|
||||||
final JSONArray children = media.getJSONObject("edge_sidecar_to_children").getJSONArray("edges");
|
final JSONArray children = media.getJSONObject("edge_sidecar_to_children").getJSONArray("edges");
|
||||||
final ViewerPostModel[] postModels = new ViewerPostModel[children.length()];
|
final List<PostChild> postModels = new ArrayList<>();
|
||||||
|
for (int i = 0; i < children.length(); ++i) {
|
||||||
for (int i = 0; i < postModels.length; ++i) {
|
final JSONObject childNode = children.getJSONObject(i).getJSONObject("node");
|
||||||
final JSONObject node = children.getJSONObject(i).getJSONObject("node");
|
final boolean isChildVideo = childNode.getBoolean("is_video");
|
||||||
final boolean isChildVideo = node.getBoolean("is_video");
|
postModels.add(new PostChild.Builder()
|
||||||
|
.setItemType(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
|
||||||
postModels[i] = new ViewerPostModel(
|
: MediaItemType.MEDIA_TYPE_IMAGE)
|
||||||
isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
|
.setDisplayUrl(isChildVideo ? childNode.getString("video_url")
|
||||||
media.getString(Constants.EXTRAS_ID),
|
: childNode.getString("display_url"))
|
||||||
isChildVideo ? node.getString("video_url") : ResponseBodyUtils.getHighQualityImage(node),
|
.setShortCode(media.getString(Constants.EXTRAS_SHORTCODE))
|
||||||
node.getString(Constants.EXTRAS_SHORTCODE),
|
.setVideoViews(isChildVideo && childNode.has("video_view_count")
|
||||||
postCaption,
|
? childNode.getLong("video_view_count")
|
||||||
profileModel,
|
: -1)
|
||||||
isChildVideo && node.has("video_view_count") ? node.getLong("video_view_count") : -1,
|
.setThumbnailUrl(childNode.getString("display_url"))
|
||||||
timestamp, media.getBoolean("viewer_has_liked"), media.getBoolean("viewer_has_saved"),
|
.setHeight(childNode.getJSONObject("dimensions").getInt("height"))
|
||||||
media.getJSONObject("edge_media_preview_like").getLong("count"),
|
.setWidth(childNode.getJSONObject("dimensions").getInt("width"))
|
||||||
media.isNull("location") ? null : media.getJSONObject("location").optString("name"),
|
.build());
|
||||||
media.isNull("location") ? null :
|
|
||||||
(media.getJSONObject("location").optString("id") + "/" +
|
|
||||||
media.getJSONObject("location").optString("slug")));
|
|
||||||
postModels[i].setSliderDisplayUrl(node.getString("display_url"));
|
|
||||||
|
|
||||||
DownloadUtils.checkExistence(downloadDir, customDir, true, postModels[i]);
|
|
||||||
}
|
}
|
||||||
|
feedModelBuilder.setSliderItems(postModels);
|
||||||
postModels[0].setCommentsCount(commentsCount);
|
|
||||||
|
|
||||||
result = postModels;
|
|
||||||
}
|
}
|
||||||
|
return feedModelBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.disconnect();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (logCollector != null) {
|
if (logCollector != null) {
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_POST_FETCHER, "doInBackground");
|
logCollector.appendException(e, LogCollector.LogFile.ASYNC_POST_FETCHER, "doInBackground");
|
||||||
}
|
}
|
||||||
Log.e(TAG, "Error fetching post", e);
|
Log.e(TAG, "Error fetching post", e);
|
||||||
|
} finally {
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -188,7 +163,7 @@ public final class PostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(final ViewerPostModel[] postModels) {
|
protected void onPostExecute(final FeedModel feedModel) {
|
||||||
if (fetchListener != null) fetchListener.onResult(postModels);
|
if (fetchListener != null) fetchListener.onResult(feedModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,182 +0,0 @@
|
|||||||
package awais.instagrabber.asyncs;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.PostModel;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
import awais.instagrabber.models.enums.PostItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.NetworkUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
|
||||||
|
|
||||||
public final class PostsFetcher extends AsyncTask<Void, Void, List<PostModel>> {
|
|
||||||
private static final String TAG = "PostsFetcher";
|
|
||||||
private final PostItemType type;
|
|
||||||
private final String endCursor;
|
|
||||||
private final String id;
|
|
||||||
private final FetchListener<List<PostModel>> fetchListener;
|
|
||||||
private String username = null;
|
|
||||||
|
|
||||||
public PostsFetcher(final String id,
|
|
||||||
final PostItemType type,
|
|
||||||
final String endCursor,
|
|
||||||
final FetchListener<List<PostModel>> fetchListener) {
|
|
||||||
this.id = id;
|
|
||||||
this.type = type;
|
|
||||||
this.endCursor = endCursor == null ? "" : endCursor;
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PostsFetcher setUsername(final String username) {
|
|
||||||
this.username = username;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<PostModel> doInBackground(final Void... voids) {
|
|
||||||
// final boolean isHashTag = id.charAt(0) == '#';
|
|
||||||
// final boolean isSaved = id.charAt(0) == '$';
|
|
||||||
// final boolean isTagged = id.charAt(0) == '%';
|
|
||||||
// final boolean isLocation = id.contains("/");
|
|
||||||
|
|
||||||
final String url;
|
|
||||||
switch (type) {
|
|
||||||
case HASHTAG:
|
|
||||||
url = "https://www.instagram.com/graphql/query/?query_hash=9b498c08113f1e09617a1703c22b2f32&variables=" +
|
|
||||||
"{\"tag_name\":\"" + id.toLowerCase() + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
|
|
||||||
break;
|
|
||||||
case LOCATION:
|
|
||||||
url = "https://www.instagram.com/graphql/query/?query_hash=36bd0f2bf5911908de389b8ceaa3be6d&variables=" +
|
|
||||||
"{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
|
|
||||||
break;
|
|
||||||
case SAVED:
|
|
||||||
url = "https://www.instagram.com/graphql/query/?query_hash=8c86fed24fa03a8a2eea2a70a80c7b6b&variables=" +
|
|
||||||
"{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
|
|
||||||
break;
|
|
||||||
case TAGGED:
|
|
||||||
url = "https://www.instagram.com/graphql/query/?query_hash=31fe64d9463cbbe58319dced405c6206&variables=" +
|
|
||||||
"{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
url = "https://www.instagram.com/graphql/query/?query_hash=18a7b935ab438c4514b1f742d8fa07a7&variables=" +
|
|
||||||
"{\"id\":\"" + id + "\",\"first\":150,\"after\":\"" + endCursor + "\"}";
|
|
||||||
}
|
|
||||||
List<PostModel> result = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
|
||||||
conn.setUseCaches(false);
|
|
||||||
conn.connect();
|
|
||||||
|
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
|
||||||
// to check if file exists
|
|
||||||
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
|
|
||||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
|
|
||||||
File customDir = null;
|
|
||||||
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
|
|
||||||
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
|
|
||||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
|
|
||||||
? ("/" + username)
|
|
||||||
: ""));
|
|
||||||
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean isHashtag = type == PostItemType.HASHTAG;
|
|
||||||
final boolean isLocation = type == PostItemType.LOCATION;
|
|
||||||
final boolean isSaved = type == PostItemType.SAVED;
|
|
||||||
final boolean isTagged = type == PostItemType.TAGGED;
|
|
||||||
final JSONObject mediaPosts = new JSONObject(NetworkUtils.readFromConnection(conn))
|
|
||||||
.getJSONObject("data")
|
|
||||||
.getJSONObject(isHashtag
|
|
||||||
? Constants.EXTRAS_HASHTAG
|
|
||||||
: (isLocation ? Constants.EXTRAS_LOCATION
|
|
||||||
: Constants.EXTRAS_USER))
|
|
||||||
.getJSONObject(isHashtag ? "edge_hashtag_to_media" :
|
|
||||||
isLocation ? "edge_location_to_media" : isSaved ? "edge_saved_media"
|
|
||||||
: isTagged ? "edge_user_to_photos_of_you"
|
|
||||||
: "edge_owner_to_timeline_media");
|
|
||||||
|
|
||||||
final String endCursor;
|
|
||||||
final boolean hasNextPage;
|
|
||||||
|
|
||||||
final JSONObject pageInfo = mediaPosts.getJSONObject("page_info");
|
|
||||||
if (pageInfo.has("has_next_page")) {
|
|
||||||
hasNextPage = pageInfo.getBoolean("has_next_page");
|
|
||||||
endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null;
|
|
||||||
} else {
|
|
||||||
hasNextPage = false;
|
|
||||||
endCursor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final JSONArray edges = mediaPosts.getJSONArray("edges");
|
|
||||||
for (int i = 0; i < edges.length(); ++i) {
|
|
||||||
final JSONObject mediaNode = edges.getJSONObject(i).getJSONObject("node");
|
|
||||||
final JSONArray captions = mediaNode.getJSONObject("edge_media_to_caption").getJSONArray("edges");
|
|
||||||
|
|
||||||
final boolean isSlider = mediaNode.has("__typename") && mediaNode.getString("__typename").equals("GraphSidecar");
|
|
||||||
final boolean isVideo = mediaNode.getBoolean("is_video");
|
|
||||||
|
|
||||||
final MediaItemType itemType;
|
|
||||||
if (isSlider) itemType = MediaItemType.MEDIA_TYPE_SLIDER;
|
|
||||||
else if (isVideo) itemType = MediaItemType.MEDIA_TYPE_VIDEO;
|
|
||||||
else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
|
|
||||||
|
|
||||||
final PostModel model = new PostModel(
|
|
||||||
itemType,
|
|
||||||
mediaNode.getString(Constants.EXTRAS_ID),
|
|
||||||
mediaNode.getString("display_url"),
|
|
||||||
mediaNode.getString("thumbnail_src"),
|
|
||||||
mediaNode.getString(Constants.EXTRAS_SHORTCODE),
|
|
||||||
captions.length() > 0 ? captions.getJSONObject(0)
|
|
||||||
.getJSONObject("node")
|
|
||||||
.getString("text")
|
|
||||||
: null,
|
|
||||||
mediaNode.getLong("taken_at_timestamp"),
|
|
||||||
mediaNode.optBoolean("viewer_has_liked"),
|
|
||||||
mediaNode.optBoolean("viewer_has_saved"),
|
|
||||||
mediaNode.isNull("edge_liked_by") ? 0 : mediaNode.getJSONObject("edge_liked_by").getLong("count")
|
|
||||||
);
|
|
||||||
result.add(model);
|
|
||||||
DownloadUtils.checkExistence(downloadDir, customDir, isSlider, model);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.isEmpty() && result.get(result.size() - 1) != null)
|
|
||||||
result.get(result.size() - 1).setPageCursor(hasNextPage, endCursor);
|
|
||||||
}
|
|
||||||
conn.disconnect();
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (logCollector != null) {
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
|
|
||||||
}
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.e(TAG, "Error fetching posts", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(final List<PostModel> postModels) {
|
|
||||||
if (fetchListener != null) fetchListener.onResult(postModels);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,57 @@
|
|||||||
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.ProfileModel;
|
||||||
|
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||||
|
import awais.instagrabber.webservices.ProfileService;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
|
public class ProfilePostFetchService implements PostFetcher.PostFetchService {
|
||||||
|
private static final String TAG = "ProfilePostFetchService";
|
||||||
|
private final ProfileService profileService;
|
||||||
|
private final ProfileModel profileModel;
|
||||||
|
private String nextCursor;
|
||||||
|
private boolean hasNextPage;
|
||||||
|
|
||||||
|
public ProfilePostFetchService(final ProfileModel profileModel) {
|
||||||
|
this.profileModel = profileModel;
|
||||||
|
profileService = ProfileService.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
|
profileService.fetchPosts(profileModel, 30, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final PostsFetchResponse result) {
|
||||||
|
if (result == null) return;
|
||||||
|
nextCursor = result.getNextCursor();
|
||||||
|
hasNextPage = result.hasNextPage();
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onResult(result.getFeedModels());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
// Log.e(TAG, "onFailure: ", t);
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
nextCursor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNextPage() {
|
||||||
|
return hasNextPage;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.enums.PostItemType;
|
||||||
|
import awais.instagrabber.webservices.ProfileService;
|
||||||
|
import awais.instagrabber.webservices.ProfileService.SavedPostsFetchResponse;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
|
public class SavedPostFetchService implements PostFetcher.PostFetchService {
|
||||||
|
private final ProfileService profileService;
|
||||||
|
private final String profileId;
|
||||||
|
private final PostItemType type;
|
||||||
|
|
||||||
|
private String nextMaxId;
|
||||||
|
private boolean moreAvailable;
|
||||||
|
|
||||||
|
public SavedPostFetchService(final String profileId, final PostItemType type) {
|
||||||
|
this.profileId = profileId;
|
||||||
|
this.type = type;
|
||||||
|
profileService = ProfileService.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
|
final ServiceCallback<SavedPostsFetchResponse> callback = new ServiceCallback<SavedPostsFetchResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final SavedPostsFetchResponse result) {
|
||||||
|
if (result == null) return;
|
||||||
|
nextMaxId = result.getNextMaxId();
|
||||||
|
moreAvailable = result.isMoreAvailable();
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onResult(result.getItems());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
// Log.e(TAG, "onFailure: ", t);
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
switch (type) {
|
||||||
|
case LIKED:
|
||||||
|
profileService.fetchLiked(nextMaxId, callback);
|
||||||
|
break;
|
||||||
|
case TAGGED:
|
||||||
|
profileService.fetchTagged(profileId, nextMaxId, callback);
|
||||||
|
break;
|
||||||
|
case SAVED:
|
||||||
|
default:
|
||||||
|
profileService.fetchSaved(nextMaxId, callback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
nextMaxId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNextPage() {
|
||||||
|
return moreAvailable;
|
||||||
|
}
|
||||||
|
}
|
@ -1,139 +0,0 @@
|
|||||||
package awais.instagrabber.asyncs.i;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.PostModel;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.NetworkUtils;
|
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
|
||||||
|
|
||||||
public final class iLikedFetcher extends AsyncTask<Void, Void, List<PostModel>> {
|
|
||||||
private static final String TAG = "iLikedFetcher";
|
|
||||||
|
|
||||||
private final String endCursor;
|
|
||||||
private final FetchListener<List<PostModel>> fetchListener;
|
|
||||||
|
|
||||||
public iLikedFetcher(final FetchListener<List<PostModel>> fetchListener) {
|
|
||||||
this.endCursor = "";
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public iLikedFetcher(final String endCursor, final FetchListener<List<PostModel>> fetchListener) {
|
|
||||||
this.endCursor = endCursor == null ? "" : endCursor;
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<PostModel> doInBackground(final Void... voids) {
|
|
||||||
final String url = "https://i.instagram.com/api/v1/feed/liked/?max_id=" + endCursor;
|
|
||||||
|
|
||||||
List<PostModel> result = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
|
||||||
conn.setUseCaches(false);
|
|
||||||
conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
|
|
||||||
conn.connect();
|
|
||||||
|
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
|
||||||
final JSONObject body = new JSONObject(NetworkUtils.readFromConnection(conn));
|
|
||||||
|
|
||||||
final String endCursor;
|
|
||||||
final boolean hasNextPage;
|
|
||||||
|
|
||||||
if (body.has("more_available")) {
|
|
||||||
hasNextPage = body.optBoolean("more_available");
|
|
||||||
endCursor = hasNextPage ? body.optString("next_max_id") : null;
|
|
||||||
} else {
|
|
||||||
hasNextPage = false;
|
|
||||||
endCursor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final JSONArray edges = body.getJSONArray("items");
|
|
||||||
for (int i = 0; i < edges.length(); ++i) {
|
|
||||||
final JSONObject mediaNode = edges.getJSONObject(i);
|
|
||||||
|
|
||||||
final boolean isSlider = mediaNode.has("carousel_media_count");
|
|
||||||
final boolean isVideo = mediaNode.has("video_duration");
|
|
||||||
|
|
||||||
final MediaItemType itemType;
|
|
||||||
if (isSlider) itemType = MediaItemType.MEDIA_TYPE_SLIDER;
|
|
||||||
else if (isVideo) itemType = MediaItemType.MEDIA_TYPE_VIDEO;
|
|
||||||
else itemType = MediaItemType.MEDIA_TYPE_IMAGE;
|
|
||||||
|
|
||||||
final PostModel model = new PostModel(
|
|
||||||
itemType,
|
|
||||||
mediaNode.getString(Constants.EXTRAS_ID),
|
|
||||||
isSlider ? ResponseBodyUtils.getHighQualityImage(mediaNode.getJSONArray("carousel_media")
|
|
||||||
.getJSONObject(0))
|
|
||||||
: ResponseBodyUtils.getHighQualityImage(mediaNode),
|
|
||||||
isSlider ? ResponseBodyUtils.getLowQualityImage(mediaNode.getJSONArray("carousel_media")
|
|
||||||
.getJSONObject(0))
|
|
||||||
: ResponseBodyUtils.getLowQualityImage(mediaNode),
|
|
||||||
mediaNode.getString("code"),
|
|
||||||
mediaNode.isNull("caption") ? null : mediaNode.getJSONObject("caption").optString("text"),
|
|
||||||
mediaNode.getLong("taken_at"),
|
|
||||||
true,
|
|
||||||
mediaNode.optBoolean("has_viewer_saved"),
|
|
||||||
mediaNode.getLong("like_count"));
|
|
||||||
result.add(model);
|
|
||||||
String username = mediaNode.getJSONObject("user").getString("username");
|
|
||||||
final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
|
|
||||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/" + username) : ""));
|
|
||||||
File customDir = null;
|
|
||||||
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
|
|
||||||
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
|
|
||||||
(Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)
|
|
||||||
? ("/" + username)
|
|
||||||
: ""));
|
|
||||||
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
|
|
||||||
}
|
|
||||||
DownloadUtils.checkExistence(downloadDir, customDir, isSlider, model);
|
|
||||||
}
|
|
||||||
|
|
||||||
final int length = result.size();
|
|
||||||
if (length >= 1 && result.get(length - 1) != null) {
|
|
||||||
result.get(length - 1).setPageCursor(hasNextPage, endCursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.disconnect();
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (logCollector != null) {
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
|
|
||||||
}
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.e(TAG, "", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(final List<PostModel> postModels) {
|
|
||||||
if (fetchListener != null) fetchListener.onResult(postModels);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,207 +0,0 @@
|
|||||||
package awais.instagrabber.asyncs.i;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.ProfileModel;
|
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.NetworkUtils;
|
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
|
||||||
|
|
||||||
public final class iPostFetcher extends AsyncTask<Void, Void, ViewerPostModel[]> {
|
|
||||||
private static final String TAG = "iPostFetcher";
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final FetchListener<ViewerPostModel[]> fetchListener;
|
|
||||||
|
|
||||||
public iPostFetcher(final String id, final FetchListener<ViewerPostModel[]> fetchListener) {
|
|
||||||
this.id = id;
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ViewerPostModel[] doInBackground(final Void... voids) {
|
|
||||||
ViewerPostModel[] result = null;
|
|
||||||
try {
|
|
||||||
final HttpURLConnection conn = (HttpURLConnection) new URL("https://i.instagram.com/api/v1/media/" + id + "/info").openConnection();
|
|
||||||
conn.setUseCaches(false);
|
|
||||||
conn.setRequestProperty("User-Agent", Constants.USER_AGENT);
|
|
||||||
conn.connect();
|
|
||||||
|
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
|
||||||
final JSONObject media = new JSONObject(NetworkUtils.readFromConnection(conn))
|
|
||||||
.getJSONArray("items")
|
|
||||||
.getJSONObject(0);
|
|
||||||
ProfileModel profileModel = null;
|
|
||||||
if (media.has("user")) {
|
|
||||||
final JSONObject user = media.getJSONObject("user");
|
|
||||||
final JSONObject friendshipStatus = user.optJSONObject("friendship_status");
|
|
||||||
boolean following = false;
|
|
||||||
boolean isRestricted = false;
|
|
||||||
boolean outgoingRequest = false;
|
|
||||||
if (friendshipStatus != null) {
|
|
||||||
following = friendshipStatus.optBoolean("following");
|
|
||||||
isRestricted = friendshipStatus.optBoolean("is_restricted");
|
|
||||||
outgoingRequest = friendshipStatus.optBoolean("outgoing_request");
|
|
||||||
}
|
|
||||||
profileModel = new ProfileModel(
|
|
||||||
user.optBoolean("is_private"),
|
|
||||||
user.optBoolean("is_private"),
|
|
||||||
user.optBoolean("is_verified"),
|
|
||||||
user.optString("pk"),
|
|
||||||
user.getString(Constants.EXTRAS_USERNAME),
|
|
||||||
user.optString("fullname"),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
user.getString("profile_pic_url"),
|
|
||||||
null,
|
|
||||||
-1,
|
|
||||||
-1,
|
|
||||||
-1,
|
|
||||||
following,
|
|
||||||
isRestricted,
|
|
||||||
false,
|
|
||||||
outgoingRequest
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (profileModel == null) {
|
|
||||||
return new ViewerPostModel[]{};
|
|
||||||
}
|
|
||||||
|
|
||||||
// to check if file exists
|
|
||||||
final boolean shouldDownloadToUserFolder = Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER);
|
|
||||||
final File downloadDir = new File(
|
|
||||||
Environment.getExternalStorageDirectory(),
|
|
||||||
"Download" + (shouldDownloadToUserFolder ? "/" + profileModel.getUsername() : ""));
|
|
||||||
File customDir = null;
|
|
||||||
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
|
|
||||||
final String customPath = Utils.settingsHelper.getString(FOLDER_PATH)
|
|
||||||
+ (shouldDownloadToUserFolder ? "/" + profileModel.getUsername() : "");
|
|
||||||
if (!TextUtils.isEmpty(customPath)) customDir = new File(customPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
final long timestamp = media.getLong("taken_at");
|
|
||||||
|
|
||||||
final boolean isVideo = media.has("has_audio") && media.optBoolean("has_audio");
|
|
||||||
final boolean isSlider = !media.isNull("carousel_media_count");
|
|
||||||
|
|
||||||
final MediaItemType mediaItemType;
|
|
||||||
if (isSlider) mediaItemType = MediaItemType.MEDIA_TYPE_SLIDER;
|
|
||||||
else if (isVideo) mediaItemType = MediaItemType.MEDIA_TYPE_VIDEO;
|
|
||||||
else mediaItemType = MediaItemType.MEDIA_TYPE_IMAGE;
|
|
||||||
|
|
||||||
final String postCaption;
|
|
||||||
final JSONObject mediaToCaption = media.optJSONObject("caption");
|
|
||||||
if (mediaToCaption == null) postCaption = null;
|
|
||||||
else postCaption = mediaToCaption.optString("text");
|
|
||||||
|
|
||||||
final long commentsCount = media.optLong("comment_count");
|
|
||||||
|
|
||||||
final JSONObject location = media.optJSONObject("location");
|
|
||||||
String locationId = null;
|
|
||||||
String locationName = null;
|
|
||||||
if (location != null) {
|
|
||||||
locationName = location.optString("name");
|
|
||||||
if (location.has("id")) {
|
|
||||||
locationId = location.getString("id");
|
|
||||||
} else if (location.has("pk")) {
|
|
||||||
locationId = location.getString("pk");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// final String locationString = location.optString("id") + "/" + location.optString("slug");
|
|
||||||
if (mediaItemType != MediaItemType.MEDIA_TYPE_SLIDER) {
|
|
||||||
final ViewerPostModel postModel = new ViewerPostModel(
|
|
||||||
mediaItemType,
|
|
||||||
media.getString(Constants.EXTRAS_ID),
|
|
||||||
isVideo ? ResponseBodyUtils.getHighQualityPost(media.optJSONArray("video_versions"), true, true, false)
|
|
||||||
: ResponseBodyUtils.getHighQualityImage(media),
|
|
||||||
media.getString("code"),
|
|
||||||
TextUtils.isEmpty(postCaption) ? null : postCaption,
|
|
||||||
profileModel,
|
|
||||||
isVideo && media.has("view_count") ? media.getLong("view_count") : -1,
|
|
||||||
timestamp, media.optBoolean("has_liked"),
|
|
||||||
media.optBoolean("has_viewer_saved"),
|
|
||||||
media.getLong("like_count"),
|
|
||||||
locationName,
|
|
||||||
locationId);
|
|
||||||
|
|
||||||
postModel.setCommentsCount(commentsCount);
|
|
||||||
|
|
||||||
DownloadUtils.checkExistence(downloadDir, customDir, false, postModel);
|
|
||||||
|
|
||||||
result = new ViewerPostModel[]{postModel};
|
|
||||||
|
|
||||||
} else {
|
|
||||||
final JSONArray children = media.getJSONArray("carousel_media");
|
|
||||||
final ViewerPostModel[] postModels = new ViewerPostModel[children.length()];
|
|
||||||
|
|
||||||
for (int i = 0; i < postModels.length; ++i) {
|
|
||||||
final JSONObject node = children.getJSONObject(i);
|
|
||||||
final boolean isChildVideo = node.has("video_duration");
|
|
||||||
|
|
||||||
postModels[i] = new ViewerPostModel(
|
|
||||||
isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
|
|
||||||
: MediaItemType.MEDIA_TYPE_IMAGE,
|
|
||||||
media.getString(Constants.EXTRAS_ID),
|
|
||||||
isChildVideo ? ResponseBodyUtils.getHighQualityPost(node.optJSONArray("video_versions"), true, true, false)
|
|
||||||
: ResponseBodyUtils.getHighQualityImage(node),
|
|
||||||
media.getString("code"),
|
|
||||||
postCaption,
|
|
||||||
profileModel,
|
|
||||||
-1,
|
|
||||||
timestamp, media.optBoolean("has_liked"),
|
|
||||||
media.optBoolean("has_viewer_saved"),
|
|
||||||
media.getLong("like_count"),
|
|
||||||
locationName,
|
|
||||||
locationId);
|
|
||||||
postModels[i].setSliderDisplayUrl(ResponseBodyUtils.getHighQualityImage(node));
|
|
||||||
DownloadUtils.checkExistence(downloadDir, customDir, true, postModels[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
postModels[0].setCommentsCount(commentsCount);
|
|
||||||
result = postModels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.disconnect();
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_POST_FETCHER, "doInBackground (i)");
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.e(TAG, "", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
if (fetchListener != null) fetchListener.doBefore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(final ViewerPostModel[] postModels) {
|
|
||||||
if (fetchListener != null) fetchListener.onResult(postModels);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package awais.instagrabber.asyncs.i;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.DiscoverTopicModel;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.LocaleUtils;
|
|
||||||
import awais.instagrabber.utils.NetworkUtils;
|
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
|
||||||
|
|
||||||
public final class iTopicFetcher extends AsyncTask<Void, Void, DiscoverTopicModel> {
|
|
||||||
private final FetchListener<DiscoverTopicModel> fetchListener;
|
|
||||||
|
|
||||||
public iTopicFetcher(final FetchListener<DiscoverTopicModel> fetchListener) {
|
|
||||||
this.fetchListener = fetchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected DiscoverTopicModel doInBackground(final Void... voids) {
|
|
||||||
final String url = "https://i.instagram.com/api/v1/discover/topical_explore/";
|
|
||||||
|
|
||||||
DiscoverTopicModel result = null;
|
|
||||||
try {
|
|
||||||
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
|
||||||
conn.setUseCaches(false);
|
|
||||||
conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
|
|
||||||
conn.setRequestProperty("Accept-Language", LocaleUtils.getCurrentLocale().getLanguage() + ",en-US;q=0.8");
|
|
||||||
conn.connect();
|
|
||||||
|
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
|
||||||
final JSONObject body = new JSONObject(NetworkUtils.readFromConnection(conn));
|
|
||||||
|
|
||||||
final JSONArray edges = body.getJSONArray("clusters");
|
|
||||||
String[] names = new String[edges.length()], ids = new String[edges.length()];
|
|
||||||
for (int i = 0; i < names.length; ++i) {
|
|
||||||
final JSONObject mediaNode = edges.getJSONObject(i);
|
|
||||||
ids[i] = mediaNode.getString("id");
|
|
||||||
names[i] = mediaNode.getString("title");
|
|
||||||
}
|
|
||||||
|
|
||||||
result = new DiscoverTopicModel(ids, names, body.getString("rank_token"));
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.disconnect();
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.ASYNC_DISCOVER_TOPICS_FETCHER, "doInBackground");
|
|
||||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(final DiscoverTopicModel discoverTopicModel) {
|
|
||||||
if (fetchListener != null) fetchListener.onResult(discoverTopicModel);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,10 +2,7 @@ package awais.instagrabber.customviews;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapShader;
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -17,36 +14,27 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyInflater;
|
|||||||
import com.facebook.drawee.generic.RoundingParams;
|
import com.facebook.drawee.generic.RoundingParams;
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
import com.facebook.drawee.view.SimpleDraweeView;
|
||||||
|
|
||||||
public final class CircularImageView extends SimpleDraweeView {
|
import awais.instagrabber.R;
|
||||||
private final int borderSize = 8;
|
|
||||||
private int color = Color.TRANSPARENT;
|
|
||||||
private final Paint paint = new Paint();
|
|
||||||
private final Paint paintBorder = new Paint();
|
|
||||||
private BitmapShader shader;
|
|
||||||
private Bitmap bitmap;
|
|
||||||
|
|
||||||
|
public class CircularImageView extends SimpleDraweeView {
|
||||||
public CircularImageView(Context context, GenericDraweeHierarchy hierarchy) {
|
public CircularImageView(Context context, GenericDraweeHierarchy hierarchy) {
|
||||||
super(context);
|
super(context);
|
||||||
setHierarchy(hierarchy);
|
setHierarchy(hierarchy);
|
||||||
setup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CircularImageView(final Context context) {
|
public CircularImageView(final Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
inflateHierarchy(context, null);
|
inflateHierarchy(context, null);
|
||||||
setup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CircularImageView(final Context context, final AttributeSet attrs) {
|
public CircularImageView(final Context context, final AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
inflateHierarchy(context, attrs);
|
inflateHierarchy(context, attrs);
|
||||||
setup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CircularImageView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
public CircularImageView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
inflateHierarchy(context, attrs);
|
inflateHierarchy(context, attrs);
|
||||||
setup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
|
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
|
||||||
@ -58,77 +46,12 @@ public final class CircularImageView extends SimpleDraweeView {
|
|||||||
GenericDraweeHierarchyInflater.updateBuilder(builder, context, attrs);
|
GenericDraweeHierarchyInflater.updateBuilder(builder, context, attrs);
|
||||||
setAspectRatio(builder.getDesiredAspectRatio());
|
setAspectRatio(builder.getDesiredAspectRatio());
|
||||||
setHierarchy(builder.build());
|
setHierarchy(builder.build());
|
||||||
|
setBackgroundResource(R.drawable.shape_oval_light);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup() {
|
|
||||||
// paint.setAntiAlias(true);
|
|
||||||
// paintBorder.setColor(color);
|
|
||||||
// paintBorder.setAntiAlias(true);
|
|
||||||
//
|
|
||||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
// setOutlineProvider(new ViewOutlineProvider() {
|
|
||||||
// private int viewHeight;
|
|
||||||
// private int viewWidth;
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void getOutline(final View view, final Outline outline) {
|
|
||||||
// if (viewHeight == 0) viewHeight = getHeight();
|
|
||||||
// if (viewWidth == 0) viewWidth = getWidth();
|
|
||||||
// outline.setRoundRect(borderSize, borderSize, viewWidth - borderSize, viewHeight - borderSize, viewHeight >> 1);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources())
|
|
||||||
// .setRoundingParams(RoundingParams.)
|
|
||||||
// .build();
|
|
||||||
// setHierarchy(hierarchy);
|
|
||||||
// invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public void onDraw(final Canvas canvas) {
|
|
||||||
// final BitmapDrawable bitmapDrawable = (BitmapDrawable) getDrawable();
|
|
||||||
// if (bitmapDrawable != null) {
|
|
||||||
// final Bitmap prevBitmap = bitmap;
|
|
||||||
// bitmap = bitmapDrawable.getBitmap();
|
|
||||||
// final boolean changed = prevBitmap != bitmap;
|
|
||||||
// if (bitmap != null) {
|
|
||||||
// final int width = getWidth();
|
|
||||||
// final int height = getHeight();
|
|
||||||
//
|
|
||||||
// if (shader == null || changed) {
|
|
||||||
// shader = new BitmapShader(Bitmap.createScaledBitmap(bitmap, width, height, true), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
|
||||||
// paint.setShader(shader);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (changed) color = 0;
|
|
||||||
// paintBorder.setColor(color);
|
|
||||||
//
|
|
||||||
// final int circleCenter = (width - borderSize) / 2;
|
|
||||||
// final int position = circleCenter + (borderSize / 2);
|
|
||||||
// canvas.drawCircle(position, position, position - 4.0f, paintBorder);
|
|
||||||
// canvas.drawCircle(position, position, circleCenter - 4.0f, paint);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// protected void onAttachedToWindow() {
|
|
||||||
// super.onAttachedToWindow();
|
|
||||||
// setLayerType(LAYER_TYPE_HARDWARE, null);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// protected void onDetachedFromWindow() {
|
|
||||||
// setLayerType(LAYER_TYPE_NONE, null);
|
|
||||||
// super.onDetachedFromWindow();
|
|
||||||
// }
|
|
||||||
|
|
||||||
public void setStoriesBorder() {
|
public void setStoriesBorder() {
|
||||||
this.color = Color.GREEN;
|
// private final int borderSize = 8;
|
||||||
// invalidate();
|
final int color = Color.GREEN;
|
||||||
// final RoundingParams roundingParams = RoundingParams.fromCornersRadius(5f);
|
|
||||||
//
|
|
||||||
RoundingParams roundingParams = getHierarchy().getRoundingParams();
|
RoundingParams roundingParams = getHierarchy().getRoundingParams();
|
||||||
if (roundingParams == null) {
|
if (roundingParams == null) {
|
||||||
roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY);
|
roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY);
|
||||||
|
@ -0,0 +1,330 @@
|
|||||||
|
package awais.instagrabber.customviews;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.lifecycle.ViewModelStoreOwner;
|
||||||
|
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||||
|
import androidx.transition.ChangeBounds;
|
||||||
|
import androidx.transition.Transition;
|
||||||
|
import androidx.transition.TransitionManager;
|
||||||
|
import androidx.work.Data;
|
||||||
|
import androidx.work.WorkInfo;
|
||||||
|
import androidx.work.WorkManager;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
|
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
||||||
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
|
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtBottom;
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
import awais.instagrabber.viewmodels.FeedViewModel;
|
||||||
|
import awais.instagrabber.workers.DownloadWorker;
|
||||||
|
|
||||||
|
public class PostsRecyclerView extends RecyclerView {
|
||||||
|
private static final String TAG = "PostsRecyclerView";
|
||||||
|
|
||||||
|
private StaggeredGridLayoutManager layoutManager;
|
||||||
|
private PostsLayoutPreferences layoutPreferences;
|
||||||
|
private PostFetcher.PostFetchService postFetchService;
|
||||||
|
private Transition transition;
|
||||||
|
private PostFetcher postFetcher;
|
||||||
|
private ViewModelStoreOwner viewModelStoreOwner;
|
||||||
|
private FeedAdapterV2 feedAdapter;
|
||||||
|
private LifecycleOwner lifeCycleOwner;
|
||||||
|
private FeedViewModel feedViewModel;
|
||||||
|
private boolean initCalled = false;
|
||||||
|
private GridSpacingItemDecoration gridSpacingItemDecoration;
|
||||||
|
private RecyclerLazyLoaderAtBottom lazyLoader;
|
||||||
|
private FeedAdapterV2.FeedItemCallback feedItemCallback;
|
||||||
|
private boolean shouldScrollToTop;
|
||||||
|
private FeedAdapterV2.SelectionModeCallback selectionModeCallback;
|
||||||
|
|
||||||
|
private final List<FetchStatusChangeListener> fetchStatusChangeListeners = new ArrayList<>();
|
||||||
|
|
||||||
|
private final FetchListener<List<FeedModel>> fetchListener = new FetchListener<List<FeedModel>>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(final List<FeedModel> result) {
|
||||||
|
final int currentPage = lazyLoader.getCurrentPage();
|
||||||
|
if (currentPage == 0) {
|
||||||
|
feedViewModel.getList().postValue(result);
|
||||||
|
shouldScrollToTop = true;
|
||||||
|
dispatchFetchStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final List<FeedModel> models = feedViewModel.getList().getValue();
|
||||||
|
final List<FeedModel> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
|
||||||
|
modelsCopy.addAll(result);
|
||||||
|
feedViewModel.getList().postValue(modelsCopy);
|
||||||
|
dispatchFetchStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
Log.e(TAG, "onFailure: ", t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) {
|
||||||
|
@Override
|
||||||
|
protected int getVerticalSnapPreference() {
|
||||||
|
return LinearSmoothScroller.SNAP_TO_START;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public PostsRecyclerView(@NonNull final Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView(@NonNull final Context context, @Nullable final AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView setViewModelStoreOwner(final ViewModelStoreOwner owner) {
|
||||||
|
if (initCalled) {
|
||||||
|
throw new IllegalArgumentException("init already called!");
|
||||||
|
}
|
||||||
|
this.viewModelStoreOwner = owner;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView setLifeCycleOwner(final LifecycleOwner lifeCycleOwner) {
|
||||||
|
if (initCalled) {
|
||||||
|
throw new IllegalArgumentException("init already called!");
|
||||||
|
}
|
||||||
|
this.lifeCycleOwner = lifeCycleOwner;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView setPostFetchService(final PostFetcher.PostFetchService postFetchService) {
|
||||||
|
if (initCalled) {
|
||||||
|
throw new IllegalArgumentException("init already called!");
|
||||||
|
}
|
||||||
|
this.postFetchService = postFetchService;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView setFeedItemCallback(@NonNull final FeedAdapterV2.FeedItemCallback feedItemCallback) {
|
||||||
|
this.feedItemCallback = feedItemCallback;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView setSelectionModeCallback(@NonNull final FeedAdapterV2.SelectionModeCallback selectionModeCallback) {
|
||||||
|
this.selectionModeCallback = selectionModeCallback;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView setLayoutPreferences(final PostsLayoutPreferences layoutPreferences) {
|
||||||
|
this.layoutPreferences = layoutPreferences;
|
||||||
|
if (initCalled) {
|
||||||
|
if (layoutPreferences == null) return this;
|
||||||
|
feedAdapter.setLayoutPreferences(layoutPreferences);
|
||||||
|
updateLayout();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
initCalled = true;
|
||||||
|
if (viewModelStoreOwner == null) {
|
||||||
|
throw new IllegalArgumentException("ViewModelStoreOwner cannot be null");
|
||||||
|
} else if (lifeCycleOwner == null) {
|
||||||
|
throw new IllegalArgumentException("LifecycleOwner cannot be null");
|
||||||
|
} else if (postFetchService == null) {
|
||||||
|
throw new IllegalArgumentException("PostFetchService cannot be null");
|
||||||
|
}
|
||||||
|
if (layoutPreferences == null) {
|
||||||
|
layoutPreferences = PostsLayoutPreferences.builder().build();
|
||||||
|
// Utils.settingsHelper.putString(Constants.PREF_POSTS_LAYOUT, layoutPreferences.getJson());
|
||||||
|
}
|
||||||
|
gridSpacingItemDecoration = new GridSpacingItemDecoration(Utils.convertDpToPx(2));
|
||||||
|
initTransition();
|
||||||
|
initAdapter();
|
||||||
|
initLayoutManager();
|
||||||
|
initSelf();
|
||||||
|
initDownloadWorkerListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTransition() {
|
||||||
|
transition = new ChangeBounds();
|
||||||
|
transition.setDuration(300);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initLayoutManager() {
|
||||||
|
layoutManager = new StaggeredGridLayoutManager(layoutPreferences.getColCount(), StaggeredGridLayoutManager.VERTICAL);
|
||||||
|
setLayoutManager(layoutManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAdapter() {
|
||||||
|
feedAdapter = new FeedAdapterV2(layoutPreferences, feedItemCallback, selectionModeCallback);
|
||||||
|
feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
||||||
|
setAdapter(feedAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSelf() {
|
||||||
|
feedViewModel = new ViewModelProvider(viewModelStoreOwner).get(FeedViewModel.class);
|
||||||
|
feedViewModel.getList().observe(lifeCycleOwner, list -> feedAdapter.submitList(list, () -> {
|
||||||
|
if (!shouldScrollToTop) return;
|
||||||
|
smoothScrollToPosition(0);
|
||||||
|
shouldScrollToTop = false;
|
||||||
|
}));
|
||||||
|
postFetcher = new PostFetcher(postFetchService, fetchListener);
|
||||||
|
if (layoutPreferences.getHasGap()) {
|
||||||
|
addItemDecoration(gridSpacingItemDecoration);
|
||||||
|
}
|
||||||
|
setHasFixedSize(true);
|
||||||
|
setNestedScrollingEnabled(true);
|
||||||
|
lazyLoader = new RecyclerLazyLoaderAtBottom(layoutManager, (page) -> {
|
||||||
|
if (postFetcher.hasMore()) {
|
||||||
|
postFetcher.fetch();
|
||||||
|
dispatchFetchStatus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addOnScrollListener(lazyLoader);
|
||||||
|
postFetcher.fetch();
|
||||||
|
dispatchFetchStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDownloadWorkerListener() {
|
||||||
|
WorkManager.getInstance(getContext())
|
||||||
|
.getWorkInfosByTagLiveData("download")
|
||||||
|
.observe(lifeCycleOwner, workInfoList -> {
|
||||||
|
for (final WorkInfo workInfo : workInfoList) {
|
||||||
|
if (workInfo == null) continue;
|
||||||
|
final Data progress = workInfo.getProgress();
|
||||||
|
final float progressPercent = progress.getFloat(DownloadWorker.PROGRESS, 0);
|
||||||
|
if (progressPercent != 100) continue;
|
||||||
|
final String url = progress.getString(DownloadWorker.URL);
|
||||||
|
final List<FeedModel> feedModels = feedViewModel.getList().getValue();
|
||||||
|
for (int i = 0; i < feedModels.size(); i++) {
|
||||||
|
final FeedModel feedModel = feedModels.get(i);
|
||||||
|
final List<String> displayUrls = getDisplayUrl(feedModel);
|
||||||
|
if (displayUrls.contains(url)) {
|
||||||
|
feedAdapter.notifyItemChanged(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getDisplayUrl(final FeedModel feedModel) {
|
||||||
|
List<String> urls = Collections.emptyList();
|
||||||
|
switch (feedModel.getItemType()) {
|
||||||
|
case MEDIA_TYPE_IMAGE:
|
||||||
|
case MEDIA_TYPE_VIDEO:
|
||||||
|
urls = Collections.singletonList(feedModel.getDisplayUrl());
|
||||||
|
break;
|
||||||
|
case MEDIA_TYPE_SLIDER:
|
||||||
|
final List<PostChild> sliderItems = feedModel.getSliderItems();
|
||||||
|
if (sliderItems != null) {
|
||||||
|
final ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||||
|
for (final PostChild child : sliderItems) {
|
||||||
|
builder.add(child.getDisplayUrl());
|
||||||
|
}
|
||||||
|
urls = builder.build();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLayout() {
|
||||||
|
post(() -> {
|
||||||
|
TransitionManager.beginDelayedTransition(this, transition);
|
||||||
|
feedAdapter.notifyDataSetChanged();
|
||||||
|
if (!layoutPreferences.getHasGap()) {
|
||||||
|
removeItemDecoration(gridSpacingItemDecoration);
|
||||||
|
} else {
|
||||||
|
addItemDecoration(gridSpacingItemDecoration);
|
||||||
|
}
|
||||||
|
if (layoutPreferences.getType() == PostsLayoutPreferences.PostsLayoutType.LINEAR) {
|
||||||
|
if (layoutManager.getSpanCount() != 1) {
|
||||||
|
layoutManager.setSpanCount(1);
|
||||||
|
setAdapter(null);
|
||||||
|
setAdapter(feedAdapter);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boolean shouldRedraw = layoutManager.getSpanCount() == 1;
|
||||||
|
layoutManager.setSpanCount(layoutPreferences.getColCount());
|
||||||
|
if (shouldRedraw) {
|
||||||
|
setAdapter(null);
|
||||||
|
setAdapter(feedAdapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
lazyLoader.resetState();
|
||||||
|
postFetcher.reset();
|
||||||
|
postFetcher.fetch();
|
||||||
|
dispatchFetchStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFetching() {
|
||||||
|
return postFetcher != null && postFetcher.isFetching();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsRecyclerView addFetchStatusChangeListener(final FetchStatusChangeListener fetchStatusChangeListener) {
|
||||||
|
if (fetchStatusChangeListener == null) return this;
|
||||||
|
fetchStatusChangeListeners.add(fetchStatusChangeListener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeFetchStatusListener(final FetchStatusChangeListener fetchStatusChangeListener) {
|
||||||
|
if (fetchStatusChangeListener == null) return;
|
||||||
|
fetchStatusChangeListeners.remove(fetchStatusChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchFetchStatus() {
|
||||||
|
for (final FetchStatusChangeListener listener : fetchStatusChangeListeners) {
|
||||||
|
listener.onFetchStatusChange(isFetching());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostsLayoutPreferences getLayoutPreferences() {
|
||||||
|
return layoutPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endSelection() {
|
||||||
|
feedAdapter.endSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface FetchStatusChangeListener {
|
||||||
|
void onFetchStatusChange(boolean fetching);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow() {
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
lifeCycleOwner = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void smoothScrollToPosition(final int position) {
|
||||||
|
smoothScroller.setTargetPosition(position);
|
||||||
|
layoutManager.startSmoothScroll(smoothScroller);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
package awais.instagrabber.customviews;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.DimenRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.facebook.drawee.generic.GenericDraweeHierarchy;
|
||||||
|
import com.facebook.drawee.generic.RoundingParams;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
|
||||||
|
public final class ProfilePicView extends CircularImageView {
|
||||||
|
private static final String TAG = "ProfilePicView";
|
||||||
|
|
||||||
|
private Size size;
|
||||||
|
private int dimensionPixelSize;
|
||||||
|
|
||||||
|
public ProfilePicView(Context context, GenericDraweeHierarchy hierarchy) {
|
||||||
|
super(context);
|
||||||
|
setHierarchy(hierarchy);
|
||||||
|
size = Size.REGULAR;
|
||||||
|
updateLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfilePicView(final Context context) {
|
||||||
|
super(context);
|
||||||
|
size = Size.REGULAR;
|
||||||
|
updateLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfilePicView(final Context context, final AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
parseAttrs(context, attrs);
|
||||||
|
updateLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfilePicView(final Context context,
|
||||||
|
final AttributeSet attrs,
|
||||||
|
final int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
parseAttrs(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseAttrs(final Context context, final AttributeSet attrs) {
|
||||||
|
final TypedArray a = context.getTheme().obtainStyledAttributes(
|
||||||
|
attrs,
|
||||||
|
R.styleable.ProfilePicView,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
|
try {
|
||||||
|
final int sizeValue = a.getInt(R.styleable.ProfilePicView_size, Size.REGULAR.getValue());
|
||||||
|
size = Size.valueOf(sizeValue);
|
||||||
|
} finally {
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLayout() {
|
||||||
|
@DimenRes final int dimenRes;
|
||||||
|
switch (size) {
|
||||||
|
case SMALL:
|
||||||
|
dimenRes = R.dimen.profile_pic_size_small;
|
||||||
|
break;
|
||||||
|
case TINY:
|
||||||
|
dimenRes = R.dimen.profile_pic_size_tiny;
|
||||||
|
break;
|
||||||
|
case LARGE:
|
||||||
|
dimenRes = R.dimen.profile_pic_size_large;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case REGULAR:
|
||||||
|
dimenRes = R.dimen.profile_pic_size_regular;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||||
|
if (layoutParams == null) {
|
||||||
|
layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
dimensionPixelSize = getResources().getDimensionPixelSize(dimenRes);
|
||||||
|
layoutParams.width = dimensionPixelSize;
|
||||||
|
layoutParams.height = dimensionPixelSize;
|
||||||
|
|
||||||
|
// invalidate();
|
||||||
|
// requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStoriesBorder() {
|
||||||
|
// private final int borderSize = 8;
|
||||||
|
final int color = Color.GREEN;
|
||||||
|
RoundingParams roundingParams = getHierarchy().getRoundingParams();
|
||||||
|
if (roundingParams == null) {
|
||||||
|
roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY);
|
||||||
|
}
|
||||||
|
roundingParams.setBorder(color, 5.0f);
|
||||||
|
getHierarchy().setRoundingParams(roundingParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Size {
|
||||||
|
TINY(0),
|
||||||
|
SMALL(1),
|
||||||
|
REGULAR(2),
|
||||||
|
LARGE(3);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
private static Map<Integer, Size> map = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (Size size : Size.values()) {
|
||||||
|
map.put(size.value, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Size(final int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Size valueOf(final int value) {
|
||||||
|
final Size size = map.get(value);
|
||||||
|
return size != null ? size : Size.REGULAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
setMeasuredDimension(dimensionPixelSize, dimensionPixelSize);
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,6 @@ public final class RamboTextView extends AppCompatTextView {
|
|||||||
private ClickableSpan clickableSpanUnderTouchOnActionDown;
|
private ClickableSpan clickableSpanUnderTouchOnActionDown;
|
||||||
private MentionClickListener mentionClickListener;
|
private MentionClickListener mentionClickListener;
|
||||||
private boolean isUrlHighlighted;
|
private boolean isUrlHighlighted;
|
||||||
private boolean isExpandable;
|
|
||||||
private boolean isExpanded;
|
private boolean isExpanded;
|
||||||
private OnLongClickListener longClickListener;
|
private OnLongClickListener longClickListener;
|
||||||
|
|
||||||
@ -59,13 +58,13 @@ public final class RamboTextView extends AppCompatTextView {
|
|||||||
this.mentionClickListener = mentionClickListener;
|
this.mentionClickListener = mentionClickListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCaptionIsExpandable(final boolean isExpandable) {
|
// public void setCaptionIsExpandable(final boolean isExpandable) {
|
||||||
this.isExpandable = isExpandable;
|
// this.isExpandable = isExpandable;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void setCaptionIsExpanded(final boolean isExpanded) {
|
// public void setCaptionIsExpanded(final boolean isExpanded) {
|
||||||
this.isExpanded = isExpanded;
|
// this.isExpanded = isExpanded;
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOnLongClickListener(@Nullable final OnLongClickListener l) {
|
public void setOnLongClickListener(@Nullable final OnLongClickListener l) {
|
||||||
@ -98,7 +97,7 @@ public final class RamboTextView extends AppCompatTextView {
|
|||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
|
final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
|
||||||
handler.postDelayed(longPressRunnable, longPressTimeout);
|
handler.postDelayed(longPressRunnable, longPressTimeout);
|
||||||
if (feedModel != null) feedModel.setMentionClicked(false);
|
// if (feedModel != null) feedModel.setMentionClicked(false);
|
||||||
if (clickableSpanUnderTouch != null) {
|
if (clickableSpanUnderTouch != null) {
|
||||||
highlightUrl(clickableSpanUnderTouch, spanText);
|
highlightUrl(clickableSpanUnderTouch, spanText);
|
||||||
}
|
}
|
||||||
@ -107,19 +106,19 @@ public final class RamboTextView extends AppCompatTextView {
|
|||||||
handler.removeCallbacks(longPressRunnable);
|
handler.removeCallbacks(longPressRunnable);
|
||||||
if (touchStartedOverAClickableSpan && clickableSpanUnderTouch == clickableSpanUnderTouchOnActionDown) {
|
if (touchStartedOverAClickableSpan && clickableSpanUnderTouch == clickableSpanUnderTouchOnActionDown) {
|
||||||
dispatchUrlClick(spanText, clickableSpanUnderTouch);
|
dispatchUrlClick(spanText, clickableSpanUnderTouch);
|
||||||
if (feedModel != null) feedModel.setMentionClicked(true);
|
// if (feedModel != null) feedModel.setMentionClicked(true);
|
||||||
}
|
}
|
||||||
cleanupOnTouchUp(spanText);
|
cleanupOnTouchUp(spanText);
|
||||||
return super.onTouchEvent(event);
|
return super.onTouchEvent(event);
|
||||||
case MotionEvent.ACTION_MOVE:
|
case MotionEvent.ACTION_MOVE:
|
||||||
// handler.removeCallbacks(longPressRunnable);
|
// handler.removeCallbacks(longPressRunnable);
|
||||||
if (feedModel != null) feedModel.setMentionClicked(false);
|
// if (feedModel != null) feedModel.setMentionClicked(false);
|
||||||
if (clickableSpanUnderTouch != null) highlightUrl(clickableSpanUnderTouch, spanText);
|
if (clickableSpanUnderTouch != null) highlightUrl(clickableSpanUnderTouch, spanText);
|
||||||
else removeUrlHighlightColor(spanText);
|
else removeUrlHighlightColor(spanText);
|
||||||
return super.onTouchEvent(event);
|
return super.onTouchEvent(event);
|
||||||
case MotionEvent.ACTION_CANCEL:
|
case MotionEvent.ACTION_CANCEL:
|
||||||
handler.removeCallbacks(longPressRunnable);
|
handler.removeCallbacks(longPressRunnable);
|
||||||
if (feedModel != null) feedModel.setMentionClicked(false);
|
// if (feedModel != null) feedModel.setMentionClicked(false);
|
||||||
cleanupOnTouchUp(spanText);
|
cleanupOnTouchUp(spanText);
|
||||||
return super.onTouchEvent(event);
|
return super.onTouchEvent(event);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,149 @@
|
|||||||
|
package awais.instagrabber.customviews;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.github.armcha.autolink.AutoLinkItem;
|
||||||
|
import io.github.armcha.autolink.AutoLinkTextView;
|
||||||
|
import io.github.armcha.autolink.MODE_EMAIL;
|
||||||
|
import io.github.armcha.autolink.MODE_HASHTAG;
|
||||||
|
import io.github.armcha.autolink.MODE_MENTION;
|
||||||
|
import io.github.armcha.autolink.MODE_URL;
|
||||||
|
import io.github.armcha.autolink.Mode;
|
||||||
|
|
||||||
|
public class RamboTextViewV2 extends AutoLinkTextView {
|
||||||
|
private final List<OnMentionClickListener> onMentionClickListeners = new ArrayList<>();
|
||||||
|
private final List<OnHashtagClickListener> onHashtagClickListeners = new ArrayList<>();
|
||||||
|
private final List<OnURLClickListener> onURLClickListeners = new ArrayList<>();
|
||||||
|
private final List<OnEmailClickListener> onEmailClickListeners = new ArrayList<>();
|
||||||
|
|
||||||
|
public RamboTextViewV2(@NonNull final Context context,
|
||||||
|
@Nullable final AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
addAutoLinkMode(MODE_HASHTAG.INSTANCE, MODE_MENTION.INSTANCE, MODE_EMAIL.INSTANCE, MODE_URL.INSTANCE);
|
||||||
|
onAutoLinkClick(autoLinkItem -> {
|
||||||
|
final Mode mode = autoLinkItem.getMode();
|
||||||
|
if (mode.equals(MODE_MENTION.INSTANCE)) {
|
||||||
|
for (final OnMentionClickListener onMentionClickListener : onMentionClickListeners) {
|
||||||
|
onMentionClickListener.onMentionClick(autoLinkItem);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mode.equals(MODE_HASHTAG.INSTANCE)) {
|
||||||
|
for (final OnHashtagClickListener onHashtagClickListener : onHashtagClickListeners) {
|
||||||
|
onHashtagClickListener.onHashtagClick(autoLinkItem);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mode.equals(MODE_URL.INSTANCE)) {
|
||||||
|
for (final OnURLClickListener onURLClickListener : onURLClickListeners) {
|
||||||
|
onURLClickListener.onURLClick(autoLinkItem);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mode.equals(MODE_EMAIL.INSTANCE)) {
|
||||||
|
for (final OnEmailClickListener onEmailClickListener : onEmailClickListeners) {
|
||||||
|
onEmailClickListener.onEmailClick(autoLinkItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {
|
||||||
|
if (onMentionClickListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onMentionClickListeners.add(onMentionClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOnMentionClickListener(final OnMentionClickListener onMentionClickListener) {
|
||||||
|
if (onMentionClickListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onMentionClickListeners.remove(onMentionClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearOnMentionClickListeners() {
|
||||||
|
onMentionClickListeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOnHashtagListener(final OnHashtagClickListener onHashtagClickListener) {
|
||||||
|
if (onHashtagClickListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onHashtagClickListeners.add(onHashtagClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOnHashtagListener(final OnHashtagClickListener onHashtagClickListener) {
|
||||||
|
if (onHashtagClickListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onHashtagClickListeners.remove(onHashtagClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearOnHashtagClickListeners() {
|
||||||
|
onHashtagClickListeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOnURLClickListener(final OnURLClickListener onURLClickListener) {
|
||||||
|
if (onURLClickListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onURLClickListeners.add(onURLClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOnURLClickListener(final OnURLClickListener onURLClickListener) {
|
||||||
|
if (onURLClickListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onURLClickListeners.remove(onURLClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearOnURLClickListeners() {
|
||||||
|
onURLClickListeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOnEmailClickListener(final OnEmailClickListener onEmailClickListener) {
|
||||||
|
if (onEmailClickListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onEmailClickListeners.add(onEmailClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOnEmailClickListener(final OnEmailClickListener onEmailClickListener) {
|
||||||
|
if (onEmailClickListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onEmailClickListeners.remove(onEmailClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearOnEmailClickListeners() {
|
||||||
|
onEmailClickListeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnMentionClickListener {
|
||||||
|
void onMentionClick(final AutoLinkItem autoLinkItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnHashtagClickListener {
|
||||||
|
void onHashtagClick(final AutoLinkItem autoLinkItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnURLClickListener {
|
||||||
|
void onURLClick(final AutoLinkItem autoLinkItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnEmailClickListener {
|
||||||
|
void onEmailClick(final AutoLinkItem autoLinkItem);
|
||||||
|
}
|
||||||
|
}
|
@ -1,199 +0,0 @@
|
|||||||
package awais.instagrabber.customviews;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.ContextWrapper;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.Window;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.view.GravityCompat;
|
|
||||||
import androidx.core.view.ViewCompat;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import androidx.viewpager.widget.PagerAdapter;
|
|
||||||
import androidx.viewpager.widget.ViewPager;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
|
|
||||||
public final class RemixDrawerLayout extends MouseDrawer implements MouseDrawer.DrawerListener {
|
|
||||||
private final FrameLayout frameLayout;
|
|
||||||
private View drawerView;
|
|
||||||
private RecyclerView highlightsList, feedPosts, feedStories;
|
|
||||||
private float startX;
|
|
||||||
|
|
||||||
public RemixDrawerLayout(@NonNull final Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemixDrawerLayout(@NonNull final Context context, @Nullable final AttributeSet attrs) {
|
|
||||||
this(context, attrs, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemixDrawerLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
|
|
||||||
super.setDrawerElevation(getDrawerElevation());
|
|
||||||
|
|
||||||
addDrawerListener(this);
|
|
||||||
|
|
||||||
frameLayout = new FrameLayout(context);
|
|
||||||
frameLayout.setPadding(0, 0, 0, 0);
|
|
||||||
super.addView(frameLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addView(@NonNull final View child, final ViewGroup.LayoutParams params) {
|
|
||||||
child.setLayoutParams(params);
|
|
||||||
addView(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addView(@NonNull final View child) {
|
|
||||||
if (child.getTag() != null) super.addView(child);
|
|
||||||
else frameLayout.addView(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onInterceptTouchEvent(@NonNull final MotionEvent ev) {
|
|
||||||
final float x = ev.getX();
|
|
||||||
final float y = ev.getY();
|
|
||||||
|
|
||||||
// another one of my own weird hack thingies to make this app work
|
|
||||||
if (feedPosts == null) feedPosts = findViewById(R.id.feedPosts);
|
|
||||||
if (feedPosts != null) {
|
|
||||||
for (int i = 0; i < feedPosts.getChildCount(); ++i) {
|
|
||||||
final View viewHolder = feedPosts.getChildAt(i);
|
|
||||||
final View mediaList = viewHolder.findViewById(R.id.media_list);
|
|
||||||
if (mediaList instanceof ViewPager) {
|
|
||||||
final ViewPager viewPager = (ViewPager) mediaList;
|
|
||||||
|
|
||||||
final Rect rect = new Rect();
|
|
||||||
viewPager.getGlobalVisibleRect(rect);
|
|
||||||
|
|
||||||
final boolean touchIsInMediaList = rect.contains((int) x, (int) y);
|
|
||||||
if (touchIsInMediaList) {
|
|
||||||
final PagerAdapter adapter = viewPager.getAdapter();
|
|
||||||
final int count = adapter != null ? adapter.getCount() : 0;
|
|
||||||
if (count < 1 || viewPager.getCurrentItem() != count - 1) return false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// thanks to Fede @ https://stackoverflow.com/questions/6920137/android-viewpager-and-horizontalscrollview/7258579#7258579
|
|
||||||
if (highlightsList == null) highlightsList = findViewById(R.id.highlightsList);
|
|
||||||
if (highlightsList != null) {
|
|
||||||
final Boolean result = handleHorizontalRecyclerView(ev, highlightsList);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (feedStories == null) feedStories = findViewById(R.id.feedStories);
|
|
||||||
if (feedStories != null) {
|
|
||||||
final Boolean result = handleHorizontalRecyclerView(ev, feedStories);
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onInterceptTouchEvent(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Boolean handleHorizontalRecyclerView(@NonNull final MotionEvent ev, final RecyclerView view) {
|
|
||||||
final float x = ev.getX();
|
|
||||||
final float y = ev.getY();
|
|
||||||
final boolean touchIsInRecycler = x >= view.getLeft() && x < view.getRight()
|
|
||||||
&& y >= view.getTop() && view.getBottom() > y;
|
|
||||||
|
|
||||||
if (touchIsInRecycler) {
|
|
||||||
final int action = ev.getActionMasked();
|
|
||||||
|
|
||||||
if (action == MotionEvent.ACTION_CANCEL) return super.onInterceptTouchEvent(ev);
|
|
||||||
|
|
||||||
if (action == MotionEvent.ACTION_DOWN) startX = x;
|
|
||||||
else if (action == MotionEvent.ACTION_MOVE) {
|
|
||||||
final int scrollRange = view.computeHorizontalScrollRange();
|
|
||||||
final int scrollOffset = view.computeHorizontalScrollOffset();
|
|
||||||
final boolean scrollable = scrollRange > view.getWidth();
|
|
||||||
final boolean draggingFromRight = startX > x;
|
|
||||||
|
|
||||||
if (scrollOffset < 1) {
|
|
||||||
if (!scrollable) return super.onInterceptTouchEvent(ev);
|
|
||||||
else if (!draggingFromRight) return super.onInterceptTouchEvent(ev);
|
|
||||||
} else if (scrollable && draggingFromRight && scrollRange - scrollOffset == view.computeHorizontalScrollExtent()) {
|
|
||||||
return super.onInterceptTouchEvent(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDrawerSlide(@NonNull final View view, @EdgeGravity final int gravity, final float slideOffset) {
|
|
||||||
drawerView = view;
|
|
||||||
final int absHorizGravity = getDrawerViewAbsoluteGravity(GravityCompat.START);
|
|
||||||
final int childAbsGravity = getDrawerViewAbsoluteGravity(drawerView);
|
|
||||||
|
|
||||||
final Window window = getActivity(getContext()).getWindow();
|
|
||||||
final boolean isRtl = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
|
|
||||||
|| window.getDecorView().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
|
|
||||||
|| getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
|
|
||||||
|
|
||||||
final int drawerViewWidth = drawerView.getWidth();
|
|
||||||
|
|
||||||
// for (int i = 0; i < frameLayout.getChildCount(); i++) {
|
|
||||||
// final View child = frameLayout.getChildAt(i);
|
|
||||||
//
|
|
||||||
// final boolean isLeftDrawer = isRtl == (childAbsGravity != absHorizGravity);
|
|
||||||
// float width = isLeftDrawer ? drawerViewWidth : -drawerViewWidth;
|
|
||||||
//
|
|
||||||
// child.setX(width * slideOffset);
|
|
||||||
// }
|
|
||||||
|
|
||||||
final boolean isLeftDrawer = isRtl == (childAbsGravity != absHorizGravity);
|
|
||||||
float width = isLeftDrawer ? drawerViewWidth : -drawerViewWidth;
|
|
||||||
|
|
||||||
frameLayout.setX(width * (isRtl ? -slideOffset : slideOffset));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openDrawer(@NonNull final View drawerView, final boolean animate) {
|
|
||||||
super.openDrawer(drawerView, animate);
|
|
||||||
post(() -> onDrawerSlide(drawerView, Gravity.NO_GRAVITY, isDrawerOpen(drawerView) ? 1f : 0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onConfigurationChanged(final Configuration newConfig) {
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
if (drawerView != null) onDrawerSlide(drawerView, Gravity.NO_GRAVITY, isDrawerOpen(drawerView) ? 1f : 0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Activity getActivity(final Context context) {
|
|
||||||
if (context != null) {
|
|
||||||
if (context instanceof Activity) return (Activity) context;
|
|
||||||
if (context instanceof ContextWrapper)
|
|
||||||
return getActivity(((ContextWrapper) context).getBaseContext());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int getDrawerViewAbsoluteGravity(final int gravity) {
|
|
||||||
return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this)) & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int getDrawerViewAbsoluteGravity(@NonNull final View drawerView) {
|
|
||||||
final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
|
|
||||||
return getDrawerViewAbsoluteGravity(gravity);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,304 @@
|
|||||||
|
package awais.instagrabber.customviews;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.transition.ChangeBounds;
|
||||||
|
import androidx.transition.ChangeTransform;
|
||||||
|
import androidx.transition.Transition;
|
||||||
|
import androidx.transition.TransitionListenerAdapter;
|
||||||
|
import androidx.transition.TransitionManager;
|
||||||
|
import androidx.transition.TransitionSet;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
|
public abstract class SharedElementTransitionDialogFragment extends DialogFragment {
|
||||||
|
// private static final String TAG = "SETDialogFragment";
|
||||||
|
private static final int DURATION = 200;
|
||||||
|
|
||||||
|
private final Map<Integer, View> startViews = new HashMap<>();
|
||||||
|
private final Map<Integer, View> destViews = new HashMap<>();
|
||||||
|
private final Map<Integer, ViewBounds> viewBoundsMap = new HashMap<>();
|
||||||
|
private final List<Animator> additionalAnimators = new ArrayList<>();
|
||||||
|
private final Handler initialBoundsHandler = new Handler();
|
||||||
|
|
||||||
|
private boolean startCalled = false;
|
||||||
|
private boolean startInitiated = false;
|
||||||
|
private int boundsCalculatedCount = 0;
|
||||||
|
|
||||||
|
protected int getAnimationDuration() {
|
||||||
|
return DURATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSharedElement(@NonNull final View startView, @NonNull final View destView) {
|
||||||
|
final int key = destView.hashCode();
|
||||||
|
startViews.put(key, startView);
|
||||||
|
destViews.put(key, destView);
|
||||||
|
initialBoundsHandler.post(() -> setupInitialBounds(startView, destView));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startPostponedEnterTransition() {
|
||||||
|
startCalled = true;
|
||||||
|
if (startInitiated) return;
|
||||||
|
if (boundsCalculatedCount < startViews.size()) return;
|
||||||
|
startInitiated = true;
|
||||||
|
final Set<Integer> keySet = startViews.keySet();
|
||||||
|
final View view = getView();
|
||||||
|
if (!(view instanceof ViewGroup)) return;
|
||||||
|
final TransitionSet transitionSet = new TransitionSet()
|
||||||
|
.setDuration(DURATION)
|
||||||
|
.setInterpolator(new AccelerateDecelerateInterpolator())
|
||||||
|
.addTransition(new ChangeBounds())
|
||||||
|
.addTransition(new ChangeTransform())
|
||||||
|
.addListener(new TransitionListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onTransitionStart(@NonNull final Transition transition) {
|
||||||
|
for (Animator animator : additionalAnimators) {
|
||||||
|
animator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionEnd(@NonNull final Transition transition) {
|
||||||
|
for (final Integer key : keySet) {
|
||||||
|
final View startView = startViews.get(key);
|
||||||
|
final View destView = destViews.get(key);
|
||||||
|
final ViewBounds viewBounds = viewBoundsMap.get(key);
|
||||||
|
if (startView == null || destView == null || viewBounds == null) return;
|
||||||
|
onEndSharedElementAnimation(startView, destView, viewBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
view.post(() -> {
|
||||||
|
TransitionManager.beginDelayedTransition((ViewGroup) view, transitionSet);
|
||||||
|
for (final Integer key : keySet) {
|
||||||
|
final View startView = startViews.get(key);
|
||||||
|
final View destView = destViews.get(key);
|
||||||
|
final ViewBounds viewBounds = viewBoundsMap.get(key);
|
||||||
|
if (startView == null || destView == null || viewBounds == null) return;
|
||||||
|
onBeforeSharedElementAnimation(startView, destView, viewBounds);
|
||||||
|
setDestBounds(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDestBounds(final int key) {
|
||||||
|
final View startView = startViews.get(key);
|
||||||
|
if (startView == null) return;
|
||||||
|
final View destView = destViews.get(key);
|
||||||
|
if (destView == null) return;
|
||||||
|
final ViewBounds viewBounds = viewBoundsMap.get(key);
|
||||||
|
if (viewBounds == null) return;
|
||||||
|
destView.setX((int) viewBounds.getDestX());
|
||||||
|
destView.setY((int) viewBounds.getDestY());
|
||||||
|
destView.setTranslationX(0);
|
||||||
|
destView.setTranslationY(0);
|
||||||
|
final ViewGroup.LayoutParams layoutParams = destView.getLayoutParams();
|
||||||
|
layoutParams.height = viewBounds.getDestHeight();
|
||||||
|
layoutParams.width = viewBounds.getDestWidth();
|
||||||
|
destView.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onBeforeSharedElementAnimation(@NonNull final View startView,
|
||||||
|
@NonNull final View destView,
|
||||||
|
@NonNull final ViewBounds viewBounds) {}
|
||||||
|
|
||||||
|
protected void onEndSharedElementAnimation(@NonNull final View startView,
|
||||||
|
@NonNull final View destView,
|
||||||
|
@NonNull final ViewBounds viewBounds) {}
|
||||||
|
|
||||||
|
private void setupInitialBounds(@NonNull final View startView, @NonNull final View destView) {
|
||||||
|
final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
|
||||||
|
private boolean firstPassDone;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreDraw() {
|
||||||
|
destView.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||||
|
if (!firstPassDone) {
|
||||||
|
getViewBounds(startView, destView, this);
|
||||||
|
firstPassDone = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final int[] location = new int[2];
|
||||||
|
startView.getLocationOnScreen(location);
|
||||||
|
final int initX = location[0];
|
||||||
|
final int initY = location[1];
|
||||||
|
destView.setX(initX);
|
||||||
|
destView.setY(initY - Utils.getStatusBarHeight(getContext()));
|
||||||
|
destView.requestLayout();
|
||||||
|
boundsCalculatedCount++;
|
||||||
|
if (startCalled) {
|
||||||
|
startPostponedEnterTransition();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
destView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getViewBounds(@NonNull final View startView,
|
||||||
|
@NonNull final View destView,
|
||||||
|
@NonNull final ViewTreeObserver.OnPreDrawListener preDrawListener) {
|
||||||
|
final ViewBounds viewBounds = new ViewBounds();
|
||||||
|
viewBounds.setDestWidth(destView.getWidth());
|
||||||
|
viewBounds.setDestHeight(destView.getHeight());
|
||||||
|
viewBounds.setDestX(destView.getX());
|
||||||
|
viewBounds.setDestY(destView.getY());
|
||||||
|
|
||||||
|
final Rect destBounds = new Rect();
|
||||||
|
destView.getDrawingRect(destBounds);
|
||||||
|
viewBounds.setDestBounds(destBounds);
|
||||||
|
|
||||||
|
final ViewGroup.LayoutParams layoutParams = destView.getLayoutParams();
|
||||||
|
|
||||||
|
final Rect startBounds = new Rect();
|
||||||
|
startView.getDrawingRect(startBounds);
|
||||||
|
viewBounds.setStartBounds(startBounds);
|
||||||
|
|
||||||
|
final int key = destView.hashCode();
|
||||||
|
viewBoundsMap.put(key, viewBounds);
|
||||||
|
|
||||||
|
layoutParams.height = startView.getHeight();
|
||||||
|
layoutParams.width = startView.getWidth();
|
||||||
|
|
||||||
|
destView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
|
||||||
|
destView.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void animateBounds(@NonNull final View startView,
|
||||||
|
// @NonNull final View destView,
|
||||||
|
// @NonNull final ViewBounds viewBounds) {
|
||||||
|
// final ValueAnimator heightAnimator = ObjectAnimator.ofInt(startView.getHeight(), viewBounds.getDestHeight());
|
||||||
|
// final ValueAnimator widthAnimator = ObjectAnimator.ofInt(startView.getWidth(), viewBounds.getDestWidth());
|
||||||
|
// heightAnimator.setDuration(DURATION);
|
||||||
|
// widthAnimator.setDuration(DURATION);
|
||||||
|
// additionalAnimators.add(heightAnimator);
|
||||||
|
// additionalAnimators.add(widthAnimator);
|
||||||
|
// heightAnimator.addUpdateListener(animation -> {
|
||||||
|
// ViewGroup.LayoutParams params = destView.getLayoutParams();
|
||||||
|
// params.height = (int) animation.getAnimatedValue();
|
||||||
|
// destView.requestLayout();
|
||||||
|
// });
|
||||||
|
// widthAnimator.addUpdateListener(animation -> {
|
||||||
|
// ViewGroup.LayoutParams params = destView.getLayoutParams();
|
||||||
|
// params.width = (int) animation.getAnimatedValue();
|
||||||
|
// destView.requestLayout();
|
||||||
|
// });
|
||||||
|
// onBeforeSharedElementAnimation(startView, destView, viewBounds);
|
||||||
|
// final float destX = viewBounds.getDestX();
|
||||||
|
// final float destY = viewBounds.getDestY();
|
||||||
|
// final AnimatorSet animatorSet = new AnimatorSet();
|
||||||
|
// animatorSet.addListener(new AnimatorListenerAdapter() {
|
||||||
|
// @Override
|
||||||
|
// public void onAnimationEnd(final Animator animation) {
|
||||||
|
// animationEnded(startView, destView, viewBounds);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// destView.animate()
|
||||||
|
// .x(destX)
|
||||||
|
// .y(destY)
|
||||||
|
// .setDuration(DURATION)
|
||||||
|
// .withStartAction(() -> {
|
||||||
|
// if (!additionalAnimatorsStarted && additionalAnimators.size() > 0) {
|
||||||
|
// additionalAnimatorsStarted = true;
|
||||||
|
// animatorSet.playTogether(additionalAnimators);
|
||||||
|
// animatorSet.start();
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .withEndAction(() -> animationEnded(startView, destView, viewBounds))
|
||||||
|
// .start();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private int endCount = 0;
|
||||||
|
// private void animationEnded(final View startView, final View destView, final ViewBounds viewBounds) {
|
||||||
|
// ++endCount;
|
||||||
|
// if (endCount != startViews.size() + 1) return;
|
||||||
|
// onEndSharedElementAnimation(startView, destView, viewBounds);
|
||||||
|
// }
|
||||||
|
|
||||||
|
protected void addAnimator(@NonNull final Animator animator) {
|
||||||
|
additionalAnimators.add(animator);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class ViewBounds {
|
||||||
|
private float destY;
|
||||||
|
private float destX;
|
||||||
|
private int destHeight;
|
||||||
|
private int destWidth;
|
||||||
|
private Rect startBounds;
|
||||||
|
private Rect destBounds;
|
||||||
|
|
||||||
|
public ViewBounds() {}
|
||||||
|
|
||||||
|
public float getDestY() {
|
||||||
|
return destY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestY(final float destY) {
|
||||||
|
this.destY = destY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDestX() {
|
||||||
|
return destX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestX(final float destX) {
|
||||||
|
this.destX = destX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDestHeight() {
|
||||||
|
return destHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestHeight(final int destHeight) {
|
||||||
|
this.destHeight = destHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDestWidth() {
|
||||||
|
return destWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestWidth(final int destWidth) {
|
||||||
|
this.destWidth = destWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rect getStartBounds() {
|
||||||
|
return startBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartBounds(final Rect startBounds) {
|
||||||
|
this.startBounds = startBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rect getDestBounds() {
|
||||||
|
return destBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestBounds(final Rect destBounds) {
|
||||||
|
this.destBounds = destBounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
startViews.clear();
|
||||||
|
destViews.clear();
|
||||||
|
viewBoundsMap.clear();
|
||||||
|
additionalAnimators.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,203 @@
|
|||||||
|
package awais.instagrabber.customviews;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.GestureDetector;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewConfiguration;
|
||||||
|
import android.view.ViewParent;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class VerticalDragHelper {
|
||||||
|
private static final String TAG = "VerticalDragHelper";
|
||||||
|
private static final float PIXELS_PER_SECOND = 10;
|
||||||
|
|
||||||
|
private final View view;
|
||||||
|
|
||||||
|
private GestureDetector gestureDetector;
|
||||||
|
private Context context;
|
||||||
|
private float flingVelocity;
|
||||||
|
private OnVerticalDragListener onVerticalDragListener;
|
||||||
|
|
||||||
|
private final GestureDetector.OnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSingleTapConfirmed(final MotionEvent e) {
|
||||||
|
view.performClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) {
|
||||||
|
float maxFlingVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
|
||||||
|
float velocityPercentY = velocityY / maxFlingVelocity;
|
||||||
|
float normalizedVelocityY = velocityPercentY * PIXELS_PER_SECOND;
|
||||||
|
if (Math.abs(normalizedVelocityY) > 4) {
|
||||||
|
flingVelocity = normalizedVelocityY;
|
||||||
|
}
|
||||||
|
return super.onFling(e1, e2, velocityX, velocityY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
private final GestureDetector.OnGestureListener dragPreventionGestureListener = new GestureDetector.SimpleOnGestureListener() {
|
||||||
|
float prevDistanceY = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) {
|
||||||
|
Log.d(TAG, "onScroll: distanceX: " + distanceX + ", distanceY: " + distanceY);
|
||||||
|
return super.onScroll(e1, e2, distanceX, distanceY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSingleTapUp(final MotionEvent e) {
|
||||||
|
Log.d(TAG, "onSingleTapUp");
|
||||||
|
return super.onSingleTapUp(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private float prevRawY;
|
||||||
|
private boolean isDragging;
|
||||||
|
private float prevRawX;
|
||||||
|
private float dX;
|
||||||
|
private float prevDY;
|
||||||
|
private GestureDetector dragPreventionGestureDetector;
|
||||||
|
|
||||||
|
public VerticalDragHelper(@NonNull final View view) {
|
||||||
|
this.view = view;
|
||||||
|
final Context context = view.getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
this.context = context;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnVerticalDragListener(@NonNull final OnVerticalDragListener onVerticalDragListener) {
|
||||||
|
this.onVerticalDragListener = onVerticalDragListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void init() {
|
||||||
|
gestureDetector = new GestureDetector(context, gestureListener);
|
||||||
|
dragPreventionGestureDetector = new GestureDetector(context, dragPreventionGestureListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onDragTouch(final MotionEvent event) {
|
||||||
|
if (onVerticalDragListener == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// dragPreventionGestureDetector.onTouchEvent(event);
|
||||||
|
if (gestureDetector.onTouchEvent(event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
return true;
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
boolean handled = false;
|
||||||
|
final float rawY = event.getRawY();
|
||||||
|
final float dY = rawY - prevRawY;
|
||||||
|
if (!isDragging) {
|
||||||
|
final float rawX = event.getRawX();
|
||||||
|
if (prevRawX != 0) {
|
||||||
|
dX = rawX - prevRawX;
|
||||||
|
}
|
||||||
|
prevRawX = rawX;
|
||||||
|
if (prevRawY != 0) {
|
||||||
|
final float dYAbs = Math.abs(dY - prevDY);
|
||||||
|
if (!isDragging && dYAbs < 50) {
|
||||||
|
final float abs = Math.abs(dY) - Math.abs(dX);
|
||||||
|
if (abs > 0) {
|
||||||
|
isDragging = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDragging) {
|
||||||
|
final ViewParent parent = view.getParent();
|
||||||
|
parent.requestDisallowInterceptTouchEvent(true);
|
||||||
|
onVerticalDragListener.onDrag(dY);
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
prevDY = dY;
|
||||||
|
prevRawY = rawY;
|
||||||
|
return handled;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
// Log.d(TAG, "onDragTouch: reset prevRawY");
|
||||||
|
prevRawY = 0;
|
||||||
|
if (flingVelocity != 0) {
|
||||||
|
onVerticalDragListener.onFling(flingVelocity);
|
||||||
|
flingVelocity = 0;
|
||||||
|
isDragging = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isDragging) {
|
||||||
|
onVerticalDragListener.onDragEnd();
|
||||||
|
isDragging = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDragging() {
|
||||||
|
return isDragging;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onGestureTouchEvent(final MotionEvent event) {
|
||||||
|
return gestureDetector.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static int DIRECTION_UP = 0;
|
||||||
|
private final static int DIRECTION_DOWN = 1;
|
||||||
|
float prevY = -1;
|
||||||
|
int edgeHitCount = 0;
|
||||||
|
float prevDirection = -1;
|
||||||
|
|
||||||
|
|
||||||
|
// private boolean shouldPreventDrag(final MotionEvent event) {
|
||||||
|
// switch (event.getAction()) {
|
||||||
|
// case MotionEvent.ACTION_DOWN:
|
||||||
|
// if (!firstDrag) {
|
||||||
|
// firstDrag = true;
|
||||||
|
// }
|
||||||
|
// return false;
|
||||||
|
// case MotionEvent.ACTION_MOVE:
|
||||||
|
// float y = event.getY();
|
||||||
|
// int direction = -2;
|
||||||
|
// if (prevY != -1) {
|
||||||
|
// final float dy = y - prevY;
|
||||||
|
// // Log.d(TAG, "shouldPreventDrag: dy: " + dy);
|
||||||
|
// if (dy > 0) {
|
||||||
|
// direction = DIRECTION_DOWN;
|
||||||
|
// // move direction is down
|
||||||
|
// } else {
|
||||||
|
// direction = DIRECTION_UP;
|
||||||
|
// // move direction is up
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// prevY = y;
|
||||||
|
// if (prevDirection == direction) {
|
||||||
|
// edgeHitCount++;
|
||||||
|
// } else {
|
||||||
|
// edgeHitCount = 1;
|
||||||
|
// }
|
||||||
|
// if (edgeHitCount >= 2) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// return true;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
public interface OnVerticalDragListener {
|
||||||
|
void onDrag(final float dY);
|
||||||
|
|
||||||
|
void onDragEnd();
|
||||||
|
|
||||||
|
void onFling(final float flingVelocity);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package awais.instagrabber.customviews;
|
||||||
|
|
||||||
|
public class VideoPlayerCallbackAdapter implements VideoPlayerViewHelper.VideoPlayerCallback {
|
||||||
|
@Override
|
||||||
|
public void onThumbnailLoaded() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThumbnailClick() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerViewLoaded() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {}
|
||||||
|
}
|
@ -0,0 +1,371 @@
|
|||||||
|
package awais.instagrabber.customviews;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.view.ContextThemeWrapper;
|
||||||
|
import androidx.appcompat.widget.PopupMenu;
|
||||||
|
|
||||||
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
|
import com.facebook.drawee.controller.BaseControllerListener;
|
||||||
|
import com.facebook.drawee.interfaces.DraweeController;
|
||||||
|
import com.facebook.imagepipeline.image.ImageInfo;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.audio.AudioListener;
|
||||||
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.databinding.LayoutExoCustomControlsBinding;
|
||||||
|
import awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding;
|
||||||
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.C.TIME_UNSET;
|
||||||
|
import static com.google.android.exoplayer2.Player.STATE_ENDED;
|
||||||
|
import static com.google.android.exoplayer2.Player.STATE_IDLE;
|
||||||
|
import static com.google.android.exoplayer2.Player.STATE_READY;
|
||||||
|
|
||||||
|
public class VideoPlayerViewHelper implements Player.EventListener {
|
||||||
|
private static final String TAG = "VideoPlayerViewHelper";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding binding;
|
||||||
|
private final float initialVolume;
|
||||||
|
private final float thumbnailAspectRatio;
|
||||||
|
private final String thumbnailUrl;
|
||||||
|
private final boolean loadPlayerOnClick;
|
||||||
|
private final awais.instagrabber.databinding.LayoutExoCustomControlsBinding controlsBinding;
|
||||||
|
private final VideoPlayerCallback videoPlayerCallback;
|
||||||
|
private final String videoUrl;
|
||||||
|
private final DefaultDataSourceFactory dataSourceFactory;
|
||||||
|
private SimpleExoPlayer player;
|
||||||
|
private PopupMenu speedPopup;
|
||||||
|
|
||||||
|
public VideoPlayerViewHelper(@NonNull final Context context,
|
||||||
|
@NonNull final LayoutVideoPlayerWithThumbnailBinding binding,
|
||||||
|
@NonNull final String videoUrl,
|
||||||
|
final float initialVolume,
|
||||||
|
final float thumbnailAspectRatio,
|
||||||
|
final String thumbnailUrl,
|
||||||
|
final boolean loadPlayerOnClick,
|
||||||
|
final LayoutExoCustomControlsBinding controlsBinding,
|
||||||
|
final VideoPlayerCallback videoPlayerCallback) {
|
||||||
|
this.context = context;
|
||||||
|
this.binding = binding;
|
||||||
|
this.initialVolume = initialVolume;
|
||||||
|
this.thumbnailAspectRatio = thumbnailAspectRatio;
|
||||||
|
this.thumbnailUrl = thumbnailUrl;
|
||||||
|
this.loadPlayerOnClick = loadPlayerOnClick;
|
||||||
|
this.controlsBinding = controlsBinding;
|
||||||
|
this.videoPlayerCallback = videoPlayerCallback;
|
||||||
|
this.videoUrl = videoUrl;
|
||||||
|
this.dataSourceFactory = new DefaultDataSourceFactory(binding.getRoot().getContext(), "instagram");
|
||||||
|
bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bind() {
|
||||||
|
binding.thumbnailParent.setOnClickListener(v -> {
|
||||||
|
if (videoPlayerCallback != null) {
|
||||||
|
videoPlayerCallback.onThumbnailClick();
|
||||||
|
}
|
||||||
|
if (loadPlayerOnClick) {
|
||||||
|
loadPlayer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setThumbnail();
|
||||||
|
setupControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setThumbnail() {
|
||||||
|
binding.thumbnail.setAspectRatio(thumbnailAspectRatio);
|
||||||
|
final ImageRequest thumbnailRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(thumbnailUrl))
|
||||||
|
.build();
|
||||||
|
final DraweeController controller = Fresco.newDraweeControllerBuilder()
|
||||||
|
.setControllerListener(new BaseControllerListener<ImageInfo>() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(final String id, final Throwable throwable) {
|
||||||
|
if (videoPlayerCallback != null) {
|
||||||
|
videoPlayerCallback.onThumbnailLoaded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinalImageSet(final String id,
|
||||||
|
final ImageInfo imageInfo,
|
||||||
|
final Animatable animatable) {
|
||||||
|
if (videoPlayerCallback != null) {
|
||||||
|
videoPlayerCallback.onThumbnailLoaded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setImageRequest(thumbnailRequest)
|
||||||
|
.build();
|
||||||
|
binding.thumbnail.setController(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPlayer() {
|
||||||
|
if (videoUrl == null) return;
|
||||||
|
if (binding.root.getDisplayedChild() == 0) {
|
||||||
|
binding.root.showNext();
|
||||||
|
}
|
||||||
|
if (videoPlayerCallback != null) {
|
||||||
|
videoPlayerCallback.onPlayerViewLoaded();
|
||||||
|
}
|
||||||
|
player = (SimpleExoPlayer) binding.playerView.getPlayer();
|
||||||
|
if (player != null) {
|
||||||
|
player.release();
|
||||||
|
}
|
||||||
|
player = new SimpleExoPlayer.Builder(context)
|
||||||
|
.setLooper(Looper.getMainLooper())
|
||||||
|
.build();
|
||||||
|
player.addListener(this);
|
||||||
|
player.setVolume(initialVolume);
|
||||||
|
player.setPlayWhenReady(true);
|
||||||
|
player.setRepeatMode(Player.REPEAT_MODE_ALL);
|
||||||
|
final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);
|
||||||
|
final MediaItem mediaItem = MediaItem.fromUri(videoUrl);
|
||||||
|
final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(mediaItem);
|
||||||
|
player.setMediaSource(mediaSource);
|
||||||
|
setupControls();
|
||||||
|
player.prepare();
|
||||||
|
binding.playerView.setPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupControls() {
|
||||||
|
if (controlsBinding == null) return;
|
||||||
|
binding.playerView.setUseController(false);
|
||||||
|
if (player == null) {
|
||||||
|
enableControls(false);
|
||||||
|
controlsBinding.playPause.setEnabled(true);
|
||||||
|
controlsBinding.playPause.setOnClickListener(v -> binding.thumbnailParent.performClick());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
enableControls(true);
|
||||||
|
final Handler handler = new Handler();
|
||||||
|
final long initialDelay = 0;
|
||||||
|
final long recurringDelay = 60;
|
||||||
|
final Runnable positionChecker = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handler.removeCallbacks(this);
|
||||||
|
if (player == null) return;
|
||||||
|
final long currentPosition = player.getCurrentPosition();
|
||||||
|
final long duration = player.getDuration();
|
||||||
|
if (duration == TIME_UNSET) {
|
||||||
|
controlsBinding.timeline.setValueFrom(0);
|
||||||
|
controlsBinding.timeline.setValueTo(0);
|
||||||
|
controlsBinding.timeline.setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
controlsBinding.timeline.setValue(Math.min(currentPosition, duration));
|
||||||
|
controlsBinding.fromTime.setText(TextUtils.millisToTimeString(currentPosition));
|
||||||
|
handler.postDelayed(this, recurringDelay);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
updatePlayPauseDrawable(player.getPlayWhenReady());
|
||||||
|
updateMuteIcon(player.getVolume());
|
||||||
|
player.addListener(new Player.EventListener() {
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged(final int state) {
|
||||||
|
switch (state) {
|
||||||
|
case Player.STATE_BUFFERING:
|
||||||
|
case STATE_IDLE:
|
||||||
|
case STATE_ENDED:
|
||||||
|
handler.removeCallbacks(positionChecker);
|
||||||
|
return;
|
||||||
|
case STATE_READY:
|
||||||
|
setupTimeline();
|
||||||
|
handler.postDelayed(positionChecker, initialDelay);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason) {
|
||||||
|
updatePlayPauseDrawable(playWhenReady);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
player.addAudioListener(new AudioListener() {
|
||||||
|
@Override
|
||||||
|
public void onVolumeChanged(final float volume) {
|
||||||
|
updateMuteIcon(volume);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
controlsBinding.timeline.addOnChangeListener((slider, value, fromUser) -> {
|
||||||
|
if (!fromUser) return;
|
||||||
|
long actualValue = (long) value;
|
||||||
|
if (actualValue < 0) {
|
||||||
|
actualValue = 0;
|
||||||
|
} else if (actualValue > player.getDuration()) {
|
||||||
|
actualValue = player.getDuration();
|
||||||
|
}
|
||||||
|
player.seekTo(actualValue);
|
||||||
|
});
|
||||||
|
controlsBinding.timeline.setLabelFormatter(value -> TextUtils.millisToTimeString((long) value));
|
||||||
|
controlsBinding.playPause.setOnClickListener(v -> player.setPlayWhenReady(!player.getPlayWhenReady()));
|
||||||
|
controlsBinding.mute.setOnClickListener(v -> toggleMute());
|
||||||
|
controlsBinding.rewWithAmount.setOnClickListener(v -> {
|
||||||
|
final long positionMs = player.getCurrentPosition() - 5000;
|
||||||
|
player.seekTo(positionMs < 0 ? 0 : positionMs);
|
||||||
|
});
|
||||||
|
controlsBinding.ffWithAmount.setOnClickListener(v -> {
|
||||||
|
long positionMs = player.getCurrentPosition() + 5000;
|
||||||
|
long duration = player.getDuration();
|
||||||
|
if (duration == TIME_UNSET) {
|
||||||
|
duration = 0;
|
||||||
|
}
|
||||||
|
player.seekTo(Math.min(positionMs, duration));
|
||||||
|
});
|
||||||
|
controlsBinding.speed.setOnClickListener(this::showMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTimeline() {
|
||||||
|
final long duration = player.getDuration();
|
||||||
|
controlsBinding.timeline.setEnabled(true);
|
||||||
|
controlsBinding.timeline.setValueFrom(0);
|
||||||
|
controlsBinding.timeline.setValueTo(duration);
|
||||||
|
controlsBinding.fromTime.setText(TextUtils.millisToTimeString(0));
|
||||||
|
controlsBinding.toTime.setText(TextUtils.millisToTimeString(duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableControls(final boolean enable) {
|
||||||
|
controlsBinding.speed.setEnabled(enable);
|
||||||
|
controlsBinding.mute.setEnabled(enable);
|
||||||
|
controlsBinding.ffWithAmount.setEnabled(enable);
|
||||||
|
controlsBinding.rewWithAmount.setEnabled(enable);
|
||||||
|
controlsBinding.fromTime.setEnabled(enable);
|
||||||
|
controlsBinding.toTime.setEnabled(enable);
|
||||||
|
controlsBinding.playPause.setEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showMenu(View anchor) {
|
||||||
|
PopupMenu popup = getPopupMenu(anchor);
|
||||||
|
popup.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private PopupMenu getPopupMenu(final View anchor) {
|
||||||
|
if (speedPopup != null) {
|
||||||
|
return speedPopup;
|
||||||
|
}
|
||||||
|
final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.popupMenuStyle);
|
||||||
|
// final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.Widget_MaterialComponents_PopupMenu_Exoplayer);
|
||||||
|
speedPopup = new PopupMenu(themeWrapper, anchor);
|
||||||
|
speedPopup.getMenuInflater().inflate(R.menu.speed_menu, speedPopup.getMenu());
|
||||||
|
speedPopup.setOnMenuItemClickListener(item -> {
|
||||||
|
float nextSpeed;
|
||||||
|
int textResId;
|
||||||
|
int itemId = item.getItemId();
|
||||||
|
if (itemId == R.id.pt_two_five_x) {
|
||||||
|
nextSpeed = 0.25f;
|
||||||
|
textResId = R.string.pt_two_five_x;
|
||||||
|
} else if (itemId == R.id.pt_five_x) {
|
||||||
|
nextSpeed = 0.5f;
|
||||||
|
textResId = R.string.pt_five_x;
|
||||||
|
} else if (itemId == R.id.pt_seven_five_x) {
|
||||||
|
nextSpeed = 0.75f;
|
||||||
|
textResId = R.string.pt_seven_five_x;
|
||||||
|
} else if (itemId == R.id.one_x) {
|
||||||
|
nextSpeed = 1f;
|
||||||
|
textResId = R.string.one_x;
|
||||||
|
} else if (itemId == R.id.one_pt_two_five_x) {
|
||||||
|
nextSpeed = 1.25f;
|
||||||
|
textResId = R.string.one_pt_two_five_x;
|
||||||
|
} else if (itemId == R.id.one_pt_five_x) {
|
||||||
|
nextSpeed = 1.5f;
|
||||||
|
textResId = R.string.one_pt_five_x;
|
||||||
|
} else if (itemId == R.id.two_x) {
|
||||||
|
nextSpeed = 2f;
|
||||||
|
textResId = R.string.two_x;
|
||||||
|
} else {
|
||||||
|
nextSpeed = 1;
|
||||||
|
textResId = R.string.one_x;
|
||||||
|
}
|
||||||
|
player.setPlaybackParameters(new PlaybackParameters(nextSpeed));
|
||||||
|
controlsBinding.speed.setText(textResId);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return speedPopup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMuteIcon(final float volume) {
|
||||||
|
if (volume == 0) {
|
||||||
|
controlsBinding.mute.setIconResource(R.drawable.ic_volume_off_24);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
controlsBinding.mute.setIconResource(R.drawable.ic_volume_up_24);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePlayPauseDrawable(final boolean playWhenReady) {
|
||||||
|
if (playWhenReady) {
|
||||||
|
controlsBinding.playPause.setIconResource(R.drawable.ic_pause_24);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
controlsBinding.playPause.setIconResource(R.drawable.ic_play_arrow_24);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason) {
|
||||||
|
if (videoPlayerCallback == null) return;
|
||||||
|
if (playWhenReady) {
|
||||||
|
videoPlayerCallback.onPlay();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
videoPlayerCallback.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerError(final ExoPlaybackException error) {
|
||||||
|
Log.e(TAG, "onPlayerError", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float toggleMute() {
|
||||||
|
if (player == null) return 0;
|
||||||
|
final float vol = player.getVolume() == 0f ? 1f : 0f;
|
||||||
|
player.setVolume(vol);
|
||||||
|
return vol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void togglePlayback() {
|
||||||
|
if (player == null) return;
|
||||||
|
final int playbackState = player.getPlaybackState();
|
||||||
|
if (playbackState == STATE_IDLE || playbackState == STATE_ENDED) return;
|
||||||
|
final boolean playWhenReady = player.getPlayWhenReady();
|
||||||
|
player.setPlayWhenReady(!playWhenReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releasePlayer() {
|
||||||
|
if (player == null) return;
|
||||||
|
player.release();
|
||||||
|
player = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
if (player == null) return;
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface VideoPlayerCallback {
|
||||||
|
void onThumbnailLoaded();
|
||||||
|
|
||||||
|
void onThumbnailClick();
|
||||||
|
|
||||||
|
void onPlayerViewLoaded();
|
||||||
|
|
||||||
|
void onPlay();
|
||||||
|
|
||||||
|
void onPause();
|
||||||
|
}
|
||||||
|
}
|
@ -572,16 +572,16 @@ public class DefaultZoomableController
|
|||||||
RectF b = mTempRect;
|
RectF b = mTempRect;
|
||||||
b.set(mImageBounds);
|
b.set(mImageBounds);
|
||||||
transform.mapRect(b);
|
transform.mapRect(b);
|
||||||
float offsetLeft =
|
final boolean shouldLimitX = shouldLimit(limitTypes, LIMIT_TRANSLATION_X);
|
||||||
shouldLimit(limitTypes, LIMIT_TRANSLATION_X)
|
float offsetLeft = shouldLimitX
|
||||||
? getOffset(
|
? getOffset(b.left, b.right, mViewBounds.left, mViewBounds.right, mImageBounds.centerX())
|
||||||
b.left, b.right, mViewBounds.left, mViewBounds.right, mImageBounds.centerX())
|
: 0;
|
||||||
: 0;
|
float offsetTop = shouldLimit(limitTypes, LIMIT_TRANSLATION_Y)
|
||||||
float offsetTop =
|
? getOffset(b.top, b.bottom, mViewBounds.top, mViewBounds.bottom, mImageBounds.centerY())
|
||||||
shouldLimit(limitTypes, LIMIT_TRANSLATION_Y)
|
: 0;
|
||||||
? getOffset(
|
if (mListener != null) {
|
||||||
b.top, b.bottom, mViewBounds.top, mViewBounds.bottom, mImageBounds.centerY())
|
mListener.onTranslationLimited(offsetLeft, offsetTop);
|
||||||
: 0;
|
}
|
||||||
if (offsetLeft != 0 || offsetTop != 0) {
|
if (offsetLeft != 0 || offsetTop != 0) {
|
||||||
transform.postTranslate(offsetLeft, offsetTop);
|
transform.postTranslate(offsetLeft, offsetTop);
|
||||||
return true;
|
return true;
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package awais.instagrabber.customviews.drawee;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.facebook.drawee.generic.GenericDraweeHierarchy;
|
||||||
|
|
||||||
|
import awais.instagrabber.customviews.VerticalDragHelper;
|
||||||
|
import awais.instagrabber.customviews.VerticalDragHelper.OnVerticalDragListener;
|
||||||
|
|
||||||
|
public class DraggableZoomableDraweeView extends ZoomableDraweeView {
|
||||||
|
private static final String TAG = "DraggableZoomableDV";
|
||||||
|
|
||||||
|
private VerticalDragHelper verticalDragHelper;
|
||||||
|
|
||||||
|
public DraggableZoomableDraweeView(final Context context, final GenericDraweeHierarchy hierarchy) {
|
||||||
|
super(context, hierarchy);
|
||||||
|
verticalDragHelper = new VerticalDragHelper(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DraggableZoomableDraweeView(final Context context) {
|
||||||
|
super(context);
|
||||||
|
verticalDragHelper = new VerticalDragHelper(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DraggableZoomableDraweeView(final Context context, final AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
verticalDragHelper = new VerticalDragHelper(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DraggableZoomableDraweeView(final Context context, final AttributeSet attrs, final int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
verticalDragHelper = new VerticalDragHelper(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnVerticalDragListener(@NonNull final OnVerticalDragListener onVerticalDragListener) {
|
||||||
|
verticalDragHelper.setOnVerticalDragListener(onVerticalDragListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int lastPointerCount;
|
||||||
|
private int lastNewPointerCount;
|
||||||
|
private boolean wasTransformCorrected;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onTransformEnd(final Matrix transform) {
|
||||||
|
super.onTransformEnd(transform);
|
||||||
|
final AnimatedZoomableController zoomableController = (AnimatedZoomableController) getZoomableController();
|
||||||
|
final TransformGestureDetector detector = zoomableController.getDetector();
|
||||||
|
lastNewPointerCount = detector.getNewPointerCount();
|
||||||
|
lastPointerCount = detector.getPointerCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onTranslationLimited(final float offsetLeft, final float offsetTop) {
|
||||||
|
super.onTranslationLimited(offsetLeft, offsetTop);
|
||||||
|
wasTransformCorrected = offsetTop != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(final MotionEvent event) {
|
||||||
|
boolean superResult = false;
|
||||||
|
if (verticalDragHelper.isDragging()) {
|
||||||
|
final boolean onDragTouch = verticalDragHelper.onDragTouch(event);
|
||||||
|
if (onDragTouch) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!verticalDragHelper.isDragging()) {
|
||||||
|
superResult = super.onTouchEvent(event);
|
||||||
|
if (wasTransformCorrected
|
||||||
|
&& (lastPointerCount == 1 || lastPointerCount == 0)
|
||||||
|
&& (lastNewPointerCount == 1 || lastNewPointerCount == 0)) {
|
||||||
|
final boolean onDragTouch = verticalDragHelper.onDragTouch(event);
|
||||||
|
if (onDragTouch) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final boolean gestureListenerResult = verticalDragHelper.onGestureTouchEvent(event);
|
||||||
|
if (gestureListenerResult) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return superResult;
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,13 @@ public class MultiZoomableControllerListener implements ZoomableController.Liste
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTranslationLimited(final float offsetLeft, final float offsetTop) {
|
||||||
|
for (ZoomableController.Listener listener : mListeners) {
|
||||||
|
listener.onTranslationLimited(offsetLeft, offsetTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void addListener(ZoomableController.Listener listener) {
|
public synchronized void addListener(ZoomableController.Listener listener) {
|
||||||
mListeners.add(listener);
|
mListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ public interface ZoomableController {
|
|||||||
* @param transform the current transform matrix
|
* @param transform the current transform matrix
|
||||||
*/
|
*/
|
||||||
void onTransformEnd(Matrix transform);
|
void onTransformEnd(Matrix transform);
|
||||||
|
|
||||||
|
void onTranslationLimited(float offsetLeft, float offsetTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,8 +33,6 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyInflater;
|
|||||||
import com.facebook.drawee.interfaces.DraweeController;
|
import com.facebook.drawee.interfaces.DraweeController;
|
||||||
import com.facebook.drawee.view.DraweeView;
|
import com.facebook.drawee.view.DraweeView;
|
||||||
|
|
||||||
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DraweeView that has zoomable capabilities.
|
* DraweeView that has zoomable capabilities.
|
||||||
@ -54,7 +52,7 @@ public class ZoomableDraweeView extends DraweeView<GenericDraweeHierarchy>
|
|||||||
private DraweeController mHugeImageController;
|
private DraweeController mHugeImageController;
|
||||||
private ZoomableController mZoomableController;
|
private ZoomableController mZoomableController;
|
||||||
private GestureDetector mTapGestureDetector;
|
private GestureDetector mTapGestureDetector;
|
||||||
private boolean mAllowTouchInterceptionWhileZoomed = true;
|
private boolean mAllowTouchInterceptionWhileZoomed = false;
|
||||||
|
|
||||||
private boolean mIsDialtoneEnabled = false;
|
private boolean mIsDialtoneEnabled = false;
|
||||||
private boolean mZoomingEnabled = true;
|
private boolean mZoomingEnabled = true;
|
||||||
@ -76,7 +74,9 @@ public class ZoomableDraweeView extends DraweeView<GenericDraweeHierarchy>
|
|||||||
private final ZoomableController.Listener mZoomableListener =
|
private final ZoomableController.Listener mZoomableListener =
|
||||||
new ZoomableController.Listener() {
|
new ZoomableController.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void onTransformBegin(Matrix transform) {}
|
public void onTransformBegin(Matrix transform) {
|
||||||
|
ZoomableDraweeView.this.onTransformBegin(transform);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTransformChanged(Matrix transform) {
|
public void onTransformChanged(Matrix transform) {
|
||||||
@ -84,7 +84,14 @@ public class ZoomableDraweeView extends DraweeView<GenericDraweeHierarchy>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTransformEnd(Matrix transform) {}
|
public void onTransformEnd(Matrix transform) {
|
||||||
|
ZoomableDraweeView.this.onTransformEnd(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTranslationLimited(final float offsetLeft, final float offsetTop) {
|
||||||
|
ZoomableDraweeView.this.onTranslationLimited(offsetLeft, offsetTop);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final GestureListenerWrapper mTapListenerWrapper = new GestureListenerWrapper();
|
private final GestureListenerWrapper mTapListenerWrapper = new GestureListenerWrapper();
|
||||||
@ -302,11 +309,10 @@ public class ZoomableDraweeView extends DraweeView<GenericDraweeHierarchy>
|
|||||||
int a = event.getActionMasked();
|
int a = event.getActionMasked();
|
||||||
FLog.v(getLogTag(), "onTouchEvent: %d, view %x, received", a, this.hashCode());
|
FLog.v(getLogTag(), "onTouchEvent: %d, view %x, received", a, this.hashCode());
|
||||||
if (!mIsDialtoneEnabled && mTapGestureDetector.onTouchEvent(event)) {
|
if (!mIsDialtoneEnabled && mTapGestureDetector.onTouchEvent(event)) {
|
||||||
FLog.v(
|
FLog.v(getLogTag(),
|
||||||
getLogTag(),
|
"onTouchEvent: %d, view %x, handled by tap gesture detector",
|
||||||
"onTouchEvent: %d, view %x, handled by tap gesture detector",
|
a,
|
||||||
a,
|
this.hashCode());
|
||||||
this.hashCode());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,23 +395,29 @@ public class ZoomableDraweeView extends DraweeView<GenericDraweeHierarchy>
|
|||||||
mZoomableController.setEnabled(false);
|
mZoomableController.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onTransformBegin(final Matrix transform) {}
|
||||||
|
|
||||||
protected void onTransformChanged(Matrix transform) {
|
protected void onTransformChanged(Matrix transform) {
|
||||||
FLog.v(getLogTag(), "onTransformChanged: view %x, transform: %s", this.hashCode(), transform);
|
FLog.v(getLogTag(), "onTransformChanged: view %x, transform: %s", this.hashCode(), transform);
|
||||||
maybeSetHugeImageController();
|
maybeSetHugeImageController();
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onTransformEnd(final Matrix transform) {}
|
||||||
|
|
||||||
|
protected void onTranslationLimited(final float offsetLeft, final float offsetTop) {}
|
||||||
|
|
||||||
protected void updateZoomableControllerBounds() {
|
protected void updateZoomableControllerBounds() {
|
||||||
getImageBounds(mImageBounds);
|
getImageBounds(mImageBounds);
|
||||||
getLimitBounds(mViewBounds);
|
getLimitBounds(mViewBounds);
|
||||||
|
// Log.d(TAG.getSimpleName(), "updateZoomableControllerBounds: mImageBounds: " + mImageBounds);
|
||||||
mZoomableController.setImageBounds(mImageBounds);
|
mZoomableController.setImageBounds(mImageBounds);
|
||||||
mZoomableController.setViewBounds(mViewBounds);
|
mZoomableController.setViewBounds(mViewBounds);
|
||||||
FLog.v(
|
FLog.v(getLogTag(),
|
||||||
getLogTag(),
|
"updateZoomableControllerBounds: view %x, view bounds: %s, image bounds: %s",
|
||||||
"updateZoomableControllerBounds: view %x, view bounds: %s, image bounds: %s",
|
this.hashCode(),
|
||||||
this.hashCode(),
|
mViewBounds,
|
||||||
mViewBounds,
|
mImageBounds);
|
||||||
mImageBounds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Class<?> getLogTag() {
|
protected Class<?> getLogTag() {
|
||||||
|
@ -4,7 +4,6 @@ import android.graphics.Rect;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
|
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
|
||||||
@ -16,16 +15,14 @@ public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
||||||
final RecyclerView.LayoutManager manager = parent.getLayoutManager();
|
final int halfSpace = spacing / 2;
|
||||||
if (manager instanceof GridLayoutManager) {
|
if (parent.getPaddingLeft() != halfSpace) {
|
||||||
final int spanCount = ((GridLayoutManager) manager).getSpanCount();
|
parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
|
||||||
final int position = parent.getChildAdapterPosition(view);
|
parent.setClipToPadding(false);
|
||||||
final int column = position % spanCount;
|
|
||||||
|
|
||||||
outRect.left = column * spacing / spanCount;
|
|
||||||
outRect.right = spacing - (column + 1) * spacing / spanCount;
|
|
||||||
if (position < spanCount) outRect.top = spacing;
|
|
||||||
outRect.bottom = spacing;
|
|
||||||
}
|
}
|
||||||
|
outRect.top = halfSpace;
|
||||||
|
outRect.bottom = halfSpace;
|
||||||
|
outRect.left = halfSpace;
|
||||||
|
outRect.right = halfSpace;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package awais.instagrabber.customviews.helpers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
|
||||||
|
public class PostFetcher {
|
||||||
|
private final PostFetchService postFetchService;
|
||||||
|
private final FetchListener<List<FeedModel>> fetchListener;
|
||||||
|
private boolean fetching;
|
||||||
|
|
||||||
|
public PostFetcher(final PostFetchService postFetchService,
|
||||||
|
final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
|
this.postFetchService = postFetchService;
|
||||||
|
this.fetchListener = fetchListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fetch() {
|
||||||
|
if (!fetching) {
|
||||||
|
fetching = true;
|
||||||
|
postFetchService.fetch(result -> {
|
||||||
|
fetching = false;
|
||||||
|
fetchListener.onResult(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
postFetchService.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFetching() {
|
||||||
|
return fetching;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMore() {
|
||||||
|
return postFetchService.hasNextPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface PostFetchService {
|
||||||
|
void fetch(FetchListener<List<FeedModel>> fetchListener);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
boolean hasNextPage();
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,41 @@
|
|||||||
package awais.instagrabber.customviews.helpers;
|
package awais.instagrabber.customviews.helpers;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||||
|
|
||||||
import awais.instagrabber.interfaces.LazyLoadListener;
|
import awais.instagrabber.interfaces.LazyLoadListener;
|
||||||
|
|
||||||
// thanks to nesquena's EndlessRecyclerViewScrollListener
|
/**
|
||||||
// https://gist.github.com/nesquena/d09dc68ff07e845cc622
|
* thanks to nesquena's <a href="https://gist.github.com/nesquena/d09dc68ff07e845cc622">EndlessRecyclerViewScrollListener</a>
|
||||||
|
*/
|
||||||
public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
|
public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
|
||||||
private int currentPage = 0; // The current offset index of data you have loaded
|
/**
|
||||||
private int previousTotalItemCount = 0; // The total number of items in the dataset after the last load
|
* The current offset index of data you have loaded
|
||||||
private boolean loading = true; // True if we are still waiting for the last set of data to load.
|
*/
|
||||||
private final int visibleThreshold; // The minimum amount of items to have below your current scroll position before loading more.
|
private int currentPage = 0;
|
||||||
|
/**
|
||||||
|
* The total number of items in the data set after the last load
|
||||||
|
*/
|
||||||
|
private int previousTotalItemCount = 0;
|
||||||
|
/**
|
||||||
|
* <code>true</code> if we are still waiting for the last set of data to load.
|
||||||
|
*/
|
||||||
|
private boolean loading = true;
|
||||||
|
/**
|
||||||
|
* The minimum amount of items to have below your current scroll position before loading more.
|
||||||
|
*/
|
||||||
|
private final int visibleThreshold;
|
||||||
private final LazyLoadListener lazyLoadListener;
|
private final LazyLoadListener lazyLoadListener;
|
||||||
private final RecyclerView.LayoutManager layoutManager;
|
private final RecyclerView.LayoutManager layoutManager;
|
||||||
|
|
||||||
public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager, final LazyLoadListener lazyLoadListener, final int threshold) {
|
public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager,
|
||||||
|
final LazyLoadListener lazyLoadListener,
|
||||||
|
final int threshold) {
|
||||||
this.layoutManager = layoutManager;
|
this.layoutManager = layoutManager;
|
||||||
this.lazyLoadListener = lazyLoadListener;
|
this.lazyLoadListener = lazyLoadListener;
|
||||||
if (threshold > 0) {
|
if (threshold > 0) {
|
||||||
@ -26,6 +44,8 @@ public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
|
|||||||
}
|
}
|
||||||
if (layoutManager instanceof GridLayoutManager) {
|
if (layoutManager instanceof GridLayoutManager) {
|
||||||
this.visibleThreshold = 5 * Math.max(3, ((GridLayoutManager) layoutManager).getSpanCount());
|
this.visibleThreshold = 5 * Math.max(3, ((GridLayoutManager) layoutManager).getSpanCount());
|
||||||
|
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
|
||||||
|
this.visibleThreshold = 4 * Math.max(3, ((StaggeredGridLayoutManager) layoutManager).getSpanCount());
|
||||||
} else if (layoutManager instanceof LinearLayoutManager) {
|
} else if (layoutManager instanceof LinearLayoutManager) {
|
||||||
this.visibleThreshold = ((LinearLayoutManager) layoutManager).getReverseLayout() ? 4 : 8;
|
this.visibleThreshold = ((LinearLayoutManager) layoutManager).getReverseLayout() ? 4 : 8;
|
||||||
} else {
|
} else {
|
||||||
@ -33,7 +53,8 @@ public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager, final LazyLoadListener lazyLoadListener) {
|
public RecyclerLazyLoader(@NonNull final RecyclerView.LayoutManager layoutManager,
|
||||||
|
final LazyLoadListener lazyLoadListener) {
|
||||||
this(layoutManager, lazyLoadListener, -1);
|
this(layoutManager, lazyLoadListener, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,22 +73,37 @@ public final class RecyclerLazyLoader extends RecyclerView.OnScrollListener {
|
|||||||
previousTotalItemCount = totalItemCount;
|
previousTotalItemCount = totalItemCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int lastVisibleItemPosition;
|
int lastVisibleItemPosition;
|
||||||
if (layoutManager instanceof GridLayoutManager) {
|
if (layoutManager instanceof GridLayoutManager) {
|
||||||
final GridLayoutManager layoutManager = (GridLayoutManager) this.layoutManager;
|
final GridLayoutManager layoutManager = (GridLayoutManager) this.layoutManager;
|
||||||
lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
|
lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
|
||||||
|
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
|
||||||
|
final StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) this.layoutManager;
|
||||||
|
final int spanCount = layoutManager.getSpanCount();
|
||||||
|
final int[] lastVisibleItemPositions = layoutManager.findLastVisibleItemPositions(null);
|
||||||
|
lastVisibleItemPosition = 0;
|
||||||
|
for (final int itemPosition : lastVisibleItemPositions) {
|
||||||
|
if (itemPosition > lastVisibleItemPosition) {
|
||||||
|
lastVisibleItemPosition = itemPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
final LinearLayoutManager layoutManager = (LinearLayoutManager) this.layoutManager;
|
final LinearLayoutManager layoutManager = (LinearLayoutManager) this.layoutManager;
|
||||||
lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
|
lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loading && lastVisibleItemPosition + visibleThreshold > totalItemCount) {
|
if (!loading && lastVisibleItemPosition + visibleThreshold > totalItemCount) {
|
||||||
if (lazyLoadListener != null)
|
|
||||||
lazyLoadListener.onLoadMore(++currentPage, totalItemCount);
|
|
||||||
loading = true;
|
loading = true;
|
||||||
|
if (lazyLoadListener != null) {
|
||||||
|
new Handler().postDelayed(() -> lazyLoadListener.onLoadMore(++currentPage, totalItemCount), 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getCurrentPage() {
|
||||||
|
return currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
public void resetState() {
|
public void resetState() {
|
||||||
this.currentPage = 0;
|
this.currentPage = 0;
|
||||||
this.previousTotalItemCount = 0;
|
this.previousTotalItemCount = 0;
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
package awais.instagrabber.customviews.helpers;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
public final class RecyclerLazyLoaderAtBottom extends RecyclerView.OnScrollListener {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final RecyclerView.LayoutManager layoutManager;
|
||||||
|
private final LazyLoadListener lazyLoadListener;
|
||||||
|
private int currentPage;
|
||||||
|
private int previousItemCount;
|
||||||
|
private boolean loading;
|
||||||
|
|
||||||
|
public RecyclerLazyLoaderAtBottom(@NonNull final RecyclerView.LayoutManager layoutManager,
|
||||||
|
final LazyLoadListener lazyLoadListener) {
|
||||||
|
this.layoutManager = layoutManager;
|
||||||
|
this.lazyLoadListener = lazyLoadListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {
|
||||||
|
super.onScrollStateChanged(recyclerView, newState);
|
||||||
|
final int itemCount = layoutManager.getItemCount();
|
||||||
|
if (itemCount > previousItemCount) {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
if (!recyclerView.canScrollVertically(RecyclerView.SCROLL_AXIS_HORIZONTAL) && newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||||
|
if (!loading && lazyLoadListener != null) {
|
||||||
|
loading = true;
|
||||||
|
new Handler().postDelayed(() -> lazyLoadListener.onLoadMore(++currentPage), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentPage() {
|
||||||
|
return currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetState() {
|
||||||
|
currentPage = 0;
|
||||||
|
previousItemCount = 0;
|
||||||
|
loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface LazyLoadListener {
|
||||||
|
void onLoadMore(final int page);
|
||||||
|
}
|
||||||
|
}
|
@ -70,7 +70,7 @@ public class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {
|
|||||||
final FeedVideoViewHolder videoHolder = getFirstVideoHolder(recyclerView, firstVisibleItemPos, lastVisibleItemPos);
|
final FeedVideoViewHolder videoHolder = getFirstVideoHolder(recyclerView, firstVisibleItemPos, lastVisibleItemPos);
|
||||||
if (videoHolder == null || videoHolder.getCurrentFeedModel() == null) {
|
if (videoHolder == null || videoHolder.getCurrentFeedModel() == null) {
|
||||||
if (currentlyPlayingViewHolder != null) {
|
if (currentlyPlayingViewHolder != null) {
|
||||||
currentlyPlayingViewHolder.stopPlaying();
|
// currentlyPlayingViewHolder.stopPlaying();
|
||||||
currentlyPlayingViewHolder = null;
|
currentlyPlayingViewHolder = null;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -80,9 +80,9 @@ public class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentlyPlayingViewHolder != null) {
|
if (currentlyPlayingViewHolder != null) {
|
||||||
currentlyPlayingViewHolder.stopPlaying();
|
// currentlyPlayingViewHolder.stopPlaying();
|
||||||
}
|
}
|
||||||
videoHolder.startPlaying();
|
// videoHolder.startPlaying();
|
||||||
currentlyPlayingViewHolder = videoHolder;
|
currentlyPlayingViewHolder = videoHolder;
|
||||||
}
|
}
|
||||||
// boolean processFirstItem = false, processLastItem = false;
|
// boolean processFirstItem = false, processLastItem = false;
|
||||||
@ -196,7 +196,7 @@ public class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {
|
|||||||
for (int pos = firstVisibleItemPos; pos <= lastVisibleItemPos; pos++) {
|
for (int pos = firstVisibleItemPos; pos <= lastVisibleItemPos; pos++) {
|
||||||
final View view = layoutManager.findViewByPosition(pos);
|
final View view = layoutManager.findViewByPosition(pos);
|
||||||
if (view != null && view.getId() == R.id.videoHolder) {
|
if (view != null && view.getId() == R.id.videoHolder) {
|
||||||
final View viewSwitcher = view.findViewById(R.id.view_switcher);
|
final View viewSwitcher = view.findViewById(R.id.root);
|
||||||
if (viewSwitcher == null) {
|
if (viewSwitcher == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -220,113 +220,113 @@ public class VideoAwareRecyclerScroller extends RecyclerView.OnScrollListener {
|
|||||||
if (currentlyPlayingViewHolder == null) {
|
if (currentlyPlayingViewHolder == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentlyPlayingViewHolder.startPlaying();
|
// currentlyPlayingViewHolder.startPlaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopPlaying() {
|
public void stopPlaying() {
|
||||||
if (currentlyPlayingViewHolder == null) {
|
if (currentlyPlayingViewHolder == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentlyPlayingViewHolder.stopPlaying();
|
// currentlyPlayingViewHolder.stopPlaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
// private synchronized void attachVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
|
// private synchronized void attachVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
|
||||||
// synchronized (LOCK) {
|
// synchronized (LOCK) {
|
||||||
// if (recyclerView != null) {
|
// if (recyclerView != null) {
|
||||||
// final RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();
|
// final RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();
|
||||||
// if (adapter instanceof FeedAdapter) {
|
// if (adapter instanceof FeedAdapter) {
|
||||||
// final SimpleExoPlayer pagerPlayer = ((FeedAdapter) adapter).pagerPlayer;
|
// final SimpleExoPlayer pagerPlayer = ((FeedAdapter) adapter).pagerPlayer;
|
||||||
// if (pagerPlayer != null) pagerPlayer.setPlayWhenReady(false);
|
// if (pagerPlayer != null) pagerPlayer.setPlayWhenReady(false);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// if (itemView == null) {
|
// if (itemView == null) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// final boolean shouldAutoplay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
|
// final boolean shouldAutoplay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
|
||||||
// final FeedModel feedModel = feedModels.get(itemPos);
|
// final FeedModel feedModel = feedModels.get(itemPos);
|
||||||
// // loadVideo(itemPos, itemView, shouldAutoplay, feedModel);
|
// // loadVideo(itemPos, itemView, shouldAutoplay, feedModel);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// private void loadVideo(final int itemPos, final View itemView, final boolean shouldAutoplay, final FeedModel feedModel) {
|
// private void loadVideo(final int itemPos, final View itemView, final boolean shouldAutoplay, final FeedModel feedModel) {
|
||||||
// final PlayerView playerView = itemView.findViewById(R.id.playerView);
|
// final PlayerView playerView = itemView.findViewById(R.id.playerView);
|
||||||
// if (playerView == null) {
|
// if (playerView == null) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// if (player != null) {
|
// if (player != null) {
|
||||||
// player.stop(true);
|
// player.stop(true);
|
||||||
// player.release();
|
// player.release();
|
||||||
// player = null;
|
// player = null;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// player = new SimpleExoPlayer.Builder(context)
|
// player = new SimpleExoPlayer.Builder(context)
|
||||||
// .setUseLazyPreparation(!shouldAutoplay)
|
// .setUseLazyPreparation(!shouldAutoplay)
|
||||||
// .build();
|
// .build();
|
||||||
// player.setPlayWhenReady(shouldAutoplay);
|
// player.setPlayWhenReady(shouldAutoplay);
|
||||||
//
|
//
|
||||||
// final View btnComments = itemView.findViewById(R.id.btnComments);
|
// final View btnComments = itemView.findViewById(R.id.btnComments);
|
||||||
// if (btnComments != null) {
|
// if (btnComments != null) {
|
||||||
// if (feedModel.getCommentsCount() <= 0) btnComments.setEnabled(false);
|
// if (feedModel.getCommentsCount() <= 0) btnComments.setEnabled(false);
|
||||||
// else {
|
// else {
|
||||||
// btnComments.setTag(feedModel);
|
// btnComments.setTag(feedModel);
|
||||||
// btnComments.setEnabled(true);
|
// btnComments.setEnabled(true);
|
||||||
// btnComments.setOnClickListener(commentClickListener);
|
// btnComments.setOnClickListener(commentClickListener);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// playerView.setPlayer(player);
|
// playerView.setPlayer(player);
|
||||||
// btnMute = itemView.findViewById(R.id.btnMute);
|
// btnMute = itemView.findViewById(R.id.btnMute);
|
||||||
// float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
|
// float vol = settingsHelper.getBoolean(Constants.MUTED_VIDEOS) ? 0f : 1f;
|
||||||
// if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
|
// if (vol == 0f && Utils.sessionVolumeFull) vol = 1f;
|
||||||
// player.setVolume(vol);
|
// player.setVolume(vol);
|
||||||
//
|
//
|
||||||
// if (btnMute != null) {
|
// if (btnMute != null) {
|
||||||
// btnMute.setVisibility(View.VISIBLE);
|
// btnMute.setVisibility(View.VISIBLE);
|
||||||
// btnMute.setImageResource(vol == 0f ? R.drawable.vol : R.drawable.mute);
|
// btnMute.setImageResource(vol == 0f ? R.drawable.vol : R.drawable.mute);
|
||||||
// btnMute.setOnClickListener(muteClickListener);
|
// btnMute.setOnClickListener(muteClickListener);
|
||||||
// }
|
// }
|
||||||
// final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory;
|
// final DataSource.Factory factory = cacheDataSourceFactory != null ? cacheDataSourceFactory : dataSourceFactory;
|
||||||
// final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory);
|
// final ProgressiveMediaSource.Factory sourceFactory = new ProgressiveMediaSource.Factory(factory);
|
||||||
// final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(Uri.parse(feedModel.getDisplayUrl()));
|
// final ProgressiveMediaSource mediaSource = sourceFactory.createMediaSource(Uri.parse(feedModel.getDisplayUrl()));
|
||||||
//
|
//
|
||||||
// player.setRepeatMode(Player.REPEAT_MODE_ALL);
|
// player.setRepeatMode(Player.REPEAT_MODE_ALL);
|
||||||
// player.prepare(mediaSource);
|
// player.prepare(mediaSource);
|
||||||
// player.setVolume(vol);
|
// player.setVolume(vol);
|
||||||
//
|
//
|
||||||
// playerView.setOnClickListener(v -> player.setPlayWhenReady(!player.getPlayWhenReady()));
|
// playerView.setOnClickListener(v -> player.setPlayWhenReady(!player.getPlayWhenReady()));
|
||||||
//
|
//
|
||||||
// if (videoChangeCallback != null) videoChangeCallback.playerChanged(itemPos, player);
|
// if (videoChangeCallback != null) videoChangeCallback.playerChanged(itemPos, player);
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// private void releaseVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
|
// private void releaseVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
|
||||||
// // Log.d("AWAISKING_APP", "release: " + itemPos);
|
// // Log.d("AWAISKING_APP", "release: " + itemPos);
|
||||||
// // if (player != null) {
|
// // if (player != null) {
|
||||||
// // player.stop(true);
|
// // player.stop(true);
|
||||||
// // player.release();
|
// // player.release();
|
||||||
// // }
|
// // }
|
||||||
// // player = null;
|
// // player = null;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// private void playVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
|
// private void playVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
|
||||||
// // if (player != null) {
|
// // if (player != null) {
|
||||||
// // final int playbackState = player.getPlaybackState();
|
// // final int playbackState = player.getPlaybackState();
|
||||||
// // if (!player.isPlaying()
|
// // if (!player.isPlaying()
|
||||||
// // || playbackState == Player.STATE_READY || playbackState == Player.STATE_ENDED
|
// // || playbackState == Player.STATE_READY || playbackState == Player.STATE_ENDED
|
||||||
// // ) {
|
// // ) {
|
||||||
// // player.setPlayWhenReady(true);
|
// // player.setPlayWhenReady(true);
|
||||||
// // }
|
// // }
|
||||||
// // }
|
// // }
|
||||||
// // if (player != null) {
|
// // if (player != null) {
|
||||||
// // player.setPlayWhenReady(true);
|
// // player.setPlayWhenReady(true);
|
||||||
// // player.getPlaybackState();
|
// // player.getPlaybackState();
|
||||||
// // }
|
// // }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// private void stopVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
|
// private void stopVideo(final int itemPos, final RecyclerView recyclerView, final View itemView) {
|
||||||
// if (player != null) {
|
// if (player != null) {
|
||||||
// player.setPlayWhenReady(false);
|
// player.setPlayWhenReady(false);
|
||||||
// player.getPlaybackState();
|
// player.getPlaybackState();
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public interface VideoChangeCallback {
|
public interface VideoChangeCallback {
|
||||||
void playerChanged(final int itemPos, final SimpleExoPlayer player);
|
void playerChanged(final int itemPos, final SimpleExoPlayer player);
|
||||||
|
@ -0,0 +1,216 @@
|
|||||||
|
package awais.instagrabber.dialogs;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.databinding.DialogPostLayoutPreferencesBinding;
|
||||||
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
|
|
||||||
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
|
public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
|
private final OnApplyListener onApplyListener;
|
||||||
|
private final PostsLayoutPreferences.Builder preferencesBuilder;
|
||||||
|
private final String layoutPreferenceKey;
|
||||||
|
private DialogPostLayoutPreferencesBinding binding;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public PostsLayoutPreferencesDialogFragment(final String layoutPreferenceKey,
|
||||||
|
@NonNull final OnApplyListener onApplyListener) {
|
||||||
|
this.layoutPreferenceKey = layoutPreferenceKey;
|
||||||
|
final PostsLayoutPreferences preferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(layoutPreferenceKey));
|
||||||
|
this.preferencesBuilder = PostsLayoutPreferences.builder().mergeFrom(preferences);
|
||||||
|
this.onApplyListener = onApplyListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull final Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
|
||||||
|
binding = DialogPostLayoutPreferencesBinding.inflate(LayoutInflater.from(context), null, false);
|
||||||
|
init();
|
||||||
|
return new MaterialAlertDialogBuilder(context)
|
||||||
|
.setView(binding.getRoot())
|
||||||
|
.setPositiveButton(R.string.apply, (dialog, which) -> {
|
||||||
|
final PostsLayoutPreferences preferences = preferencesBuilder.build();
|
||||||
|
final String json = preferences.getJson();
|
||||||
|
settingsHelper.putString(layoutPreferenceKey, json);
|
||||||
|
onApplyListener.onApply(preferences);
|
||||||
|
})
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
final Dialog dialog = getDialog();
|
||||||
|
if (dialog == null) return;
|
||||||
|
final Window window = dialog.getWindow();
|
||||||
|
if (window == null) return;
|
||||||
|
window.setWindowAnimations(R.style.dialog_window_animation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
initLayoutToggle();
|
||||||
|
if (preferencesBuilder.getType() != PostsLayoutPreferences.PostsLayoutType.LINEAR) {
|
||||||
|
initStaggeredOrGridOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initStaggeredOrGridOptions() {
|
||||||
|
initColCountToggle();
|
||||||
|
initNamesToggle();
|
||||||
|
initAvatarsToggle();
|
||||||
|
initCornersToggle();
|
||||||
|
initGapToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initLayoutToggle() {
|
||||||
|
final int selectedLayoutId = getSelectedLayoutId();
|
||||||
|
binding.layoutToggle.check(selectedLayoutId);
|
||||||
|
if (selectedLayoutId == R.id.layout_linear) {
|
||||||
|
binding.staggeredOrGridOptions.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
binding.layoutToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
|
||||||
|
if (isChecked) {
|
||||||
|
if (checkedId == R.id.layout_linear) {
|
||||||
|
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.LINEAR);
|
||||||
|
preferencesBuilder.setColCount(1);
|
||||||
|
binding.staggeredOrGridOptions.setVisibility(View.GONE);
|
||||||
|
} else if (checkedId == R.id.layout_staggered) {
|
||||||
|
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.STAGGERED_GRID);
|
||||||
|
if (preferencesBuilder.getColCount() == 1) {
|
||||||
|
preferencesBuilder.setColCount(2);
|
||||||
|
}
|
||||||
|
binding.staggeredOrGridOptions.setVisibility(View.VISIBLE);
|
||||||
|
initStaggeredOrGridOptions();
|
||||||
|
} else {
|
||||||
|
preferencesBuilder.setType(PostsLayoutPreferences.PostsLayoutType.GRID);
|
||||||
|
if (preferencesBuilder.getColCount() == 1) {
|
||||||
|
preferencesBuilder.setColCount(2);
|
||||||
|
}
|
||||||
|
binding.staggeredOrGridOptions.setVisibility(View.VISIBLE);
|
||||||
|
initStaggeredOrGridOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initColCountToggle() {
|
||||||
|
binding.colCountToggle.check(getSelectedColCountId());
|
||||||
|
binding.colCountToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
|
||||||
|
if (!isChecked) return;
|
||||||
|
if (checkedId == R.id.col_count_two) {
|
||||||
|
preferencesBuilder.setColCount(2);
|
||||||
|
} else {
|
||||||
|
preferencesBuilder.setColCount(3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAvatarsToggle() {
|
||||||
|
binding.showAvatarToggle.setChecked(preferencesBuilder.isAvatarVisible());
|
||||||
|
binding.avatarSizeToggle.check(getSelectedAvatarSizeId());
|
||||||
|
binding.showAvatarToggle.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
preferencesBuilder.setAvatarVisible(isChecked);
|
||||||
|
binding.labelAvatarSize.setVisibility(isChecked ? View.VISIBLE : View.GONE);
|
||||||
|
binding.avatarSizeToggle.setVisibility(isChecked ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
binding.labelAvatarSize.setVisibility(preferencesBuilder.isAvatarVisible() ? View.VISIBLE : View.GONE);
|
||||||
|
binding.avatarSizeToggle.setVisibility(preferencesBuilder.isAvatarVisible() ? View.VISIBLE : View.GONE);
|
||||||
|
binding.avatarSizeToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
|
||||||
|
if (!isChecked) return;
|
||||||
|
if (checkedId == R.id.avatar_size_tiny) {
|
||||||
|
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.TINY);
|
||||||
|
} else if (checkedId == R.id.avatar_size_small) {
|
||||||
|
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.SMALL);
|
||||||
|
} else {
|
||||||
|
preferencesBuilder.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.REGULAR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initNamesToggle() {
|
||||||
|
binding.showNamesToggle.setChecked(preferencesBuilder.isNameVisible());
|
||||||
|
binding.showNamesToggle.setOnCheckedChangeListener((buttonView, isChecked) -> preferencesBuilder.setNameVisible(isChecked));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initCornersToggle() {
|
||||||
|
binding.cornersToggle.check(getSelectedCornersId());
|
||||||
|
binding.cornersToggle.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
|
||||||
|
if (!isChecked) return;
|
||||||
|
if (checkedId == R.id.corners_round) {
|
||||||
|
preferencesBuilder.setHasRoundedCorners(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
preferencesBuilder.setHasRoundedCorners(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initGapToggle() {
|
||||||
|
binding.showGapToggle.setChecked(preferencesBuilder.getHasGap());
|
||||||
|
binding.showGapToggle.setOnCheckedChangeListener((buttonView, isChecked) -> preferencesBuilder.setHasGap(isChecked));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSelectedLayoutId() {
|
||||||
|
switch (preferencesBuilder.getType()) {
|
||||||
|
case STAGGERED_GRID:
|
||||||
|
return R.id.layout_staggered;
|
||||||
|
case LINEAR:
|
||||||
|
return R.id.layout_linear;
|
||||||
|
default:
|
||||||
|
case GRID:
|
||||||
|
return R.id.layout_grid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSelectedColCountId() {
|
||||||
|
switch (preferencesBuilder.getColCount()) {
|
||||||
|
case 2:
|
||||||
|
return R.id.col_count_two;
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
return R.id.col_count_three;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSelectedCornersId() {
|
||||||
|
if (preferencesBuilder.getHasRoundedCorners()) {
|
||||||
|
return R.id.corners_round;
|
||||||
|
}
|
||||||
|
return R.id.corners_square;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSelectedAvatarSizeId() {
|
||||||
|
switch (preferencesBuilder.getProfilePicSize()) {
|
||||||
|
case TINY:
|
||||||
|
return R.id.avatar_size_tiny;
|
||||||
|
case SMALL:
|
||||||
|
return R.id.avatar_size_small;
|
||||||
|
case REGULAR:
|
||||||
|
default:
|
||||||
|
return R.id.avatar_size_regular;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnApplyListener {
|
||||||
|
void onApply(final PostsLayoutPreferences preferences);
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,6 @@ import com.facebook.imagepipeline.image.ImageInfo;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.asyncs.DownloadAsync;
|
|
||||||
import awais.instagrabber.asyncs.ProfilePictureFetcher;
|
import awais.instagrabber.asyncs.ProfilePictureFetcher;
|
||||||
import awais.instagrabber.databinding.DialogProfilepicBinding;
|
import awais.instagrabber.databinding.DialogProfilepicBinding;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
@ -147,54 +146,9 @@ public class ProfilePicDialogFragment extends DialogFragment {
|
|||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
if (dir.exists() || dir.mkdirs()) {
|
if (dir.exists() || dir.mkdirs()) {
|
||||||
final File saveFile = new File(dir, name + '_' + System.currentTimeMillis() + ".jpg");
|
final File saveFile = new File(dir, name + '_' + System.currentTimeMillis() + ".jpg");
|
||||||
new DownloadAsync(context,
|
DownloadUtils.download(context, url, saveFile.getAbsolutePath());
|
||||||
url,
|
|
||||||
saveFile,
|
|
||||||
result -> {
|
|
||||||
final int toastRes = result != null && result.exists() ?
|
|
||||||
R.string.downloader_downloaded_in_folder : R.string.downloader_error_download_file;
|
|
||||||
Toast.makeText(context, toastRes, Toast.LENGTH_SHORT).show();
|
|
||||||
})
|
|
||||||
.setItems(null, name)
|
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Toast.makeText(context, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// private void showImageInfo() {
|
|
||||||
// final Drawable drawable = profileBinding.imageViewer.getDrawable();
|
|
||||||
// if (drawable != null) {
|
|
||||||
// final StringBuilder info = new StringBuilder(
|
|
||||||
// getString(R.string.profile_viewer_imageinfo, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()));
|
|
||||||
// if (drawable instanceof BitmapDrawable) {
|
|
||||||
// final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
|
|
||||||
// if (bitmap != null) {
|
|
||||||
// final String colorDepthPrefix = getString(R.string.profile_viewer_colordepth_prefix);
|
|
||||||
// switch (bitmap.getConfig()) {
|
|
||||||
// case ALPHA_8:
|
|
||||||
// info.append(colorDepthPrefix).append(" 8-bits(A)");
|
|
||||||
// break;
|
|
||||||
// case RGB_565:
|
|
||||||
// info.append(colorDepthPrefix).append(" 16-bits-A");
|
|
||||||
// break;
|
|
||||||
// case ARGB_4444:
|
|
||||||
// info.append(colorDepthPrefix).append(" 16-bits+A");
|
|
||||||
// break;
|
|
||||||
// case ARGB_8888:
|
|
||||||
// info.append(colorDepthPrefix).append(" 32-bits+A");
|
|
||||||
// break;
|
|
||||||
// case RGBA_F16:
|
|
||||||
// info.append(colorDepthPrefix).append(" 64-bits+A");
|
|
||||||
// break;
|
|
||||||
// case HARDWARE:
|
|
||||||
// info.append(colorDepthPrefix).append(" auto");
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// profileBinding.imageInfo.setText(info);
|
|
||||||
// profileBinding.imageInfo.setVisibility(View.VISIBLE);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
package awais.instagrabber.directdownload;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
import awais.instagrabber.asyncs.PostFetcher;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.IntentModel;
|
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.models.enums.IntentModelType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.IntentUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
|
|
||||||
public final class DirectDownload extends Activity {
|
|
||||||
private boolean isFound = false;
|
|
||||||
private Intent intent;
|
|
||||||
private Context context;
|
|
||||||
private NotificationManagerCompat notificationManager;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
notificationManager = NotificationManagerCompat.from(getApplicationContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWindowAttributesChanged(final WindowManager.LayoutParams params) {
|
|
||||||
super.onWindowAttributesChanged(params);
|
|
||||||
if (!isFound) {
|
|
||||||
intent = getIntent();
|
|
||||||
context = getApplicationContext();
|
|
||||||
if (intent != null && context != null) {
|
|
||||||
isFound = true;
|
|
||||||
checkIntent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Resources getResources() {
|
|
||||||
if (!isFound) {
|
|
||||||
intent = getIntent();
|
|
||||||
context = getApplicationContext();
|
|
||||||
if (intent != null && context != null) {
|
|
||||||
isFound = true;
|
|
||||||
checkIntent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.getResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void checkIntent() {
|
|
||||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
|
|
||||||
doDownload();
|
|
||||||
else {
|
|
||||||
final Handler handler = new Handler(Looper.getMainLooper());
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(context, R.string.direct_download_perms_ask, Toast.LENGTH_LONG).show();
|
|
||||||
handler.removeCallbacks(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ActivityCompat.requestPermissions(this, DownloadUtils.PERMS, 8020);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void doDownload() {
|
|
||||||
final String action = intent.getAction();
|
|
||||||
if (TextUtils.isEmpty(action) || Intent.ACTION_MAIN.equals(action)) return;
|
|
||||||
boolean error = true;
|
|
||||||
|
|
||||||
String data = null;
|
|
||||||
final Bundle extras = intent.getExtras();
|
|
||||||
if (extras != null) {
|
|
||||||
final Object extraData = extras.get(Intent.EXTRA_TEXT);
|
|
||||||
if (extraData != null) {
|
|
||||||
error = false;
|
|
||||||
data = extraData.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
final Uri intentData = intent.getData();
|
|
||||||
if (intentData != null) data = intentData.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data != null && !TextUtils.isEmpty(data)) {
|
|
||||||
final IntentModel model = IntentUtils.parseUrl(data);
|
|
||||||
if (model != null && model.getType() == IntentModelType.POST) {
|
|
||||||
final String text = model.getText();
|
|
||||||
|
|
||||||
new PostFetcher(text, new FetchListener<ViewerPostModel[]>() {
|
|
||||||
@Override
|
|
||||||
public void doBefore() {
|
|
||||||
final Notification fetchingPostNotif = new NotificationCompat.Builder(context, Constants.DOWNLOAD_CHANNEL_ID)
|
|
||||||
.setCategory(NotificationCompat.CATEGORY_STATUS).setSmallIcon(R.mipmap.ic_launcher)
|
|
||||||
.setAutoCancel(false).setPriority(NotificationCompat.PRIORITY_MIN)
|
|
||||||
.setContentText(context.getString(R.string.direct_download_loading)).build();
|
|
||||||
notificationManager.notify(1900000000, fetchingPostNotif);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResult(final ViewerPostModel[] result) {
|
|
||||||
if (notificationManager != null) notificationManager.cancel(1900000000);
|
|
||||||
if (result != null) {
|
|
||||||
if (result.length == 1) {
|
|
||||||
DownloadUtils.batchDownload(context, result[0].getProfileModel().getUsername(), DownloadMethod.DOWNLOAD_DIRECT,
|
|
||||||
Arrays.asList(result));
|
|
||||||
} else if (result.length > 1) {
|
|
||||||
context.startActivity(new Intent(context, MultiDirectDialog.class)
|
|
||||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
|
||||||
.putExtra(Constants.EXTRAS_POST, result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
package awais.instagrabber.directdownload;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
import awais.instagrabber.activities.BaseLanguageActivity;
|
|
||||||
import awais.instagrabber.adapters.PostsAdapter;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
|
||||||
import awais.instagrabber.models.BasePostModel;
|
|
||||||
import awais.instagrabber.models.PostModel;
|
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
|
|
||||||
public final class MultiDirectDialog extends BaseLanguageActivity {
|
|
||||||
public final ArrayList<BasePostModel> selectedItems = new ArrayList<>();
|
|
||||||
private PostsAdapter postsAdapter;
|
|
||||||
private MenuItem btnDownload;
|
|
||||||
private String username = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.dialog_direct);
|
|
||||||
|
|
||||||
final Toolbar toolbar = findViewById(R.id.toolbar);
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
|
|
||||||
final ViewerPostModel[] postModels;
|
|
||||||
final Intent intent = getIntent();
|
|
||||||
if (intent == null || !intent.hasExtra(Constants.EXTRAS_POST)
|
|
||||||
|| (postModels = (ViewerPostModel[]) intent.getSerializableExtra(Constants.EXTRAS_POST)) == null) {
|
|
||||||
Utils.errorFinish(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
username = postModels[0].getProfileModel().getUsername();
|
|
||||||
toolbar.setTitle(username);
|
|
||||||
toolbar.setSubtitle(postModels[0].getShortCode());
|
|
||||||
|
|
||||||
final RecyclerView recyclerView = findViewById(R.id.mainPosts);
|
|
||||||
recyclerView.setNestedScrollingEnabled(false);
|
|
||||||
recyclerView.setLayoutManager(new GridAutofitLayoutManager(this, Utils.convertDpToPx(130)));
|
|
||||||
recyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
|
||||||
|
|
||||||
final ArrayList<PostModel> models = new ArrayList<>(postModels.length - 1);
|
|
||||||
for (final ViewerPostModel postModel : postModels)
|
|
||||||
models.add(new PostModel(postModel.getItemType(), postModel.getPostId(), postModel.getDisplayUrl(),
|
|
||||||
postModel.getSliderDisplayUrl(), postModel.getShortCode(), postModel.getPostCaption(), postModel.getTimestamp(),
|
|
||||||
postModel.getLike(), postModel.getBookmark(), postModel.getLikes()));
|
|
||||||
|
|
||||||
// postsAdapter = new PostsAdapter(models, v -> {
|
|
||||||
// final Object tag = v.getTag();
|
|
||||||
// if (tag instanceof PostModel) {
|
|
||||||
// final PostModel postModel = (PostModel) tag;
|
|
||||||
// if (postsAdapter.isSelecting) toggleSelection(postModel);
|
|
||||||
// else {
|
|
||||||
// Utils.batchDownload(this, username, DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(postModel));
|
|
||||||
// finish();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }, v -> {
|
|
||||||
// final Object tag = v.getTag();
|
|
||||||
// if (tag instanceof PostModel) {
|
|
||||||
// postsAdapter.isSelecting = true;
|
|
||||||
// toggleSelection((PostModel) tag);
|
|
||||||
// }
|
|
||||||
// return true;
|
|
||||||
// });
|
|
||||||
|
|
||||||
recyclerView.setAdapter(postsAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
|
||||||
DownloadUtils.batchDownload(this, username, DownloadMethod.DOWNLOAD_DIRECT, selectedItems);
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.menu, menu);
|
|
||||||
btnDownload = menu.findItem(R.id.action_download);
|
|
||||||
menu.findItem(R.id.action_search).setVisible(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleSelection(final PostModel postModel) {
|
|
||||||
if (postModel != null && postsAdapter != null) {
|
|
||||||
if (postModel.isSelected()) selectedItems.remove(postModel);
|
|
||||||
else selectedItems.add(postModel);
|
|
||||||
postModel.setSelected(!postModel.isSelected());
|
|
||||||
notifyAdapter(postModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyAdapter(final PostModel postModel) {
|
|
||||||
// if (selectedItems.size() < 1) postsAdapter.isSelecting = false;
|
|
||||||
// if (postModel.getPosition() < 0) postsAdapter.notifyDataSetChanged();
|
|
||||||
// else postsAdapter.notifyItemChanged(postModel.getPosition(), postModel);
|
|
||||||
//
|
|
||||||
// if (btnDownload != null) btnDownload.setVisible(postsAdapter.isSelecting);
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,69 +12,129 @@ import android.text.TextWatcher;
|
|||||||
import android.text.style.RelativeSizeSpan;
|
import android.text.style.RelativeSizeSpan;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.LinearLayoutCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.CommentsAdapter;
|
import awais.instagrabber.adapters.CommentsAdapter;
|
||||||
import awais.instagrabber.asyncs.CommentsFetcher;
|
import awais.instagrabber.asyncs.CommentsFetcher;
|
||||||
import awais.instagrabber.databinding.FragmentCommentsBinding;
|
import awais.instagrabber.databinding.FragmentCommentsBinding;
|
||||||
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
|
||||||
import awais.instagrabber.models.CommentModel;
|
import awais.instagrabber.models.CommentModel;
|
||||||
import awais.instagrabber.models.ProfileModel;
|
import awais.instagrabber.models.ProfileModel;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
|
import awais.instagrabber.viewmodels.CommentsViewModel;
|
||||||
import awais.instagrabber.webservices.MediaService;
|
import awais.instagrabber.webservices.MediaService;
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||||
|
|
||||||
public final class CommentsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public final class CommentsViewerFragment extends BottomSheetDialogFragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
private static final String TAG = "CommentsViewerFragment";
|
private static final String TAG = "CommentsViewerFragment";
|
||||||
|
|
||||||
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
|
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
|
||||||
|
|
||||||
private CommentsAdapter commentsAdapter;
|
private CommentsAdapter commentsAdapter;
|
||||||
private CommentModel commentModel;
|
|
||||||
private FragmentCommentsBinding binding;
|
private FragmentCommentsBinding binding;
|
||||||
private String shortCode;
|
private String shortCode;
|
||||||
private String userId;
|
private String userId;
|
||||||
private Resources resources;
|
private Resources resources;
|
||||||
private InputMethodManager imm;
|
private InputMethodManager imm;
|
||||||
private AppCompatActivity fragmentActivity;
|
private AppCompatActivity fragmentActivity;
|
||||||
private LinearLayout root;
|
private LinearLayoutCompat root;
|
||||||
private boolean shouldRefresh = true;
|
private boolean shouldRefresh = true;
|
||||||
private MediaService mediaService;
|
private MediaService mediaService;
|
||||||
private String postId;
|
private String postId;
|
||||||
|
private CommentsViewModel commentsViewModel;
|
||||||
|
|
||||||
|
private final CommentsAdapter.CommentCallback commentCallback = new CommentsAdapter.CommentCallback() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final CommentModel comment) {
|
||||||
|
onCommentClick(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHashtagClick(final String hashtag) {
|
||||||
|
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||||
|
NavHostFragment.findNavController(CommentsViewerFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMentionClick(final String mention) {
|
||||||
|
openProfile(mention);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onURLClick(final String url) {
|
||||||
|
Utils.openURL(getContext(), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmailClick(final String emailAddress) {
|
||||||
|
Utils.openEmailAddress(getContext(), emailAddress);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final View.OnClickListener newCommentListener = v -> {
|
||||||
|
final Editable text = binding.commentText.getText();
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (text == null || TextUtils.isEmpty(text.toString())) {
|
||||||
|
Toast.makeText(context, R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||||
|
if (userId == null) return;
|
||||||
|
String replyToId = null;
|
||||||
|
final CommentModel commentModel = commentsAdapter.getSelected();
|
||||||
|
if (commentModel != null) {
|
||||||
|
replyToId = commentModel.getId();
|
||||||
|
}
|
||||||
|
mediaService.comment(postId, text.toString(), userId, replyToId, CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final Boolean result) {
|
||||||
|
commentsAdapter.clearSelection();
|
||||||
|
binding.commentText.setText("");
|
||||||
|
if (!result) {
|
||||||
|
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
Log.e(TAG, "Error during comment", t);
|
||||||
|
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
fragmentActivity = (AppCompatActivity) getActivity();
|
fragmentActivity = (AppCompatActivity) getActivity();
|
||||||
mediaService = MediaService.getInstance();
|
mediaService = MediaService.getInstance();
|
||||||
setHasOptionsMenu(true);
|
// setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -85,6 +145,8 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
binding = FragmentCommentsBinding.inflate(getLayoutInflater());
|
binding = FragmentCommentsBinding.inflate(getLayoutInflater());
|
||||||
|
binding.swipeRefreshLayout.setEnabled(false);
|
||||||
|
binding.swipeRefreshLayout.setNestedScrollingEnabled(false);
|
||||||
root = binding.getRoot();
|
root = binding.getRoot();
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
@ -96,34 +158,33 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
|
|||||||
shouldRefresh = false;
|
shouldRefresh = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// @Override
|
||||||
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
// public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.follow, menu);
|
// inflater.inflate(R.menu.follow, menu);
|
||||||
menu.findItem(R.id.action_compare).setVisible(false);
|
// menu.findItem(R.id.action_compare).setVisible(false);
|
||||||
final MenuItem menuSearch = menu.findItem(R.id.action_search);
|
// final MenuItem menuSearch = menu.findItem(R.id.action_search);
|
||||||
final SearchView searchView = (SearchView) menuSearch.getActionView();
|
// final SearchView searchView = (SearchView) menuSearch.getActionView();
|
||||||
searchView.setQueryHint(getResources().getString(R.string.action_search));
|
// searchView.setQueryHint(getResources().getString(R.string.action_search));
|
||||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
// searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
// @Override
|
||||||
public boolean onQueryTextSubmit(final String query) {
|
// public boolean onQueryTextSubmit(final String query) {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public boolean onQueryTextChange(final String query) {
|
// public boolean onQueryTextChange(final String query) {
|
||||||
if (commentsAdapter != null) commentsAdapter.getFilter().filter(query);
|
// // if (commentsAdapter != null) commentsAdapter.getFilter().filter(query);
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
new CommentsFetcher(shortCode, commentModels -> {
|
new CommentsFetcher(shortCode, commentModels -> {
|
||||||
|
commentsViewModel.getList().postValue(commentModels);
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
|
|
||||||
binding.rvComments.setAdapter(commentsAdapter);
|
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,9 +194,14 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
|
|||||||
shortCode = fragmentArgs.getShortCode();
|
shortCode = fragmentArgs.getShortCode();
|
||||||
postId = fragmentArgs.getPostId();
|
postId = fragmentArgs.getPostId();
|
||||||
userId = fragmentArgs.getPostUserId();
|
userId = fragmentArgs.getPostUserId();
|
||||||
setTitle();
|
// setTitle();
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
|
commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class);
|
||||||
|
binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
commentsAdapter = new CommentsAdapter(commentCallback);
|
||||||
|
binding.rvComments.setAdapter(commentsAdapter);
|
||||||
|
commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList);
|
||||||
resources = getResources();
|
resources = getResources();
|
||||||
if (!TextUtils.isEmpty(cookie)) {
|
if (!TextUtils.isEmpty(cookie)) {
|
||||||
binding.commentField.setStartIconVisible(false);
|
binding.commentField.setStartIconVisible(false);
|
||||||
@ -155,121 +221,107 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
|
|||||||
public void afterTextChanged(final Editable s) {}
|
public void afterTextChanged(final Editable s) {}
|
||||||
});
|
});
|
||||||
binding.commentField.setStartIconOnClickListener(v -> {
|
binding.commentField.setStartIconOnClickListener(v -> {
|
||||||
if (commentModel != null) {
|
commentsAdapter.clearSelection();
|
||||||
final View focus = binding.rvComments.findViewWithTag(commentModel);
|
|
||||||
focus.setBackgroundColor(0x00000000);
|
|
||||||
commentModel = null;
|
|
||||||
}
|
|
||||||
binding.commentText.setText("");
|
binding.commentText.setText("");
|
||||||
});
|
});
|
||||||
binding.commentField.setEndIconOnClickListener(newCommentListener);
|
binding.commentField.setEndIconOnClickListener(newCommentListener);
|
||||||
}
|
}
|
||||||
new CommentsFetcher(this.shortCode, commentModels -> {
|
onRefresh();
|
||||||
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
|
|
||||||
binding.rvComments.setAdapter(commentsAdapter);
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTitle() {
|
// private void setTitle() {
|
||||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
// final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||||
if (actionBar == null) return;
|
// if (actionBar == null) return;
|
||||||
actionBar.setTitle(R.string.title_comments);
|
// actionBar.setTitle(R.string.title_comments);
|
||||||
// actionBar.setSubtitle(shortCode);
|
// actionBar.setSubtitle(shortCode);
|
||||||
}
|
// }
|
||||||
|
|
||||||
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
|
private void onCommentClick(final CommentModel commentModel) {
|
||||||
|
final String username = commentModel.getProfileModel().getUsername();
|
||||||
|
final SpannableString title = new SpannableString(username + ":\n" + commentModel.getText());
|
||||||
|
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||||
|
|
||||||
|
String[] commentDialogList;
|
||||||
|
|
||||||
|
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||||
|
if (!TextUtils.isEmpty(cookie)
|
||||||
|
&& userIdFromCookie != null
|
||||||
|
&& (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.equals(userId))) {
|
||||||
|
commentDialogList = new String[]{
|
||||||
|
resources.getString(R.string.open_profile),
|
||||||
|
resources.getString(R.string.view_pfp),
|
||||||
|
resources.getString(R.string.comment_viewer_copy_user),
|
||||||
|
// resources.getString(R.string.comment_viewer_copy_comment),
|
||||||
|
resources.getString(R.string.comment_viewer_reply_comment),
|
||||||
|
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
|
||||||
|
: resources.getString(R.string.comment_viewer_like_comment),
|
||||||
|
resources.getString(R.string.comment_viewer_delete_comment)
|
||||||
|
};
|
||||||
|
} else if (!TextUtils.isEmpty(cookie)) {
|
||||||
|
commentDialogList = new String[]{
|
||||||
|
resources.getString(R.string.open_profile),
|
||||||
|
resources.getString(R.string.view_pfp),
|
||||||
|
resources.getString(R.string.comment_viewer_copy_user),
|
||||||
|
// resources.getString(R.string.comment_viewer_copy_comment),
|
||||||
|
resources.getString(R.string.comment_viewer_reply_comment),
|
||||||
|
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
|
||||||
|
: resources.getString(R.string.comment_viewer_like_comment),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
commentDialogList = new String[]{
|
||||||
|
resources.getString(R.string.open_profile),
|
||||||
|
resources.getString(R.string.view_pfp),
|
||||||
|
resources.getString(R.string.comment_viewer_copy_user),
|
||||||
|
// resources.getString(R.string.comment_viewer_copy_comment)
|
||||||
|
};
|
||||||
|
}
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
if (commentModel == null) {
|
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
final ProfileModel profileModel = commentModel.getProfileModel();
|
||||||
return;
|
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||||
}
|
switch (which) {
|
||||||
final ProfileModel profileModel = commentModel.getProfileModel();
|
case 0: // open profile
|
||||||
switch (which) {
|
openProfile("@" + profileModel.getUsername());
|
||||||
case 0: // open profile
|
break;
|
||||||
openProfile(profileModel.getUsername());
|
case 1: // view profile pic
|
||||||
break;
|
final FragmentManager fragmentManager = getParentFragmentManager();
|
||||||
case 1: // view profile pic
|
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
|
||||||
final FragmentManager fragmentManager = getParentFragmentManager();
|
profileModel.getUsername(),
|
||||||
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(),
|
profileModel.getHdProfilePic());
|
||||||
profileModel.getUsername(),
|
final FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||||
profileModel.getHdProfilePic());
|
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
final FragmentTransaction ft = fragmentManager.beginTransaction();
|
.add(fragment, "profilePicDialog")
|
||||||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
.commit();
|
||||||
.add(fragment, "profilePicDialog")
|
break;
|
||||||
.commit();
|
case 2: // copy username
|
||||||
break;
|
Utils.copyText(context, profileModel.getUsername());
|
||||||
case 2: // copy username
|
break;
|
||||||
Utils.copyText(context, profileModel.getUsername());
|
// case 3: // copy comment
|
||||||
break;
|
// Utils.copyText(context, commentModel.getText().toString());
|
||||||
case 3: // copy comment
|
// break;
|
||||||
Utils.copyText(context, commentModel.getText().toString());
|
case 3: // reply to comment
|
||||||
break;
|
// final View focus = binding.rvComments.findViewWithTag(commentModel);
|
||||||
case 4: // reply to comment
|
// focus.setBackgroundColor(0x80888888);
|
||||||
final View focus = binding.rvComments.findViewWithTag(commentModel);
|
commentsAdapter.setSelected(commentModel);
|
||||||
focus.setBackgroundColor(0x80888888);
|
String mention = "@" + profileModel.getUsername() + " ";
|
||||||
String mention = "@" + profileModel.getUsername() + " ";
|
binding.commentText.setText(mention);
|
||||||
binding.commentText.setText(mention);
|
binding.commentText.requestFocus();
|
||||||
binding.commentText.requestFocus();
|
binding.commentText.setSelection(mention.length());
|
||||||
binding.commentText.setSelection(mention.length());
|
binding.commentText.postDelayed(() -> {
|
||||||
binding.commentText.postDelayed(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
|
imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
|
||||||
if (imm == null) return;
|
if (imm == null) return;
|
||||||
imm.showSoftInput(binding.commentText, 0);
|
imm.showSoftInput(binding.commentText, 0);
|
||||||
|
}, 200);
|
||||||
|
break;
|
||||||
|
case 4: // like/unlike comment
|
||||||
|
if (csrfToken == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, 200);
|
if (!commentModel.getLiked()) {
|
||||||
break;
|
mediaService.commentLike(commentModel.getId(), csrfToken, new ServiceCallback<Boolean>() {
|
||||||
case 5: // like/unlike comment
|
|
||||||
if (!commentModel.getLiked()) {
|
|
||||||
mediaService.commentLike(commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final Boolean result) {
|
|
||||||
commentModel = null;
|
|
||||||
if (!result) {
|
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
Log.e(TAG, "Error liking comment", t);
|
|
||||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mediaService.commentUnlike(commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final Boolean result) {
|
|
||||||
commentModel = null;
|
|
||||||
if (!result) {
|
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
Log.e(TAG, "Error unliking comment", t);
|
|
||||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 6: // delete comment
|
|
||||||
final String userId = CookieUtils.getUserIdFromCookie(cookie);
|
|
||||||
if (userId == null) return;
|
|
||||||
mediaService.deleteComment(
|
|
||||||
postId, userId, commentModel.getId(), CookieUtils.getCsrfTokenFromCookie(cookie),
|
|
||||||
new ServiceCallback<Boolean>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final Boolean result) {
|
public void onSuccess(final Boolean result) {
|
||||||
commentModel = null;
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
@ -279,112 +331,62 @@ public final class CommentsViewerFragment extends Fragment implements SwipeRefre
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(final Throwable t) {
|
public void onFailure(final Throwable t) {
|
||||||
Log.e(TAG, "Error deleting comment", t);
|
Log.e(TAG, "Error liking comment", t);
|
||||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
};
|
mediaService.commentUnlike(commentModel.getId(), csrfToken, new ServiceCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final Boolean result) {
|
||||||
|
if (!result) {
|
||||||
|
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
private final View.OnClickListener clickListener = v -> {
|
@Override
|
||||||
final Object tag = v.getTag();
|
public void onFailure(final Throwable t) {
|
||||||
if (tag instanceof CommentModel) {
|
Log.e(TAG, "Error unliking comment", t);
|
||||||
commentModel = (CommentModel) tag;
|
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 5: // delete comment
|
||||||
|
final String userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||||
|
if (userId == null) return;
|
||||||
|
mediaService.deleteComment(
|
||||||
|
postId, userId, commentModel.getId(), csrfToken,
|
||||||
|
new ServiceCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final Boolean result) {
|
||||||
|
if (!result) {
|
||||||
|
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
final String username = commentModel.getProfileModel().getUsername();
|
@Override
|
||||||
final SpannableString title = new SpannableString(username + ":\n" + commentModel.getText());
|
public void onFailure(final Throwable t) {
|
||||||
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
Log.e(TAG, "Error deleting comment", t);
|
||||||
|
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
String[] commentDialogList;
|
}
|
||||||
|
});
|
||||||
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
break;
|
||||||
if (!TextUtils.isEmpty(cookie)
|
|
||||||
&& userIdFromCookie != null
|
|
||||||
&& (userIdFromCookie.equals(commentModel.getProfileModel().getId()) || userIdFromCookie.equals(userId))) {
|
|
||||||
commentDialogList = new String[]{
|
|
||||||
resources.getString(R.string.open_profile),
|
|
||||||
resources.getString(R.string.view_pfp),
|
|
||||||
resources.getString(R.string.comment_viewer_copy_user),
|
|
||||||
resources.getString(R.string.comment_viewer_copy_comment),
|
|
||||||
resources.getString(R.string.comment_viewer_reply_comment),
|
|
||||||
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
|
|
||||||
: resources.getString(R.string.comment_viewer_like_comment),
|
|
||||||
resources.getString(R.string.comment_viewer_delete_comment)
|
|
||||||
};
|
|
||||||
} else if (!TextUtils.isEmpty(cookie)) {
|
|
||||||
commentDialogList = new String[]{
|
|
||||||
resources.getString(R.string.open_profile),
|
|
||||||
resources.getString(R.string.view_pfp),
|
|
||||||
resources.getString(R.string.comment_viewer_copy_user),
|
|
||||||
resources.getString(R.string.comment_viewer_copy_comment),
|
|
||||||
resources.getString(R.string.comment_viewer_reply_comment),
|
|
||||||
commentModel.getLiked() ? resources.getString(R.string.comment_viewer_unlike_comment)
|
|
||||||
: resources.getString(R.string.comment_viewer_like_comment),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
commentDialogList = new String[]{
|
|
||||||
resources.getString(R.string.open_profile),
|
|
||||||
resources.getString(R.string.view_pfp),
|
|
||||||
resources.getString(R.string.comment_viewer_copy_user),
|
|
||||||
resources.getString(R.string.comment_viewer_copy_comment)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
final Context context = getContext();
|
};
|
||||||
if (context == null) return;
|
new AlertDialog.Builder(context)
|
||||||
new AlertDialog.Builder(context)
|
.setTitle(title)
|
||||||
.setTitle(title)
|
.setItems(commentDialogList, profileDialogListener)
|
||||||
.setItems(commentDialogList, profileDialogListener)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.show();
|
||||||
.show();
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
|
|
||||||
if (isHashtag) {
|
|
||||||
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalHashTagFragment(text);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
openProfile(text);
|
|
||||||
};
|
|
||||||
|
|
||||||
private final View.OnClickListener newCommentListener = v -> {
|
|
||||||
final Editable text = binding.commentText.getText();
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
if (text == null || TextUtils.isEmpty(text.toString())) {
|
|
||||||
Toast.makeText(context, R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String userId = CookieUtils.getUserIdFromCookie(cookie);
|
|
||||||
if (userId == null) return;
|
|
||||||
String replyToId = null;
|
|
||||||
if (commentModel != null) {
|
|
||||||
replyToId = commentModel.getId();
|
|
||||||
}
|
|
||||||
mediaService.comment(postId, text.toString(), userId, replyToId, CookieUtils.getCsrfTokenFromCookie(cookie), new ServiceCallback<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final Boolean result) {
|
|
||||||
commentModel = null;
|
|
||||||
binding.commentText.setText("");
|
|
||||||
if (!result) {
|
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
Log.e(TAG, "Error during comment", t);
|
|
||||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private void openProfile(final String username) {
|
private void openProfile(final String username) {
|
||||||
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment("@" + username);
|
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package awais.instagrabber.fragments;
|
package awais.instagrabber.fragments;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -12,6 +12,8 @@ import android.text.style.StyleSpan;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -22,81 +24,79 @@ import androidx.activity.OnBackPressedDispatcher;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.core.content.PermissionChecker;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.activities.MainActivity;
|
import awais.instagrabber.activities.MainActivity;
|
||||||
import awais.instagrabber.adapters.PostsAdapter;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.asyncs.HashtagFetcher;
|
import awais.instagrabber.asyncs.HashtagFetcher;
|
||||||
import awais.instagrabber.asyncs.PostsFetcher;
|
import awais.instagrabber.asyncs.HashtagPostFetchService;
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
|
||||||
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.databinding.FragmentHashtagBinding;
|
import awais.instagrabber.databinding.FragmentHashtagBinding;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.models.HashtagModel;
|
import awais.instagrabber.models.HashtagModel;
|
||||||
import awais.instagrabber.models.PostModel;
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.models.StoryModel;
|
import awais.instagrabber.models.StoryModel;
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.models.enums.FavoriteType;
|
import awais.instagrabber.models.enums.FavoriteType;
|
||||||
import awais.instagrabber.models.enums.PostItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.DataBox;
|
import awais.instagrabber.utils.DataBox;
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.PostsViewModel;
|
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
import awais.instagrabber.webservices.StoriesService;
|
import awais.instagrabber.webservices.StoriesService;
|
||||||
import awais.instagrabber.webservices.TagsService;
|
import awais.instagrabber.webservices.TagsService;
|
||||||
import awaisomereport.LogCollector;
|
import awaisomereport.LogCollector;
|
||||||
|
|
||||||
|
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||||
|
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
import static awais.instagrabber.utils.Utils.logCollector;
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
private static final String TAG = "HashTagFragment";
|
private static final String TAG = "HashTagFragment";
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
|
||||||
|
|
||||||
|
public static final String ARG_HASHTAG = "hashtag";
|
||||||
|
|
||||||
private MainActivity fragmentActivity;
|
private MainActivity fragmentActivity;
|
||||||
private FragmentHashtagBinding binding;
|
private FragmentHashtagBinding binding;
|
||||||
private NestedCoordinatorLayout root;
|
private NestedCoordinatorLayout root;
|
||||||
private boolean shouldRefresh = true, hasStories = false;
|
private boolean shouldRefresh = true;
|
||||||
|
private boolean hasStories = false;
|
||||||
private String hashtag;
|
private String hashtag;
|
||||||
private HashtagModel hashtagModel;
|
private HashtagModel hashtagModel;
|
||||||
private PostsViewModel postsViewModel;
|
|
||||||
private PostsAdapter postsAdapter;
|
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
private StoriesService storiesService;
|
private StoriesService storiesService;
|
||||||
private boolean hasNextPage;
|
|
||||||
private String endCursor;
|
|
||||||
private AsyncTask<?, ?, ?> currentlyExecuting;
|
private AsyncTask<?, ?, ?> currentlyExecuting;
|
||||||
private boolean isLoggedIn;
|
private boolean isLoggedIn;
|
||||||
private TagsService tagsService;
|
private TagsService tagsService;
|
||||||
private boolean isPullToRefresh;
|
private boolean storiesFetching;
|
||||||
|
private Set<FeedModel> selectedFeedModels;
|
||||||
|
private FeedModel downloadFeedModel;
|
||||||
|
private int downloadChildPosition = -1;
|
||||||
|
|
||||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
setEnabled(false);
|
binding.posts.endSelection();
|
||||||
remove();
|
|
||||||
if (postsAdapter == null) return;
|
|
||||||
postsAdapter.clearSelection();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||||
@ -104,53 +104,146 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
new PrimaryActionModeCallback.CallbacksHelper() {
|
new PrimaryActionModeCallback.CallbacksHelper() {
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy(final ActionMode mode) {
|
public void onDestroy(final ActionMode mode) {
|
||||||
onBackPressedCallback.handleOnBackPressed();
|
binding.posts.endSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_download) {
|
if (item.getItemId() == R.id.action_download) {
|
||||||
if (postsAdapter == null || hashtag == null) {
|
if (HashTagFragment.this.selectedFeedModels == null) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return false;
|
if (context == null) return false;
|
||||||
DownloadUtils.batchDownload(context,
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
hashtag,
|
DownloadUtils.download(context, ImmutableList.copyOf(HashTagFragment.this.selectedFeedModels));
|
||||||
DownloadMethod.DOWNLOAD_MAIN,
|
binding.posts.endSelection();
|
||||||
postsAdapter.getSelectedModels());
|
return true;
|
||||||
checkAndResetAction();
|
}
|
||||||
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(final List<PostModel> result) {
|
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
||||||
if (result == null) return;
|
}
|
||||||
binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
|
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
@Override
|
||||||
List<PostModel> finalList = postModels == null || postModels.isEmpty()
|
public void onSliderClick(final FeedModel feedModel, final int position) {
|
||||||
? new ArrayList<>()
|
openPostDialog(feedModel, null, null, position);
|
||||||
: new ArrayList<>(postModels);
|
}
|
||||||
if (isPullToRefresh) {
|
|
||||||
finalList = result;
|
@Override
|
||||||
isPullToRefresh = false;
|
public void onCommentsClick(final FeedModel feedModel) {
|
||||||
} else {
|
final NavDirections commentsAction = HashTagFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||||
finalList.addAll(result);
|
feedModel.getShortCode(),
|
||||||
|
feedModel.getPostId(),
|
||||||
|
feedModel.getProfileModel().getId()
|
||||||
|
);
|
||||||
|
NavHostFragment.findNavController(HashTagFragment.this).navigate(commentsAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
|
DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
finalList.addAll(result);
|
downloadFeedModel = feedModel;
|
||||||
postsViewModel.getList().postValue(finalList);
|
downloadChildPosition = childPosition;
|
||||||
PostModel model = null;
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||||
if (!result.isEmpty()) {
|
}
|
||||||
model = result.get(result.size() - 1);
|
|
||||||
|
@Override
|
||||||
|
public void onHashtagClick(final String hashtag) {
|
||||||
|
final NavDirections action = HashTagFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||||
|
NavHostFragment.findNavController(HashTagFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections action = HashTagFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
|
||||||
|
NavHostFragment.findNavController(HashTagFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMentionClick(final String mention) {
|
||||||
|
navigateToProfile(mention.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNameClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onURLClick(final String url) {
|
||||||
|
Utils.openURL(getContext(), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmailClick(final String emailId) {
|
||||||
|
Utils.openEmailAddress(getContext(), emailId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openPostDialog(final FeedModel feedModel,
|
||||||
|
final View profilePicView,
|
||||||
|
final View mainPostImage,
|
||||||
|
final int position) {
|
||||||
|
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||||
|
.builder(feedModel);
|
||||||
|
if (position >= 0) {
|
||||||
|
builder.setPosition(position);
|
||||||
|
}
|
||||||
|
final PostViewV2Fragment fragment = builder
|
||||||
|
.setSharedProfilePicElement(profilePicView)
|
||||||
|
.setSharedMainPostElement(mainPostImage)
|
||||||
|
.build();
|
||||||
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionStart() {
|
||||||
|
if (!onBackPressedCallback.isEnabled()) {
|
||||||
|
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
|
onBackPressedCallback.setEnabled(true);
|
||||||
|
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
|
}
|
||||||
|
if (actionMode == null) {
|
||||||
|
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionChange(final Set<FeedModel> selectedFeedModels) {
|
||||||
|
final String title = getString(R.string.number_selected, selectedFeedModels.size());
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.setTitle(title);
|
||||||
|
}
|
||||||
|
HashTagFragment.this.selectedFeedModels = selectedFeedModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionEnd() {
|
||||||
|
if (onBackPressedCallback.isEnabled()) {
|
||||||
|
onBackPressedCallback.setEnabled(false);
|
||||||
|
onBackPressedCallback.remove();
|
||||||
|
}
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.finish();
|
||||||
|
actionMode = null;
|
||||||
}
|
}
|
||||||
if (model == null) return;
|
|
||||||
endCursor = model.getEndCursor();
|
|
||||||
hasNextPage = model.hasNextPage();
|
|
||||||
model.setPageCursor(false, null);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,6 +253,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
fragmentActivity = (MainActivity) requireActivity();
|
fragmentActivity = (MainActivity) requireActivity();
|
||||||
tagsService = TagsService.getInstance();
|
tagsService = TagsService.getInstance();
|
||||||
storiesService = StoriesService.getInstance();
|
storiesService = StoriesService.getInstance();
|
||||||
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -184,9 +278,8 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
isPullToRefresh = true;
|
binding.posts.refresh();
|
||||||
endCursor = null;
|
fetchStories();
|
||||||
fetchHashtagModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -196,10 +289,35 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||||
super.onDestroy();
|
inflater.inflate(R.menu.topic_posts_menu, menu);
|
||||||
if (postsViewModel != null) {
|
}
|
||||||
postsViewModel.getList().postValue(Collections.emptyList());
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.layout) {
|
||||||
|
showPostsLayoutPreferences();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||||
|
if (downloadFeedModel == null) return;
|
||||||
|
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
|
||||||
|
downloadFeedModel = null;
|
||||||
|
downloadChildPosition = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
|
||||||
|
DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
|
||||||
|
binding.posts.endSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,113 +327,42 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
||||||
final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments());
|
final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments());
|
||||||
hashtag = fragmentArgs.getHashtag();
|
hashtag = fragmentArgs.getHashtag();
|
||||||
setTitle();
|
|
||||||
setupPosts();
|
|
||||||
fetchHashtagModel();
|
fetchHashtagModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPosts() {
|
|
||||||
postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
|
||||||
binding.mainPosts.setLayoutManager(layoutManager);
|
|
||||||
binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
|
||||||
postsAdapter = new PostsAdapter((postModel, position) -> {
|
|
||||||
if (postsAdapter.isSelecting()) {
|
|
||||||
if (actionMode == null) return;
|
|
||||||
final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (checkAndResetAction()) return;
|
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
|
||||||
if (postModels == null || postModels.size() == 0) return;
|
|
||||||
if (postModels.get(0) == null) return;
|
|
||||||
final String postId = postModels.get(0).getPostId();
|
|
||||||
final boolean isId = postId != null && isLoggedIn;
|
|
||||||
final String[] idsOrShortCodes = new String[postModels.size()];
|
|
||||||
for (int i = 0; i < postModels.size(); i++) {
|
|
||||||
idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
|
|
||||||
: postModels.get(i).getShortCode();
|
|
||||||
}
|
|
||||||
final NavDirections action = HashTagFragmentDirections.actionGlobalPostViewFragment(
|
|
||||||
position,
|
|
||||||
idsOrShortCodes,
|
|
||||||
isId);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
|
|
||||||
}, (model, position) -> {
|
|
||||||
if (!postsAdapter.isSelecting()) {
|
|
||||||
checkAndResetAction();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
|
||||||
onBackPressedCallback.setEnabled(true);
|
|
||||||
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
|
||||||
final String title = getString(R.string.number_selected, 1);
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
|
|
||||||
binding.mainPosts.setAdapter(postsAdapter);
|
|
||||||
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
|
||||||
if (!hasNextPage || getContext() == null) return;
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
fetchPosts();
|
|
||||||
endCursor = null;
|
|
||||||
});
|
|
||||||
binding.mainPosts.addOnScrollListener(lazyLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fetchHashtagModel() {
|
private void fetchHashtagModel() {
|
||||||
stopCurrentExecutor();
|
stopCurrentExecutor();
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
currentlyExecuting = new HashtagFetcher(hashtag.substring(1), result -> {
|
currentlyExecuting = new HashtagFetcher(hashtag.substring(1), result -> {
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
hashtagModel = result;
|
hashtagModel = result;
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
if (hashtagModel == null) {
|
if (hashtagModel == null) {
|
||||||
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetchPosts();
|
setTitle();
|
||||||
|
setHashtagDetails();
|
||||||
|
setupPosts();
|
||||||
|
fetchStories();
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchPosts() {
|
private void setupPosts() {
|
||||||
stopCurrentExecutor();
|
binding.posts.setViewModelStoreOwner(this)
|
||||||
|
.setLifeCycleOwner(this)
|
||||||
|
.setPostFetchService(new HashtagPostFetchService(hashtagModel))
|
||||||
|
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_HASHTAG_POSTS_LAYOUT)))
|
||||||
|
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||||
|
.setFeedItemCallback(feedItemCallback)
|
||||||
|
.setSelectionModeCallback(selectionModeCallback)
|
||||||
|
.init();
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
if (TextUtils.isEmpty(hashtag)) return;
|
}
|
||||||
currentlyExecuting = new PostsFetcher(hashtag.substring(1), PostItemType.HASHTAG, endCursor, postsFetchListener)
|
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
if (isLoggedIn) {
|
|
||||||
storiesService.getUserStory(hashtagModel.getName(),
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
new ServiceCallback<List<StoryModel>>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final List<StoryModel> storyModels) {
|
|
||||||
if (storyModels != null && !storyModels.isEmpty()) {
|
|
||||||
binding.mainHashtagImage.setStoriesBorder();
|
|
||||||
hasStories = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private void setHashtagDetails() {
|
||||||
public void onFailure(final Throwable t) {
|
if (isLoggedIn) {
|
||||||
Log.e(TAG, "Error", t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
binding.btnFollowTag.setVisibility(View.VISIBLE);
|
binding.btnFollowTag.setVisibility(View.VISIBLE);
|
||||||
binding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow);
|
binding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow);
|
||||||
binding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing()
|
binding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing()
|
||||||
@ -324,9 +371,35 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
binding.btnFollowTag.setOnClickListener(v -> {
|
binding.btnFollowTag.setOnClickListener(v -> {
|
||||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||||
binding.btnFollowTag.setClickable(false);
|
if (csrfToken != null) {
|
||||||
if (!hashtagModel.getFollowing()) {
|
binding.btnFollowTag.setClickable(false);
|
||||||
tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
if (!hashtagModel.getFollowing()) {
|
||||||
|
tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final Boolean result) {
|
||||||
|
binding.btnFollowTag.setClickable(true);
|
||||||
|
if (!result) {
|
||||||
|
Log.e(TAG, "onSuccess: result is false");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull final Throwable t) {
|
||||||
|
binding.btnFollowTag.setClickable(true);
|
||||||
|
Log.e(TAG, "onFailure: ", t);
|
||||||
|
final String message = t.getMessage();
|
||||||
|
Snackbar.make(root,
|
||||||
|
message != null ? message
|
||||||
|
: getString(R.string.downloader_unknown_error),
|
||||||
|
BaseTransientBottomBar.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final Boolean result) {
|
public void onSuccess(final Boolean result) {
|
||||||
binding.btnFollowTag.setClickable(true);
|
binding.btnFollowTag.setClickable(true);
|
||||||
@ -349,31 +422,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final Boolean result) {
|
|
||||||
binding.btnFollowTag.setClickable(true);
|
|
||||||
if (!result) {
|
|
||||||
Log.e(TAG, "onSuccess: result is false");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull final Throwable t) {
|
|
||||||
binding.btnFollowTag.setClickable(true);
|
|
||||||
Log.e(TAG, "onFailure: ", t);
|
|
||||||
final String message = t.getMessage();
|
|
||||||
Snackbar.make(root,
|
|
||||||
message != null ? message
|
|
||||||
: getString(R.string.downloader_unknown_error),
|
|
||||||
BaseTransientBottomBar.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
binding.btnFollowTag.setVisibility(View.GONE);
|
binding.btnFollowTag.setVisibility(View.GONE);
|
||||||
@ -420,16 +469,43 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
binding.mainTagPostCount.setText(span);
|
binding.mainTagPostCount.setText(span);
|
||||||
binding.mainTagPostCount.setVisibility(View.VISIBLE);
|
binding.mainTagPostCount.setVisibility(View.VISIBLE);
|
||||||
binding.mainHashtagImage.setOnClickListener(v -> {
|
binding.mainHashtagImage.setOnClickListener(v -> {
|
||||||
if (hasStories) {
|
if (!hasStories) return;
|
||||||
// show stories
|
// show stories
|
||||||
final NavDirections action = HashTagFragmentDirections
|
final NavDirections action = HashTagFragmentDirections
|
||||||
.actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
|
.actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fetchStories() {
|
||||||
|
if (!isLoggedIn) return;
|
||||||
|
storiesFetching = true;
|
||||||
|
storiesService.getUserStory(
|
||||||
|
hashtagModel.getName(),
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
new ServiceCallback<List<StoryModel>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final List<StoryModel> storyModels) {
|
||||||
|
if (storyModels != null && !storyModels.isEmpty()) {
|
||||||
|
binding.mainHashtagImage.setStoriesBorder();
|
||||||
|
hasStories = true;
|
||||||
|
} else {
|
||||||
|
hasStories = false;
|
||||||
|
}
|
||||||
|
storiesFetching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
Log.e(TAG, "Error", t);
|
||||||
|
storiesFetching = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void stopCurrentExecutor() {
|
public void stopCurrentExecutor() {
|
||||||
if (currentlyExecuting != null) {
|
if (currentlyExecuting != null) {
|
||||||
try {
|
try {
|
||||||
@ -445,24 +521,28 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
private void setTitle() {
|
private void setTitle() {
|
||||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
Log.d(TAG, "setting title: " + hashtag);
|
// Log.d(TAG, "setting title: " + hashtag);
|
||||||
final Handler handler = new Handler();
|
actionBar.setTitle(hashtag);
|
||||||
handler.postDelayed(() -> actionBar.setTitle(hashtag), 200);
|
// final Handler handler = new Handler();
|
||||||
|
// handler.postDelayed(() -> , 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAndResetAction() {
|
private void updateSwipeRefreshState() {
|
||||||
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching() || storiesFetching);
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
private void navigateToProfile(final String username) {
|
||||||
onBackPressedCallback.setEnabled(false);
|
final NavController navController = NavHostFragment.findNavController(this);
|
||||||
onBackPressedCallback.remove();
|
final Bundle bundle = new Bundle();
|
||||||
}
|
bundle.putString("username", username);
|
||||||
if (actionMode != null) {
|
navController.navigate(R.id.action_global_profileFragment, bundle);
|
||||||
actionMode.finish();
|
}
|
||||||
actionMode = null;
|
|
||||||
}
|
private void showPostsLayoutPreferences() {
|
||||||
return true;
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||||
|
Constants.PREF_HASHTAG_POSTS_LAYOUT,
|
||||||
|
preferences -> new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200));
|
||||||
|
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,20 @@ package awais.instagrabber.fragments;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.style.RelativeSizeSpan;
|
import android.text.style.RelativeSizeSpan;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -23,134 +27,221 @@ import androidx.activity.OnBackPressedDispatcher;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.core.content.PermissionChecker;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.activities.MainActivity;
|
import awais.instagrabber.activities.MainActivity;
|
||||||
import awais.instagrabber.adapters.PostsAdapter;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.asyncs.LocationFetcher;
|
import awais.instagrabber.asyncs.LocationFetcher;
|
||||||
import awais.instagrabber.asyncs.PostsFetcher;
|
import awais.instagrabber.asyncs.LocationPostFetchService;
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
|
||||||
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.databinding.FragmentLocationBinding;
|
import awais.instagrabber.databinding.FragmentLocationBinding;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.models.LocationModel;
|
import awais.instagrabber.models.LocationModel;
|
||||||
import awais.instagrabber.models.PostModel;
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.models.StoryModel;
|
import awais.instagrabber.models.StoryModel;
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.models.enums.FavoriteType;
|
import awais.instagrabber.models.enums.FavoriteType;
|
||||||
import awais.instagrabber.models.enums.PostItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.DataBox;
|
import awais.instagrabber.utils.DataBox;
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.PostsViewModel;
|
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
import awais.instagrabber.webservices.StoriesService;
|
import awais.instagrabber.webservices.StoriesService;
|
||||||
import awaisomereport.LogCollector;
|
import awaisomereport.LogCollector;
|
||||||
|
|
||||||
|
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||||
|
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
import static awais.instagrabber.utils.Utils.logCollector;
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
private static final String TAG = "LocationFragment";
|
private static final String TAG = "LocationFragment";
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
|
||||||
|
|
||||||
private MainActivity fragmentActivity;
|
private MainActivity fragmentActivity;
|
||||||
private FragmentLocationBinding binding;
|
private FragmentLocationBinding binding;
|
||||||
private NestedCoordinatorLayout root;
|
private NestedCoordinatorLayout root;
|
||||||
private boolean shouldRefresh = true, hasStories = false;
|
private boolean shouldRefresh = true;
|
||||||
|
private boolean hasStories = false;
|
||||||
private String locationId;
|
private String locationId;
|
||||||
private LocationModel locationModel;
|
private LocationModel locationModel;
|
||||||
private PostsViewModel postsViewModel;
|
|
||||||
private PostsAdapter postsAdapter;
|
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
private StoriesService storiesService;
|
private StoriesService storiesService;
|
||||||
private boolean hasNextPage;
|
|
||||||
private String endCursor;
|
|
||||||
private AsyncTask<?, ?, ?> currentlyExecuting;
|
private AsyncTask<?, ?, ?> currentlyExecuting;
|
||||||
private boolean isLoggedIn;
|
private boolean isLoggedIn;
|
||||||
private boolean isPullToRefresh;
|
private boolean storiesFetching;
|
||||||
|
private Set<FeedModel> selectedFeedModels;
|
||||||
|
private FeedModel downloadFeedModel;
|
||||||
|
private int downloadChildPosition = -1;
|
||||||
|
|
||||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
if (postsAdapter == null) {
|
binding.posts.endSelection();
|
||||||
setEnabled(false);
|
|
||||||
remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
postsAdapter.clearSelection();
|
|
||||||
setEnabled(false);
|
|
||||||
remove();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||||
R.menu.multi_select_download_menu, new PrimaryActionModeCallback.CallbacksHelper() {
|
R.menu.multi_select_download_menu, new PrimaryActionModeCallback.CallbacksHelper() {
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy(final ActionMode mode) {
|
public void onDestroy(final ActionMode mode) {
|
||||||
onBackPressedCallback.handleOnBackPressed();
|
binding.posts.endSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(final ActionMode mode,
|
public boolean onActionItemClicked(final ActionMode mode,
|
||||||
final MenuItem item) {
|
final MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_download) {
|
if (item.getItemId() == R.id.action_download) {
|
||||||
if (postsAdapter == null || locationId == null) {
|
if (LocationFragment.this.selectedFeedModels == null) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return false;
|
if (context == null) return false;
|
||||||
DownloadUtils.batchDownload(context,
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
locationId,
|
DownloadUtils.download(context, ImmutableList.copyOf(LocationFragment.this.selectedFeedModels));
|
||||||
DownloadMethod.DOWNLOAD_MAIN,
|
binding.posts.endSelection();
|
||||||
postsAdapter.getSelectedModels());
|
return true;
|
||||||
checkAndResetAction();
|
}
|
||||||
return true;
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(final List<PostModel> result) {
|
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
||||||
if (result == null) return;
|
}
|
||||||
binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
|
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
@Override
|
||||||
List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>()
|
public void onSliderClick(final FeedModel feedModel, final int position) {
|
||||||
: new ArrayList<>(postModels);
|
openPostDialog(feedModel, null, null, position);
|
||||||
if (isPullToRefresh) {
|
}
|
||||||
finalList = result;
|
|
||||||
isPullToRefresh = false;
|
@Override
|
||||||
} else {
|
public void onCommentsClick(final FeedModel feedModel) {
|
||||||
finalList.addAll(result);
|
final NavDirections commentsAction = LocationFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||||
|
feedModel.getShortCode(),
|
||||||
|
feedModel.getPostId(),
|
||||||
|
feedModel.getProfileModel().getId()
|
||||||
|
);
|
||||||
|
NavHostFragment.findNavController(LocationFragment.this).navigate(commentsAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
|
DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
postsViewModel.getList().postValue(finalList);
|
downloadFeedModel = feedModel;
|
||||||
PostModel model = null;
|
downloadChildPosition = childPosition;
|
||||||
if (!result.isEmpty()) {
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||||
model = result.get(result.size() - 1);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHashtagClick(final String hashtag) {
|
||||||
|
final NavDirections action = LocationFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||||
|
NavHostFragment.findNavController(LocationFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections action = LocationFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
|
||||||
|
NavHostFragment.findNavController(LocationFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMentionClick(final String mention) {
|
||||||
|
navigateToProfile(mention.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNameClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onURLClick(final String url) {
|
||||||
|
Utils.openURL(getContext(), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmailClick(final String emailId) {
|
||||||
|
Utils.openEmailAddress(getContext(), emailId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openPostDialog(final FeedModel feedModel,
|
||||||
|
final View profilePicView,
|
||||||
|
final View mainPostImage,
|
||||||
|
final int position) {
|
||||||
|
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||||
|
.builder(feedModel);
|
||||||
|
if (position >= 0) {
|
||||||
|
builder.setPosition(position);
|
||||||
|
}
|
||||||
|
final PostViewV2Fragment fragment = builder
|
||||||
|
.setSharedProfilePicElement(profilePicView)
|
||||||
|
.setSharedMainPostElement(mainPostImage)
|
||||||
|
.build();
|
||||||
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionStart() {
|
||||||
|
if (!onBackPressedCallback.isEnabled()) {
|
||||||
|
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
|
onBackPressedCallback.setEnabled(true);
|
||||||
|
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
|
}
|
||||||
|
if (actionMode == null) {
|
||||||
|
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionChange(final Set<FeedModel> selectedFeedModels) {
|
||||||
|
final String title = getString(R.string.number_selected, selectedFeedModels.size());
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.setTitle(title);
|
||||||
|
}
|
||||||
|
LocationFragment.this.selectedFeedModels = selectedFeedModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionEnd() {
|
||||||
|
if (onBackPressedCallback.isEnabled()) {
|
||||||
|
onBackPressedCallback.setEnabled(false);
|
||||||
|
onBackPressedCallback.remove();
|
||||||
|
}
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.finish();
|
||||||
|
actionMode = null;
|
||||||
}
|
}
|
||||||
if (model == null) return;
|
|
||||||
endCursor = model.getEndCursor();
|
|
||||||
hasNextPage = model.hasNextPage();
|
|
||||||
model.setPageCursor(false, null);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -159,6 +250,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
fragmentActivity = (MainActivity) requireActivity();
|
fragmentActivity = (MainActivity) requireActivity();
|
||||||
storiesService = StoriesService.getInstance();
|
storiesService = StoriesService.getInstance();
|
||||||
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -185,9 +277,8 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
isPullToRefresh = true;
|
binding.posts.refresh();
|
||||||
endCursor = null;
|
fetchStories();
|
||||||
fetchLocationModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -197,10 +288,35 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||||
super.onDestroy();
|
inflater.inflate(R.menu.topic_posts_menu, menu);
|
||||||
if (postsViewModel != null) {
|
}
|
||||||
postsViewModel.getList().postValue(Collections.emptyList());
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.layout) {
|
||||||
|
showPostsLayoutPreferences();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||||
|
if (downloadFeedModel == null) return;
|
||||||
|
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
|
||||||
|
downloadFeedModel = null;
|
||||||
|
downloadChildPosition = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
|
||||||
|
DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
|
||||||
|
binding.posts.endSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,66 +329,19 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
binding.favChip.setVisibility(View.GONE);
|
binding.favChip.setVisibility(View.GONE);
|
||||||
binding.btnMap.setVisibility(View.GONE);
|
binding.btnMap.setVisibility(View.GONE);
|
||||||
setTitle();
|
setTitle();
|
||||||
setupPosts();
|
|
||||||
fetchLocationModel();
|
fetchLocationModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPosts() {
|
private void setupPosts() {
|
||||||
postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
|
binding.posts.setViewModelStoreOwner(this)
|
||||||
final Context context = getContext();
|
.setLifeCycleOwner(this)
|
||||||
if (context == null) return;
|
.setPostFetchService(new LocationPostFetchService(locationModel))
|
||||||
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_LOCATION_POSTS_LAYOUT)))
|
||||||
binding.mainPosts.setLayoutManager(layoutManager);
|
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||||
binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
.setFeedItemCallback(feedItemCallback)
|
||||||
postsAdapter = new PostsAdapter((postModel, position) -> {
|
.setSelectionModeCallback(selectionModeCallback)
|
||||||
if (postsAdapter.isSelecting()) {
|
.init();
|
||||||
if (actionMode == null) return;
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (checkAndResetAction()) return;
|
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
|
||||||
if (postModels == null || postModels.size() == 0) return;
|
|
||||||
if (postModels.get(0) == null) return;
|
|
||||||
final String postId = postModels.get(0).getPostId();
|
|
||||||
final boolean isId = postId != null && isLoggedIn;
|
|
||||||
final String[] idsOrShortCodes = new String[postModels.size()];
|
|
||||||
for (int i = 0; i < postModels.size(); i++) {
|
|
||||||
idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
|
|
||||||
: postModels.get(i).getShortCode();
|
|
||||||
}
|
|
||||||
final NavDirections action = LocationFragmentDirections.actionGlobalPostViewFragment(
|
|
||||||
position,
|
|
||||||
idsOrShortCodes,
|
|
||||||
isId);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
}, (model, position) -> {
|
|
||||||
if (!postsAdapter.isSelecting()) {
|
|
||||||
checkAndResetAction();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity
|
|
||||||
.getOnBackPressedDispatcher();
|
|
||||||
onBackPressedCallback.setEnabled(true);
|
|
||||||
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
|
||||||
final String title = getString(R.string.number_selected, 1);
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
|
|
||||||
binding.mainPosts.setAdapter(postsAdapter);
|
|
||||||
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
|
||||||
if (!hasNextPage) return;
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
fetchPosts();
|
|
||||||
endCursor = null;
|
|
||||||
});
|
|
||||||
binding.mainPosts.addOnScrollListener(lazyLoader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchLocationModel() {
|
private void fetchLocationModel() {
|
||||||
@ -289,34 +358,15 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
}
|
}
|
||||||
setTitle();
|
setTitle();
|
||||||
setupLocationDetails();
|
setupLocationDetails();
|
||||||
fetchPosts();
|
setupPosts();
|
||||||
|
fetchStories();
|
||||||
|
// fetchPosts();
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupLocationDetails() {
|
private void setupLocationDetails() {
|
||||||
final String locationId = locationModel.getId();
|
final String locationId = locationModel.getId();
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
// binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
if (isLoggedIn) {
|
|
||||||
storiesService.getUserStory(locationId,
|
|
||||||
null,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
new ServiceCallback<List<StoryModel>>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final List<StoryModel> storyModels) {
|
|
||||||
if (storyModels != null && !storyModels.isEmpty()) {
|
|
||||||
binding.mainLocationImage.setStoriesBorder();
|
|
||||||
hasStories = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
Log.e(TAG, "Error", t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
binding.mainLocationImage.setImageURI(locationModel.getSdProfilePic());
|
binding.mainLocationImage.setImageURI(locationModel.getSdProfilePic());
|
||||||
final String postCount = String.valueOf(locationModel.getPostCount());
|
final String postCount = String.valueOf(locationModel.getPostCount());
|
||||||
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline,
|
final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline,
|
||||||
@ -327,8 +377,8 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
binding.mainLocPostCount.setVisibility(View.VISIBLE);
|
binding.mainLocPostCount.setVisibility(View.VISIBLE);
|
||||||
binding.locationFullName.setText(locationModel.getName());
|
binding.locationFullName.setText(locationModel.getName());
|
||||||
CharSequence biography = locationModel.getBio();
|
CharSequence biography = locationModel.getBio();
|
||||||
binding.locationBiography.setCaptionIsExpandable(true);
|
// binding.locationBiography.setCaptionIsExpandable(true);
|
||||||
binding.locationBiography.setCaptionIsExpanded(true);
|
// binding.locationBiography.setCaptionIsExpanded(true);
|
||||||
|
|
||||||
if (TextUtils.isEmpty(biography)) {
|
if (TextUtils.isEmpty(biography)) {
|
||||||
binding.locationBiography.setVisibility(View.GONE);
|
binding.locationBiography.setVisibility(View.GONE);
|
||||||
@ -405,18 +455,38 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
final NavDirections action = LocationFragmentDirections
|
final NavDirections action = LocationFragmentDirections
|
||||||
.actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName());
|
.actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName());
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchPosts() {
|
private void fetchStories() {
|
||||||
stopCurrentExecutor();
|
if (isLoggedIn) {
|
||||||
currentlyExecuting = new PostsFetcher(locationModel.getId(), PostItemType.LOCATION, endCursor, postsFetchListener)
|
storiesFetching = true;
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
storiesService.getUserStory(locationId,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
new ServiceCallback<List<StoryModel>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final List<StoryModel> storyModels) {
|
||||||
|
if (storyModels != null && !storyModels.isEmpty()) {
|
||||||
|
binding.mainLocationImage.setStoriesBorder();
|
||||||
|
hasStories = true;
|
||||||
|
}
|
||||||
|
storiesFetching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
Log.e(TAG, "Error", t);
|
||||||
|
storiesFetching = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopCurrentExecutor() {
|
private void stopCurrentExecutor() {
|
||||||
if (currentlyExecuting != null) {
|
if (currentlyExecuting != null) {
|
||||||
try {
|
try {
|
||||||
currentlyExecuting.cancel(true);
|
currentlyExecuting.cancel(true);
|
||||||
@ -435,18 +505,21 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAndResetAction() {
|
private void updateSwipeRefreshState() {
|
||||||
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching() || storiesFetching);
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
private void navigateToProfile(final String username) {
|
||||||
onBackPressedCallback.setEnabled(false);
|
final NavController navController = NavHostFragment.findNavController(this);
|
||||||
onBackPressedCallback.remove();
|
final Bundle bundle = new Bundle();
|
||||||
}
|
bundle.putString("username", username);
|
||||||
if (actionMode != null) {
|
navController.navigate(R.id.action_global_profileFragment, bundle);
|
||||||
actionMode.finish();
|
}
|
||||||
actionMode = null;
|
|
||||||
}
|
private void showPostsLayoutPreferences() {
|
||||||
return true;
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||||
|
Constants.PREF_LOCATION_POSTS_LAYOUT,
|
||||||
|
preferences -> new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200));
|
||||||
|
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import awais.instagrabber.R;
|
|||||||
import awais.instagrabber.adapters.NotificationsAdapter;
|
import awais.instagrabber.adapters.NotificationsAdapter;
|
||||||
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
|
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
|
||||||
import awais.instagrabber.asyncs.NotificationsFetcher;
|
import awais.instagrabber.asyncs.NotificationsFetcher;
|
||||||
|
import awais.instagrabber.asyncs.PostFetcher;
|
||||||
import awais.instagrabber.databinding.FragmentNotificationsViewerBinding;
|
import awais.instagrabber.databinding.FragmentNotificationsViewerBinding;
|
||||||
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
|
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
import awais.instagrabber.interfaces.MentionClickListener;
|
||||||
@ -75,7 +76,8 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
|||||||
} else {
|
} else {
|
||||||
commentDialogList = new String[]{getString(R.string.open_profile)};
|
commentDialogList = new String[]{getString(R.string.open_profile)};
|
||||||
}
|
}
|
||||||
if (getContext() == null) return;
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
|
final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -101,9 +103,18 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final NavDirections action = MorePreferencesFragmentDirections
|
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||||
.actionGlobalPostViewFragment(0, new String[]{model.getShortCode()}, false);
|
.setCancelable(false)
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
.setView(R.layout.dialog_opening_post)
|
||||||
|
.create();
|
||||||
|
alertDialog.show();
|
||||||
|
new PostFetcher(model.getShortCode(), feedModel -> {
|
||||||
|
final PostViewV2Fragment fragment = PostViewV2Fragment
|
||||||
|
.builder(feedModel)
|
||||||
|
.build();
|
||||||
|
fragment.setOnShowListener(dialog1 -> alertDialog.dismiss());
|
||||||
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
|
}).execute();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||||
@ -125,7 +136,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
new AlertDialog.Builder(getContext())
|
new AlertDialog.Builder(context)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setItems(commentDialogList, profileDialogListener)
|
.setItems(commentDialogList, profileDialogListener)
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
@ -1,342 +0,0 @@
|
|||||||
package awais.instagrabber.fragments;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import androidx.navigation.NavDirections;
|
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
import awais.instagrabber.adapters.PostViewAdapter;
|
|
||||||
import awais.instagrabber.adapters.PostViewAdapter.OnPostViewChildViewClickListener;
|
|
||||||
import awais.instagrabber.asyncs.PostFetcher;
|
|
||||||
import awais.instagrabber.asyncs.i.iPostFetcher;
|
|
||||||
import awais.instagrabber.databinding.FragmentPostViewBinding;
|
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
|
||||||
import awais.instagrabber.models.ViewerPostModelWrapper;
|
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
import awais.instagrabber.viewmodels.ViewerPostViewModel;
|
|
||||||
import awais.instagrabber.webservices.MediaService;
|
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
|
||||||
|
|
||||||
import static androidx.core.content.ContextCompat.checkSelfPermission;
|
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
|
||||||
|
|
||||||
public class PostViewFragment extends Fragment {
|
|
||||||
private static final String TAG = "PostViewFragment";
|
|
||||||
private static final String COOKIE = settingsHelper.getString(Constants.COOKIE);
|
|
||||||
|
|
||||||
private FragmentActivity fragmentActivity;
|
|
||||||
private FragmentPostViewBinding binding;
|
|
||||||
private ViewPager2 root;
|
|
||||||
private boolean shouldRefresh = true;
|
|
||||||
private ViewerPostViewModel viewerPostViewModel;
|
|
||||||
private boolean isId;
|
|
||||||
private int currentPostIndex;
|
|
||||||
private List<String> idOrCodeList;
|
|
||||||
private boolean hasInitialResult = false;
|
|
||||||
private PostViewAdapter adapter;
|
|
||||||
private boolean session;
|
|
||||||
private MediaService mediaService;
|
|
||||||
|
|
||||||
private FetchListener<ViewerPostModel[]> pfl = result -> {
|
|
||||||
if (result == null) return;
|
|
||||||
if (result.length <= 0) return;
|
|
||||||
final List<ViewerPostModelWrapper> viewerPostModels = viewerPostViewModel.getList().getValue();
|
|
||||||
final List<ViewerPostModelWrapper> temp = viewerPostModels == null ? new ArrayList<>(idOrCodeList.size())
|
|
||||||
: new ArrayList<>(viewerPostModels);
|
|
||||||
final ViewerPostModel firstPost = result[0];
|
|
||||||
if (firstPost == null) return;
|
|
||||||
String idOrCode = isId ? firstPost.getPostId() : firstPost.getShortCode();
|
|
||||||
if (idOrCode == null) return;
|
|
||||||
if (isId) {
|
|
||||||
// the post id is appended with `_` in the result
|
|
||||||
idOrCode = idOrCode.substring(0, idOrCode.indexOf('_'));
|
|
||||||
}
|
|
||||||
final int index = idOrCodeList.indexOf(idOrCode);
|
|
||||||
if (index < 0) return;
|
|
||||||
final ViewerPostModelWrapper viewerPostModelWrapper = temp.get(index);
|
|
||||||
viewerPostModelWrapper.setViewerPostModels(result);
|
|
||||||
temp.set(index, viewerPostModelWrapper);
|
|
||||||
viewerPostViewModel.getList().setValue(temp);
|
|
||||||
adapter.notifyItemChanged(index);
|
|
||||||
if (!hasInitialResult) {
|
|
||||||
Log.d(TAG, "setting delayed position to: " + currentPostIndex);
|
|
||||||
binding.getRoot()
|
|
||||||
.postDelayed(() -> binding.getRoot().setCurrentItem(currentPostIndex), 200);
|
|
||||||
}
|
|
||||||
hasInitialResult = true;
|
|
||||||
};
|
|
||||||
private MentionClickListener mentionListener = (view, text, isHashtag, isLocation) -> {
|
|
||||||
if (isHashtag) {
|
|
||||||
final NavDirections action = PostViewFragmentDirections
|
|
||||||
.actionGlobalHashTagFragment(text);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isLocation) {
|
|
||||||
final NavDirections action = PostViewFragmentDirections
|
|
||||||
.actionGlobalLocationFragment(text);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final NavDirections action = PostViewFragmentDirections
|
|
||||||
.actionGlobalProfileFragment("@" + text);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
};
|
|
||||||
private OnPostViewChildViewClickListener clickListener = (v, wrapper, postPosition, childPosition) -> {
|
|
||||||
final ViewerPostModel postModel = wrapper.getViewerPostModels()[0];
|
|
||||||
final String username = postModel.getProfileModel().getUsername();
|
|
||||||
final int id = v.getId();
|
|
||||||
switch (id) {
|
|
||||||
case R.id.viewerCaption:
|
|
||||||
break;
|
|
||||||
case R.id.btnComments:
|
|
||||||
String postId = postModel.getPostId();
|
|
||||||
if (postId.contains("_")) postId = postId.substring(0, postId.indexOf("_"));
|
|
||||||
final NavDirections commentsAction = PostViewFragmentDirections.actionGlobalCommentsViewerFragment(
|
|
||||||
postModel.getShortCode(),
|
|
||||||
postId,
|
|
||||||
postModel.getProfileModel().getId()
|
|
||||||
);
|
|
||||||
NavHostFragment.findNavController(this).navigate(commentsAction);
|
|
||||||
break;
|
|
||||||
case R.id.btnDownload:
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
if (checkSelfPermission(context,
|
|
||||||
DownloadUtils.PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
showDownloadDialog(Arrays.asList(wrapper.getViewerPostModels()),
|
|
||||||
childPosition,
|
|
||||||
username);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
requestPermissions(DownloadUtils.PERMS, 8020);
|
|
||||||
break;
|
|
||||||
case R.id.ivProfilePic:
|
|
||||||
case R.id.title:
|
|
||||||
mentionListener.onClick(null, username, false, false);
|
|
||||||
break;
|
|
||||||
case R.id.btnLike:
|
|
||||||
if (mediaService != null) {
|
|
||||||
final String userId = CookieUtils.getUserIdFromCookie(COOKIE);
|
|
||||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(COOKIE);
|
|
||||||
v.setEnabled(false);
|
|
||||||
final ServiceCallback<Boolean> likeCallback = new ServiceCallback<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final Boolean result) {
|
|
||||||
v.setEnabled(true);
|
|
||||||
if (result) {
|
|
||||||
postModel.setManualLike(!postModel.getLike());
|
|
||||||
adapter.notifyItemChanged(postPosition);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.e(TAG, "like/unlike unsuccessful!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
v.setEnabled(true);
|
|
||||||
Log.e(TAG, "Error during like/unlike", t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (!postModel.getLike()) {
|
|
||||||
mediaService.like(postModel.getPostId(), userId, csrfToken, likeCallback);
|
|
||||||
} else {
|
|
||||||
mediaService.unlike(postModel.getPostId(), userId, csrfToken, likeCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case R.id.btnBookmark:
|
|
||||||
if (mediaService != null) {
|
|
||||||
final String userId = CookieUtils.getUserIdFromCookie(COOKIE);
|
|
||||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(COOKIE);
|
|
||||||
v.setEnabled(false);
|
|
||||||
final ServiceCallback<Boolean> saveCallback = new ServiceCallback<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final Boolean result) {
|
|
||||||
v.setEnabled(true);
|
|
||||||
if (result) {
|
|
||||||
postModel.setBookmarked(!postModel.getBookmark());
|
|
||||||
adapter.notifyItemChanged(postPosition);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.e(TAG, "save/unsave unsuccessful!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
v.setEnabled(true);
|
|
||||||
Log.e(TAG, "Error during save/unsave", t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (!postModel.getBookmark()) {
|
|
||||||
mediaService.save(postModel.getPostId(), userId, csrfToken, saveCallback);
|
|
||||||
} else {
|
|
||||||
mediaService.unsave(postModel.getPostId(), userId, csrfToken, saveCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private PostViewAdapter.OnPostCaptionLongClickListener captionLongClickListener = text -> {
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
Utils.copyText(context, text);
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
fragmentActivity = getActivity();
|
|
||||||
mediaService = MediaService.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
|
||||||
@Nullable final ViewGroup container,
|
|
||||||
@Nullable final Bundle savedInstanceState) {
|
|
||||||
if (root != null) {
|
|
||||||
shouldRefresh = false;
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
binding = FragmentPostViewBinding.inflate(inflater, container, false);
|
|
||||||
root = binding.getRoot();
|
|
||||||
setupViewPager();
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
|
||||||
if (!shouldRefresh) return;
|
|
||||||
init();
|
|
||||||
shouldRefresh = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupViewPager() {
|
|
||||||
viewerPostViewModel = new ViewModelProvider(fragmentActivity)
|
|
||||||
.get(ViewerPostViewModel.class);
|
|
||||||
adapter = new PostViewAdapter(clickListener, captionLongClickListener, mentionListener);
|
|
||||||
root.setAdapter(adapter);
|
|
||||||
root.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageSelected(final int position) {
|
|
||||||
// Log.d(TAG, "onPageSelected: " + position + ", hasInitialResult: " + hasInitialResult);
|
|
||||||
if (!hasInitialResult) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentPostIndex = position;
|
|
||||||
fetchPost();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
viewerPostViewModel.getList().observe(fragmentActivity, list -> adapter.submitList(list));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
if (getArguments() == null) return;
|
|
||||||
final PostViewFragmentArgs fragmentArgs = PostViewFragmentArgs.fromBundle(getArguments());
|
|
||||||
final String[] idOrCodeArray = fragmentArgs.getIdOrCodeArray();
|
|
||||||
if (idOrCodeArray.length == 0) return;
|
|
||||||
currentPostIndex = fragmentArgs.getIndex();
|
|
||||||
if (currentPostIndex < 0) return;
|
|
||||||
if (currentPostIndex >= idOrCodeArray.length) return;
|
|
||||||
idOrCodeList = Arrays.asList(idOrCodeArray);
|
|
||||||
viewerPostViewModel.getList().setValue(createPlaceholderModels(idOrCodeArray.length));
|
|
||||||
isId = fragmentArgs.getIsId();
|
|
||||||
fetchPost();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ViewerPostModelWrapper> createPlaceholderModels(final int size) {
|
|
||||||
final List<ViewerPostModelWrapper> viewerPostModels = new ArrayList<>(size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
// viewerPostModels.add(new ViewerPostModel[]{ViewerPostModel.getDefaultModel(-i, "")});
|
|
||||||
viewerPostModels.add(new ViewerPostModelWrapper(i, null));
|
|
||||||
}
|
|
||||||
return viewerPostModels;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fetchPost() {
|
|
||||||
// Log.d(TAG, "fetchPost, currentPostIndex: " + currentPostIndex);
|
|
||||||
final List<ViewerPostModelWrapper> list = viewerPostViewModel.getList().getValue();
|
|
||||||
if (list != null) {
|
|
||||||
final ViewerPostModelWrapper viewerPostModels = list.get(currentPostIndex);
|
|
||||||
if (viewerPostModels != null && viewerPostModels
|
|
||||||
.getViewerPostModels() != null && viewerPostModels
|
|
||||||
.getViewerPostModels().length > 0) {
|
|
||||||
Log.d(TAG, "returning without fetching");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currentPostIndex >= idOrCodeList.size() || currentPostIndex < 0) return;
|
|
||||||
final String idOrShortCode = idOrCodeList.get(currentPostIndex);
|
|
||||||
if (isId) {
|
|
||||||
new iPostFetcher(idOrShortCode, pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
new PostFetcher(idOrShortCode, pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDownloadDialog(final List<ViewerPostModel> postModels,
|
|
||||||
final int childPosition,
|
|
||||||
final String username) {
|
|
||||||
final List<ViewerPostModel> postModelsToDownload = new ArrayList<>();
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
if (!session && postModels.size() > 1) {
|
|
||||||
final DialogInterface.OnClickListener clickListener = (dialog, which) -> {
|
|
||||||
if (which == DialogInterface.BUTTON_NEGATIVE) {
|
|
||||||
postModelsToDownload.addAll(postModels);
|
|
||||||
} else if (which == DialogInterface.BUTTON_POSITIVE) {
|
|
||||||
postModelsToDownload.add(postModels.get(childPosition));
|
|
||||||
} else {
|
|
||||||
session = true;
|
|
||||||
postModelsToDownload.add(postModels.get(childPosition));
|
|
||||||
}
|
|
||||||
if (postModelsToDownload.size() > 0) {
|
|
||||||
DownloadUtils.batchDownload(context,
|
|
||||||
username,
|
|
||||||
DownloadMethod.DOWNLOAD_POST_VIEWER,
|
|
||||||
postModelsToDownload);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
new AlertDialog.Builder(context)
|
|
||||||
.setTitle(R.string.post_viewer_download_dialog_title)
|
|
||||||
.setMessage(R.string.post_viewer_download_message)
|
|
||||||
.setNeutralButton(R.string.post_viewer_download_session, clickListener)
|
|
||||||
.setPositiveButton(R.string.post_viewer_download_current, clickListener)
|
|
||||||
.setNegativeButton(R.string.post_viewer_download_album, clickListener).show();
|
|
||||||
} else {
|
|
||||||
DownloadUtils.batchDownload(context,
|
|
||||||
username,
|
|
||||||
DownloadMethod.DOWNLOAD_POST_VIEWER,
|
|
||||||
Collections.singletonList(postModels.get(childPosition)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -2,16 +2,15 @@ package awais.instagrabber.fragments;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.os.Handler;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.activity.OnBackPressedDispatcher;
|
import androidx.activity.OnBackPressedDispatcher;
|
||||||
@ -19,64 +18,55 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.content.PermissionChecker;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.navigation.NavController;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.PostsAdapter;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.asyncs.PostsFetcher;
|
import awais.instagrabber.asyncs.SavedPostFetchService;
|
||||||
import awais.instagrabber.asyncs.i.iLikedFetcher;
|
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.databinding.FragmentSavedBinding;
|
import awais.instagrabber.databinding.FragmentSavedBinding;
|
||||||
|
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||||
import awais.instagrabber.fragments.main.ProfileFragmentDirections;
|
import awais.instagrabber.fragments.main.ProfileFragmentDirections;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.models.PostModel;
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.models.enums.PostItemType;
|
import awais.instagrabber.models.enums.PostItemType;
|
||||||
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.PostsViewModel;
|
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||||
|
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||||
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public final class SavedViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public final class SavedViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
private static AsyncTask<?, ?, ?> currentlyExecuting;
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
private PostsAdapter postsAdapter;
|
private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
|
||||||
private boolean hasNextPage;
|
|
||||||
private FragmentSavedBinding binding;
|
private FragmentSavedBinding binding;
|
||||||
private String username;
|
private String username;
|
||||||
private String endCursor;
|
|
||||||
private RecyclerLazyLoader lazyLoader;
|
|
||||||
private ArrayList<PostModel> selectedItems = new ArrayList<>();
|
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
private PostsViewModel postsViewModel;
|
private SwipeRefreshLayout root;
|
||||||
private LinearLayout root;
|
|
||||||
private AppCompatActivity fragmentActivity;
|
private AppCompatActivity fragmentActivity;
|
||||||
private boolean shouldRefresh = true;
|
private boolean shouldRefresh = true;
|
||||||
private PostItemType type;
|
private PostItemType type;
|
||||||
private String profileId;
|
private String profileId;
|
||||||
|
private Set<FeedModel> selectedFeedModels;
|
||||||
|
private FeedModel downloadFeedModel;
|
||||||
|
private int downloadChildPosition = -1;
|
||||||
|
|
||||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
setEnabled(false);
|
binding.posts.endSelection();
|
||||||
remove();
|
|
||||||
if (postsAdapter == null) return;
|
|
||||||
postsAdapter.clearSelection();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||||
@ -84,70 +74,153 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
|||||||
new PrimaryActionModeCallback.CallbacksHelper() {
|
new PrimaryActionModeCallback.CallbacksHelper() {
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy(final ActionMode mode) {
|
public void onDestroy(final ActionMode mode) {
|
||||||
onBackPressedCallback.handleOnBackPressed();
|
binding.posts.endSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_download) {
|
if (item.getItemId() == R.id.action_download) {
|
||||||
if (postsAdapter == null || username == null) {
|
if (SavedViewerFragment.this.selectedFeedModels == null) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return false;
|
if (context == null) return false;
|
||||||
DownloadUtils.batchDownload(context,
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
username,
|
DownloadUtils.download(context, ImmutableList.copyOf(SavedViewerFragment.this.selectedFeedModels));
|
||||||
DownloadMethod.DOWNLOAD_SAVED,
|
binding.posts.endSelection();
|
||||||
postsAdapter.getSelectedModels());
|
return true;
|
||||||
checkAndResetAction();
|
}
|
||||||
return true;
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(final List<PostModel> result) {
|
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
||||||
final List<PostModel> current = postsViewModel.getList().getValue();
|
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
||||||
if (result != null && !result.isEmpty()) {
|
}
|
||||||
if (current == null) {
|
|
||||||
postsViewModel.getList().postValue(result);
|
|
||||||
} else {
|
|
||||||
final List<PostModel> currentCopy = new ArrayList<>(current);
|
|
||||||
currentCopy.addAll(result);
|
|
||||||
postsViewModel.getList().postValue(currentCopy);
|
|
||||||
}
|
|
||||||
binding.mainPosts.post(() -> {
|
|
||||||
binding.mainPosts.setNestedScrollingEnabled(true);
|
|
||||||
binding.mainPosts.setVisibility(View.VISIBLE);
|
|
||||||
});
|
|
||||||
|
|
||||||
final PostModel model = !result.isEmpty() ? result.get(result.size() - 1) : null;
|
@Override
|
||||||
if (model != null) {
|
public void onSliderClick(final FeedModel feedModel, final int position) {
|
||||||
endCursor = model.getEndCursor();
|
openPostDialog(feedModel, null, null, position);
|
||||||
hasNextPage = model.hasNextPage();
|
}
|
||||||
if (hasNextPage) {
|
|
||||||
fetchPosts();
|
@Override
|
||||||
} else {
|
public void onCommentsClick(final FeedModel feedModel) {
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
final NavDirections commentsAction = ProfileFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||||
}
|
feedModel.getShortCode(),
|
||||||
model.setPageCursor(false, null);
|
feedModel.getPostId(),
|
||||||
}
|
feedModel.getProfileModel().getId()
|
||||||
} else if (current == null) {
|
);
|
||||||
final Context context = getContext();
|
NavHostFragment.findNavController(SavedViewerFragment.this).navigate(commentsAction);
|
||||||
if (context == null) return;
|
}
|
||||||
Toast.makeText(context, R.string.empty_list, Toast.LENGTH_SHORT).show();
|
|
||||||
NavHostFragment.findNavController(SavedViewerFragment.this).popBackStack();
|
@Override
|
||||||
|
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
|
DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadFeedModel = feedModel;
|
||||||
|
downloadChildPosition = childPosition;
|
||||||
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHashtagClick(final String hashtag) {
|
||||||
|
final NavDirections action = ProfileFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||||
|
NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections action = ProfileFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
|
||||||
|
NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMentionClick(final String mention) {
|
||||||
|
navigateToProfile(mention.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNameClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onURLClick(final String url) {
|
||||||
|
Utils.openURL(getContext(), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmailClick(final String emailId) {
|
||||||
|
Utils.openEmailAddress(getContext(), emailId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openPostDialog(final FeedModel feedModel,
|
||||||
|
final View profilePicView,
|
||||||
|
final View mainPostImage,
|
||||||
|
final int position) {
|
||||||
|
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||||
|
.builder(feedModel);
|
||||||
|
if (position >= 0) {
|
||||||
|
builder.setPosition(position);
|
||||||
|
}
|
||||||
|
final PostViewV2Fragment fragment = builder
|
||||||
|
.setSharedProfilePicElement(profilePicView)
|
||||||
|
.setSharedMainPostElement(mainPostImage)
|
||||||
|
.build();
|
||||||
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionStart() {
|
||||||
|
if (!onBackPressedCallback.isEnabled()) {
|
||||||
|
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
|
onBackPressedCallback.setEnabled(true);
|
||||||
|
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
|
}
|
||||||
|
if (actionMode == null) {
|
||||||
|
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionChange(final Set<FeedModel> selectedFeedModels) {
|
||||||
|
final String title = getString(R.string.number_selected, selectedFeedModels.size());
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.setTitle(title);
|
||||||
|
}
|
||||||
|
SavedViewerFragment.this.selectedFeedModels = selectedFeedModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionEnd() {
|
||||||
|
if (onBackPressedCallback.isEnabled()) {
|
||||||
|
onBackPressedCallback.setEnabled(false);
|
||||||
|
onBackPressedCallback.remove();
|
||||||
|
}
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.finish();
|
||||||
|
actionMode = null;
|
||||||
}
|
}
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private Observer<List<PostModel>> listObserver;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
fragmentActivity = (AppCompatActivity) getActivity();
|
fragmentActivity = (AppCompatActivity) getActivity();
|
||||||
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -164,22 +237,34 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||||
if (!shouldRefresh) return;
|
if (!shouldRefresh) return;
|
||||||
|
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||||
init();
|
init();
|
||||||
|
shouldRefresh = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.saved_viewer_menu, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.layout) {
|
||||||
|
showPostsLayoutPreferences();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
setTitle();
|
setTitle();
|
||||||
observeData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void observeData() {
|
@Override
|
||||||
postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
|
public void onRefresh() {
|
||||||
postsViewModel.getList().removeObserver(listObserver);
|
binding.posts.refresh();
|
||||||
if (postsAdapter != null) {
|
|
||||||
postsViewModel.getList().observe(getViewLifecycleOwner(), listObserver);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
@ -189,122 +274,89 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
|||||||
username = fragmentArgs.getUsername();
|
username = fragmentArgs.getUsername();
|
||||||
profileId = fragmentArgs.getProfileId();
|
profileId = fragmentArgs.getProfileId();
|
||||||
type = fragmentArgs.getType();
|
type = fragmentArgs.getType();
|
||||||
setTitle();
|
setupPosts();
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
// postsAdapter = new PostsAdapter((postModel, position) -> {
|
||||||
// autoloadPosts = Utils.settingsHelper.getBoolean(AUTOLOAD_POSTS);
|
// if (postsAdapter.isSelecting()) {
|
||||||
binding.mainPosts.setNestedScrollingEnabled(false);
|
// if (actionMode == null) return;
|
||||||
final Context context = getContext();
|
// final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
|
||||||
if (context == null) return;
|
// actionMode.setTitle(title);
|
||||||
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
// return;
|
||||||
binding.mainPosts.setLayoutManager(layoutManager);
|
// }
|
||||||
binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
// if (checkAndResetAction()) return;
|
||||||
postsAdapter = new PostsAdapter((postModel, position) -> {
|
// final List<PostModel> postModels = postsViewModel.getList().getValue();
|
||||||
if (postsAdapter.isSelecting()) {
|
// if (postModels == null || postModels.size() == 0) return;
|
||||||
if (actionMode == null) return;
|
// if (postModels.get(0) == null) return;
|
||||||
final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
|
// final String postId = postModels.get(0).getPostId();
|
||||||
actionMode.setTitle(title);
|
// final boolean isId = postId != null;
|
||||||
return;
|
// final String[] idsOrShortCodes = new String[postModels.size()];
|
||||||
}
|
// for (int i = 0; i < postModels.size(); i++) {
|
||||||
if (checkAndResetAction()) return;
|
// final PostModel tempPostModel = postModels.get(i);
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
// final String tempId = tempPostModel.getPostId();
|
||||||
if (postModels == null || postModels.size() == 0) return;
|
// final String finalPostId = type == PostItemType.LIKED ? tempId.substring(0, tempId.indexOf("_")) : tempId;
|
||||||
if (postModels.get(0) == null) return;
|
// idsOrShortCodes[i] = isId ? finalPostId
|
||||||
final String postId = postModels.get(0).getPostId();
|
// : tempPostModel.getShortCode();
|
||||||
final boolean isId = postId != null;
|
// }
|
||||||
final String[] idsOrShortCodes = new String[postModels.size()];
|
// final NavDirections action = ProfileFragmentDirections.actionGlobalPostViewFragment(
|
||||||
for (int i = 0; i < postModels.size(); i++) {
|
// position,
|
||||||
final PostModel tempPostModel = postModels.get(i);
|
// idsOrShortCodes,
|
||||||
final String tempId = tempPostModel.getPostId();
|
// isId);
|
||||||
final String finalPostId = type == PostItemType.LIKED ? tempId.substring(0, tempId.indexOf("_")) : tempId;
|
// NavHostFragment.findNavController(this).navigate(action);
|
||||||
idsOrShortCodes[i] = isId ? finalPostId
|
// }, (model, position) -> {
|
||||||
: tempPostModel.getShortCode();
|
// if (!postsAdapter.isSelecting()) {
|
||||||
}
|
// checkAndResetAction();
|
||||||
final NavDirections action = ProfileFragmentDirections.actionGlobalPostViewFragment(
|
// return true;
|
||||||
position,
|
// }
|
||||||
idsOrShortCodes,
|
// final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
isId);
|
// if (onBackPressedCallback.isEnabled()) return true;
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
// actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
}, (model, position) -> {
|
// final String title = getString(R.string.number_selected, 1);
|
||||||
if (!postsAdapter.isSelecting()) {
|
// actionMode.setTitle(title);
|
||||||
checkAndResetAction();
|
// onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
return true;
|
// return true;
|
||||||
}
|
// });
|
||||||
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
|
||||||
if (onBackPressedCallback.isEnabled()) return true;
|
|
||||||
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
|
||||||
final String title = getString(R.string.number_selected, 1);
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
binding.mainPosts.setAdapter(postsAdapter);
|
|
||||||
listObserver = list -> postsAdapter.submitList(list);
|
|
||||||
observeData();
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
|
|
||||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
|
||||||
if (hasNextPage) {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
fetchPosts();
|
|
||||||
endCursor = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
binding.mainPosts.addOnScrollListener(lazyLoader);
|
|
||||||
fetchPosts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchPosts() {
|
private void setupPosts() {
|
||||||
stopCurrentExecutor();
|
binding.posts.setViewModelStoreOwner(this)
|
||||||
final AsyncTask<Void, Void, List<PostModel>> asyncTask;
|
.setLifeCycleOwner(this)
|
||||||
|
.setPostFetchService(new SavedPostFetchService(profileId, type))
|
||||||
|
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(getPostsLayoutPreferenceKey())))
|
||||||
|
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||||
|
.setFeedItemCallback(feedItemCallback)
|
||||||
|
.setSelectionModeCallback(selectionModeCallback)
|
||||||
|
.init();
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String getPostsLayoutPreferenceKey() {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case LIKED:
|
case LIKED:
|
||||||
asyncTask = new iLikedFetcher(endCursor, postsFetchListener);
|
return Constants.PREF_LIKED_POSTS_LAYOUT;
|
||||||
break;
|
|
||||||
case SAVED:
|
|
||||||
case TAGGED:
|
case TAGGED:
|
||||||
if (TextUtils.isEmpty(profileId)) return;
|
return Constants.PREF_TAGGED_POSTS_LAYOUT;
|
||||||
asyncTask = new PostsFetcher(profileId, type, endCursor, postsFetchListener);
|
case SAVED:
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return;
|
return Constants.PREF_SAVED_POSTS_LAYOUT;
|
||||||
}
|
}
|
||||||
currentlyExecuting = asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRefresh() {
|
|
||||||
if (lazyLoader != null) lazyLoader.resetState();
|
|
||||||
stopCurrentExecutor();
|
|
||||||
endCursor = null;
|
|
||||||
postsViewModel.getList().postValue(Collections.emptyList());
|
|
||||||
selectedItems.clear();
|
|
||||||
if (postsAdapter != null) {
|
|
||||||
// postsAdapter.isSelecting = false;
|
|
||||||
postsAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
fetchPosts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED && selectedItems.size() > 0) {
|
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
DownloadUtils.batchDownload(context, null, DownloadMethod.DOWNLOAD_SAVED, selectedItems);
|
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||||
|
if (downloadFeedModel == null) return;
|
||||||
|
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
|
||||||
|
downloadFeedModel = null;
|
||||||
|
downloadChildPosition = -1;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
|
||||||
|
DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
|
||||||
public static void stopCurrentExecutor() {
|
binding.posts.endSelection();
|
||||||
if (currentlyExecuting != null) {
|
|
||||||
try {
|
|
||||||
currentlyExecuting.cancel(true);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null)
|
|
||||||
logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor");
|
|
||||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,9 +365,6 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
|||||||
if (actionBar == null) return;
|
if (actionBar == null) return;
|
||||||
final int titleRes;
|
final int titleRes;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SAVED:
|
|
||||||
titleRes = R.string.saved;
|
|
||||||
break;
|
|
||||||
case LIKED:
|
case LIKED:
|
||||||
titleRes = R.string.liked;
|
titleRes = R.string.liked;
|
||||||
break;
|
break;
|
||||||
@ -323,24 +372,29 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
|||||||
titleRes = R.string.tagged;
|
titleRes = R.string.tagged;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return; // no other types supported in this view
|
case SAVED:
|
||||||
|
titleRes = R.string.saved;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
actionBar.setTitle(titleRes);
|
actionBar.setTitle(titleRes);
|
||||||
actionBar.setSubtitle(username);
|
actionBar.setSubtitle(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAndResetAction() {
|
private void updateSwipeRefreshState() {
|
||||||
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching());
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
private void navigateToProfile(final String username) {
|
||||||
onBackPressedCallback.setEnabled(false);
|
final NavController navController = NavHostFragment.findNavController(this);
|
||||||
onBackPressedCallback.remove();
|
final Bundle bundle = new Bundle();
|
||||||
}
|
bundle.putString("username", username);
|
||||||
if (actionMode != null) {
|
navController.navigate(R.id.action_global_profileFragment, bundle);
|
||||||
actionMode.finish();
|
}
|
||||||
actionMode = null;
|
|
||||||
}
|
private void showPostsLayoutPreferences() {
|
||||||
return true;
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||||
|
getPostsLayoutPreferenceKey(),
|
||||||
|
preferences -> new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200));
|
||||||
|
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,9 +6,7 @@ import android.content.Intent;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.drawable.Animatable;
|
import android.graphics.drawable.Animatable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
@ -45,14 +43,16 @@ import com.facebook.drawee.interfaces.DraweeController;
|
|||||||
import com.facebook.imagepipeline.image.ImageInfo;
|
import com.facebook.imagepipeline.image.ImageInfo;
|
||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.source.LoadEventInfo;
|
||||||
|
import com.google.android.exoplayer2.source.MediaLoadData;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -62,7 +62,7 @@ import java.util.List;
|
|||||||
import awais.instagrabber.BuildConfig;
|
import awais.instagrabber.BuildConfig;
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.StoriesAdapter;
|
import awais.instagrabber.adapters.StoriesAdapter;
|
||||||
import awais.instagrabber.asyncs.DownloadAsync;
|
import awais.instagrabber.asyncs.PostFetcher;
|
||||||
import awais.instagrabber.asyncs.QuizAction;
|
import awais.instagrabber.asyncs.QuizAction;
|
||||||
import awais.instagrabber.asyncs.RespondAction;
|
import awais.instagrabber.asyncs.RespondAction;
|
||||||
import awais.instagrabber.asyncs.SeenAction;
|
import awais.instagrabber.asyncs.SeenAction;
|
||||||
@ -81,7 +81,6 @@ import awais.instagrabber.models.stickers.PollModel;
|
|||||||
import awais.instagrabber.models.stickers.QuestionModel;
|
import awais.instagrabber.models.stickers.QuestionModel;
|
||||||
import awais.instagrabber.models.stickers.QuizModel;
|
import awais.instagrabber.models.stickers.QuizModel;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
@ -94,8 +93,6 @@ import awaisomereport.LogCollector;
|
|||||||
|
|
||||||
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_THRESHOLD;
|
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_THRESHOLD;
|
||||||
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD;
|
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD;
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
|
||||||
import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
|
import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
import static awais.instagrabber.utils.Utils.logCollector;
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
@ -316,15 +313,7 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
final Object feedStoryModel = isRightSwipe
|
final Object feedStoryModel = isRightSwipe
|
||||||
? finalModels.get(index - 1)
|
? finalModels.get(index - 1)
|
||||||
: finalModels.size() == index + 1 ? null : finalModels.get(index + 1);
|
: finalModels.size() == index + 1 ? null : finalModels.get(index + 1);
|
||||||
if (feedStoryModel != null) {
|
paginateStories(feedStoryModel, context, isRightSwipe, currentFeedStoryIndex == finalModels.size() - 2);
|
||||||
if (fetching) {
|
|
||||||
Toast.makeText(context, R.string.be_patient, Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetching = true;
|
|
||||||
currentFeedStoryIndex = isRightSwipe ? (index - 1) : (index + 1);
|
|
||||||
resetView();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isRightSwipe) {
|
if (isRightSwipe) {
|
||||||
@ -359,6 +348,15 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (hasFeedStories) {
|
||||||
|
binding.btnBackward.setVisibility(currentFeedStoryIndex == 0 ? View.INVISIBLE : View.VISIBLE);
|
||||||
|
binding.btnForward.setVisibility(currentFeedStoryIndex == finalModels.size() - 1 ? View.INVISIBLE : View.VISIBLE);
|
||||||
|
binding.btnBackward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex - 1), context, true, false));
|
||||||
|
binding.btnForward.setOnClickListener(v -> paginateStories(finalModels.get(currentFeedStoryIndex + 1), context, false,
|
||||||
|
currentFeedStoryIndex == finalModels.size() - 2));
|
||||||
|
}
|
||||||
|
|
||||||
binding.imageViewer.setTapListener(simpleOnGestureListener);
|
binding.imageViewer.setTapListener(simpleOnGestureListener);
|
||||||
binding.spotify.setOnClickListener(v -> {
|
binding.spotify.setOnClickListener(v -> {
|
||||||
final Object tag = v.getTag();
|
final Object tag = v.getTag();
|
||||||
@ -371,14 +369,19 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
binding.viewStoryPost.setOnClickListener(v -> {
|
binding.viewStoryPost.setOnClickListener(v -> {
|
||||||
final Object tag = v.getTag();
|
final Object tag = v.getTag();
|
||||||
if (!(tag instanceof CharSequence)) return;
|
if (!(tag instanceof CharSequence)) return;
|
||||||
final String postId = tag.toString();
|
final String shortCode = tag.toString();
|
||||||
final boolean isId = tag.toString().matches("^[\\d]+$");
|
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||||
final String[] idsOrShortCodes = new String[]{postId};
|
.setCancelable(false)
|
||||||
final NavDirections action = HashTagFragmentDirections.actionGlobalPostViewFragment(
|
.setView(R.layout.dialog_opening_post)
|
||||||
0,
|
.create();
|
||||||
idsOrShortCodes,
|
alertDialog.show();
|
||||||
isId);
|
new PostFetcher(shortCode, feedModel -> {
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
final PostViewV2Fragment fragment = PostViewV2Fragment
|
||||||
|
.builder(feedModel)
|
||||||
|
.build();
|
||||||
|
fragment.setOnShowListener(dialog -> alertDialog.dismiss());
|
||||||
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
|
}).execute();
|
||||||
});
|
});
|
||||||
final View.OnClickListener storyActionListener = v -> {
|
final View.OnClickListener storyActionListener = v -> {
|
||||||
final Object tag = v.getTag();
|
final Object tag = v.getTag();
|
||||||
@ -509,7 +512,7 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
storiesViewModel.getList().setValue(Collections.emptyList());
|
storiesViewModel.getList().setValue(Collections.emptyList());
|
||||||
if (currentStoryMediaId == null) return;
|
if (currentStoryMediaId == null) return;
|
||||||
final ServiceCallback storyCallback = new ServiceCallback<List<StoryModel>>() {
|
final ServiceCallback<List<StoryModel>> storyCallback = new ServiceCallback<List<StoryModel>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final List<StoryModel> storyModels) {
|
public void onSuccess(final List<StoryModel> storyModels) {
|
||||||
fetching = false;
|
fetching = false;
|
||||||
@ -604,40 +607,13 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void downloadStory() {
|
private void downloadStory() {
|
||||||
int error = 0;
|
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
if (currentStory != null) {
|
if (currentStory == null) {
|
||||||
File dir = new File(Environment.getExternalStorageDirectory(), "Download");
|
|
||||||
|
|
||||||
if (settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
|
|
||||||
final String customPath = settingsHelper.getString(FOLDER_PATH);
|
|
||||||
if (!TextUtils.isEmpty(customPath)) dir = new File(customPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER) && !TextUtils.isEmpty(currentStoryUsername))
|
|
||||||
dir = new File(dir, currentStoryUsername);
|
|
||||||
|
|
||||||
if (dir.exists() || dir.mkdirs()) {
|
|
||||||
final String storyUrl = currentStory.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO
|
|
||||||
? currentStory.getVideoUrl()
|
|
||||||
: currentStory.getStoryUrl();
|
|
||||||
final File saveFile = new File(dir, currentStory.getStoryMediaId() + "_" + currentStory.getTimestamp()
|
|
||||||
+ DownloadUtils.getExtensionFromModel(storyUrl, currentStory));
|
|
||||||
|
|
||||||
new DownloadAsync(context, storyUrl, saveFile, result -> {
|
|
||||||
final int toastRes = result != null && result.exists() ? R.string.downloader_complete
|
|
||||||
: R.string.downloader_error_download_file;
|
|
||||||
Toast.makeText(context, toastRes, Toast.LENGTH_SHORT).show();
|
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
|
|
||||||
} else error = 1;
|
|
||||||
} else error = 2;
|
|
||||||
|
|
||||||
if (error == 1)
|
|
||||||
Toast.makeText(context, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show();
|
|
||||||
else if (error == 2)
|
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DownloadUtils.download(context, currentStory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupImage() {
|
private void setupImage() {
|
||||||
@ -687,8 +663,10 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
binding.playerView.setPlayer(player);
|
binding.playerView.setPlayer(player);
|
||||||
player.setPlayWhenReady(settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
|
player.setPlayWhenReady(settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
|
||||||
|
|
||||||
|
final Uri uri = Uri.parse(url);
|
||||||
|
final MediaItem mediaItem = MediaItem.fromUri(uri);
|
||||||
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(context, "instagram"))
|
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(context, "instagram"))
|
||||||
.createMediaSource(Uri.parse(url));
|
.createMediaSource(mediaItem);
|
||||||
mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
|
mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onLoadCompleted(final int windowIndex,
|
public void onLoadCompleted(final int windowIndex,
|
||||||
@ -732,7 +710,8 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
binding.progressView.setVisibility(View.GONE);
|
binding.progressView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
player.prepare(mediaSource);
|
player.setMediaSource(mediaSource);
|
||||||
|
player.prepare();
|
||||||
|
|
||||||
binding.playerView.setOnClickListener(v -> {
|
binding.playerView.setOnClickListener(v -> {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
@ -751,12 +730,10 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
if (t == '@') {
|
if (t == '@') {
|
||||||
final NavDirections action = HashTagFragmentDirections.actionGlobalProfileFragment(username);
|
final NavDirections action = HashTagFragmentDirections.actionGlobalProfileFragment(username);
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
}
|
} else if (t == '#') {
|
||||||
else if (t == '#') {
|
|
||||||
final NavDirections action = HashTagFragmentDirections.actionGlobalHashTagFragment(username.substring(1));
|
final NavDirections action = HashTagFragmentDirections.actionGlobalHashTagFragment(username.substring(1));
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final NavDirections action = ProfileFragmentDirections.actionGlobalLocationFragment(username.split(" \\(")[1].replace(")", ""));
|
final NavDirections action = ProfileFragmentDirections.actionGlobalLocationFragment(username.split(" \\(")[1].replace(")", ""));
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
}
|
}
|
||||||
@ -768,4 +745,18 @@ public class StoryViewerFragment extends Fragment {
|
|||||||
try { player.release(); } catch (Exception ignored) { }
|
try { player.release(); } catch (Exception ignored) { }
|
||||||
player = null;
|
player = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void paginateStories(Object feedStory, Context context, boolean backward, boolean last) {
|
||||||
|
if (feedStory != null) {
|
||||||
|
if (fetching) {
|
||||||
|
Toast.makeText(context, R.string.be_patient, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetching = true;
|
||||||
|
binding.btnBackward.setVisibility(currentFeedStoryIndex == 1 && backward ? View.INVISIBLE : View.VISIBLE);
|
||||||
|
binding.btnForward.setVisibility(last ? View.INVISIBLE : View.VISIBLE);
|
||||||
|
currentFeedStoryIndex = backward ? (currentFeedStoryIndex - 1) : (currentFeedStoryIndex + 1);
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,428 @@
|
|||||||
|
package awais.instagrabber.fragments;
|
||||||
|
|
||||||
|
import android.animation.ArgbEvaluator;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.view.ActionMode;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
|
import androidx.activity.OnBackPressedDispatcher;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.PermissionChecker;
|
||||||
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
|
import androidx.navigation.NavDirections;
|
||||||
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
import androidx.transition.ChangeBounds;
|
||||||
|
import androidx.transition.TransitionInflater;
|
||||||
|
import androidx.transition.TransitionSet;
|
||||||
|
|
||||||
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
|
import com.facebook.drawee.controller.BaseControllerListener;
|
||||||
|
import com.facebook.drawee.interfaces.DraweeController;
|
||||||
|
import com.facebook.imagepipeline.image.ImageInfo;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import awais.instagrabber.R;
|
||||||
|
import awais.instagrabber.activities.MainActivity;
|
||||||
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
|
import awais.instagrabber.asyncs.DiscoverPostFetchService;
|
||||||
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
|
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
||||||
|
import awais.instagrabber.databinding.FragmentTopicPostsBinding;
|
||||||
|
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||||
|
import awais.instagrabber.fragments.main.DiscoverFragmentDirections;
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
|
import awais.instagrabber.models.TopicCluster;
|
||||||
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
|
import awais.instagrabber.utils.Utils;
|
||||||
|
import awais.instagrabber.webservices.DiscoverService;
|
||||||
|
|
||||||
|
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||||
|
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||||
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
|
public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
|
||||||
|
|
||||||
|
private MainActivity fragmentActivity;
|
||||||
|
private FragmentTopicPostsBinding binding;
|
||||||
|
private NestedCoordinatorLayout root;
|
||||||
|
private boolean shouldRefresh = true;
|
||||||
|
private TopicCluster topicCluster;
|
||||||
|
private ActionMode actionMode;
|
||||||
|
private Set<FeedModel> selectedFeedModels;
|
||||||
|
private FeedModel downloadFeedModel;
|
||||||
|
private int downloadChildPosition = -1;
|
||||||
|
|
||||||
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
binding.posts.endSelection();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||||
|
R.menu.multi_select_download_menu, new PrimaryActionModeCallback.CallbacksHelper() {
|
||||||
|
@Override
|
||||||
|
public void onDestroy(final ActionMode mode) {
|
||||||
|
binding.posts.endSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onActionItemClicked(final ActionMode mode,
|
||||||
|
final MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_download) {
|
||||||
|
if (TopicPostsFragment.this.selectedFeedModels == null) return false;
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return false;
|
||||||
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
|
DownloadUtils.download(context, ImmutableList.copyOf(TopicPostsFragment.this.selectedFeedModels));
|
||||||
|
binding.posts.endSelection();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
||||||
|
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSliderClick(final FeedModel feedModel, final int position) {
|
||||||
|
openPostDialog(feedModel, null, null, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommentsClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections commentsAction = DiscoverFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||||
|
feedModel.getShortCode(),
|
||||||
|
feedModel.getPostId(),
|
||||||
|
feedModel.getProfileModel().getId()
|
||||||
|
);
|
||||||
|
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(commentsAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
|
DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadFeedModel = feedModel;
|
||||||
|
downloadChildPosition = -1;
|
||||||
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHashtagClick(final String hashtag) {
|
||||||
|
final NavDirections action = DiscoverFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||||
|
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections action = DiscoverFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
|
||||||
|
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMentionClick(final String mention) {
|
||||||
|
navigateToProfile(mention.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNameClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onURLClick(final String url) {
|
||||||
|
Utils.openURL(getContext(), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmailClick(final String emailId) {
|
||||||
|
Utils.openEmailAddress(getContext(), emailId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openPostDialog(final FeedModel feedModel,
|
||||||
|
final View profilePicView,
|
||||||
|
final View mainPostImage,
|
||||||
|
final int position) {
|
||||||
|
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||||
|
.builder(feedModel);
|
||||||
|
if (position >= 0) {
|
||||||
|
builder.setPosition(position);
|
||||||
|
}
|
||||||
|
final PostViewV2Fragment fragment = builder
|
||||||
|
.setSharedProfilePicElement(profilePicView)
|
||||||
|
.setSharedMainPostElement(mainPostImage)
|
||||||
|
.build();
|
||||||
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionStart() {
|
||||||
|
if (!onBackPressedCallback.isEnabled()) {
|
||||||
|
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
|
onBackPressedCallback.setEnabled(true);
|
||||||
|
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
|
}
|
||||||
|
if (actionMode == null) {
|
||||||
|
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionChange(final Set<FeedModel> selectedFeedModels) {
|
||||||
|
final String title = getString(R.string.number_selected, selectedFeedModels.size());
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.setTitle(title);
|
||||||
|
}
|
||||||
|
TopicPostsFragment.this.selectedFeedModels = selectedFeedModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionEnd() {
|
||||||
|
if (onBackPressedCallback.isEnabled()) {
|
||||||
|
onBackPressedCallback.setEnabled(false);
|
||||||
|
onBackPressedCallback.remove();
|
||||||
|
}
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.finish();
|
||||||
|
actionMode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
fragmentActivity = (MainActivity) requireActivity();
|
||||||
|
final TransitionSet transitionSet = new TransitionSet();
|
||||||
|
transitionSet.addTransition(new ChangeBounds())
|
||||||
|
.addTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move))
|
||||||
|
.setDuration(200);
|
||||||
|
setSharedElementEnterTransition(transitionSet);
|
||||||
|
postponeEnterTransition();
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||||
|
@Nullable final ViewGroup container,
|
||||||
|
@Nullable final Bundle savedInstanceState) {
|
||||||
|
if (root != null) {
|
||||||
|
shouldRefresh = false;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
binding = FragmentTopicPostsBinding.inflate(inflater, container, false);
|
||||||
|
root = binding.getRoot();
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||||
|
if (!shouldRefresh) return;
|
||||||
|
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||||
|
init();
|
||||||
|
shouldRefresh = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.topic_posts_menu, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.layout) {
|
||||||
|
showPostsLayoutPreferences();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
fragmentActivity.setToolbar(binding.toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRefresh() {
|
||||||
|
binding.posts.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
resetToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
resetToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||||
|
if (downloadFeedModel == null) return;
|
||||||
|
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
|
||||||
|
downloadFeedModel = null;
|
||||||
|
downloadChildPosition = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
|
||||||
|
DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
|
||||||
|
binding.posts.endSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetToolbar() {
|
||||||
|
fragmentActivity.resetToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
if (getArguments() == null) return;
|
||||||
|
final TopicPostsFragmentArgs fragmentArgs = TopicPostsFragmentArgs.fromBundle(getArguments());
|
||||||
|
topicCluster = fragmentArgs.getTopicCluster();
|
||||||
|
setupToolbar(fragmentArgs.getTitleColor(), fragmentArgs.getBackgroundColor());
|
||||||
|
setupPosts();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupToolbar(final int titleColor, final int backgroundColor) {
|
||||||
|
if (topicCluster == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.cover.setTransitionName("cover-" + topicCluster.getId());
|
||||||
|
fragmentActivity.setToolbar(binding.toolbar);
|
||||||
|
binding.collapsingToolbarLayout.setTitle(topicCluster.getTitle());
|
||||||
|
final int collapsedTitleTextColor = ColorUtils.setAlphaComponent(titleColor, 0xFF);
|
||||||
|
final int expandedTitleTextColor = ColorUtils.setAlphaComponent(titleColor, 0x99);
|
||||||
|
binding.collapsingToolbarLayout.setExpandedTitleColor(expandedTitleTextColor);
|
||||||
|
binding.collapsingToolbarLayout.setCollapsedTitleTextColor(collapsedTitleTextColor);
|
||||||
|
binding.collapsingToolbarLayout.setContentScrimColor(backgroundColor);
|
||||||
|
final Drawable navigationIcon = binding.toolbar.getNavigationIcon();
|
||||||
|
final Drawable overflowIcon = binding.toolbar.getOverflowIcon();
|
||||||
|
if (navigationIcon != null && overflowIcon != null) {
|
||||||
|
final Drawable navDrawable = navigationIcon.mutate();
|
||||||
|
final Drawable overflowDrawable = overflowIcon.mutate();
|
||||||
|
navDrawable.setAlpha(0xFF);
|
||||||
|
overflowDrawable.setAlpha(0xFF);
|
||||||
|
final ArgbEvaluator argbEvaluator = new ArgbEvaluator();
|
||||||
|
binding.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||||
|
final int totalScrollRange = appBarLayout.getTotalScrollRange();
|
||||||
|
final float current = totalScrollRange + verticalOffset;
|
||||||
|
final float fraction = current / totalScrollRange;
|
||||||
|
final int tempColor = (int) argbEvaluator.evaluate(fraction, collapsedTitleTextColor, expandedTitleTextColor);
|
||||||
|
navDrawable.setColorFilter(tempColor, PorterDuff.Mode.SRC_ATOP);
|
||||||
|
overflowDrawable.setColorFilter(tempColor, PorterDuff.Mode.SRC_ATOP);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
final GradientDrawable gd = new GradientDrawable(
|
||||||
|
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||||
|
new int[]{Color.TRANSPARENT, backgroundColor});
|
||||||
|
binding.background.setBackground(gd);
|
||||||
|
setupCover();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCover() {
|
||||||
|
final String coverUrl = topicCluster.getCoverMedia().getDisplayUrl();
|
||||||
|
final DraweeController controller = Fresco
|
||||||
|
.newDraweeControllerBuilder()
|
||||||
|
.setOldController(binding.cover.getController())
|
||||||
|
.setUri(coverUrl)
|
||||||
|
.setControllerListener(new BaseControllerListener<ImageInfo>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final String id, final Throwable throwable) {
|
||||||
|
super.onFailure(id, throwable);
|
||||||
|
startPostponedEnterTransition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinalImageSet(final String id,
|
||||||
|
@Nullable final ImageInfo imageInfo,
|
||||||
|
@Nullable final Animatable animatable) {
|
||||||
|
startPostponedEnterTransition();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
binding.cover.setController(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupPosts() {
|
||||||
|
final DiscoverService.TopicalExploreRequest topicalExploreRequest = new DiscoverService.TopicalExploreRequest();
|
||||||
|
topicalExploreRequest.setClusterId(topicCluster.getId());
|
||||||
|
binding.posts.setViewModelStoreOwner(this)
|
||||||
|
.setLifeCycleOwner(this)
|
||||||
|
.setPostFetchService(new DiscoverPostFetchService(topicalExploreRequest))
|
||||||
|
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_TOPIC_POSTS_LAYOUT)))
|
||||||
|
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||||
|
.setFeedItemCallback(feedItemCallback)
|
||||||
|
.setSelectionModeCallback(selectionModeCallback)
|
||||||
|
.init();
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSwipeRefreshState() {
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateToProfile(final String username) {
|
||||||
|
final NavController navController = NavHostFragment.findNavController(this);
|
||||||
|
final Bundle bundle = new Bundle();
|
||||||
|
bundle.putString("username", username);
|
||||||
|
navController.navigate(R.id.action_global_profileFragment, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPostsLayoutPreferences() {
|
||||||
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||||
|
Constants.PREF_TOPIC_POSTS_LAYOUT,
|
||||||
|
preferences -> new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200));
|
||||||
|
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -26,6 +27,8 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.ViewModel;
|
import androidx.lifecycle.ViewModel;
|
||||||
@ -55,10 +58,12 @@ import awais.instagrabber.ProfileNavGraphDirections;
|
|||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.DirectMessageItemsAdapter;
|
import awais.instagrabber.adapters.DirectMessageItemsAdapter;
|
||||||
import awais.instagrabber.asyncs.ImageUploader;
|
import awais.instagrabber.asyncs.ImageUploader;
|
||||||
|
import awais.instagrabber.asyncs.PostFetcher;
|
||||||
import awais.instagrabber.asyncs.direct_messages.DirectMessageInboxThreadFetcher;
|
import awais.instagrabber.asyncs.direct_messages.DirectMessageInboxThreadFetcher;
|
||||||
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster;
|
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster;
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
||||||
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;
|
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;
|
||||||
|
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
import awais.instagrabber.interfaces.MentionClickListener;
|
||||||
import awais.instagrabber.models.ImageUploadOptions;
|
import awais.instagrabber.models.ImageUploadOptions;
|
||||||
@ -78,6 +83,7 @@ import awais.instagrabber.utils.Utils;
|
|||||||
public class DirectMessageThreadFragment extends Fragment {
|
public class DirectMessageThreadFragment extends Fragment {
|
||||||
private static final String TAG = "DirectMessagesThreadFmt";
|
private static final String TAG = "DirectMessagesThreadFmt";
|
||||||
private static final int PICK_IMAGE = 100;
|
private static final int PICK_IMAGE = 100;
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
|
|
||||||
private AppCompatActivity fragmentActivity;
|
private AppCompatActivity fragmentActivity;
|
||||||
private String threadId;
|
private String threadId;
|
||||||
@ -169,8 +175,9 @@ public class DirectMessageThreadFragment extends Fragment {
|
|||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private LinearLayout root;
|
private ConstraintLayout root;
|
||||||
private boolean shouldRefresh = true;
|
private boolean shouldRefresh = true;
|
||||||
|
private DirectItemModel.DirectItemMediaModel downloadMediaItem;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
@ -234,16 +241,14 @@ public class DirectMessageThreadFragment extends Fragment {
|
|||||||
switch (itemType) {
|
switch (itemType) {
|
||||||
case MEDIA_SHARE:
|
case MEDIA_SHARE:
|
||||||
case CLIP:
|
case CLIP:
|
||||||
final long postId = directItemModel.getMediaModel().getPk();
|
case FELIX_SHARE:
|
||||||
final boolean isId = true;
|
final String shortCode = directItemModel.getMediaModel().getCode();
|
||||||
final NavController navController = NavHostFragment.findNavController(this);
|
new PostFetcher(shortCode, feedModel -> {
|
||||||
final NavDirections action = DirectMessageThreadFragmentDirections
|
final PostViewV2Fragment fragment = PostViewV2Fragment
|
||||||
.actionGlobalPostViewFragment(
|
.builder(feedModel)
|
||||||
0,
|
.build();
|
||||||
new String[]{String.valueOf(postId)},
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
isId
|
}).execute();
|
||||||
);
|
|
||||||
navController.navigate(action);
|
|
||||||
break;
|
break;
|
||||||
case LINK:
|
case LINK:
|
||||||
Intent linkIntent = new Intent(Intent.ACTION_VIEW);
|
Intent linkIntent = new Intent(Intent.ACTION_VIEW);
|
||||||
@ -258,18 +263,7 @@ public class DirectMessageThreadFragment extends Fragment {
|
|||||||
break;
|
break;
|
||||||
case RAVEN_MEDIA:
|
case RAVEN_MEDIA:
|
||||||
case MEDIA:
|
case MEDIA:
|
||||||
final ProfileModel user = getUser(directItemModel.getUserId());
|
downloadItem(context);
|
||||||
final DirectItemModel.DirectItemMediaModel selectedItem =
|
|
||||||
itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia();
|
|
||||||
final String url = selectedItem.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO
|
|
||||||
? selectedItem.getVideoUrl()
|
|
||||||
: selectedItem.getThumbUrl();
|
|
||||||
if (url == null) {
|
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
DownloadUtils.dmDownload(context, user.getUsername(), DownloadMethod.DOWNLOAD_DIRECT, selectedItem);
|
|
||||||
Toast.makeText(context, R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case STORY_SHARE:
|
case STORY_SHARE:
|
||||||
if (directItemModel.getReelShare() != null) {
|
if (directItemModel.getReelShare() != null) {
|
||||||
@ -302,11 +296,13 @@ public class DirectMessageThreadFragment extends Fragment {
|
|||||||
} else if (which == 1) {
|
} else if (which == 1) {
|
||||||
sendText(null, directItemModel.getItemId(), directItemModel.isLiked());
|
sendText(null, directItemModel.getItemId(), directItemModel.isLiked());
|
||||||
} else if (which == 2) {
|
} else if (which == 2) {
|
||||||
if (directItemModel == null)
|
if (directItemModel == null) {
|
||||||
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||||
else if (String.valueOf(directItemModel.getUserId()).equals(myId))
|
} else if (String.valueOf(directItemModel.getUserId()).equals(myId)) {
|
||||||
new ThreadAction().execute("delete", directItemModel.getItemId());
|
new ThreadAction().execute("delete", directItemModel.getItemId());
|
||||||
else searchUsername(getUser(directItemModel.getUserId()).getUsername());
|
} else {
|
||||||
|
searchUsername(getUser(directItemModel.getUserId()).getUsername());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
final View.OnClickListener onClickListener = v -> {
|
final View.OnClickListener onClickListener = v -> {
|
||||||
@ -322,6 +318,7 @@ public class DirectMessageThreadFragment extends Fragment {
|
|||||||
switch (itemType) {
|
switch (itemType) {
|
||||||
case MEDIA_SHARE:
|
case MEDIA_SHARE:
|
||||||
case CLIP:
|
case CLIP:
|
||||||
|
case FELIX_SHARE:
|
||||||
firstOption = R.string.view_post;
|
firstOption = R.string.view_post;
|
||||||
break;
|
break;
|
||||||
case LINK:
|
case LINK:
|
||||||
@ -371,6 +368,26 @@ public class DirectMessageThreadFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void downloadItem(final Context context) {
|
||||||
|
final ProfileModel user = getUser(directItemModel.getUserId());
|
||||||
|
final DirectItemModel.DirectItemMediaModel selectedItem = directItemModel.getItemType() == DirectItemType.MEDIA
|
||||||
|
? directItemModel.getMediaModel()
|
||||||
|
: directItemModel.getRavenMediaModel().getMedia();
|
||||||
|
final String url = selectedItem.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO
|
||||||
|
? selectedItem.getVideoUrl()
|
||||||
|
: selectedItem.getThumbUrl();
|
||||||
|
if (url == null) {
|
||||||
|
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
if (ContextCompat.checkSelfPermission(context, DownloadUtils.PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
DownloadUtils.dmDownload(context, user.getUsername(), selectedItem.getId(), url);
|
||||||
|
} else {
|
||||||
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
Toast.makeText(context, R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@ -428,6 +445,17 @@ public class DirectMessageThreadFragment extends Fragment {
|
|||||||
if (listViewModel != null) listViewModel.getList().postValue(Collections.emptyList());
|
if (listViewModel != null) listViewModel.getList().postValue(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||||
|
downloadItem(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void sendText(final String text, final String itemId, final boolean delete) {
|
private void sendText(final String text, final String itemId, final boolean delete) {
|
||||||
DirectThreadBroadcaster.TextBroadcastOptions textOptions = null;
|
DirectThreadBroadcaster.TextBroadcastOptions textOptions = null;
|
||||||
DirectThreadBroadcaster.ReactionBroadcastOptions reactionOptions = null;
|
DirectThreadBroadcaster.ReactionBroadcastOptions reactionOptions = null;
|
||||||
|
@ -1,126 +1,84 @@
|
|||||||
package awais.instagrabber.fragments.main;
|
package awais.instagrabber.fragments.main;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.activity.OnBackPressedDispatcher;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.fragment.FragmentNavigator;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.activities.MainActivity;
|
import awais.instagrabber.activities.MainActivity;
|
||||||
import awais.instagrabber.adapters.DiscoverAdapter;
|
import awais.instagrabber.adapters.DiscoverTopicsAdapter;
|
||||||
import awais.instagrabber.asyncs.DiscoverFetcher;
|
|
||||||
import awais.instagrabber.asyncs.i.iTopicFetcher;
|
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.databinding.FragmentDiscoverBinding;
|
import awais.instagrabber.databinding.FragmentDiscoverBinding;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
|
||||||
import awais.instagrabber.models.DiscoverItemModel;
|
|
||||||
import awais.instagrabber.models.DiscoverTopicModel;
|
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.DiscoverItemViewModel;
|
import awais.instagrabber.viewmodels.TopicClusterViewModel;
|
||||||
|
import awais.instagrabber.webservices.DiscoverService;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
|
private static final String TAG = "DiscoverFragment";
|
||||||
|
|
||||||
private MainActivity fragmentActivity;
|
private MainActivity fragmentActivity;
|
||||||
private CoordinatorLayout root;
|
private CoordinatorLayout root;
|
||||||
private FragmentDiscoverBinding binding;
|
private FragmentDiscoverBinding binding;
|
||||||
private DiscoverAdapter discoverAdapter;
|
|
||||||
private RecyclerLazyLoader lazyLoader;
|
|
||||||
private boolean discoverHasMore = false;
|
|
||||||
private String[] topicIds;
|
|
||||||
private String rankToken;
|
|
||||||
private String currentTopic;
|
|
||||||
private String discoverEndMaxId;
|
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
private DiscoverItemViewModel discoverItemViewModel;
|
private TopicClusterViewModel topicClusterViewModel;
|
||||||
private boolean shouldRefresh = true;
|
private boolean shouldRefresh = true;
|
||||||
private boolean isPullToRefresh;
|
private DiscoverService discoverService;
|
||||||
|
|
||||||
private final FetchListener<DiscoverTopicModel> topicFetchListener = new FetchListener<DiscoverTopicModel>() {
|
// private final FetchListener<DiscoverItemModel[]> postsFetchListener = new FetchListener<DiscoverItemModel[]>() {
|
||||||
@Override
|
// @Override
|
||||||
public void doBefore() {}
|
// public void doBefore() {}
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public void onResult(final DiscoverTopicModel result) {
|
// public void onResult(final DiscoverItemModel[] result) {
|
||||||
if (result != null) {
|
// if (result == null || result.length <= 0) {
|
||||||
topicIds = result.getIds();
|
// binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
rankToken = result.getToken();
|
// final Context context = getContext();
|
||||||
final Context context = getContext();
|
// if (context == null) return;
|
||||||
if (context == null) return;
|
// Toast.makeText(context, R.string.discover_empty, Toast.LENGTH_SHORT).show();
|
||||||
final ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(
|
// return;
|
||||||
context,
|
// }
|
||||||
android.R.layout.simple_spinner_dropdown_item,
|
// List<DiscoverItemModel> current = discoverItemViewModel.getList().getValue();
|
||||||
result.getNames()
|
// final List<DiscoverItemModel> resultList = Arrays.asList(result);
|
||||||
);
|
// current = current == null ? new ArrayList<>() : new ArrayList<>(current); // copy to modifiable list
|
||||||
binding.discoverType.setAdapter(spinnerArrayAdapter);
|
// if (isPullToRefresh) {
|
||||||
}
|
// current = resultList;
|
||||||
}
|
// isPullToRefresh = false;
|
||||||
};
|
// } else {
|
||||||
private final FetchListener<DiscoverItemModel[]> postsFetchListener = new FetchListener<DiscoverItemModel[]>() {
|
// current.addAll(resultList);
|
||||||
@Override
|
// }
|
||||||
public void doBefore() {}
|
// discoverItemViewModel.getList().postValue(current);
|
||||||
|
// binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
@Override
|
// final DiscoverItemModel discoverItemModel = result[result.length - 1];
|
||||||
public void onResult(final DiscoverItemModel[] result) {
|
// if (discoverItemModel != null) {
|
||||||
if (result == null || result.length <= 0) {
|
// discoverEndMaxId = discoverItemModel.getNextMaxId();
|
||||||
binding.discoverSwipeRefreshLayout.setRefreshing(false);
|
// discoverHasMore = discoverItemModel.hasMore();
|
||||||
final Context context = getContext();
|
// discoverItemModel.setMore(false, null);
|
||||||
if (context == null) return;
|
// }
|
||||||
Toast.makeText(context, R.string.discover_empty, Toast.LENGTH_SHORT).show();
|
// }
|
||||||
return;
|
// };
|
||||||
}
|
|
||||||
List<DiscoverItemModel> current = discoverItemViewModel.getList().getValue();
|
|
||||||
final List<DiscoverItemModel> resultList = Arrays.asList(result);
|
|
||||||
current = current == null ? new ArrayList<>() : new ArrayList<>(current); // copy to modifiable list
|
|
||||||
if (isPullToRefresh) {
|
|
||||||
current = resultList;
|
|
||||||
isPullToRefresh = false;
|
|
||||||
} else {
|
|
||||||
current.addAll(resultList);
|
|
||||||
}
|
|
||||||
discoverItemViewModel.getList().postValue(current);
|
|
||||||
binding.discoverSwipeRefreshLayout.setRefreshing(false);
|
|
||||||
final DiscoverItemModel discoverItemModel = result[result.length - 1];
|
|
||||||
if (discoverItemModel != null) {
|
|
||||||
discoverEndMaxId = discoverItemModel.getNextMaxId();
|
|
||||||
discoverHasMore = discoverItemModel.hasMore();
|
|
||||||
discoverItemModel.setMore(false, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
remove();
|
remove();
|
||||||
if (discoverAdapter == null) return;
|
// if (discoverAdapter == null) return;
|
||||||
discoverAdapter.clearSelection();
|
// discoverAdapter.clearSelection();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||||
@ -134,14 +92,14 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_download) {
|
if (item.getItemId() == R.id.action_download) {
|
||||||
if (discoverAdapter == null) return false;
|
// if (discoverAdapter == null) return false;
|
||||||
final Context context = getContext();
|
// final Context context = getContext();
|
||||||
if (context == null) return false;
|
// if (context == null) return false;
|
||||||
DownloadUtils.batchDownload(context,
|
// DownloadUtils.batchDownload(context,
|
||||||
null,
|
// null,
|
||||||
DownloadMethod.DOWNLOAD_DISCOVER,
|
// DownloadMethod.DOWNLOAD_DISCOVER,
|
||||||
discoverAdapter.getSelectedModels());
|
// discoverAdapter.getSelectedModels());
|
||||||
checkAndResetAction();
|
// checkAndResetAction();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -152,6 +110,7 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
fragmentActivity = (MainActivity) requireActivity();
|
fragmentActivity = (MainActivity) requireActivity();
|
||||||
|
discoverService = DiscoverService.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -170,96 +129,159 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||||
if (!shouldRefresh) return;
|
if (!shouldRefresh) return;
|
||||||
binding.discoverSwipeRefreshLayout.setOnRefreshListener(this);
|
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||||
setupExplore();
|
init();
|
||||||
shouldRefresh = false;
|
shouldRefresh = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
// setExitSharedElementCallback(new SharedElementCallback() {
|
||||||
|
// @Override
|
||||||
|
// public void onSharedElementsArrived(final List<String> sharedElementNames,
|
||||||
|
// final List<View> sharedElements,
|
||||||
|
// final OnSharedElementsReadyListener listener) {
|
||||||
|
// super.onSharedElementsArrived(sharedElementNames, sharedElements, listener);
|
||||||
|
// Log.d(TAG, "onSharedElementsArrived: sharedElementNames: " + sharedElementNames);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onSharedElementEnd(final List<String> sharedElementNames,
|
||||||
|
// final List<View> sharedElements,
|
||||||
|
// final List<View> sharedElementSnapshots) {
|
||||||
|
// super.onSharedElementEnd(sharedElementNames, sharedElements, sharedElementSnapshots);
|
||||||
|
// Log.d(TAG, "onSharedElementEnd: sharedElementNames: " + sharedElementNames);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onSharedElementStart(final List<String> sharedElementNames,
|
||||||
|
// final List<View> sharedElements,
|
||||||
|
// final List<View> sharedElementSnapshots) {
|
||||||
|
// super.onSharedElementStart(sharedElementNames, sharedElements, sharedElementSnapshots);
|
||||||
|
// Log.d(TAG, "onSharedElementStart: sharedElementNames: " + sharedElementNames);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
setupTopics();
|
||||||
|
fetchTopics();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
isPullToRefresh = true;
|
|
||||||
discoverEndMaxId = null;
|
|
||||||
lazyLoader.resetState();
|
|
||||||
fetchPosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupExplore() {
|
|
||||||
discoverItemViewModel = new ViewModelProvider(fragmentActivity).get(DiscoverItemViewModel.class);
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
|
||||||
binding.discoverPosts.setLayoutManager(layoutManager);
|
|
||||||
binding.discoverPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
|
||||||
binding.discoverType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
|
||||||
if (topicIds == null || topicIds.length <= 0) return;
|
|
||||||
currentTopic = topicIds[pos];
|
|
||||||
onRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {}
|
|
||||||
});
|
|
||||||
discoverAdapter = new DiscoverAdapter((model, position) -> {
|
|
||||||
if (discoverAdapter.isSelecting()) {
|
|
||||||
if (actionMode == null) return;
|
|
||||||
final String title = getString(R.string.number_selected, discoverAdapter.getSelectedModels().size());
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (checkAndResetAction()) return;
|
|
||||||
final List<DiscoverItemModel> discoverItemModels = discoverItemViewModel.getList().getValue();
|
|
||||||
if (discoverItemModels == null || discoverItemModels.size() == 0) return;
|
|
||||||
if (discoverItemModels.get(0) == null) return;
|
|
||||||
final String postId = discoverItemModels.get(0).getPostId();
|
|
||||||
final boolean isId = postId != null;
|
|
||||||
final String[] idsOrShortCodes = new String[discoverItemModels.size()];
|
|
||||||
for (int i = 0; i < discoverItemModels.size(); i++) {
|
|
||||||
idsOrShortCodes[i] = isId ? discoverItemModels.get(i).getPostId()
|
|
||||||
: discoverItemModels.get(i).getShortCode();
|
|
||||||
}
|
|
||||||
final NavDirections action = DiscoverFragmentDirections.actionGlobalPostViewFragment(
|
|
||||||
position,
|
|
||||||
idsOrShortCodes,
|
|
||||||
isId);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
}, (model, position) -> {
|
|
||||||
if (!discoverAdapter.isSelecting()) {
|
|
||||||
checkAndResetAction();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
|
||||||
final String title = getString(R.string.number_selected, 1);
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
binding.discoverPosts.setAdapter(discoverAdapter);
|
|
||||||
discoverItemViewModel.getList().observe(fragmentActivity, discoverAdapter::submitList);
|
|
||||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
|
||||||
if (discoverHasMore) {
|
|
||||||
fetchPosts();
|
|
||||||
}
|
|
||||||
}, 3);
|
|
||||||
binding.discoverPosts.addOnScrollListener(lazyLoader);
|
|
||||||
fetchTopics();
|
fetchTopics();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchTopics() {
|
public void setupTopics() {
|
||||||
new iTopicFetcher(topicFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
topicClusterViewModel = new ViewModelProvider(fragmentActivity).get(TopicClusterViewModel.class);
|
||||||
|
binding.topicsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(2)));
|
||||||
|
final DiscoverTopicsAdapter adapter = new DiscoverTopicsAdapter((topicCluster, root, cover, title, titleColor, backgroundColor) -> {
|
||||||
|
final FragmentNavigator.Extras.Builder builder = new FragmentNavigator.Extras.Builder()
|
||||||
|
.addSharedElement(cover, "cover-" + topicCluster.getId());
|
||||||
|
// .addSharedElement(title, "title-" + topicCluster.getId());
|
||||||
|
final DiscoverFragmentDirections.ActionDiscoverFragmentToTopicPostsFragment action = DiscoverFragmentDirections
|
||||||
|
.actionDiscoverFragmentToTopicPostsFragment(topicCluster, titleColor, backgroundColor);
|
||||||
|
NavHostFragment.findNavController(this).navigate(action, builder.build());
|
||||||
|
});
|
||||||
|
binding.topicsRecyclerView.setAdapter(adapter);
|
||||||
|
topicClusterViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchPosts() {
|
private void setupExplore() {
|
||||||
binding.discoverSwipeRefreshLayout.setRefreshing(true);
|
// discoverItemViewModel = new ViewModelProvider(fragmentActivity).get(DiscoverItemViewModel.class);
|
||||||
new DiscoverFetcher(currentTopic, discoverEndMaxId, rankToken, postsFetchListener, false)
|
// final Context context = getContext();
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
// if (context == null) return;
|
||||||
|
// final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
||||||
|
// binding.postsRecyclerView.setLayoutManager(layoutManager);
|
||||||
|
// binding.postsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
||||||
|
// binding.discoverType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
// @Override
|
||||||
|
// public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||||
|
// if (topicIds == null || topicIds.length <= 0) return;
|
||||||
|
// currentTopic = topicIds[pos];
|
||||||
|
// onRefresh();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onNothingSelected(AdapterView<?> parent) {}
|
||||||
|
// });
|
||||||
|
// discoverAdapter = new DiscoverAdapter((model, position) -> {
|
||||||
|
// if (discoverAdapter.isSelecting()) {
|
||||||
|
// if (actionMode == null) return;
|
||||||
|
// final String title = getString(R.string.number_selected, discoverAdapter.getSelectedModels().size());
|
||||||
|
// actionMode.setTitle(title);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if (checkAndResetAction()) return;
|
||||||
|
// final List<DiscoverItemModel> discoverItemModels = discoverItemViewModel.getList().getValue();
|
||||||
|
// if (discoverItemModels == null || discoverItemModels.size() == 0) return;
|
||||||
|
// if (discoverItemModels.get(0) == null) return;
|
||||||
|
// final String postId = discoverItemModels.get(0).getPostId();
|
||||||
|
// final boolean isId = postId != null;
|
||||||
|
// final String[] idsOrShortCodes = new String[discoverItemModels.size()];
|
||||||
|
// for (int i = 0; i < discoverItemModels.size(); i++) {
|
||||||
|
// idsOrShortCodes[i] = isId ? discoverItemModels.get(i).getPostId()
|
||||||
|
// : discoverItemModels.get(i).getShortCode();
|
||||||
|
// }
|
||||||
|
// final NavDirections action = DiscoverFragmentDirections.actionGlobalPostViewFragment(
|
||||||
|
// position,
|
||||||
|
// idsOrShortCodes,
|
||||||
|
// isId);
|
||||||
|
// NavHostFragment.findNavController(this).navigate(action);
|
||||||
|
// }, (model, position) -> {
|
||||||
|
// if (!discoverAdapter.isSelecting()) {
|
||||||
|
// checkAndResetAction();
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
|
// if (onBackPressedCallback.isEnabled()) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
|
// final String title = getString(R.string.number_selected, 1);
|
||||||
|
// actionMode.setTitle(title);
|
||||||
|
// onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
|
// return true;
|
||||||
|
// });
|
||||||
|
// binding.postsRecyclerView.setAdapter(discoverAdapter);
|
||||||
|
// discoverItemViewModel.getList().observe(fragmentActivity, discoverAdapter::submitList);
|
||||||
|
// lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
||||||
|
// if (discoverHasMore) {
|
||||||
|
// fetchPosts();
|
||||||
|
// }
|
||||||
|
// }, 3);
|
||||||
|
// binding.postsRecyclerView.addOnScrollListener(lazyLoader);
|
||||||
|
// binding.postsRecyclerView.setViewModelStoreOwner(this)
|
||||||
|
// .setLifeCycleOwner(this)
|
||||||
|
// .setPostFetchService(new DiscoverPostFetchService())
|
||||||
|
// .setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_PROFILE_POSTS_LAYOUT)))
|
||||||
|
// .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||||
|
// .setFeedItemCallback(feedItemCallback)
|
||||||
|
// .init();
|
||||||
|
// binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fetchTopics() {
|
||||||
|
// new iTopicFetcher(topicFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
|
discoverService.topicalExplore(new DiscoverService.TopicalExploreRequest(), new ServiceCallback<DiscoverService.TopicalExploreResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final DiscoverService.TopicalExploreResponse result) {
|
||||||
|
topicClusterViewModel.getList().postValue(result.getClusters());
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
Log.e(TAG, "onFailure", t);
|
||||||
|
binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void fetchPosts() {
|
||||||
|
// binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
|
// new DiscoverFetcher(currentTopic, discoverEndMaxId, rankToken, postsFetchListener, false)
|
||||||
|
// .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
// }
|
||||||
|
|
||||||
private boolean checkAndResetAction() {
|
private boolean checkAndResetAction() {
|
||||||
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,295 +1,245 @@
|
|||||||
package awais.instagrabber.fragments.main;
|
package awais.instagrabber.fragments.main;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
|
import androidx.activity.OnBackPressedDispatcher;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
|
import androidx.core.content.PermissionChecker;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import com.facebook.common.executors.UiThreadImmediateExecutorService;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.facebook.datasource.BaseDataSubscriber;
|
|
||||||
import com.facebook.datasource.DataSource;
|
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
|
||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.activities.MainActivity;
|
import awais.instagrabber.activities.MainActivity;
|
||||||
import awais.instagrabber.adapters.FeedAdapter;
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.adapters.FeedStoriesAdapter;
|
import awais.instagrabber.adapters.FeedStoriesAdapter;
|
||||||
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
|
import awais.instagrabber.asyncs.FeedPostFetchService;
|
||||||
import awais.instagrabber.asyncs.FeedFetcher;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
import awais.instagrabber.customviews.RamboTextView;
|
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.customviews.helpers.VideoAwareRecyclerScroller;
|
|
||||||
import awais.instagrabber.databinding.FragmentFeedBinding;
|
import awais.instagrabber.databinding.FragmentFeedBinding;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||||
import awais.instagrabber.models.BasePostModel;
|
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.models.FeedStoryModel;
|
import awais.instagrabber.models.FeedStoryModel;
|
||||||
import awais.instagrabber.models.PostModel;
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.models.ProfileModel;
|
|
||||||
import awais.instagrabber.models.ViewerPostModel;
|
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
import awais.instagrabber.utils.DownloadUtils;
|
||||||
import awais.instagrabber.utils.NumberUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.FeedStoriesViewModel;
|
import awais.instagrabber.viewmodels.FeedStoriesViewModel;
|
||||||
import awais.instagrabber.viewmodels.FeedViewModel;
|
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
import awais.instagrabber.webservices.StoriesService;
|
import awais.instagrabber.webservices.StoriesService;
|
||||||
|
|
||||||
|
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||||
|
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
private static final String TAG = "FeedFragment";
|
private static final String TAG = "FeedFragment";
|
||||||
private static final double MAX_VIDEO_HEIGHT = 0.9 * Utils.displayMetrics.heightPixels;
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
private static final int RESIZED_VIDEO_HEIGHT = (int) (0.8 * Utils.displayMetrics.heightPixels);
|
private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
|
||||||
|
|
||||||
private MainActivity fragmentActivity;
|
private MainActivity fragmentActivity;
|
||||||
private CoordinatorLayout root;
|
private CoordinatorLayout root;
|
||||||
private FragmentFeedBinding binding;
|
private FragmentFeedBinding binding;
|
||||||
private StoriesService storiesService;
|
private StoriesService storiesService;
|
||||||
private boolean feedHasNextPage = false;
|
|
||||||
private String feedEndCursor = null;
|
|
||||||
private FeedViewModel feedViewModel;
|
|
||||||
private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
|
|
||||||
private boolean shouldRefresh = true;
|
private boolean shouldRefresh = true;
|
||||||
private boolean isPullToRefresh;
|
private FeedStoriesViewModel feedStoriesViewModel;
|
||||||
|
private boolean storiesFetching;
|
||||||
|
private ActionMode actionMode;
|
||||||
|
private Set<FeedModel> selectedFeedModels;
|
||||||
|
private FeedModel downloadFeedModel;
|
||||||
|
private int downloadChildPosition = -1;
|
||||||
|
|
||||||
private final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
private final FetchListener<FeedModel[]> feedFetchListener = new FetchListener<FeedModel[]>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void doBefore() {
|
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
||||||
binding.feedSwipeRefreshLayout.post(() -> binding.feedSwipeRefreshLayout.setRefreshing(true));
|
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResult(final FeedModel[] result) {
|
public void onSliderClick(final FeedModel feedModel, final int position) {
|
||||||
if (result == null || result.length <= 0) {
|
openPostDialog(feedModel, null, null, position);
|
||||||
binding.feedSwipeRefreshLayout.setRefreshing(false);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommentsClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||||
|
feedModel.getShortCode(),
|
||||||
|
feedModel.getPostId(),
|
||||||
|
feedModel.getProfileModel().getId()
|
||||||
|
);
|
||||||
|
NavHostFragment.findNavController(FeedFragment.this).navigate(commentsAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
|
DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final List<FeedModel> currentFeedModelList = feedViewModel.getList().getValue();
|
downloadFeedModel = feedModel;
|
||||||
final Map<String, FeedModel> thumbToFeedMap = new HashMap<>();
|
downloadChildPosition = childPosition;
|
||||||
for (final FeedModel feedModel : result) {
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||||
thumbToFeedMap.put(feedModel.getThumbnailUrl(), feedModel);
|
}
|
||||||
}
|
|
||||||
final BaseDataSubscriber<Void> subscriber = new BaseDataSubscriber<Void>() {
|
|
||||||
int success = 0;
|
|
||||||
int failed = 0;
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHashtagClick(final String hashtag) {
|
||||||
|
final NavDirections action = FeedFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||||
|
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections action = FeedFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
|
||||||
|
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMentionClick(final String mention) {
|
||||||
|
navigateToProfile(mention.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNameClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onURLClick(final String url) {
|
||||||
|
Utils.openURL(getContext(), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmailClick(final String emailId) {
|
||||||
|
Utils.openEmailAddress(getContext(), emailId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openPostDialog(final FeedModel feedModel,
|
||||||
|
final View profilePicView,
|
||||||
|
final View mainPostImage,
|
||||||
|
final int position) {
|
||||||
|
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||||
|
.builder(feedModel);
|
||||||
|
if (position >= 0) {
|
||||||
|
builder.setPosition(position);
|
||||||
|
}
|
||||||
|
final PostViewV2Fragment fragment = builder
|
||||||
|
.setSharedProfilePicElement(profilePicView)
|
||||||
|
.setSharedMainPostElement(mainPostImage)
|
||||||
|
.build();
|
||||||
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
binding.feedRecyclerView.endSelection();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||||
|
R.menu.multi_select_download_menu,
|
||||||
|
new PrimaryActionModeCallback.CallbacksHelper() {
|
||||||
@Override
|
@Override
|
||||||
protected void onNewResultImpl(@NonNull final DataSource<Void> dataSource) {
|
public void onDestroy(final ActionMode mode) {
|
||||||
final Map<String, Object> extras = dataSource.getExtras();
|
binding.feedRecyclerView.endSelection();
|
||||||
if (extras == null) return;
|
|
||||||
final Uri thumbUri = (Uri) extras.get("uri_source");
|
|
||||||
if (thumbUri == null) return;
|
|
||||||
final Integer encodedWidth = (Integer) extras.get("encoded_width");
|
|
||||||
final Integer encodedHeight = (Integer) extras.get("encoded_height");
|
|
||||||
if (encodedWidth == null || encodedHeight == null) return;
|
|
||||||
final FeedModel feedModel = thumbToFeedMap.get(thumbUri.toString());
|
|
||||||
if (feedModel == null) return;
|
|
||||||
int requiredWidth = Utils.displayMetrics.widthPixels;
|
|
||||||
int resultingHeight = NumberUtils
|
|
||||||
.getResultingHeight(requiredWidth, encodedHeight, encodedWidth);
|
|
||||||
if (feedModel
|
|
||||||
.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO && resultingHeight >= MAX_VIDEO_HEIGHT) {
|
|
||||||
// If its a video and the height is too large, need to reduce the height,
|
|
||||||
// so that entire video fits on screen
|
|
||||||
resultingHeight = RESIZED_VIDEO_HEIGHT;
|
|
||||||
requiredWidth = NumberUtils.getResultingWidth(RESIZED_VIDEO_HEIGHT,
|
|
||||||
resultingHeight,
|
|
||||||
requiredWidth);
|
|
||||||
}
|
|
||||||
feedModel.setImageWidth(requiredWidth);
|
|
||||||
feedModel.setImageHeight(resultingHeight);
|
|
||||||
success++;
|
|
||||||
updateAdapter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFailureImpl(@NonNull final DataSource<Void> dataSource) {
|
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
||||||
failed++;
|
if (item.getItemId() == R.id.action_download) {
|
||||||
updateAdapter();
|
if (FeedFragment.this.selectedFeedModels == null) return false;
|
||||||
}
|
final Context context = getContext();
|
||||||
|
if (context == null) return false;
|
||||||
public void updateAdapter() {
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
if (failed + success != result.length) return;
|
DownloadUtils.download(context, ImmutableList.copyOf(FeedFragment.this.selectedFeedModels));
|
||||||
List<FeedModel> finalList = currentFeedModelList == null || currentFeedModelList.isEmpty()
|
binding.feedRecyclerView.endSelection();
|
||||||
? new ArrayList<>()
|
return true;
|
||||||
: new ArrayList<>(currentFeedModelList);
|
}
|
||||||
final List<FeedModel> resultList = Arrays.asList(result);
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
|
||||||
if (isPullToRefresh) {
|
return true;
|
||||||
finalList = resultList;
|
|
||||||
isPullToRefresh = false;
|
|
||||||
} else {
|
|
||||||
finalList.addAll(resultList);
|
|
||||||
}
|
}
|
||||||
feedViewModel.getList().postValue(finalList);
|
return false;
|
||||||
final PostModel feedPostModel = result[result.length - 1];
|
|
||||||
if (feedPostModel != null) {
|
|
||||||
feedEndCursor = feedPostModel.getEndCursor();
|
|
||||||
feedHasNextPage = feedPostModel.hasNextPage();
|
|
||||||
feedPostModel.setPageCursor(false, null);
|
|
||||||
}
|
|
||||||
binding.feedSwipeRefreshLayout.setRefreshing(false);
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
|
||||||
|
|
||||||
for (final FeedModel feedModel : result) {
|
@Override
|
||||||
final DataSource<Void> ds = Fresco.getImagePipeline()
|
public void onSelectionStart() {
|
||||||
.prefetchToBitmapCache(ImageRequest.fromUri(feedModel.getThumbnailUrl()), null);
|
if (!onBackPressedCallback.isEnabled()) {
|
||||||
ds.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
|
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
|
onBackPressedCallback.setEnabled(true);
|
||||||
|
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
|
}
|
||||||
|
if (actionMode == null) {
|
||||||
|
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionChange(final Set<FeedModel> selectedFeedModels) {
|
||||||
|
final String title = getString(R.string.number_selected, selectedFeedModels.size());
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.setTitle(title);
|
||||||
|
}
|
||||||
|
FeedFragment.this.selectedFeedModels = selectedFeedModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionEnd() {
|
||||||
|
if (onBackPressedCallback.isEnabled()) {
|
||||||
|
onBackPressedCallback.setEnabled(false);
|
||||||
|
onBackPressedCallback.remove();
|
||||||
|
}
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.finish();
|
||||||
|
actionMode = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
|
|
||||||
if (isHashtag) {
|
|
||||||
final NavDirections action = FeedFragmentDirections.actionGlobalHashTagFragment(text);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isLocation) {
|
|
||||||
final NavDirections action = FeedFragmentDirections.actionGlobalLocationFragment(text);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final NavDirections action = FeedFragmentDirections.actionGlobalProfileFragment("@" + text);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
};
|
|
||||||
private final View.OnClickListener postViewClickListener = v -> {
|
|
||||||
final Object tag = v.getTag();
|
|
||||||
if (!(tag instanceof FeedModel)) return;
|
|
||||||
|
|
||||||
final FeedModel feedModel = (FeedModel) tag;
|
private void navigateToProfile(final String username) {
|
||||||
if (v instanceof RamboTextView) {
|
final NavController navController = NavHostFragment.findNavController(this);
|
||||||
if (feedModel.isMentionClicked()) feedModel.toggleCaption();
|
final Bundle bundle = new Bundle();
|
||||||
feedModel.setMentionClicked(false);
|
bundle.putString("username", username);
|
||||||
if (!FeedItemViewHolder.expandCollapseTextView((RamboTextView) v, feedModel.getPostCaption()))
|
navController.navigate(R.id.action_global_profileFragment, bundle);
|
||||||
feedModel.toggleCaption();
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int id = v.getId();
|
|
||||||
switch (id) {
|
|
||||||
case R.id.btnComments:
|
|
||||||
final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
|
|
||||||
feedModel.getShortCode(),
|
|
||||||
feedModel.getPostId(),
|
|
||||||
feedModel.getProfileModel().getId()
|
|
||||||
);
|
|
||||||
NavHostFragment.findNavController(this).navigate(commentsAction);
|
|
||||||
break;
|
|
||||||
case R.id.viewStoryPost:
|
|
||||||
final List<FeedModel> feedModels = feedViewModel.getList().getValue();
|
|
||||||
if (feedModels == null || feedModels.size() == 0) return;
|
|
||||||
if (feedModels.get(0) == null) return;
|
|
||||||
final String postId = feedModels.get(0).getPostId();
|
|
||||||
final boolean isId = postId != null;
|
|
||||||
final String[] idsOrShortCodes = new String[feedModels.size()];
|
|
||||||
for (int i = 0; i < feedModels.size(); i++) {
|
|
||||||
idsOrShortCodes[i] = isId ? feedModels.get(i).getPostId()
|
|
||||||
: feedModels.get(i).getShortCode();
|
|
||||||
}
|
|
||||||
final NavDirections action = FeedFragmentDirections.actionGlobalPostViewFragment(
|
|
||||||
feedModel.getPosition(),
|
|
||||||
idsOrShortCodes,
|
|
||||||
isId);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case R.id.btnDownload:
|
|
||||||
ProfileModel profileModel = feedModel.getProfileModel();
|
|
||||||
final String username = profileModel != null ? profileModel.getUsername() : null;
|
|
||||||
|
|
||||||
final ViewerPostModel[] sliderItems = feedModel.getSliderItems();
|
|
||||||
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
if (feedModel
|
|
||||||
.getItemType() != MediaItemType.MEDIA_TYPE_SLIDER || sliderItems == null || sliderItems.length == 1)
|
|
||||||
DownloadUtils.batchDownload(context,
|
|
||||||
username,
|
|
||||||
DownloadMethod.DOWNLOAD_FEED,
|
|
||||||
Collections.singletonList(feedModel));
|
|
||||||
else {
|
|
||||||
final ArrayList<BasePostModel> postModels = new ArrayList<>();
|
|
||||||
final DialogInterface.OnClickListener clickListener1 = (dialog, which) -> {
|
|
||||||
postModels.clear();
|
|
||||||
|
|
||||||
final boolean breakWhenFoundSelected = which == DialogInterface.BUTTON_POSITIVE;
|
|
||||||
|
|
||||||
for (final ViewerPostModel sliderItem : sliderItems) {
|
|
||||||
if (sliderItem != null) {
|
|
||||||
if (!breakWhenFoundSelected) postModels.add(sliderItem);
|
|
||||||
else if (sliderItem.isSelected()) {
|
|
||||||
postModels.add(sliderItem);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// shows 0 items on first item of viewpager cause onPageSelected hasn't been called yet
|
|
||||||
if (breakWhenFoundSelected && postModels.size() == 0) {
|
|
||||||
postModels.add(sliderItems[0]);
|
|
||||||
}
|
|
||||||
if (postModels.size() > 0) {
|
|
||||||
DownloadUtils.batchDownload(context,
|
|
||||||
username,
|
|
||||||
DownloadMethod.DOWNLOAD_FEED,
|
|
||||||
postModels);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
new AlertDialog.Builder(context)
|
|
||||||
.setTitle(R.string.post_viewer_download_dialog_title).setPositiveButton(
|
|
||||||
R.string.post_viewer_download_current,
|
|
||||||
clickListener1)
|
|
||||||
.setNegativeButton(R.string.post_viewer_download_album, clickListener1)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case R.id.ivProfilePic:
|
|
||||||
profileModel = feedModel.getProfileModel();
|
|
||||||
if (profileModel != null) mentionClickListener.onClick(null, profileModel.getUsername(), false, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private FeedStoriesViewModel feedStoriesViewModel;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
fragmentActivity = (MainActivity) requireActivity();
|
fragmentActivity = (MainActivity) requireActivity();
|
||||||
storiesService = StoriesService.getInstance();
|
storiesService = StoriesService.getInstance();
|
||||||
// feedService = FeedService.getInstance();
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -314,58 +264,80 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
shouldRefresh = false;
|
shouldRefresh = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.feed_menu, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.layout) {
|
||||||
|
showPostsLayoutPreferences();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
if (videoAwareRecyclerScroller != null) {
|
// if (videoAwareRecyclerScroller != null) {
|
||||||
videoAwareRecyclerScroller.stopPlaying();
|
// videoAwareRecyclerScroller.stopPlaying();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
binding.feedSwipeRefreshLayout.setRefreshing(false);
|
binding.feedSwipeRefreshLayout.setRefreshing(false);
|
||||||
if (videoAwareRecyclerScroller != null && shouldAutoPlay) {
|
// if (videoAwareRecyclerScroller != null && shouldAutoPlay) {
|
||||||
videoAwareRecyclerScroller.startPlaying();
|
// videoAwareRecyclerScroller.startPlaying();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
isPullToRefresh = true;
|
binding.feedRecyclerView.refresh();
|
||||||
feedEndCursor = null;
|
|
||||||
fetchFeed();
|
|
||||||
fetchStories();
|
fetchStories();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupFeed() {
|
@Override
|
||||||
feedViewModel = new ViewModelProvider(fragmentActivity).get(FeedViewModel.class);
|
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
|
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||||
binding.feedRecyclerView.setLayoutManager(layoutManager);
|
if (downloadFeedModel == null) return;
|
||||||
binding.feedRecyclerView.setHasFixedSize(true);
|
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
|
||||||
final FeedAdapter feedAdapter = new FeedAdapter(postViewClickListener, mentionClickListener);
|
downloadFeedModel = null;
|
||||||
feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
downloadChildPosition = -1;
|
||||||
binding.feedRecyclerView.setAdapter(feedAdapter);
|
return;
|
||||||
feedViewModel.getList().observe(fragmentActivity, feedAdapter::submitList);
|
}
|
||||||
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
|
||||||
if (feedHasNextPage) {
|
DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
|
||||||
fetchFeed();
|
binding.feedRecyclerView.endSelection();
|
||||||
}
|
|
||||||
});
|
|
||||||
if (shouldAutoPlay) {
|
|
||||||
videoAwareRecyclerScroller = new VideoAwareRecyclerScroller();
|
|
||||||
binding.feedRecyclerView.addOnScrollListener(videoAwareRecyclerScroller);
|
|
||||||
}
|
}
|
||||||
binding.feedRecyclerView.addOnScrollListener(lazyLoader);
|
|
||||||
fetchFeed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchFeed() {
|
private void setupFeed() {
|
||||||
|
binding.feedRecyclerView.setViewModelStoreOwner(this)
|
||||||
|
.setLifeCycleOwner(this)
|
||||||
|
.setPostFetchService(new FeedPostFetchService())
|
||||||
|
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_POSTS_LAYOUT)))
|
||||||
|
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||||
|
.setFeedItemCallback(feedItemCallback)
|
||||||
|
.setSelectionModeCallback(selectionModeCallback)
|
||||||
|
.init();
|
||||||
binding.feedSwipeRefreshLayout.setRefreshing(true);
|
binding.feedSwipeRefreshLayout.setRefreshing(true);
|
||||||
new FeedFetcher(feedEndCursor, feedFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
// if (shouldAutoPlay) {
|
||||||
|
// videoAwareRecyclerScroller = new VideoAwareRecyclerScroller();
|
||||||
|
// binding.feedRecyclerView.addOnScrollListener(videoAwareRecyclerScroller);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSwipeRefreshState() {
|
||||||
|
binding.feedSwipeRefreshLayout.setRefreshing(binding.feedRecyclerView.isFetching() || storiesFetching);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupFeedStories() {
|
private void setupFeedStories() {
|
||||||
@ -383,16 +355,34 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void fetchStories() {
|
private void fetchStories() {
|
||||||
|
storiesFetching = true;
|
||||||
|
updateSwipeRefreshState();
|
||||||
storiesService.getFeedStories(new ServiceCallback<List<FeedStoryModel>>() {
|
storiesService.getFeedStories(new ServiceCallback<List<FeedStoryModel>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final List<FeedStoryModel> result) {
|
public void onSuccess(final List<FeedStoryModel> result) {
|
||||||
feedStoriesViewModel.getList().postValue(result);
|
feedStoriesViewModel.getList().postValue(result);
|
||||||
|
storiesFetching = false;
|
||||||
|
updateSwipeRefreshState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(final Throwable t) {
|
public void onFailure(final Throwable t) {
|
||||||
Log.e(TAG, "failed", t);
|
Log.e(TAG, "failed", t);
|
||||||
|
storiesFetching = false;
|
||||||
|
updateSwipeRefreshState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showPostsLayoutPreferences() {
|
||||||
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||||
|
Constants.PREF_POSTS_LAYOUT,
|
||||||
|
preferences -> new Handler().postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));
|
||||||
|
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scrollToTop() {
|
||||||
|
binding.feedRecyclerView.smoothScrollToPosition(0);
|
||||||
|
binding.storiesContainer.setExpanded(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,13 @@ package awais.instagrabber.fragments.main;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.method.LinkMovementMethod;
|
|
||||||
import android.text.style.RelativeSizeSpan;
|
import android.text.style.RelativeSizeSpan;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -19,7 +19,6 @@ import android.view.MenuInflater;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
@ -29,10 +28,12 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
|
import androidx.core.content.PermissionChecker;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
@ -41,36 +42,36 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|||||||
|
|
||||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import awais.instagrabber.ProfileNavGraphDirections;
|
import awais.instagrabber.ProfileNavGraphDirections;
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.activities.MainActivity;
|
import awais.instagrabber.activities.MainActivity;
|
||||||
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.adapters.HighlightsAdapter;
|
import awais.instagrabber.adapters.HighlightsAdapter;
|
||||||
import awais.instagrabber.adapters.PostsAdapter;
|
|
||||||
import awais.instagrabber.asyncs.HighlightsFetcher;
|
import awais.instagrabber.asyncs.HighlightsFetcher;
|
||||||
import awais.instagrabber.asyncs.PostsFetcher;
|
|
||||||
import awais.instagrabber.asyncs.ProfileFetcher;
|
import awais.instagrabber.asyncs.ProfileFetcher;
|
||||||
|
import awais.instagrabber.asyncs.ProfilePostFetchService;
|
||||||
import awais.instagrabber.asyncs.UsernameFetcher;
|
import awais.instagrabber.asyncs.UsernameFetcher;
|
||||||
import awais.instagrabber.asyncs.direct_messages.CreateThreadAction;
|
import awais.instagrabber.asyncs.direct_messages.CreateThreadAction;
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper;
|
||||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
|
||||||
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.databinding.FragmentProfileBinding;
|
import awais.instagrabber.databinding.FragmentProfileBinding;
|
||||||
|
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||||
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
||||||
|
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
import awais.instagrabber.interfaces.MentionClickListener;
|
||||||
import awais.instagrabber.models.PostModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.models.ProfileModel;
|
import awais.instagrabber.models.ProfileModel;
|
||||||
import awais.instagrabber.models.StoryModel;
|
import awais.instagrabber.models.StoryModel;
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
|
||||||
import awais.instagrabber.models.enums.FavoriteType;
|
import awais.instagrabber.models.enums.FavoriteType;
|
||||||
import awais.instagrabber.models.enums.PostItemType;
|
import awais.instagrabber.models.enums.PostItemType;
|
||||||
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
|
||||||
@ -82,17 +83,19 @@ import awais.instagrabber.utils.DownloadUtils;
|
|||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.HighlightsViewModel;
|
import awais.instagrabber.viewmodels.HighlightsViewModel;
|
||||||
import awais.instagrabber.viewmodels.PostsViewModel;
|
|
||||||
import awais.instagrabber.webservices.FriendshipService;
|
import awais.instagrabber.webservices.FriendshipService;
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
import awais.instagrabber.webservices.StoriesService;
|
import awais.instagrabber.webservices.StoriesService;
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.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.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
private static final String TAG = "ProfileFragment";
|
private static final String TAG = "ProfileFragment";
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
|
||||||
|
|
||||||
private MainActivity fragmentActivity;
|
private MainActivity fragmentActivity;
|
||||||
private CoordinatorLayout root;
|
private CoordinatorLayout root;
|
||||||
@ -101,21 +104,21 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
private String cookie;
|
private String cookie;
|
||||||
private String username;
|
private String username;
|
||||||
private ProfileModel profileModel;
|
private ProfileModel profileModel;
|
||||||
private PostsViewModel postsViewModel;
|
|
||||||
private PostsAdapter postsAdapter;
|
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
private Handler usernameSettingHandler;
|
private Handler usernameSettingHandler;
|
||||||
private FriendshipService friendshipService;
|
private FriendshipService friendshipService;
|
||||||
private StoriesService storiesService;
|
private StoriesService storiesService;
|
||||||
private boolean shouldRefresh = true, hasStories = false;
|
private boolean shouldRefresh = true;
|
||||||
private boolean hasNextPage;
|
private boolean hasStories = false;
|
||||||
private String endCursor;
|
|
||||||
private AsyncTask<Void, Void, List<PostModel>> currentlyExecuting;
|
|
||||||
private boolean isPullToRefresh;
|
|
||||||
private HighlightsAdapter highlightsAdapter;
|
private HighlightsAdapter highlightsAdapter;
|
||||||
private HighlightsViewModel highlightsViewModel;
|
private HighlightsViewModel highlightsViewModel;
|
||||||
private MenuItem blockMenuItem;
|
private MenuItem blockMenuItem;
|
||||||
private MenuItem restrictMenuItem;
|
private MenuItem restrictMenuItem;
|
||||||
|
private boolean highlightsFetching;
|
||||||
|
private boolean postsSetupDone = false;
|
||||||
|
private Set<FeedModel> selectedFeedModels;
|
||||||
|
private FeedModel downloadFeedModel;
|
||||||
|
private int downloadChildPosition = -1;
|
||||||
|
|
||||||
private final Runnable usernameSettingRunnable = () -> {
|
private final Runnable usernameSettingRunnable = () -> {
|
||||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||||
@ -129,10 +132,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
setEnabled(false);
|
binding.postsRecyclerView.endSelection();
|
||||||
remove();
|
|
||||||
if (postsAdapter == null) return;
|
|
||||||
postsAdapter.clearSelection();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||||
@ -140,58 +140,26 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
new CallbacksHelper() {
|
new CallbacksHelper() {
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy(final ActionMode mode) {
|
public void onDestroy(final ActionMode mode) {
|
||||||
onBackPressedCallback.handleOnBackPressed();
|
binding.postsRecyclerView.endSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_download) {
|
if (item.getItemId() == R.id.action_download) {
|
||||||
if (postsAdapter == null || username == null) {
|
if (ProfileFragment.this.selectedFeedModels == null) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return false;
|
if (context == null) return false;
|
||||||
DownloadUtils.batchDownload(context,
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
username,
|
DownloadUtils.download(context, ImmutableList.copyOf(ProfileFragment.this.selectedFeedModels));
|
||||||
DownloadMethod.DOWNLOAD_MAIN,
|
binding.postsRecyclerView.endSelection();
|
||||||
postsAdapter.getSelectedModels());
|
return true;
|
||||||
checkAndResetAction();
|
}
|
||||||
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
|
||||||
@Override
|
|
||||||
public void onResult(final List<PostModel> result) {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
if (result == null || result.isEmpty()) {
|
|
||||||
binding.privatePage1.setImageResource(R.drawable.ic_cancel);
|
|
||||||
binding.privatePage2.setText(R.string.empty_acc);
|
|
||||||
binding.privatePage.setVisibility(View.VISIBLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
binding.privatePage.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
|
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
|
||||||
List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>()
|
|
||||||
: new ArrayList<>(postModels);
|
|
||||||
if (isPullToRefresh) {
|
|
||||||
finalList = result;
|
|
||||||
isPullToRefresh = false;
|
|
||||||
} else {
|
|
||||||
finalList.addAll(result);
|
|
||||||
}
|
|
||||||
postsViewModel.getList().postValue(finalList);
|
|
||||||
final PostModel lastPostModel = result.get(result.size() - 1);
|
|
||||||
if (lastPostModel == null) return;
|
|
||||||
endCursor = lastPostModel.getEndCursor();
|
|
||||||
hasNextPage = lastPostModel.hasNextPage();
|
|
||||||
lastPostModel.setPageCursor(false, null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
|
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
|
||||||
Log.d(TAG, "action...");
|
Log.d(TAG, "action...");
|
||||||
if (isHashtag) {
|
if (isHashtag) {
|
||||||
@ -210,6 +178,128 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
action.setUsername("@" + text);
|
action.setUsername("@" + text);
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
};
|
};
|
||||||
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
||||||
|
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSliderClick(final FeedModel feedModel, final int position) {
|
||||||
|
openPostDialog(feedModel, null, null, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommentsClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||||
|
feedModel.getShortCode(),
|
||||||
|
feedModel.getPostId(),
|
||||||
|
feedModel.getProfileModel().getId()
|
||||||
|
);
|
||||||
|
NavHostFragment.findNavController(ProfileFragment.this).navigate(commentsAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadClick(final FeedModel feedModel, final int childPosition) {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||||
|
DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadFeedModel = feedModel;
|
||||||
|
downloadChildPosition = childPosition;
|
||||||
|
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHashtagClick(final String hashtag) {
|
||||||
|
final NavDirections action = FeedFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||||
|
NavHostFragment.findNavController(ProfileFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections action = FeedFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
|
||||||
|
NavHostFragment.findNavController(ProfileFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMentionClick(final String mention) {
|
||||||
|
navigateToProfile(mention.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNameClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
|
||||||
|
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onURLClick(final String url) {
|
||||||
|
Utils.openURL(getContext(), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmailClick(final String emailId) {
|
||||||
|
Utils.openEmailAddress(getContext(), emailId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openPostDialog(final FeedModel feedModel,
|
||||||
|
final View profilePicView,
|
||||||
|
final View mainPostImage,
|
||||||
|
final int position) {
|
||||||
|
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||||
|
.builder(feedModel);
|
||||||
|
if (position >= 0) {
|
||||||
|
builder.setPosition(position);
|
||||||
|
}
|
||||||
|
final PostViewV2Fragment fragment = builder
|
||||||
|
.setSharedProfilePicElement(profilePicView)
|
||||||
|
.setSharedMainPostElement(mainPostImage)
|
||||||
|
.build();
|
||||||
|
fragment.show(getChildFragmentManager(), "post_view");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionStart() {
|
||||||
|
if (!onBackPressedCallback.isEnabled()) {
|
||||||
|
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
|
onBackPressedCallback.setEnabled(true);
|
||||||
|
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
|
}
|
||||||
|
if (actionMode == null) {
|
||||||
|
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionChange(final Set<FeedModel> selectedFeedModels) {
|
||||||
|
final String title = getString(R.string.number_selected, selectedFeedModels.size());
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.setTitle(title);
|
||||||
|
}
|
||||||
|
ProfileFragment.this.selectedFeedModels = selectedFeedModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionEnd() {
|
||||||
|
if (onBackPressedCallback.isEnabled()) {
|
||||||
|
onBackPressedCallback.setEnabled(false);
|
||||||
|
onBackPressedCallback.remove();
|
||||||
|
}
|
||||||
|
if (actionMode != null) {
|
||||||
|
actionMode.finish();
|
||||||
|
actionMode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
@ -264,7 +354,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.profile_menu, menu);
|
inflater.inflate(R.menu.profile_menu, menu);
|
||||||
// favMenuItem = menu.findItem(R.id.favourites);
|
|
||||||
blockMenuItem = menu.findItem(R.id.block);
|
blockMenuItem = menu.findItem(R.id.block);
|
||||||
if (blockMenuItem != null) {
|
if (blockMenuItem != null) {
|
||||||
blockMenuItem.setVisible(false);
|
blockMenuItem.setVisible(false);
|
||||||
@ -277,6 +366,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.layout) {
|
||||||
|
showPostsLayoutPreferences();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (item.getItemId() == R.id.restrict) {
|
if (item.getItemId() == R.id.restrict) {
|
||||||
if (!isLoggedIn) return false;
|
if (!isLoggedIn) return false;
|
||||||
final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
|
final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
|
||||||
@ -343,10 +436,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
isPullToRefresh = true;
|
|
||||||
endCursor = null;
|
|
||||||
fetchProfileDetails();
|
fetchProfileDetails();
|
||||||
fetchPosts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -355,14 +445,30 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
if (usernameSettingHandler != null) {
|
if (usernameSettingHandler != null) {
|
||||||
usernameSettingHandler.removeCallbacks(usernameSettingRunnable);
|
usernameSettingHandler.removeCallbacks(usernameSettingRunnable);
|
||||||
}
|
}
|
||||||
if (postsViewModel != null) {
|
|
||||||
postsViewModel.getList().postValue(Collections.emptyList());
|
|
||||||
}
|
|
||||||
if (highlightsViewModel != null) {
|
if (highlightsViewModel != null) {
|
||||||
highlightsViewModel.getList().postValue(Collections.emptyList());
|
highlightsViewModel.getList().postValue(Collections.emptyList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return;
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||||
|
if (downloadFeedModel == null) return;
|
||||||
|
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
|
||||||
|
downloadFeedModel = null;
|
||||||
|
downloadChildPosition = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
|
||||||
|
DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
|
||||||
|
binding.postsRecyclerView.endSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
if (getArguments() != null) {
|
if (getArguments() != null) {
|
||||||
final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments());
|
final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments());
|
||||||
@ -381,7 +487,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
binding.swipeRefreshLayout.setEnabled(true);
|
binding.swipeRefreshLayout.setEnabled(true);
|
||||||
setupPosts();
|
|
||||||
setupHighlights();
|
setupHighlights();
|
||||||
setupCommonListeners();
|
setupCommonListeners();
|
||||||
fetchUsername();
|
fetchUsername();
|
||||||
@ -416,7 +521,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
|
|
||||||
private void fetchProfileDetails() {
|
private void fetchProfileDetails() {
|
||||||
if (TextUtils.isEmpty(username)) return;
|
if (TextUtils.isEmpty(username)) return;
|
||||||
new ProfileFetcher(username.substring(1), profileModel -> {
|
new ProfileFetcher(username.trim().substring(1), profileModel -> {
|
||||||
if (getContext() == null) return;
|
if (getContext() == null) return;
|
||||||
this.profileModel = profileModel;
|
this.profileModel = profileModel;
|
||||||
// final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
// final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||||
@ -440,87 +545,18 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!postsSetupDone) {
|
||||||
|
setupPosts();
|
||||||
|
} else {
|
||||||
|
binding.postsRecyclerView.refresh();
|
||||||
|
}
|
||||||
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
||||||
final String profileId = profileModel.getId();
|
final String profileId = profileModel.getId();
|
||||||
|
|
||||||
final String myId = CookieUtils.getUserIdFromCookie(cookie);
|
final String myId = CookieUtils.getUserIdFromCookie(cookie);
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
storiesService.getUserStory(profileId,
|
fetchStoryAndHighlights(profileId);
|
||||||
profileModel.getUsername(),
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
new ServiceCallback<List<StoryModel>>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final List<StoryModel> storyModels) {
|
|
||||||
if (storyModels != null && !storyModels.isEmpty()) {
|
|
||||||
binding.mainProfileImage.setStoriesBorder();
|
|
||||||
hasStories = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
Log.e(TAG, "Error", t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
new HighlightsFetcher(profileId,
|
|
||||||
result -> {
|
|
||||||
if (result != null) {
|
|
||||||
binding.highlightsList.setVisibility(View.VISIBLE);
|
|
||||||
highlightsViewModel.getList().postValue(result);
|
|
||||||
} else binding.highlightsList.setVisibility(View.GONE);
|
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
if (profileId.equals(myId)) {
|
|
||||||
binding.btnTagged.setVisibility(View.VISIBLE);
|
|
||||||
binding.btnSaved.setVisibility(View.VISIBLE);
|
|
||||||
binding.btnLiked.setVisibility(View.VISIBLE);
|
|
||||||
binding.btnDM.setVisibility(View.GONE);
|
|
||||||
binding.btnSaved.setText(R.string.saved);
|
|
||||||
} else {
|
|
||||||
binding.btnTagged.setVisibility(View.GONE);
|
|
||||||
binding.btnSaved.setVisibility(View.GONE);
|
|
||||||
binding.btnLiked.setVisibility(View.GONE);
|
|
||||||
binding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism?
|
|
||||||
binding.btnFollow.setVisibility(View.VISIBLE);
|
|
||||||
if (profileModel.getFollowing()) {
|
|
||||||
binding.btnFollow.setText(R.string.unfollow);
|
|
||||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
|
||||||
} else if (profileModel.getRequested()) {
|
|
||||||
binding.btnFollow.setText(R.string.cancel);
|
|
||||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
|
||||||
} else {
|
|
||||||
binding.btnFollow.setText(R.string.follow);
|
|
||||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_24);
|
|
||||||
}
|
|
||||||
if (restrictMenuItem != null) {
|
|
||||||
restrictMenuItem.setVisible(true);
|
|
||||||
if (profileModel.getRestricted()) {
|
|
||||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
|
||||||
} else {
|
|
||||||
restrictMenuItem.setTitle(R.string.restrict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
|
|
||||||
if (blockMenuItem != null) {
|
|
||||||
blockMenuItem.setVisible(true);
|
|
||||||
if (profileModel.getBlocked()) {
|
|
||||||
blockMenuItem.setTitle(R.string.unblock);
|
|
||||||
} else {
|
|
||||||
blockMenuItem.setTitle(R.string.block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!profileModel.isReallyPrivate() && restrictMenuItem != null) {
|
|
||||||
restrictMenuItem.setVisible(true);
|
|
||||||
if (profileModel.getRestricted()) {
|
|
||||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
|
||||||
} else {
|
|
||||||
restrictMenuItem.setTitle(R.string.restrict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
setupButtons(profileId, myId);
|
||||||
if (!profileId.equals(myId)) {
|
if (!profileId.equals(myId)) {
|
||||||
binding.favCb.setVisibility(View.VISIBLE);
|
binding.favCb.setVisibility(View.VISIBLE);
|
||||||
final boolean isFav = Utils.dataBox.getFavorite(username.substring(1), FavoriteType.USER) != null;
|
final boolean isFav = Utils.dataBox.getFavorite(username.substring(1), FavoriteType.USER) != null;
|
||||||
@ -529,7 +565,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
} else {
|
} else {
|
||||||
binding.favCb.setVisibility(View.GONE);
|
binding.favCb.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
binding.mainProfileImage.setImageURI(profileModel.getSdProfilePic());
|
binding.mainProfileImage.setImageURI(profileModel.getHdProfilePic());
|
||||||
|
|
||||||
final long followersCount = profileModel.getFollowersCount();
|
final long followersCount = profileModel.getFollowersCount();
|
||||||
final long followingCount = profileModel.getFollowingCount();
|
final long followingCount = profileModel.getFollowingCount();
|
||||||
@ -561,35 +597,40 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
binding.mainFullName.setText(TextUtils.isEmpty(profileModel.getName()) ? profileModel.getUsername()
|
binding.mainFullName.setText(TextUtils.isEmpty(profileModel.getName()) ? profileModel.getUsername()
|
||||||
: profileModel.getName());
|
: profileModel.getName());
|
||||||
|
|
||||||
CharSequence biography = profileModel.getBiography();
|
final String biography = profileModel.getBiography();
|
||||||
binding.mainBiography.setCaptionIsExpandable(true);
|
if (!TextUtils.isEmpty(biography)) {
|
||||||
binding.mainBiography.setCaptionIsExpanded(true);
|
|
||||||
if (TextUtils.hasMentions(biography)) {
|
|
||||||
biography = TextUtils.getMentionText(biography);
|
|
||||||
binding.mainBiography.setText(biography, TextView.BufferType.SPANNABLE);
|
|
||||||
binding.mainBiography.setMentionClickListener(mentionClickListener);
|
|
||||||
} else {
|
|
||||||
binding.mainBiography.setText(biography);
|
binding.mainBiography.setText(biography);
|
||||||
binding.mainBiography.setMentionClickListener(null);
|
binding.mainBiography.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);
|
||||||
|
});
|
||||||
|
binding.mainBiography.addOnMentionClickListener(autoLinkItem -> {
|
||||||
|
final String originalText = autoLinkItem.getOriginalText().trim();
|
||||||
|
navigateToProfile(originalText);
|
||||||
|
});
|
||||||
|
binding.mainBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(getContext(),
|
||||||
|
autoLinkItem.getOriginalText().trim()));
|
||||||
|
binding.mainBiography.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim()));
|
||||||
|
binding.mainBiography.setOnLongClickListener(v -> {
|
||||||
|
if (context != null) Utils.copyText(context, biography);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
binding.mainBiography.setOnLongClickListener(v -> {
|
|
||||||
if (context != null) Utils.copyText(context, profileModel.getBiography());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
final String url = profileModel.getUrl();
|
final String url = profileModel.getUrl();
|
||||||
if (TextUtils.isEmpty(url)) {
|
if (TextUtils.isEmpty(url)) {
|
||||||
binding.mainUrl.setVisibility(View.GONE);
|
binding.mainUrl.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
binding.mainUrl.setVisibility(View.VISIBLE);
|
binding.mainUrl.setVisibility(View.VISIBLE);
|
||||||
binding.mainUrl.setText(TextUtils.getSpannableUrl(url));
|
binding.mainUrl.setText(url);
|
||||||
binding.mainUrl.setMovementMethod(LinkMovementMethod.getInstance());
|
binding.mainUrl.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim()));
|
||||||
|
binding.mainUrl.setOnLongClickListener(v -> {
|
||||||
|
if (context != null) Utils.copyText(context, url);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.mainFullName.setSelected(true);
|
|
||||||
binding.mainBiography.setEnabled(true);
|
|
||||||
binding.mainBiography.setClickable(true);
|
|
||||||
|
|
||||||
if (!profileModel.isReallyPrivate()) {
|
if (!profileModel.isReallyPrivate()) {
|
||||||
binding.mainFollowing.setClickable(true);
|
binding.mainFollowing.setClickable(true);
|
||||||
binding.mainFollowers.setClickable(true);
|
binding.mainFollowers.setClickable(true);
|
||||||
@ -608,7 +649,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
binding.mainPosts.setVisibility(View.VISIBLE);
|
binding.postsRecyclerView.setVisibility(View.VISIBLE);
|
||||||
fetchPosts();
|
fetchPosts();
|
||||||
} else {
|
} else {
|
||||||
binding.mainFollowers.setClickable(false);
|
binding.mainFollowers.setClickable(false);
|
||||||
@ -618,15 +659,97 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
binding.privatePage1.setImageResource(R.drawable.lock);
|
binding.privatePage1.setImageResource(R.drawable.lock);
|
||||||
binding.privatePage2.setText(R.string.priv_acc);
|
binding.privatePage2.setText(R.string.priv_acc);
|
||||||
binding.privatePage.setVisibility(View.VISIBLE);
|
binding.privatePage.setVisibility(View.VISIBLE);
|
||||||
binding.mainPosts.setVisibility(View.GONE);
|
binding.postsRecyclerView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupButtons(final String profileId, final String myId) {
|
||||||
|
if (isLoggedIn) {
|
||||||
|
if (profileId.equals(myId)) {
|
||||||
|
binding.btnTagged.setVisibility(View.VISIBLE);
|
||||||
|
binding.btnSaved.setVisibility(View.VISIBLE);
|
||||||
|
binding.btnLiked.setVisibility(View.VISIBLE);
|
||||||
|
binding.btnDM.setVisibility(View.GONE);
|
||||||
|
binding.btnSaved.setText(R.string.saved);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.btnTagged.setVisibility(View.GONE);
|
||||||
|
binding.btnSaved.setVisibility(View.GONE);
|
||||||
|
binding.btnLiked.setVisibility(View.GONE);
|
||||||
|
binding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism?
|
||||||
|
binding.btnFollow.setVisibility(View.VISIBLE);
|
||||||
|
if (profileModel.getFollowing()) {
|
||||||
|
binding.btnFollow.setText(R.string.unfollow);
|
||||||
|
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||||
|
} else if (profileModel.getRequested()) {
|
||||||
|
binding.btnFollow.setText(R.string.cancel);
|
||||||
|
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||||
|
} else {
|
||||||
|
binding.btnFollow.setText(R.string.follow);
|
||||||
|
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_24);
|
||||||
|
}
|
||||||
|
if (restrictMenuItem != null) {
|
||||||
|
restrictMenuItem.setVisible(true);
|
||||||
|
if (profileModel.getRestricted()) {
|
||||||
|
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||||
|
} else {
|
||||||
|
restrictMenuItem.setTitle(R.string.restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
|
||||||
|
if (blockMenuItem != null) {
|
||||||
|
blockMenuItem.setVisible(true);
|
||||||
|
if (profileModel.getBlocked()) {
|
||||||
|
blockMenuItem.setTitle(R.string.unblock);
|
||||||
|
} else {
|
||||||
|
blockMenuItem.setTitle(R.string.block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!profileModel.isReallyPrivate() && restrictMenuItem != null) {
|
||||||
|
restrictMenuItem.setVisible(true);
|
||||||
|
if (profileModel.getRestricted()) {
|
||||||
|
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||||
|
} else {
|
||||||
|
restrictMenuItem.setTitle(R.string.restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchStoryAndHighlights(final String profileId) {
|
||||||
|
storiesService.getUserStory(profileId,
|
||||||
|
profileModel.getUsername(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
new ServiceCallback<List<StoryModel>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final List<StoryModel> storyModels) {
|
||||||
|
if (storyModels != null && !storyModels.isEmpty()) {
|
||||||
|
binding.mainProfileImage.setStoriesBorder();
|
||||||
|
hasStories = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
Log.e(TAG, "Error", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
new HighlightsFetcher(profileId,
|
||||||
|
result -> {
|
||||||
|
highlightsFetching = false;
|
||||||
|
if (result != null) {
|
||||||
|
binding.highlightsList.setVisibility(View.VISIBLE);
|
||||||
|
highlightsViewModel.getList().postValue(result);
|
||||||
|
} else binding.highlightsList.setVisibility(View.GONE);
|
||||||
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
private void setupCommonListeners() {
|
private void setupCommonListeners() {
|
||||||
|
|
||||||
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||||
// final boolean isSelf = isLoggedIn && profileModel != null && userIdFromCookie != null && userIdFromCookie
|
|
||||||
// .equals(profileModel.getId());
|
|
||||||
binding.btnFollow.setOnClickListener(v -> {
|
binding.btnFollow.setOnClickListener(v -> {
|
||||||
if (profileModel.getFollowing() || profileModel.getRequested()) {
|
if (profileModel.getFollowing() || profileModel.getRequested()) {
|
||||||
friendshipService.unfollow(
|
friendshipService.unfollow(
|
||||||
@ -636,7 +759,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||||
Log.d(TAG, "Unfollow success: " + result);
|
// Log.d(TAG, "Unfollow success: " + result);
|
||||||
onRefresh();
|
onRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,7 +776,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
new ServiceCallback<FriendshipRepoChangeRootResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
public void onSuccess(final FriendshipRepoChangeRootResponse result) {
|
||||||
Log.d(TAG, "Follow success: " + result);
|
// Log.d(TAG, "Follow success: " + result);
|
||||||
onRefresh();
|
onRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -682,12 +805,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
PostItemType.TAGGED);
|
PostItemType.TAGGED);
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
});
|
});
|
||||||
binding.btnDM.setOnClickListener(v -> {
|
binding.btnDM.setOnClickListener(v -> new CreateThreadAction(cookie, profileModel.getId(), threadId -> {
|
||||||
new CreateThreadAction(cookie, profileModel.getId(), threadId -> {
|
final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToDMThreadFragment(threadId, profileModel.getUsername());
|
||||||
final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToDMThreadFragment(threadId, profileModel.getUsername());
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
}).execute());
|
||||||
}).execute();
|
|
||||||
});
|
|
||||||
binding.mainProfileImage.setOnClickListener(v -> {
|
binding.mainProfileImage.setOnClickListener(v -> {
|
||||||
if (!hasStories) {
|
if (!hasStories) {
|
||||||
// show profile pic
|
// show profile pic
|
||||||
@ -760,8 +881,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(), username, profileModel.getHdProfilePic());
|
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getId(), username, profileModel.getHdProfilePic());
|
||||||
final FragmentTransaction ft = fragmentManager.beginTransaction();
|
final FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
.add(fragment, "profilePicDialog")
|
.add(fragment, "profilePicDialog")
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,62 +894,20 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupPosts() {
|
private void setupPosts() {
|
||||||
postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
|
binding.postsRecyclerView.setViewModelStoreOwner(this)
|
||||||
final Context context = getContext();
|
.setLifeCycleOwner(this)
|
||||||
if (context == null) return;
|
.setPostFetchService(new ProfilePostFetchService(profileModel))
|
||||||
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_PROFILE_POSTS_LAYOUT)))
|
||||||
binding.mainPosts.setLayoutManager(layoutManager);
|
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||||
binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
.setFeedItemCallback(feedItemCallback)
|
||||||
postsAdapter = new PostsAdapter((postModel, position) -> {
|
.setSelectionModeCallback(selectionModeCallback)
|
||||||
if (postsAdapter.isSelecting()) {
|
.init();
|
||||||
if (actionMode == null) return;
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
final String title = getString(R.string.number_selected,
|
postsSetupDone = true;
|
||||||
postsAdapter.getSelectedModels().size());
|
}
|
||||||
actionMode.setTitle(title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (checkAndResetAction()) return;
|
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
|
||||||
if (postModels == null || postModels.size() == 0) return;
|
|
||||||
if (postModels.get(0) == null) return;
|
|
||||||
final String postId = isLoggedIn ? postModels.get(0).getPostId() : postModels.get(0).getShortCode();
|
|
||||||
final boolean isId = isLoggedIn && postId != null;
|
|
||||||
final String[] idsOrShortCodes = new String[postModels.size()];
|
|
||||||
for (int i = 0; i < postModels.size(); i++) {
|
|
||||||
idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
|
|
||||||
: postModels.get(i).getShortCode();
|
|
||||||
}
|
|
||||||
final NavDirections action = ProfileFragmentDirections.actionGlobalPostViewFragment(
|
|
||||||
position,
|
|
||||||
idsOrShortCodes,
|
|
||||||
isId);
|
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
|
||||||
|
|
||||||
}, (model, position) -> {
|
private void updateSwipeRefreshState() {
|
||||||
if (!postsAdapter.isSelecting()) {
|
binding.swipeRefreshLayout.setRefreshing(binding.postsRecyclerView.isFetching() || highlightsFetching);
|
||||||
checkAndResetAction();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
|
||||||
onBackPressedCallback.setEnabled(true);
|
|
||||||
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
|
||||||
final String title = getString(R.string.number_selected, 1);
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
|
|
||||||
binding.mainPosts.setAdapter(postsAdapter);
|
|
||||||
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
|
||||||
if (!hasNextPage) return;
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
fetchPosts();
|
|
||||||
endCursor = null;
|
|
||||||
});
|
|
||||||
binding.mainPosts.addOnScrollListener(lazyLoader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupHighlights() {
|
private void setupHighlights() {
|
||||||
@ -847,36 +926,24 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void fetchPosts() {
|
private void fetchPosts() {
|
||||||
stopCurrentExecutor();
|
// stopCurrentExecutor();
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
currentlyExecuting = new PostsFetcher(profileModel.getId(), PostItemType.MAIN, endCursor, postsFetchListener)
|
// currentlyExecuting = new PostsFetcher(profileModel.getId(), PostItemType.MAIN, endCursor, postsFetchListener)
|
||||||
.setUsername(profileModel.getUsername())
|
// .setUsername(profileModel.getUsername())
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
// .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopCurrentExecutor() {
|
private void navigateToProfile(final String username) {
|
||||||
if (currentlyExecuting != null) {
|
final NavController navController = NavHostFragment.findNavController(this);
|
||||||
try {
|
final Bundle bundle = new Bundle();
|
||||||
currentlyExecuting.cancel(true);
|
bundle.putString("username", username);
|
||||||
} catch (final Exception e) {
|
navController.navigate(R.id.action_global_profileFragment, bundle);
|
||||||
if (logCollector != null) logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor");
|
|
||||||
Log.e(TAG, "", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAndResetAction() {
|
private void showPostsLayoutPreferences() {
|
||||||
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||||
return false;
|
Constants.PREF_PROFILE_POSTS_LAYOUT,
|
||||||
}
|
preferences -> new Handler().postDelayed(() -> binding.postsRecyclerView.setLayoutPreferences(preferences), 200));
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||||
onBackPressedCallback.setEnabled(false);
|
|
||||||
onBackPressedCallback.remove();
|
|
||||||
}
|
|
||||||
if (actionMode != null) {
|
|
||||||
actionMode.finish();
|
|
||||||
actionMode = null;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,24 +26,26 @@ public class AboutFragment extends BasePreferencesFragment {
|
|||||||
generalCategory.addPreference(getRepoPreference());
|
generalCategory.addPreference(getRepoPreference());
|
||||||
generalCategory.addPreference(getFeedbackPreference());
|
generalCategory.addPreference(getFeedbackPreference());
|
||||||
|
|
||||||
final PreferenceCategory thirdPartyCategory = new PreferenceCategory(context);
|
|
||||||
screen.addPreference(thirdPartyCategory);
|
|
||||||
thirdPartyCategory.setTitle(R.string.about_category_3pt);
|
|
||||||
//thirdPartyCategory.setSummary(R.string.about_category_3pt_summary);
|
|
||||||
thirdPartyCategory.setIconSpaceReserved(false);
|
|
||||||
// alphabetical order!!!
|
|
||||||
thirdPartyCategory.addPreference(getExoPlayerPreference());
|
|
||||||
thirdPartyCategory.addPreference(getFrescoPreference());
|
|
||||||
thirdPartyCategory.addPreference(getJsoupPreference());
|
|
||||||
thirdPartyCategory.addPreference(getMDIPreference());
|
|
||||||
thirdPartyCategory.addPreference(getRetrofitPreference());
|
|
||||||
|
|
||||||
final PreferenceCategory licenseCategory = new PreferenceCategory(context);
|
final PreferenceCategory licenseCategory = new PreferenceCategory(context);
|
||||||
screen.addPreference(licenseCategory);
|
screen.addPreference(licenseCategory);
|
||||||
licenseCategory.setTitle(R.string.about_category_license);
|
licenseCategory.setTitle(R.string.about_category_license);
|
||||||
licenseCategory.setIconSpaceReserved(false);
|
licenseCategory.setIconSpaceReserved(false);
|
||||||
licenseCategory.addPreference(getLicensePreference());
|
licenseCategory.addPreference(getLicensePreference());
|
||||||
licenseCategory.addPreference(getLiabilityPreference());
|
licenseCategory.addPreference(getLiabilityPreference());
|
||||||
|
|
||||||
|
final PreferenceCategory thirdPartyCategory = new PreferenceCategory(context);
|
||||||
|
screen.addPreference(thirdPartyCategory);
|
||||||
|
thirdPartyCategory.setTitle(R.string.about_category_3pt);
|
||||||
|
//thirdPartyCategory.setSummary(R.string.about_category_3pt_summary);
|
||||||
|
thirdPartyCategory.setIconSpaceReserved(false);
|
||||||
|
// alphabetical order!!!
|
||||||
|
thirdPartyCategory.addPreference(getAutolinkPreference());
|
||||||
|
thirdPartyCategory.addPreference(getExoPlayerPreference());
|
||||||
|
thirdPartyCategory.addPreference(getFrescoPreference());
|
||||||
|
thirdPartyCategory.addPreference(getIcafePreference());
|
||||||
|
thirdPartyCategory.addPreference(getJsoupPreference());
|
||||||
|
thirdPartyCategory.addPreference(getMDIPreference());
|
||||||
|
thirdPartyCategory.addPreference(getRetrofitPreference());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Preference getDocsPreference() {
|
private Preference getDocsPreference() {
|
||||||
@ -55,7 +57,7 @@ public class AboutFragment extends BasePreferencesFragment {
|
|||||||
preference.setIconSpaceReserved(false);
|
preference.setIconSpaceReserved(false);
|
||||||
preference.setOnPreferenceClickListener(p -> {
|
preference.setOnPreferenceClickListener(p -> {
|
||||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse("https://instagrabber.austinhuang.me"));
|
intent.setData(Uri.parse("https://barinsta.austinhuang.me"));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -71,7 +73,7 @@ public class AboutFragment extends BasePreferencesFragment {
|
|||||||
preference.setIconSpaceReserved(false);
|
preference.setIconSpaceReserved(false);
|
||||||
preference.setOnPreferenceClickListener(p -> {
|
preference.setOnPreferenceClickListener(p -> {
|
||||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse("https://github.com/austinhuang0131/instagrabber"));
|
intent.setData(Uri.parse("https://github.com/austinhuang0131/barinsta"));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -174,6 +176,38 @@ public class AboutFragment extends BasePreferencesFragment {
|
|||||||
return preference;
|
return preference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Preference getAutolinkPreference() {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return null;
|
||||||
|
final Preference preference = new Preference(context);
|
||||||
|
preference.setTitle("AutoLinkTextViewV2");
|
||||||
|
preference.setSummary("Copyright (C) 2019 Arman Chatikyan. Apache Version 2.0.");
|
||||||
|
preference.setIconSpaceReserved(false);
|
||||||
|
preference.setOnPreferenceClickListener(p -> {
|
||||||
|
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse("https://github.com/armcha/AutoLinkTextViewV2"));
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return preference;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Preference getIcafePreference() {
|
||||||
|
final Context context = getContext();
|
||||||
|
if (context == null) return null;
|
||||||
|
final Preference preference = new Preference(context);
|
||||||
|
preference.setTitle("ICAFE");
|
||||||
|
preference.setSummary("Copyright (C) 2014-2019 Wen Yu. Eclipse Version 2.0.");
|
||||||
|
preference.setIconSpaceReserved(false);
|
||||||
|
preference.setOnPreferenceClickListener(p -> {
|
||||||
|
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse("https://github.com/dragon66/icafe"));
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return preference;
|
||||||
|
}
|
||||||
|
|
||||||
private Preference getLicensePreference() {
|
private Preference getLicensePreference() {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return null;
|
if (context == null) return null;
|
||||||
|
@ -128,7 +128,7 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
|||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
final int resourceId = mainNavIds.getResourceId(i, -1);
|
final int resourceId = mainNavIds.getResourceId(i, -1);
|
||||||
if (resourceId < 0) continue;
|
if (resourceId < 0) continue;
|
||||||
values[i] = String.valueOf(resourceId);
|
values[i] = getResources().getResourceEntryName(resourceId);
|
||||||
}
|
}
|
||||||
mainNavIds.recycle();
|
mainNavIds.recycle();
|
||||||
preference.setKey(Constants.DEFAULT_TAB);
|
preference.setKey(Constants.DEFAULT_TAB);
|
||||||
|
@ -51,7 +51,7 @@ public class ThemePreferencesFragment extends BasePreferencesFragment {
|
|||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
final int resourceId = lightThemeValues.getResourceId(i, -1);
|
final int resourceId = lightThemeValues.getResourceId(i, -1);
|
||||||
if (resourceId < 0) continue;
|
if (resourceId < 0) continue;
|
||||||
values[i] = String.valueOf(resourceId);
|
values[i] = getResources().getResourceEntryName(resourceId);
|
||||||
}
|
}
|
||||||
lightThemeValues.recycle();
|
lightThemeValues.recycle();
|
||||||
preference.setKey(Constants.PREF_LIGHT_THEME);
|
preference.setKey(Constants.PREF_LIGHT_THEME);
|
||||||
@ -76,7 +76,7 @@ public class ThemePreferencesFragment extends BasePreferencesFragment {
|
|||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
final int resourceId = darkThemeValues.getResourceId(i, -1);
|
final int resourceId = darkThemeValues.getResourceId(i, -1);
|
||||||
if (resourceId < 0) continue;
|
if (resourceId < 0) continue;
|
||||||
values[i] = String.valueOf(resourceId);
|
values[i] = getResources().getResourceEntryName(resourceId);
|
||||||
}
|
}
|
||||||
darkThemeValues.recycle();
|
darkThemeValues.recycle();
|
||||||
preference.setKey(Constants.PREF_DARK_THEME);
|
preference.setKey(Constants.PREF_DARK_THEME);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user