Browse Source
Merge remote-tracking branch 'origin/master'
renovate/androidx.fragment-fragment-ktx-1.x
Merge remote-tracking branch 'origin/master'
renovate/androidx.fragment-fragment-ktx-1.x
Austin Huang
3 years ago
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
14 changed files with 431 additions and 482 deletions
-
129app/src/main/java/awais/instagrabber/activities/DirectorySelectActivity.java
-
132app/src/main/java/awais/instagrabber/activities/DirectorySelectActivity.kt
-
5app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
-
2app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemLinkViewHolder.java
-
216app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.java
-
180app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.kt
-
11app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt
-
2app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.kt
-
9app/src/main/java/awais/instagrabber/utils/Utils.java
-
113app/src/main/java/awais/instagrabber/viewmodels/DirectorySelectActivityViewModel.java
-
89app/src/main/java/awais/instagrabber/viewmodels/DirectorySelectActivityViewModel.kt
-
9app/src/main/res/navigation/more_nav_graph.xml
-
8app/src/main/res/navigation/settings_nav_graph.xml
-
2build.gradle
@ -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 |
||||
|
} |
||||
|
} |
@ -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) |
||||
|
} |
||||
|
} |
@ -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) |
||||
|
} |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue