MainActivity.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. package info.knacki.pass.ui;
  2. import android.Manifest;
  3. import android.content.ClipData;
  4. import android.content.ClipboardManager;
  5. import android.content.Intent;
  6. import android.os.Build;
  7. import android.os.Bundle;
  8. import android.support.v7.app.AppCompatActivity;
  9. import android.view.Menu;
  10. import android.view.MenuItem;
  11. import android.widget.ScrollView;
  12. import android.widget.Toast;
  13. import java.io.File;
  14. import java.io.IOException;
  15. import java.util.logging.Level;
  16. import java.util.logging.Logger;
  17. import info.knacki.pass.R;
  18. import info.knacki.pass.generator.PasswordGenerator;
  19. import info.knacki.pass.generator.ui.PasswordGeneratorWizard;
  20. import info.knacki.pass.io.FileInterfaceFactory;
  21. import info.knacki.pass.io.FileUtils;
  22. import info.knacki.pass.io.IFileInterface;
  23. import info.knacki.pass.io.OnResponseListener;
  24. import info.knacki.pass.io.PathUtils;
  25. import info.knacki.pass.settings.SettingsManager;
  26. import info.knacki.pass.settings.ui.SettingsActivity;
  27. import info.knacki.pass.ui.alertPrompt.AlertPromptGenerator;
  28. import info.knacki.pass.ui.alertPrompt.views.SimpleTextEdit;
  29. import info.knacki.pass.ui.alertPrompt.views.TextEditAndCheckbox;
  30. import info.knacki.pass.ui.alertPrompt.views.TextView;
  31. import info.knacki.pass.ui.passwordList.EditablePasswordListView;
  32. import info.knacki.pass.ui.passwordList.PasswordEditListener;
  33. import info.knacki.pass.ui.passwordPicker.PasswordPickerFactory;
  34. public class MainActivity extends AppCompatActivity implements PasswordEditListener {
  35. private final Logger log = Logger.getLogger(MainActivity.class.getName());
  36. private EditablePasswordListView fPasswordListView;
  37. protected TextEditAndCheckbox fLastFileName = null;
  38. protected void CreateNewFolder() {
  39. AlertPromptGenerator.StaticMake(this)
  40. .setCancelable(true)
  41. .setView(new SimpleTextEdit(this))
  42. .setPositiveButton(R.string.add, (dialogInterface, view) -> {
  43. final String filename = ((SimpleTextEdit) view).getStr();
  44. if (filename.length() == 0) {
  45. Toast.makeText(MainActivity.this, "Error: Empty file name", Toast.LENGTH_LONG).show();
  46. return;
  47. }
  48. File f = new File(fPasswordListView.fCurrentDir +"/" +filename);
  49. if (!f.mkdir()) {
  50. Toast.makeText(MainActivity.this, "Error: cannot create folder", Toast.LENGTH_LONG).show();
  51. }
  52. fPasswordListView.refresh();
  53. })
  54. .setNegativeButton(R.string.cancel, null)
  55. .setTitle(R.string.add_title)
  56. .show();
  57. }
  58. protected void CreateNewPassword() {
  59. if (fLastFileName == null) {
  60. fLastFileName = new TextEditAndCheckbox(this);
  61. }
  62. AlertPromptGenerator.StaticMake(this)
  63. .setCancelable(true)
  64. .setView(new TextEditAndCheckbox(this)
  65. .setText(fLastFileName.getStr())
  66. .setCheckboxCaption(R.string.add_generate)
  67. .setChecked(fLastFileName.isChecked()))
  68. .setPositiveButton(R.string.add, (dialogInterface, view) -> {
  69. fLastFileName = (TextEditAndCheckbox) view;
  70. final String filename = fLastFileName.getStr();
  71. if (filename.length() == 0) {
  72. Toast.makeText(MainActivity.this, "Error: Empty file name", Toast.LENGTH_LONG).show();
  73. return;
  74. }
  75. File f = new File(fPasswordListView.fCurrentDir +"/" +filename +FileInterfaceFactory.GetExtension(SettingsManager.GetDefaultEncryptionType(MainActivity.this)));
  76. try {
  77. FileUtils.Touch(f);
  78. fPasswordListView.refresh();
  79. }
  80. catch (IOException e) {
  81. Toast.makeText(MainActivity.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
  82. log.log(Level.WARNING, e.getMessage(), e);
  83. return;
  84. }
  85. if (fLastFileName.isChecked()) {
  86. GeneratePassword(f, true);
  87. } else {
  88. EditFile(f, true);
  89. }
  90. fLastFileName = null;
  91. })
  92. .setNegativeButton(R.string.cancel, (dialogInterface, s) -> dialogInterface.cancel())
  93. .setTitle(R.string.add_title)
  94. .show();
  95. }
  96. void requestPermissions() {
  97. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  98. requestPermissions(new String[] {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
  99. }
  100. }
  101. @Override
  102. protected void onResume() {
  103. super.onResume();
  104. fPasswordListView.refresh();
  105. }
  106. @Override
  107. protected void onCreate(Bundle savedInstanceState) {
  108. super.onCreate(savedInstanceState);
  109. setContentView(R.layout.activity_pass_list);
  110. setTitle(R.string.title);
  111. fPasswordListView = new EditablePasswordListView(this, this, PathUtils.GetPassDir(this), false);
  112. ((ScrollView)findViewById(R.id.passwordListContainer)).addView(fPasswordListView);
  113. requestPermissions();
  114. }
  115. @Override
  116. public boolean onOptionsItemSelected(MenuItem item) {
  117. switch (item.getItemId()) {
  118. case 0:
  119. break;
  120. case R.id.id_sync:
  121. GitPullActivity.StartSyncActivity(this);
  122. break;
  123. case R.id.new_folder:
  124. CreateNewFolder();
  125. break;
  126. case R.id.new_password:
  127. CreateNewPassword();
  128. break;
  129. case R.id.settings:
  130. ShowSettings();
  131. break;
  132. case R.id.trash:
  133. boolean checked = !item.isChecked();
  134. item.setChecked(checked);
  135. fPasswordListView.SetDisplayHiddenFiles(checked);
  136. default:
  137. log.log(Level.WARNING, "Error: Unknown MenuItem id: " +item.getItemId());
  138. return false;
  139. }
  140. return true;
  141. }
  142. @Override
  143. public boolean onCreateOptionsMenu(Menu menu) {
  144. getMenuInflater().inflate(R.menu.main_menu, menu);
  145. if (SettingsManager.GetVCS(this) == null) {
  146. // Disable sync option
  147. menu.removeItem(R.id.id_sync);
  148. }
  149. return super.onCreateOptionsMenu(menu);
  150. }
  151. protected void GeneratePassword(final File f, final boolean removeOnError) {
  152. final PasswordGeneratorWizard wiz = new PasswordGeneratorWizard(this);
  153. AlertPromptGenerator.StaticMake(this)
  154. .setCancelable(true)
  155. .setView(wiz)
  156. .setPositiveButton(R.string.add, (dialogInterface, view) -> {
  157. IFileInterface writer = FileInterfaceFactory.GetFileInterface(MainActivity.this, PasswordPickerFactory.GetPasswordPicker(MainActivity.this), f);
  158. try {
  159. writer.WriteFile(PasswordGenerator.generate(wiz), new OnResponseListener<Void>() {
  160. @Override
  161. public void OnResponse(Void result) {
  162. }
  163. @Override
  164. public void OnError(final String msg, final Throwable e) {
  165. MainActivity.this.runOnUiThread(() -> {
  166. Toast.makeText(MainActivity.this, "Error: " + msg, Toast.LENGTH_LONG).show();
  167. log.log(Level.WARNING, msg, e);
  168. });
  169. }
  170. });
  171. }
  172. catch (IFileInterface.InvalidPasswordException e) {
  173. if (removeOnError)
  174. FileUtils.DeleteFile(f);
  175. log.log(Level.WARNING, e.getMessage(), e);
  176. Toast.makeText(MainActivity.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
  177. fPasswordListView.refresh();
  178. }
  179. })
  180. .setNegativeButton(R.string.cancel, null)
  181. .setTitle(R.string.generate_title)
  182. .show();
  183. }
  184. protected void EditFile(final File f, boolean editMode) {
  185. Intent i = new Intent(this, EditPasswordActivity.class);
  186. i.putExtra(EditPasswordActivity.INTENT_PATH, f.getAbsolutePath());
  187. i.putExtra(EditPasswordActivity.INTENT_EDIT, editMode);
  188. startActivity(i);
  189. }
  190. protected void ShowSettings() {
  191. startActivity(new Intent(this, SettingsActivity.class));
  192. }
  193. @Override
  194. public void OnPasswordClicked(File f) {
  195. EditFile(f, false);
  196. }
  197. @Override
  198. public void OnRemovePassword(File f) {
  199. AlertPromptGenerator.StaticMake(this)
  200. .setTitle(R.string.are_you_sure)
  201. .setView(new TextView(this).SetText(String.format(getString(R.string.about_to_rm_file), f.getName())))
  202. .setPositiveButton(R.string.ok, (dialogInterface, view) -> {
  203. FileUtils.TrashFile(f);
  204. fPasswordListView.refresh();
  205. })
  206. .setNegativeButton(R.string.cancel, null)
  207. .show();
  208. fPasswordListView.refresh();
  209. }
  210. private void DoRemoveDir(File f) {
  211. FileUtils.Rmdir(f);
  212. fPasswordListView.refresh();
  213. }
  214. @Override
  215. public void OnRemoveDirectory(final File f) {
  216. if (f.listFiles().length > 0) {
  217. AlertPromptGenerator.StaticMake(this)
  218. .setTitle(R.string.are_you_sure)
  219. .setView(new TextView(this)
  220. .SetText(R.string.about_to_rm_directory))
  221. .setPositiveButton(R.string.ok, (dialogInterface, view) -> DoRemoveDir(f))
  222. .setNegativeButton(R.string.cancel, null)
  223. .show();
  224. } else {
  225. DoRemoveDir(f);
  226. }
  227. }
  228. @Override
  229. public void OnRestorePassword(File f) {
  230. FileUtils.RestoreFile(f);
  231. fPasswordListView.refresh();
  232. }
  233. @Override
  234. public void OnCopyToClipboard(File f) {
  235. FileInterfaceFactory.GetFileInterface(this, PasswordPickerFactory.GetPasswordPicker(this), f).ReadFile(new OnResponseListener<String>() {
  236. @Override
  237. public void OnResponse(String result) {
  238. ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
  239. if (clip != null) {
  240. clip.setPrimaryClip(ClipData.newPlainText("password", result));
  241. Toast.makeText(MainActivity.this, getString(R.string.copied_clipboard), Toast.LENGTH_LONG).show();
  242. }
  243. }
  244. @Override
  245. public void OnError(String msg, Throwable e) {
  246. Toast.makeText(MainActivity.this, "Error: " +msg, Toast.LENGTH_LONG).show();
  247. log.log(Level.WARNING, msg, e);
  248. }
  249. });
  250. }
  251. @Override
  252. public void OnEncryptionInformation(File f) {
  253. Intent i = new Intent(this, EncryptionInformationActivity.class);
  254. i.putExtra(EncryptionInformationActivity.FILE_PATH, f.getAbsolutePath());
  255. startActivity(i);
  256. }
  257. @Override
  258. public void onBackPressed() {
  259. if (!fPasswordListView.ShowParent())
  260. super.onBackPressed();
  261. }
  262. }