That fuck shit the fascists are using
at master 316 lines 12 kB view raw
1package org.tm.archive.preferences; 2 3import android.Manifest; 4import android.app.Activity; 5import android.content.Intent; 6import android.net.Uri; 7import android.os.Build; 8import android.os.Bundle; 9import android.text.format.DateFormat; 10import android.text.method.LinkMovementMethod; 11import android.view.LayoutInflater; 12import android.view.View; 13import android.view.ViewGroup; 14import android.widget.ProgressBar; 15import android.widget.TextView; 16 17import androidx.annotation.NonNull; 18import androidx.annotation.Nullable; 19import androidx.annotation.RequiresApi; 20import androidx.core.text.HtmlCompat; 21import androidx.fragment.app.Fragment; 22 23import com.google.android.material.timepicker.MaterialTimePicker; 24import com.google.android.material.timepicker.TimeFormat; 25 26import org.greenrobot.eventbus.EventBus; 27import org.greenrobot.eventbus.Subscribe; 28import org.greenrobot.eventbus.ThreadMode; 29import org.signal.core.util.ThreadUtil; 30import org.signal.core.util.logging.Log; 31import org.tm.archive.R; 32import org.tm.archive.backup.BackupDialog; 33import org.tm.archive.backup.BackupEvent; 34import org.tm.archive.database.NoExternalStorageException; 35import org.tm.archive.dependencies.ApplicationDependencies; 36import org.tm.archive.jobs.LocalBackupJob; 37import org.tm.archive.keyvalue.SignalStore; 38import org.tm.archive.permissions.Permissions; 39import org.tm.archive.service.LocalBackupListener; 40import org.tm.archive.util.BackupUtil; 41import org.tm.archive.util.JavaTimeExtensionsKt; 42import org.tm.archive.util.StorageUtil; 43import org.tm.archive.util.TextSecurePreferences; 44 45import java.text.NumberFormat; 46import java.time.LocalTime; 47import java.util.Locale; 48import java.util.Objects; 49 50public class BackupsPreferenceFragment extends Fragment { 51 52 private static final String TAG = Log.tag(BackupsPreferenceFragment.class); 53 54 private static final short CHOOSE_BACKUPS_LOCATION_REQUEST_CODE = 26212; 55 56 private View create; 57 private View folder; 58 private View verify; 59 private View timer; 60 private TextView timeLabel; 61 private TextView toggle; 62 private TextView info; 63 private TextView summary; 64 private TextView folderName; 65 private ProgressBar progress; 66 private TextView progressSummary; 67 68 private final NumberFormat formatter = NumberFormat.getInstance(); 69 70 @Override 71 public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 72 return inflater.inflate(R.layout.fragment_backups, container, false); 73 } 74 75 @Override 76 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 77 create = view.findViewById(R.id.fragment_backup_create); 78 folder = view.findViewById(R.id.fragment_backup_folder); 79 verify = view.findViewById(R.id.fragment_backup_verify); 80 timer = view.findViewById(R.id.fragment_backup_time); 81 timeLabel = view.findViewById(R.id.fragment_backup_time_value); 82 toggle = view.findViewById(R.id.fragment_backup_toggle); 83 info = view.findViewById(R.id.fragment_backup_info); 84 summary = view.findViewById(R.id.fragment_backup_create_summary); 85 folderName = view.findViewById(R.id.fragment_backup_folder_name); 86 progress = view.findViewById(R.id.fragment_backup_progress); 87 progressSummary = view.findViewById(R.id.fragment_backup_progress_summary); 88 89 toggle.setOnClickListener(unused -> onToggleClicked()); 90 create.setOnClickListener(unused -> onCreateClicked()); 91 verify.setOnClickListener(unused -> BackupDialog.showVerifyBackupPassphraseDialog(requireContext())); 92 timer.setOnClickListener(unused -> pickTime()); 93 94 formatter.setMinimumFractionDigits(1); 95 formatter.setMaximumFractionDigits(1); 96 97 EventBus.getDefault().register(this); 98 } 99 100 @Override 101 public void onResume() { 102 super.onResume(); 103 104 setBackupStatus(); 105 setBackupSummary(); 106 setInfo(); 107 } 108 109 @Override 110 public void onDestroyView() { 111 super.onDestroyView(); 112 EventBus.getDefault().unregister(this); 113 } 114 115 @Override 116 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 117 Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); 118 } 119 120 @Override 121 public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 122 if (Build.VERSION.SDK_INT >= 29 && 123 requestCode == CHOOSE_BACKUPS_LOCATION_REQUEST_CODE && 124 resultCode == Activity.RESULT_OK && 125 data != null && 126 data.getData() != null) 127 { 128 BackupDialog.showEnableBackupDialog(requireContext(), 129 data, 130 StorageUtil.getDisplayPath(requireContext(), data.getData()), 131 this::setBackupsEnabled); 132 } else { 133 Log.w(TAG, "Unknown activity result. code: " + requestCode + " resultCode: " + resultCode + " data present: " + (data != null)); 134 } 135 } 136 137 @Subscribe(threadMode = ThreadMode.MAIN) 138 public void onEvent(BackupEvent event) { 139 if (event.getType() == BackupEvent.Type.PROGRESS || event.getType() == BackupEvent.Type.PROGRESS_VERIFYING) { 140 create.setEnabled(false); 141 summary.setText(getString(event.getType() == BackupEvent.Type.PROGRESS ? R.string.BackupsPreferenceFragment__in_progress 142 : R.string.BackupsPreferenceFragment__verifying_backup)); 143 progress.setVisibility(View.VISIBLE); 144 progressSummary.setVisibility(event.getCount() > 0 ? View.VISIBLE : View.GONE); 145 146 if (event.getEstimatedTotalCount() == 0) { 147 progress.setIndeterminate(true); 148 progressSummary.setText(getString(R.string.BackupsPreferenceFragment__d_so_far, event.getCount())); 149 } else { 150 double completionPercentage = event.getCompletionPercentage(); 151 152 progress.setIndeterminate(false); 153 progress.setMax(100); 154 progress.setProgress((int) completionPercentage); 155 progressSummary.setText(getString(R.string.BackupsPreferenceFragment__s_so_far, formatter.format(completionPercentage))); 156 } 157 } else if (event.getType() == BackupEvent.Type.FINISHED) { 158 create.setEnabled(true); 159 progress.setVisibility(View.GONE); 160 progressSummary.setVisibility(View.GONE); 161 setBackupSummary(); 162 ThreadUtil.runOnMainDelayed(this::setBackupSummary, 100); 163 } 164 } 165 166 private void setBackupStatus() { 167 if (SignalStore.settings().isBackupEnabled()) { 168 if (BackupUtil.canUserAccessBackupDirectory(requireContext())) { 169 setBackupsEnabled(); 170 } else { 171 Log.w(TAG, "Cannot access backup directory. Disabling backups."); 172 173 BackupUtil.disableBackups(requireContext()); 174 setBackupsDisabled(); 175 } 176 } else { 177 setBackupsDisabled(); 178 } 179 } 180 181 private void setBackupSummary() { 182 summary.setText(getString(R.string.BackupsPreferenceFragment__last_backup, BackupUtil.getLastBackupTime(requireContext(), Locale.getDefault()))); 183 } 184 185 private void setBackupFolderName() { 186 folder.setVisibility(View.GONE); 187 188 if (BackupUtil.canUserAccessBackupDirectory(requireContext())) { 189 if (BackupUtil.isUserSelectionRequired(requireContext()) && 190 BackupUtil.canUserAccessBackupDirectory(requireContext())) 191 { 192 Uri backupUri = Objects.requireNonNull(SignalStore.settings().getSignalBackupDirectory()); 193 194 folder.setVisibility(View.VISIBLE); 195 folderName.setText(StorageUtil.getDisplayPath(requireContext(), backupUri)); 196 } else if (StorageUtil.canWriteInSignalStorageDir()) { 197 try { 198 folder.setVisibility(View.VISIBLE); 199 folderName.setText(StorageUtil.getOrCreateBackupDirectory().getPath()); 200 } catch (NoExternalStorageException e) { 201 Log.w(TAG, "Could not display folder name.", e); 202 } 203 } 204 } 205 } 206 207 private void setInfo() { 208 String link = String.format("<a href=\"%s\">%s</a>", getString(R.string.backup_support_url), getString(R.string.BackupsPreferenceFragment__learn_more)); 209 String infoText = getString(R.string.BackupsPreferenceFragment__to_restore_a_backup, link); 210 211 info.setText(HtmlCompat.fromHtml(infoText, 0)); 212 info.setMovementMethod(LinkMovementMethod.getInstance()); 213 } 214 215 private void onToggleClicked() { 216 if (BackupUtil.isUserSelectionRequired(requireContext())) { 217 onToggleClickedApi29(); 218 } else { 219 onToggleClickedLegacy(); 220 } 221 } 222 223 @RequiresApi(29) 224 private void onToggleClickedApi29() { 225 if (!SignalStore.settings().isBackupEnabled()) { 226 BackupDialog.showChooseBackupLocationDialog(this, CHOOSE_BACKUPS_LOCATION_REQUEST_CODE); 227 } else { 228 BackupDialog.showDisableBackupDialog(requireContext(), this::setBackupsDisabled); 229 } 230 } 231 232 private void onToggleClickedLegacy() { 233 Permissions.with(this) 234 .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) 235 .ifNecessary() 236 .onAllGranted(() -> { 237 if (!SignalStore.settings().isBackupEnabled()) { 238 BackupDialog.showEnableBackupDialog(requireContext(), null, null, this::setBackupsEnabled); 239 } else { 240 BackupDialog.showDisableBackupDialog(requireContext(), this::setBackupsDisabled); 241 } 242 }) 243 .withPermanentDenialDialog(getString(R.string.BackupsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups)) 244 .execute(); 245 } 246 247 private void onCreateClicked() { 248 if (BackupUtil.isUserSelectionRequired(requireContext())) { 249 onCreateClickedApi29(); 250 } else { 251 onCreateClickedLegacy(); 252 } 253 } 254 255 @RequiresApi(29) 256 private void onCreateClickedApi29() { 257 Log.i(TAG, "Queueing backup..."); 258 LocalBackupJob.enqueue(true); 259 } 260 261 private void pickTime() { 262 int timeFormat = DateFormat.is24HourFormat(requireContext()) ? TimeFormat.CLOCK_24H : TimeFormat.CLOCK_12H; 263 final MaterialTimePicker timePickerFragment = new MaterialTimePicker.Builder() 264 .setTimeFormat(timeFormat) 265 .setHour(SignalStore.settings().getBackupHour()) 266 .setMinute(SignalStore.settings().getBackupMinute()) 267 .setTitleText(R.string.BackupsPreferenceFragment__set_backup_time) 268 .build(); 269 timePickerFragment.addOnPositiveButtonClickListener(v -> { 270 int hour = timePickerFragment.getHour(); 271 int minute = timePickerFragment.getMinute(); 272 SignalStore.settings().setBackupSchedule(hour, minute); 273 updateTimeLabel(); 274 TextSecurePreferences.setNextBackupTime(requireContext(), 0); 275 LocalBackupListener.schedule(requireContext()); 276 }); 277 timePickerFragment.show(getChildFragmentManager(), "TIME_PICKER"); 278 } 279 280 private void onCreateClickedLegacy() { 281 Permissions.with(this) 282 .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) 283 .ifNecessary() 284 .onAllGranted(() -> { 285 Log.i(TAG, "Queuing backup..."); 286 LocalBackupJob.enqueue(true); 287 }) 288 .withPermanentDenialDialog(getString(R.string.BackupsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups)) 289 .execute(); 290 } 291 292 private void updateTimeLabel() { 293 final int backupHour = SignalStore.settings().getBackupHour(); 294 final int backupMinute = SignalStore.settings().getBackupMinute(); 295 LocalTime time = LocalTime.of(backupHour, backupMinute); 296 timeLabel.setText(JavaTimeExtensionsKt.formatHours(time, requireContext())); 297 } 298 299 private void setBackupsEnabled() { 300 toggle.setText(R.string.BackupsPreferenceFragment__turn_off); 301 create.setVisibility(View.VISIBLE); 302 verify.setVisibility(View.VISIBLE); 303 timer.setVisibility(View.VISIBLE); 304 updateTimeLabel(); 305 setBackupFolderName(); 306 } 307 308 private void setBackupsDisabled() { 309 toggle.setText(R.string.BackupsPreferenceFragment__turn_on); 310 create.setVisibility(View.GONE); 311 folder.setVisibility(View.GONE); 312 verify.setVisibility(View.GONE); 313 timer.setVisibility(View.GONE); 314 ApplicationDependencies.getJobManager().cancelAllInQueue(LocalBackupJob.QUEUE); 315 } 316}