mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-11-04 05:25:35 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master'
This commit is contained in:
		
						commit
						0535df25b0
					
				@ -1,129 +0,0 @@
 | 
			
		||||
package awais.instagrabber.activities;
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint;
 | 
			
		||||
import android.content.ActivityNotFoundException;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.provider.DocumentsContract;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
import androidx.lifecycle.ViewModelProvider;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.io.StringWriter;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.R;
 | 
			
		||||
import awais.instagrabber.databinding.ActivityDirectorySelectBinding;
 | 
			
		||||
import awais.instagrabber.dialogs.ConfirmDialogFragment;
 | 
			
		||||
import awais.instagrabber.utils.AppExecutors;
 | 
			
		||||
import awais.instagrabber.viewmodels.DirectorySelectActivityViewModel;
 | 
			
		||||
 | 
			
		||||
public class DirectorySelectActivity extends BaseLanguageActivity {
 | 
			
		||||
    private static final String TAG = DirectorySelectActivity.class.getSimpleName();
 | 
			
		||||
    public static final int SELECT_DIR_REQUEST_CODE = 0x01;
 | 
			
		||||
    private static final int ERROR_REQUEST_CODE = 0x02;
 | 
			
		||||
 | 
			
		||||
    private Uri initialUri;
 | 
			
		||||
    private ActivityDirectorySelectBinding binding;
 | 
			
