/** * Passman Android App * * @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com) * @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es) * @license GNU AGPL version 3 or any later version *

* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. *

* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. *

* You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package es.wolfi.app.passman.fragments; import android.app.ProgressDialog; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.koushikdutta.async.future.FutureCallback; import com.loopj.android.http.AsyncHttpResponseHandler; import java.util.concurrent.atomic.AtomicBoolean; import butterknife.BindView; import butterknife.ButterKnife; import es.wolfi.app.ResponseHandlers.CredentialAddFileResponseHandler; import es.wolfi.app.ResponseHandlers.CredentialSaveResponseHandler; import es.wolfi.app.passman.R; import es.wolfi.app.passman.SettingValues; import es.wolfi.app.passman.SingleTon; import es.wolfi.app.passman.activities.PasswordListActivity; import es.wolfi.app.passman.adapters.CustomFieldEditAdapter; import es.wolfi.app.passman.adapters.FileEditAdapter; import es.wolfi.passman.API.Credential; import es.wolfi.passman.API.CustomField; import es.wolfi.passman.API.Vault; import es.wolfi.utils.FileUtils; import es.wolfi.utils.ProgressUtils; /** * A simple {@link Fragment} subclass. * Use the {@link CredentialAddFragment#newInstance} factory method to * create an instance of this fragment. */ public class CredentialAddFragment extends Fragment implements View.OnClickListener { public static String CREDENTIAL = "credential"; @BindView(R.id.add_credential_label_header) TextView label_header; @BindView(R.id.add_credential_label) EditText label; @BindView(R.id.add_credential_user) EditText user; @BindView(R.id.add_credential_password) EditText password; @BindView(R.id.add_credential_email) EditText email; @BindView(R.id.add_credential_url) EditText url; @BindView(R.id.add_credential_description) EditText description; @BindView(R.id.filesList) RecyclerView filesList; @BindView(R.id.customFieldsList) RecyclerView customFieldsList; @BindView(R.id.customFieldType) Spinner customFieldType; private OnCredentialFragmentInteraction mListener; private Credential credential; private FileEditAdapter fed; private CustomFieldEditAdapter cfed; private RecyclerView filesListRecyclerView; private RecyclerView customFieldsListRecyclerView; private AtomicBoolean alreadySaving = new AtomicBoolean(false); public CredentialAddFragment() { // Required empty public constructor } /** * Use this factory method to create a new instance of this fragment. * * @return A new instance of fragment CredentialAddFragment. */ public static CredentialAddFragment newInstance() { return new CredentialAddFragment(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_credential_add, container, false); FloatingActionButton saveCredentialButton = (FloatingActionButton) view.findViewById(R.id.SaveCredentialButton); saveCredentialButton.setOnClickListener(this); saveCredentialButton.setVisibility(View.VISIBLE); Button addFileButton = (Button) view.findViewById(R.id.AddFileButton); addFileButton.setOnClickListener(this.getAddFileButtonListener()); addFileButton.setVisibility(View.VISIBLE); Button addCustomFieldButton = (Button) view.findViewById(R.id.AddCustomFieldButton); addCustomFieldButton.setOnClickListener(this.getAddCustomFieldButtonListener()); addCustomFieldButton.setVisibility(View.VISIBLE); fed = new FileEditAdapter(credential); cfed = new CustomFieldEditAdapter(credential); return view; } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnCredentialFragmentInteraction) { mListener = (OnCredentialFragmentInteraction) context; } } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); ButterKnife.bind(this, view); filesListRecyclerView = (RecyclerView) view.findViewById(R.id.filesList); filesListRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); filesListRecyclerView.setAdapter(fed); customFieldsListRecyclerView = (RecyclerView) view.findViewById(R.id.customFieldsList); customFieldsListRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); customFieldsListRecyclerView.setAdapter(cfed); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Vault v = (Vault) SingleTon.getTon().getExtra(SettingValues.ACTIVE_VAULT.toString()); this.credential = new Credential(); this.credential.setVault(v); } @Override public void onDetach() { super.onDetach(); mListener = null; } public void addSelectedFile(String encodedFile, String fileName, String mimeType, int fileSize, int requestCode) { Context context = getContext(); final ProgressDialog progress = ProgressUtils.showLoadingSequence(context); final AsyncHttpResponseHandler responseHandler = new CredentialAddFileResponseHandler(progress, getView(), fileName, requestCode, fed, cfed); // Start encryption a little later so that the main thread does not get stuck in the file selection dialog and it can close. new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { credential.uploadFile(context, encodedFile, fileName, mimeType, fileSize, responseHandler, progress); } }, 100); } public View.OnClickListener getAddFileButtonListener() { return new View.OnClickListener() { @Override public void onClick(View view) { ((PasswordListActivity) requireActivity()).selectFileToAdd(FileUtils.activityRequestFileCode.credentialAddFile.ordinal()); } }; } public View.OnClickListener getAddCustomFieldButtonListener() { return new View.OnClickListener() { @Override public void onClick(View view) { if (customFieldType.getSelectedItem().toString().equals("File")) { ((PasswordListActivity) requireActivity()).selectFileToAdd(FileUtils.activityRequestFileCode.credentialAddCustomFieldFile.ordinal()); } else { CustomField cf = new CustomField(); cf.setLabel("newLabel" + (cfed.getItemCount() + 1)); cf.setSecret(customFieldType.getSelectedItem().toString().toLowerCase().equals("password")); cf.setFieldType(customFieldType.getSelectedItem().toString().toLowerCase()); cf.setValue(""); cfed.addCustomField(cf); cfed.notifyDataSetChanged(); } } }; } @Override public void onClick(View view) { if (alreadySaving.get()) { return; } if (label.getText().toString().equals("")) { label_header.setTextColor(getResources().getColor(R.color.danger)); return; } this.credential.setLabel(label.getText().toString()); this.credential.setUsername(user.getText().toString()); this.credential.setPassword(password.getText().toString()); this.credential.setEmail(email.getText().toString()); this.credential.setUrl(url.getText().toString()); this.credential.setDescription(description.getText().toString()); this.credential.setOtp("{}"); this.credential.setFiles(fed.getFilesString()); this.credential.setCustomFields(cfed.getCustomFieldsString()); this.credential.setTags(""); this.credential.setFavicon(""); this.credential.setCompromised(false); this.credential.setHidden(false); alreadySaving.set(true); Context context = getContext(); final ProgressDialog progress = ProgressUtils.showLoadingSequence(context); final AsyncHttpResponseHandler responseHandler = new CredentialSaveResponseHandler(alreadySaving, false, progress, view, (PasswordListActivity) getActivity(), getFragmentManager()); this.credential.save(context, responseHandler); } /** * This interface must be implemented by activities that contain this * fragment to allow an interaction in this fragment to be communicated * to the activity and potentially other fragments contained in that * activity. *

* See the Android Training lesson Communicating with Other Fragments for more information. */ public interface OnCredentialFragmentInteraction { // TODO: Update argument type and name void onCredentialFragmentInteraction(Credential credential); void saveCredential(Credential credential, Context c, FutureCallback cb); } }