		||||
    private DirectorySelectActivityViewModel viewModel;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(@Nullable final Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        binding = ActivityDirectorySelectBinding.inflate(getLayoutInflater());
 | 
			
		||||
        setContentView(binding.getRoot());
 | 
			
		||||
        viewModel = new ViewModelProvider(this).get(DirectorySelectActivityViewModel.class);
 | 
			
		||||
        setupObservers();
 | 
			
		||||
        binding.selectDir.setOnClickListener(v -> openDirectoryChooser());
 | 
			
		||||
        AppExecutors.INSTANCE.getMainThread().execute(() -> viewModel.setInitialUri(getIntent()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupObservers() {
 | 
			
		||||
        viewModel.getMessage().observe(this, message -> binding.message.setText(message));
 | 
			
		||||
        viewModel.getPrevUri().observe(this, prevUri -> {
 | 
			
		||||
            if (prevUri == null) {
 | 
			
		||||
                binding.prevUri.setVisibility(View.GONE);
 | 
			
		||||
                binding.message2.setVisibility(View.GONE);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            binding.prevUri.setText(prevUri);
 | 
			
		||||
            binding.prevUri.setVisibility(View.VISIBLE);
 | 
			
		||||
            binding.message2.setVisibility(View.VISIBLE);
 | 
			
		||||
        });
 | 
			
		||||
        viewModel.getDirSuccess().observe(this, success -> binding.selectDir.setVisibility(success ? View.GONE : View.VISIBLE));
 | 
			
		||||
        viewModel.isLoading().observe(this, loading -> {
 | 
			
		||||
            binding.message.setVisibility(loading ? View.GONE : View.VISIBLE);
 | 
			
		||||
            binding.loadingIndicator.setVisibility(loading ? View.VISIBLE : View.GONE);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void openDirectoryChooser() {
 | 
			
		||||
        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialUri != null) {
 | 
			
		||||
            intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri);
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            startActivityForResult(intent, SELECT_DIR_REQUEST_CODE);
 | 
			
		||||
        } catch (ActivityNotFoundException e) {
 | 
			
		||||
            Log.e(TAG, "openDirectoryChooser: ", e);
 | 
			
		||||
            showErrorDialog(getString(R.string.no_directory_picker_activity));
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.e(TAG, "openDirectoryChooser: ", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("StringFormatInvalid")
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
 | 
			
		||||
        super.onActivityResult(requestCode, resultCode, data);
 | 
			
		||||
        if (requestCode != SELECT_DIR_REQUEST_CODE) return;
 | 
			
		||||
        if (resultCode != RESULT_OK) {
 | 
			
		||||
            showErrorDialog(getString(R.string.select_a_folder));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (data == null || data.getData() == null) {
 | 
			
		||||
            showErrorDialog(getString(R.string.select_a_folder));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!"com.android.externalstorage.documents".equals(data.getData().getAuthority())) {
 | 
			
		||||
            showErrorDialog(getString(R.string.dir_select_no_download_folder, data.getData().getAuthority()));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        AppExecutors.INSTANCE.getMainThread().execute(() -> {
 | 
			
		||||
            try {
 | 
			
		||||
                viewModel.setupSelectedDir(data);
 | 
			
		||||
                final Intent intent = new Intent(this, MainActivity.class);
 | 
			
		||||
                startActivity(intent);
 | 
			
		||||
                finish();
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                // Should not come to this point.
 | 
			
		||||
                // If it does, we have to show this error to the user so that they can report it.
 | 
			
		||||
                try (final StringWriter sw = new StringWriter();
 | 
			
		||||
                     final PrintWriter pw = new PrintWriter(sw)) {
 | 
			
		||||
                    e.printStackTrace(pw);
 | 
			
		||||
                    showErrorDialog("Please report this error to the developers:\n\n" + sw.toString());
 | 
			
		||||
                } catch (IOException ioException) {
 | 
			
		||||
                    Log.e(TAG, "onActivityResult: ", ioException);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void showErrorDialog(@NonNull final String message) {
 | 
			
		||||
        final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(
 | 
			
		||||
                ERROR_REQUEST_CODE,
 | 
			
		||||
                R.string.error,
 | 
			
		||||
                message,
 | 
			
		||||
                R.string.ok,
 | 
			
		||||
                0,
 | 
			
		||||
                0
 | 
			
		||||
        );
 | 
			
		||||
        dialogFragment.show(getSupportFragmentManager(), ConfirmDialogFragment.class.getSimpleName());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,132 @@
 | 
			
		||||
package awais.instagrabber.activities
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint
 | 
			
		||||
import android.content.ActivityNotFoundException
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.provider.DocumentsContract
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import android.view.View
 | 
			
		||||
import androidx.activity.viewModels
 | 
			
		||||
import awais.instagrabber.R
 | 
			
		||||
import awais.instagrabber.databinding.ActivityDirectorySelectBinding
 | 
			
		||||
import awais.instagrabber.dialogs.ConfirmDialogFragment
 | 
			
		||||
import awais.instagrabber.utils.AppExecutors.mainThread
 | 
			
		||||
import awais.instagrabber.utils.Constants
 | 
			
		||||
import awais.instagrabber.utils.extensions.TAG
 | 
			
		||||
import awais.instagrabber.viewmodels.DirectorySelectActivityViewModel
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.io.PrintWriter
 | 
			
		||||
import java.io.StringWriter
 | 
			
		||||
 | 
			
		||||
class DirectorySelectActivity : BaseLanguageActivity() {
 | 
			
		||||
    private var initialUri: Uri? = null
 | 
			
		||||
 | 
			
		||||
    private lateinit var binding: ActivityDirectorySelectBinding
 | 
			
		||||
 | 
			
		||||
    private val viewModel: DirectorySelectActivityViewModel by viewModels()
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        binding = ActivityDirectorySelectBinding.inflate(layoutInflater)
 | 
			
		||||
        setContentView(binding.root)
 | 
			
		||||
        val intent = intent
 | 
			
		||||
        viewModel.setInitialUri(intent)
 | 
			
		||||
        setupObservers()
 | 
			
		||||
        binding.selectDir.setOnClickListener { openDirectoryChooser() }
 | 
			
		||||
        initialUri = intent.getParcelableExtra(Constants.EXTRA_INITIAL_URI)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setupObservers() {
 | 
			
		||||
        viewModel.message.observe(this, { message: String? -> binding.message.text = message })
 | 
			
		||||
        viewModel.prevUri.observe(this, { prevUri: String? ->
 | 
			
		||||
            if (prevUri == null) {
 | 
			
		||||
                binding.prevUri.visibility = View.GONE
 | 
			
		||||
                binding.message2.visibility = View.GONE
 | 
			
		||||
                return@observe
 | 
			
		||||
            }
 | 
			
		||||
            binding.prevUri.text = prevUri
 | 
			
		||||
            binding.prevUri.visibility = View.VISIBLE
 | 
			
		||||
            binding.message2.visibility = View.VISIBLE
 | 
			
		||||
        })
 | 
			
		||||
        viewModel.dirSuccess.observe(this, { success: Boolean -> binding.selectDir.visibility = if (success) View.GONE else View.VISIBLE })
 | 
			
		||||
        viewModel.loading.observe(this, { loading: Boolean ->
 | 
			
		||||
            binding.message.visibility = if (loading) View.GONE else View.VISIBLE
 | 
			
		||||
            binding.loadingIndicator.visibility = if (loading) View.VISIBLE else View.GONE
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun openDirectoryChooser() {
 | 
			
		||||
        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialUri != null) {
 | 
			
		||||
            intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri)
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            startActivityForResult(intent, SELECT_DIR_REQUEST_CODE)
 | 
			
		||||
        } catch (e: ActivityNotFoundException) {
 | 
			
		||||
            Log.e(TAG, "openDirectoryChooser: ", e)
 | 
			
		||||
            showErrorDialog(getString(R.string.no_directory_picker_activity))
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            Log.e(TAG, "openDirectoryChooser: ", e)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("StringFormatInvalid")
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        super.onActivityResult(requestCode, resultCode, data)
 | 
			
		||||
        if (requestCode != SELECT_DIR_REQUEST_CODE) return
 | 
			
		||||
        if (resultCode != RESULT_OK) {
 | 
			
		||||
            showErrorDialog(getString(R.string.select_a_folder))
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        if (data == null || data.data == null) {
 | 
			
		||||
            showErrorDialog(getString(R.string.select_a_folder))
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        val authority = data.data?.authority
 | 
			
		||||
        if ("com.android.externalstorage.documents" != authority) {
 | 
			
		||||
            showErrorDialog(getString(R.string.dir_select_no_download_folder, authority))
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        mainThread.execute({
 | 
			
		||||
            try {
 | 
			
		||||
                viewModel.setupSelectedDir(data)
 | 
			
		||||
                val intent = Intent(this, MainActivity::class.java)
 | 
			
		||||
                startActivity(intent)
 | 
			
		||||
                finish()
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                // Should not come to this point.
 | 
			
		||||
                // If it does, we have to show this error to the user so that they can report it.
 | 
			
		||||
                try {
 | 
			
		||||
                    StringWriter().use { sw ->
 | 
			
		||||
                        PrintWriter(sw).use { pw ->
 | 
			
		||||
                            e.printStackTrace(pw)
 | 
			
		||||
                            showErrorDialog("Please report this error to the developers:\n\n$sw")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (ioException: IOException) {
 | 
			
		||||
                    Log.e(TAG, "onActivityResult: ", ioException)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }, 500)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun showErrorDialog(message: String) {
 | 
			
		||||
        val dialogFragment = ConfirmDialogFragment.newInstance(
 | 
			
		||||
            ERROR_REQUEST_CODE,
 | 
			
		||||
            R.string.error,
 | 
			
		||||
            message,
 | 
			
		||||
            R.string.ok,
 | 
			
		||||
            0,
 | 
			
		||||
            0
 | 
			
		||||
        )
 | 
			
		||||
        dialogFragment.show(supportFragmentManager, ConfirmDialogFragment::class.java.simpleName)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val SELECT_DIR_REQUEST_CODE = 0x01
 | 
			
		||||
        private const val ERROR_REQUEST_CODE = 0x02
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -11,6 +11,7 @@ import androidx.recyclerview.widget.ListAdapter;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
 | 
			
		||||
@ -24,12 +25,12 @@ public final class NotificationsAdapter extends ListAdapter<Notification, Notifi
 | 
			
		||||
    private static final DiffUtil.ItemCallback<Notification> DIFF_CALLBACK = new DiffUtil.ItemCallback<Notification>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean areItemsTheSame(final Notification oldItem, final Notification newItem) {
 | 
			
		||||
            return oldItem.getPk().equals(newItem.getPk());
 | 
			
		||||
            return Objects.requireNonNull(oldItem.getPk()).equals(newItem.getPk());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean areContentsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
 | 
			
		||||
            return oldItem.getPk().equals(newItem.getPk()) && oldItem.getType() == newItem.getType();
 | 
			
		||||
            return Objects.requireNonNull(oldItem.getPk()).equals(newItem.getPk()) && Objects.equals(oldItem.getType(), newItem.getType());
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -72,8 +73,8 @@ public final class NotificationsAdapter extends ListAdapter<Notification, Notifi
 | 
			
		||||
 | 
			
		||||
    private List<Notification> sort(final List<Notification> list) {
 | 
			
		||||
        final List<Notification> listCopy = new ArrayList<>(list).stream()
 | 
			
		||||
                .filter(i -> i.getType() != null)
 | 
			
		||||
                .collect(Collectors.toList());
 | 
			
		||||
                                                                 .filter(i -> i.getType() != null)
 | 
			
		||||
                                                                 .collect(Collectors.toList());
 | 
			
		||||
        Collections.sort(listCopy, (o1, o2) -> {
 | 
			
		||||
            // keep requests at top
 | 
			
		||||
            if (o1.getType() == o2.getType()
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,9 @@ public class DirectItemLinkViewHolder extends DirectItemViewHolder {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
 | 
			
		||||
        final DirectItemLink link = item.getLink();
 | 
			
		||||
        if (link == null) return;
 | 
			
		||||
        final DirectItemLinkContext linkContext = link.getLinkContext();
 | 
			
		||||
        if (linkContext == null) return;
 | 
			
		||||
        final String linkImageUrl = linkContext.getLinkImageUrl();
 | 
			
		||||
        if (TextUtils.isEmpty(linkImageUrl)) {
 | 
			
		||||
            binding.preview.setVisibility(View.GONE);
 | 
			
		||||
 | 
			
		||||
@ -1,216 +0,0 @@
 | 
			
		||||
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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,180 @@
 | 
			
		||||
package awais.instagrabber.dialogs
 | 
			
		||||
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.content.DialogInterface
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.CompoundButton
 | 
			
		||||
import androidx.fragment.app.DialogFragment
 | 
			
		||||
import awais.instagrabber.R
 | 
			
		||||
import awais.instagrabber.databinding.DialogPostLayoutPreferencesBinding
 | 
			
		||||
import awais.instagrabber.models.PostsLayoutPreferences
 | 
			
		||||
import awais.instagrabber.models.PostsLayoutPreferences.PostsLayoutType
 | 
			
		||||
import awais.instagrabber.models.PostsLayoutPreferences.ProfilePicSize
 | 
			
		||||
import awais.instagrabber.utils.Utils
 | 
			
		||||
import com.google.android.material.button.MaterialButtonToggleGroup
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
 | 
			
		||||
class PostsLayoutPreferencesDialogFragment(
 | 
			
		||||
    private val layoutPreferenceKey: String,
 | 
			
		||||
    private val onApplyListener: OnApplyListener
 | 
			
		||||
) : DialogFragment() {
 | 
			
		||||
 | 
			
		||||
    private lateinit var binding: DialogPostLayoutPreferencesBinding
 | 
			
		||||
 | 
			
		||||
    private val preferencesBuilder: PostsLayoutPreferences.Builder
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        val preferences = PostsLayoutPreferences.fromJson(Utils.settingsHelper.getString(layoutPreferenceKey))
 | 
			
		||||
        preferencesBuilder = PostsLayoutPreferences.builder().mergeFrom(preferences)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
 | 
			
		||||
        binding = DialogPostLayoutPreferencesBinding.inflate(layoutInflater)
 | 
			
		||||
        init()
 | 
			
		||||
        return MaterialAlertDialogBuilder(requireContext())
 | 
			
		||||
            .setView(binding.getRoot())
 | 
			
		||||
            .setPositiveButton(R.string.apply) { _: DialogInterface?, _: Int ->
 | 
			
		||||
                val preferences = preferencesBuilder.build()
 | 
			
		||||
                val json = preferences.json
 | 
			
		||||
                Utils.settingsHelper.putString(layoutPreferenceKey, json)
 | 
			
		||||
                onApplyListener.onApply(preferences)
 | 
			
		||||
            }
 | 
			
		||||
            .create()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun init() {
 | 
			
		||||
        initLayoutToggle()
 | 
			
		||||
        if (preferencesBuilder.type != PostsLayoutType.LINEAR) {
 | 
			
		||||
            initStaggeredOrGridOptions()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initStaggeredOrGridOptions() {
 | 
			
		||||
        initColCountToggle()
 | 
			
		||||
        initNamesToggle()
 | 
			
		||||
        initAvatarsToggle()
 | 
			
		||||
        initCornersToggle()
 | 
			
		||||
        initGapToggle()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initLayoutToggle() {
 | 
			
		||||
        val selectedLayoutId = selectedLayoutId
 | 
			
		||||
        binding.layoutToggle.check(selectedLayoutId)
 | 
			
		||||
        if (selectedLayoutId == R.id.layout_linear) {
 | 
			
		||||
            binding.staggeredOrGridOptions.visibility = View.GONE
 | 
			
		||||
        }
 | 
			
		||||
        binding.layoutToggle.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->
 | 
			
		||||
            if (!isChecked) return@addOnButtonCheckedListener
 | 
			
		||||
            when (checkedId) {
 | 
			
		||||
                R.id.layout_linear -> {
 | 
			
		||||
                    preferencesBuilder.type = PostsLayoutType.LINEAR
 | 
			
		||||
                    preferencesBuilder.colCount = 1
 | 
			
		||||
                    binding.staggeredOrGridOptions.visibility = View.GONE
 | 
			
		||||
                }
 | 
			
		||||
                R.id.layout_staggered -> {
 | 
			
		||||
                    preferencesBuilder.type = PostsLayoutType.STAGGERED_GRID
 | 
			
		||||
                    if (preferencesBuilder.colCount == 1) {
 | 
			
		||||
                        preferencesBuilder.colCount = 2
 | 
			
		||||
                    }
 | 
			
		||||
                    binding.staggeredOrGridOptions.visibility = View.VISIBLE
 | 
			
		||||
                    initStaggeredOrGridOptions()
 | 
			
		||||
                }
 | 
			
		||||
                else -> {
 | 
			
		||||
                    preferencesBuilder.type = PostsLayoutType.GRID
 | 
			
		||||
                    if (preferencesBuilder.colCount == 1) {
 | 
			
		||||
                        preferencesBuilder.colCount = 2
 | 
			
		||||
                    }
 | 
			
		||||
                    binding.staggeredOrGridOptions.visibility = View.VISIBLE
 | 
			
		||||
                    initStaggeredOrGridOptions()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initColCountToggle() {
 | 
			
		||||
        binding.colCountToggle.check(selectedColCountId)
 | 
			
		||||
        binding.colCountToggle.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->
 | 
			
		||||
            if (!isChecked) return@addOnButtonCheckedListener
 | 
			
		||||
            preferencesBuilder.colCount = (if (checkedId == R.id.col_count_two) 2 else 3)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initAvatarsToggle() {
 | 
			
		||||
        binding.showAvatarToggle.isChecked = preferencesBuilder.isAvatarVisible
 | 
			
		||||
        binding.avatarSizeToggle.check(selectedAvatarSizeId)
 | 
			
		||||
        binding.showAvatarToggle.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
 | 
			
		||||
            preferencesBuilder.isAvatarVisible = isChecked
 | 
			
		||||
            binding.labelAvatarSize.visibility = if (isChecked) View.VISIBLE else View.GONE
 | 
			
		||||
            binding.avatarSizeToggle.visibility = if (isChecked) View.VISIBLE else View.GONE
 | 
			
		||||
        }
 | 
			
		||||
        binding.labelAvatarSize.visibility = if (preferencesBuilder.isAvatarVisible) View.VISIBLE else View.GONE
 | 
			
		||||
        binding.avatarSizeToggle.visibility = if (preferencesBuilder.isAvatarVisible) View.VISIBLE else View.GONE
 | 
			
		||||
        binding.avatarSizeToggle.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->
 | 
			
		||||
            if (!isChecked) return@addOnButtonCheckedListener
 | 
			
		||||
            preferencesBuilder.profilePicSize = when (checkedId) {
 | 
			
		||||
                R.id.avatar_size_tiny -> ProfilePicSize.TINY
 | 
			
		||||
                R.id.avatar_size_small -> ProfilePicSize.SMALL
 | 
			
		||||
                else -> ProfilePicSize.REGULAR
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initNamesToggle() {
 | 
			
		||||
        binding.showNamesToggle.isChecked = preferencesBuilder.isNameVisible
 | 
			
		||||
        binding.showNamesToggle.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
 | 
			
		||||
            preferencesBuilder.isNameVisible = isChecked
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initCornersToggle() {
 | 
			
		||||
        binding.cornersToggle.check(selectedCornersId)
 | 
			
		||||
        binding.cornersToggle.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->
 | 
			
		||||
            if (!isChecked) return@addOnButtonCheckedListener
 | 
			
		||||
            if (checkedId == R.id.corners_round) {
 | 
			
		||||
                preferencesBuilder.hasRoundedCorners = true
 | 
			
		||||
                return@addOnButtonCheckedListener
 | 
			
		||||
            }
 | 
			
		||||
            preferencesBuilder.hasRoundedCorners = false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initGapToggle() {
 | 
			
		||||
        binding.showGapToggle.isChecked = preferencesBuilder.hasGap
 | 
			
		||||
        binding.showGapToggle.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
 | 
			
		||||
            preferencesBuilder.hasGap = isChecked
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val selectedLayoutId: Int
 | 
			
		||||
        get() = when (preferencesBuilder.type) {
 | 
			
		||||
            PostsLayoutType.STAGGERED_GRID -> R.id.layout_staggered
 | 
			
		||||
            PostsLayoutType.LINEAR -> R.id.layout_linear
 | 
			
		||||
            PostsLayoutType.GRID -> R.id.layout_grid
 | 
			
		||||
            else -> R.id.layout_grid
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private val selectedColCountId: Int
 | 
			
		||||
        get() = when (preferencesBuilder.colCount) {
 | 
			
		||||
            2 -> R.id.col_count_two
 | 
			
		||||
            3 -> R.id.col_count_three
 | 
			
		||||
            else -> R.id.col_count_three
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private val selectedCornersId: Int
 | 
			
		||||
        get() = if (preferencesBuilder.hasRoundedCorners) {
 | 
			
		||||
            R.id.corners_round
 | 
			
		||||
        } else R.id.corners_square
 | 
			
		||||
 | 
			
		||||
    private val selectedAvatarSizeId: Int
 | 
			
		||||
        get() = when (preferencesBuilder.profilePicSize) {
 | 
			
		||||
            ProfilePicSize.TINY -> R.id.avatar_size_tiny
 | 
			
		||||
            ProfilePicSize.SMALL -> R.id.avatar_size_small
 | 
			
		||||
            ProfilePicSize.REGULAR -> R.id.avatar_size_regular
 | 
			
		||||
            else -> R.id.avatar_size_regular
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    fun interface OnApplyListener {
 | 
			
		||||
        fun onApply(preferences: PostsLayoutPreferences)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -34,13 +34,10 @@ import awais.instagrabber.customviews.RamboTextViewV2
 | 
			
		||||
import awais.instagrabber.customviews.RamboTextViewV2.*
 | 
			
		||||
import awais.instagrabber.databinding.FragmentProfileBinding
 | 
			
		||||
import awais.instagrabber.db.repositories.FavoriteRepository
 | 
			
		||||
import awais.instagrabber.dialogs.ConfirmDialogFragment
 | 
			
		||||
import awais.instagrabber.dialogs.*
 | 
			
		||||
import awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback
 | 
			
		||||
import awais.instagrabber.dialogs.MultiOptionDialogFragment
 | 
			
		||||
import awais.instagrabber.dialogs.MultiOptionDialogFragment.MultiOptionDialogSingleCallback
 | 
			
		||||
import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option
 | 
			
		||||
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment
 | 
			
		||||
import awais.instagrabber.dialogs.ProfilePicDialogFragment
 | 
			
		||||
import awais.instagrabber.fragments.UserSearchMode
 | 
			
		||||
import awais.instagrabber.managers.DirectMessagesManager
 | 
			
		||||
import awais.instagrabber.models.Resource
 | 
			
		||||
@ -1002,10 +999,10 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun showPostsLayoutPreferences() {
 | 
			
		||||
        val fragment = PostsLayoutPreferencesDialogFragment(Constants.PREF_PROFILE_POSTS_LAYOUT) { preferences ->
 | 
			
		||||
            layoutPreferences = preferences
 | 
			
		||||
        val fragment = PostsLayoutPreferencesDialogFragment(Constants.PREF_PROFILE_POSTS_LAYOUT) {
 | 
			
		||||
            layoutPreferences = it
 | 
			
		||||
            Handler(Looper.getMainLooper()).postDelayed(
 | 
			
		||||
                { binding.postsRecyclerView.layoutPreferences = preferences },
 | 
			
		||||
                { binding.postsRecyclerView.layoutPreferences = it },
 | 
			
		||||
                200
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import awais.instagrabber.models.enums.NotificationType.Companion.valueOfType
 | 
			
		||||
 | 
			
		||||
class Notification(val args: NotificationArgs,
 | 
			
		||||
                   private val storyType: Int,
 | 
			
		||||
                   val pk: String) {
 | 
			
		||||
                   val pk: String?) {
 | 
			
		||||
    val type: NotificationType?
 | 
			
		||||
        get() = valueOfType(storyType)
 | 
			
		||||
}
 | 
			
		||||
@ -195,10 +195,15 @@ public final class Utils {
 | 
			
		||||
        if (context == null || TextUtils.isEmpty(url)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        final Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
 | 
			
		||||
        i.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
 | 
			
		||||
        i.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
 | 
			
		||||
        try {
 | 
			
		||||
            String url1 = url;
 | 
			
		||||
            // add http:// if string doesn't have http:// or https://
 | 
			
		||||
            if (!url.startsWith("http://") && !url.startsWith("https://")) {
 | 
			
		||||
                url1 = "http://" + url;
 | 
			
		||||
            }
 | 
			
		||||
            final Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url1));
 | 
			
		||||
            i.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
 | 
			
		||||
            i.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
 | 
			
		||||
            context.startActivity(i);
 | 
			
		||||
        } catch (ActivityNotFoundException e) {
 | 
			
		||||
            Log.e(TAG, "openURL: No activity found to handle URLs", e);
 | 
			
		||||
 | 
			
		||||
@ -1,113 +0,0 @@
 | 
			
		||||
package awais.instagrabber.viewmodels;
 | 
			
		||||
 | 
			
		||||
import android.app.Application;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.UriPermission;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Parcelable;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
import androidx.documentfile.provider.DocumentFile;
 | 
			
		||||
import androidx.lifecycle.AndroidViewModel;
 | 
			
		||||
import androidx.lifecycle.LiveData;
 | 
			
		||||
import androidx.lifecycle.MutableLiveData;
 | 
			
		||||
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
import java.net.URLDecoder;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.R;
 | 
			
		||||
import awais.instagrabber.utils.Constants;
 | 
			
		||||
import awais.instagrabber.utils.DownloadUtils;
 | 
			
		||||
import awais.instagrabber.utils.TextUtils;
 | 
			
		||||
import awais.instagrabber.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.FOLDER_PATH;
 | 
			
		||||
 | 
			
		||||
public class DirectorySelectActivityViewModel extends AndroidViewModel {
 | 
			
		||||
    private static final String TAG = DirectorySelectActivityViewModel.class.getSimpleName();
 | 
			
		||||
 | 
			
		||||
    private final MutableLiveData<String> message = new MutableLiveData<>();
 | 
			
		||||
    private final MutableLiveData<String> prevUri = new MutableLiveData<>();
 | 
			
		||||
    private final MutableLiveData<Boolean> loading = new MutableLiveData<>(false);
 | 
			
		||||
    private final MutableLiveData<Boolean> dirSuccess = new MutableLiveData<>(false);
 | 
			
		||||
 | 
			
		||||
    public DirectorySelectActivityViewModel(final Application application) {
 | 
			
		||||
        super(application);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public LiveData<String> getMessage() {
 | 
			
		||||
        return message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public LiveData<String> getPrevUri() {
 | 
			
		||||
        return prevUri;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public LiveData<Boolean> isLoading() {
 | 
			
		||||
        return loading;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public LiveData<Boolean> getDirSuccess() {
 | 
			
		||||
        return dirSuccess;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setInitialUri(final Intent intent) {
 | 
			
		||||
        if (intent == null) {
 | 
			
		||||
            setMessage(null);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        final Parcelable initialUriParcelable = intent.getParcelableExtra(Constants.EXTRA_INITIAL_URI);
 | 
			
		||||
        if (!(initialUriParcelable instanceof Uri)) {
 | 
			
		||||
            setMessage(null);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        setMessage((Uri) initialUriParcelable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setMessage(@Nullable final Uri initialUri) {
 | 
			
		||||
        if (initialUri == null) {
 | 
			
		||||
            final String prevVersionFolderPath = Utils.settingsHelper.getString(FOLDER_PATH);
 | 
			
		||||
            if (TextUtils.isEmpty(prevVersionFolderPath)) {
 | 
			
		||||
                // default message
 | 
			
		||||
                message.postValue(getApplication().getString(R.string.dir_select_default_message));
 | 
			
		||||
                prevUri.postValue(null);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            message.postValue(getApplication().getString(R.string.dir_select_reselect_message));
 | 
			
		||||
            prevUri.postValue(prevVersionFolderPath);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        final List<UriPermission> existingPermissions = getApplication().getContentResolver().getPersistedUriPermissions();
 | 
			
		||||
        final boolean anyMatch = existingPermissions.stream().anyMatch(uriPermission -> uriPermission.getUri().equals(initialUri));
 | 
			
		||||
        final DocumentFile documentFile = DocumentFile.fromSingleUri(getApplication(), initialUri);
 | 
			
		||||
        String path;
 | 
			
		||||
        try {
 | 
			
		||||
            path = URLDecoder.decode(initialUri.toString(), StandardCharsets.UTF_8.toString());
 | 
			
		||||
        } catch (UnsupportedEncodingException e) {
 | 
			
		||||
            path = initialUri.toString();
 | 
			
		||||
        }
 | 
			
		||||
        if (!anyMatch) {
 | 
			
		||||
            message.postValue(getApplication().getString(R.string.dir_select_permission_revoked_message));
 | 
			
		||||
            prevUri.postValue(path);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (documentFile == null || !documentFile.exists() || documentFile.lastModified() == 0) {
 | 
			
		||||
            message.postValue(getApplication().getString(R.string.dir_select_folder_not_exist));
 | 
			
		||||
            prevUri.postValue(path);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setupSelectedDir(@NonNull final Intent data) throws DownloadUtils.ReselectDocumentTreeException {
 | 
			
		||||
        loading.postValue(true);
 | 
			
		||||
        try {
 | 
			
		||||
            Utils.setupSelectedDir(getApplication(), data);
 | 
			
		||||
            message.postValue(getApplication().getString(R.string.dir_select_success_message));
 | 
			
		||||
            dirSuccess.postValue(true);
 | 
			
		||||
        } finally {
 | 
			
		||||
            loading.postValue(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,89 @@
 | 
			
		||||
package awais.instagrabber.viewmodels
 | 
			
		||||
 | 
			
		||||
import android.app.Application
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.content.UriPermission
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import androidx.documentfile.provider.DocumentFile
 | 
			
		||||
import androidx.lifecycle.AndroidViewModel
 | 
			
		||||
import androidx.lifecycle.LiveData
 | 
			
		||||
import androidx.lifecycle.MutableLiveData
 | 
			
		||||
import awais.instagrabber.R
 | 
			
		||||
import awais.instagrabber.fragments.settings.PreferenceKeys
 | 
			
		||||
import awais.instagrabber.utils.Constants
 | 
			
		||||
import awais.instagrabber.utils.DownloadUtils.ReselectDocumentTreeException
 | 
			
		||||
import awais.instagrabber.utils.TextUtils.isEmpty
 | 
			
		||||
import awais.instagrabber.utils.Utils
 | 
			
		||||
import java.io.UnsupportedEncodingException
 | 
			
		||||
import java.net.URLDecoder
 | 
			
		||||
import java.nio.charset.StandardCharsets
 | 
			
		||||
 | 
			
		||||
class DirectorySelectActivityViewModel(application: Application) : AndroidViewModel(application) {
 | 
			
		||||
    private val _message = MutableLiveData<String>()
 | 
			
		||||
    private val _prevUri = MutableLiveData<String?>()
 | 
			
		||||
    private val _loading = MutableLiveData(false)
 | 
			
		||||
    private val _dirSuccess = MutableLiveData(false)
 | 
			
		||||
 | 
			
		||||
    val message: LiveData<String> = _message
 | 
			
		||||
    val prevUri: LiveData<String?> = _prevUri
 | 
			
		||||
    val loading: LiveData<Boolean> = _loading
 | 
			
		||||
    val dirSuccess: LiveData<Boolean> = _dirSuccess
 | 
			
		||||
 | 
			
		||||
    fun setInitialUri(intent: Intent?) {
 | 
			
		||||
        if (intent == null) {
 | 
			
		||||
            setMessage(null)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        val initialUriParcelable = intent.getParcelableExtra<Parcelable>(Constants.EXTRA_INITIAL_URI)
 | 
			
		||||
        if (initialUriParcelable !is Uri) {
 | 
			
		||||
            setMessage(null)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        setMessage(initialUriParcelable as Uri?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setMessage(initialUri: Uri?) {
 | 
			
		||||
        if (initialUri == null) {
 | 
			
		||||
            val prevVersionFolderPath = Utils.settingsHelper.getString(PreferenceKeys.FOLDER_PATH)
 | 
			
		||||
            if (isEmpty(prevVersionFolderPath)) {
 | 
			
		||||
                // default message
 | 
			
		||||
                _message.postValue(getApplication<Application>().getString(R.string.dir_select_default_message))
 | 
			
		||||
                _prevUri.postValue(null)
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            _message.postValue(getApplication<Application>().getString(R.string.dir_select_reselect_message))
 | 
			
		||||
            _prevUri.postValue(prevVersionFolderPath)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        val existingPermissions = getApplication<Application>().contentResolver.persistedUriPermissions
 | 
			
		||||
        val anyMatch = existingPermissions.stream().anyMatch { uriPermission: UriPermission -> uriPermission.uri == initialUri }
 | 
			
		||||
        val documentFile = DocumentFile.fromSingleUri(getApplication(), initialUri)
 | 
			
		||||
        val path: String = try {
 | 
			
		||||
            URLDecoder.decode(initialUri.toString(), StandardCharsets.UTF_8.toString())
 | 
			
		||||
        } catch (e: UnsupportedEncodingException) {
 | 
			
		||||
            initialUri.toString()
 | 
			
		||||
        }
 | 
			
		||||
        if (!anyMatch) {
 | 
			
		||||
            _message.postValue(getApplication<Application>().getString(R.string.dir_select_permission_revoked_message))
 | 
			
		||||
            _prevUri.postValue(path)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        if (documentFile == null || !documentFile.exists() || documentFile.lastModified() == 0L) {
 | 
			
		||||
            _message.postValue(getApplication<Application>().getString(R.string.dir_select_folder_not_exist))
 | 
			
		||||
            _prevUri.postValue(path)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Throws(ReselectDocumentTreeException::class)
 | 
			
		||||
    fun setupSelectedDir(data: Intent) {
 | 
			
		||||
        _loading.postValue(true)
 | 
			
		||||
        try {
 | 
			
		||||
            Utils.setupSelectedDir(getApplication(), data)
 | 
			
		||||
            _message.postValue(getApplication<Application>().getString(R.string.dir_select_success_message))
 | 
			
		||||
            _dirSuccess.postValue(true)
 | 
			
		||||
        } finally {
 | 
			
		||||
            _loading.postValue(false)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -542,4 +542,13 @@
 | 
			
		||||
            android:id="@+id/action_to_profile"
 | 
			
		||||
            app:destination="@id/profile_non_top" />
 | 
			
		||||
    </fragment>
 | 
			
		||||
 | 
			
		||||
    <fragment
 | 
			
		||||
        android:id="@+id/aboutFragment"
 | 
			
		||||
        android:name="awais.instagrabber.fragments.settings.AboutFragment"
 | 
			
		||||
        android:label="@string/action_about" />
 | 
			
		||||
    <fragment
 | 
			
		||||
        android:id="@+id/backupPreferencesFragment"
 | 
			
		||||
        android:name="awais.instagrabber.fragments.settings.BackupPreferencesFragment"
 | 
			
		||||
        android:label="@string/backup_and_restore" />
 | 
			
		||||
</navigation>
 | 
			
		||||
@ -33,18 +33,10 @@
 | 
			
		||||
            android:id="@+id/action_settings_to_post"
 | 
			
		||||
            app:destination="@id/postPreferencesFragment" />
 | 
			
		||||
    </fragment>
 | 
			
		||||
    <fragment
 | 
			
		||||
        android:id="@+id/aboutFragment"
 | 
			
		||||
        android:name="awais.instagrabber.fragments.settings.AboutFragment"
 | 
			
		||||
        android:label="@string/action_about" />
 | 
			
		||||
    <fragment
 | 
			
		||||
        android:id="@+id/themePreferencesFragment"
 | 
			
		||||
        android:name="awais.instagrabber.fragments.settings.ThemePreferencesFragment"
 | 
			
		||||
        android:label="@string/theme_settings" />
 | 
			
		||||
    <fragment
 | 
			
		||||
        android:id="@+id/backupPreferencesFragment"
 | 
			
		||||
        android:name="awais.instagrabber.fragments.settings.BackupPreferencesFragment"
 | 
			
		||||
        android:label="@string/backup_and_restore" />
 | 
			
		||||
    <fragment
 | 
			
		||||
        android:id="@+id/localePreferencesFragment"
 | 
			
		||||
        android:name="awais.instagrabber.fragments.settings.LocalePreferencesFragment"
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
buildscript {
 | 
			
		||||
    ext{
 | 
			
		||||
        kotlin_version = '1.5.20'
 | 
			
		||||
        kotlin_version = '1.5.21'
 | 
			
		||||
        nav_version = "2.4.0-alpha04"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user