diff --git a/.idea/modules.xml b/.idea/modules.xml index c298687..24ae196 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,8 +2,8 @@ - + \ No newline at end of file diff --git a/MISPauth .pdf b/MISPauth .pdf deleted file mode 100644 index d2e41b2..0000000 Binary files a/MISPauth .pdf and /dev/null differ diff --git a/Screenshots/main-screen.png b/Screenshots/main-screen.png deleted file mode 100755 index 9861fab..0000000 Binary files a/Screenshots/main-screen.png and /dev/null differ diff --git a/Screenshots/my-org-screen.png b/Screenshots/my-org-screen.png deleted file mode 100755 index 7cded1f..0000000 Binary files a/Screenshots/my-org-screen.png and /dev/null differ diff --git a/Screenshots/scan-screen.png b/Screenshots/scan-screen.png deleted file mode 100755 index 43043ab..0000000 Binary files a/Screenshots/scan-screen.png and /dev/null differ diff --git a/Screenshots/share-screen.png b/Screenshots/share-screen.png deleted file mode 100755 index 50fa435..0000000 Binary files a/Screenshots/share-screen.png and /dev/null differ diff --git a/Screenshots/upload-screen.png b/Screenshots/upload-screen.png deleted file mode 100755 index c4dadfc..0000000 Binary files a/Screenshots/upload-screen.png and /dev/null differ diff --git a/app/build.gradle b/app/build.gradle index 347c82a..85861d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,10 +2,10 @@ apply plugin: 'com.android.application' android { compileSdkVersion 27 - buildToolsVersion "27.0.3" + buildToolsVersion "28.0.0" defaultConfig { applicationId "de.overview.wg.its.mispauth" - minSdkVersion 21 + minSdkVersion 23 targetSdkVersion 27 versionCode 1 versionName "1.0" @@ -17,6 +17,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + packagingOptions { + exclude 'META-INF/DEPENDENCIES' + } } dependencies { @@ -30,6 +33,13 @@ dependencies { implementation 'com.android.support:design:27.1.1' implementation 'com.android.volley:volley:1.1.0' - implementation 'com.github.kenglxn.QRGen:android:2.4.0' + implementation 'com.github.kenglxn.QRGen:android:2.5.0' + implementation group: 'org.mongodb', name: 'bson', version: '3.8.0' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5' + + + implementation 'com.google.android.gms:play-services-vision:15.0.2' + implementation 'com.android.support:gridlayout-v7:27.1.1' + implementation 'com.ernestoyaquello.stepperform:vertical-stepper-form:0.9.9' } diff --git a/app/src/androidTest/java/de/overview/wg/its/mispauth/ExampleInstrumentedTest.java b/app/src/androidTest/java/de/overview/wg/its/mispauth/ExampleInstrumentedTest.java index 4435194..0f87c70 100644 --- a/app/src/androidTest/java/de/overview/wg/its/mispauth/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/de/overview/wg/its/mispauth/ExampleInstrumentedTest.java @@ -16,11 +16,11 @@ import static org.junit.Assert.*; */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); - assertEquals("de.overview.wg.its.mispauth", appContext.getPackageName()); - } + assertEquals("de.overview.wg.its.mispauth", appContext.getPackageName()); + } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5e7973a..0f8a22f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,8 +12,9 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - - + @@ -22,15 +23,19 @@ + android:screenOrientation="portrait" + android:name=".SyncActivity" + android:label="Sync" + android:parentActivityName=".MainActivity" + android:theme="@style/AppTheme.Fullscreen"> + + android:name=".CredentialsActivity" + android:label="@string/credentials_activity" + android:parentActivityName=".MainActivity"/> + \ No newline at end of file diff --git a/app/src/main/java/de/overview/wg/its/mispauth/CredentialsActivity.java b/app/src/main/java/de/overview/wg/its/mispauth/CredentialsActivity.java new file mode 100644 index 0000000..9480de9 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/CredentialsActivity.java @@ -0,0 +1,417 @@ +package de.overview.wg.its.mispauth; + +import android.annotation.SuppressLint; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Build; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.design.widget.TextInputLayout; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ProgressBar; +import android.widget.TextView; +import com.android.volley.VolleyError; +import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; +import de.overview.wg.its.mispauth.auxiliary.ReadableError; +import de.overview.wg.its.mispauth.cam.DialogFactory; +import de.overview.wg.its.mispauth.model.Organisation; +import de.overview.wg.its.mispauth.model.User; +import de.overview.wg.its.mispauth.network.MispRequest; +import org.json.JSONException; +import org.json.JSONObject; + +@SuppressWarnings("ConstantConditions") +@SuppressLint("SetTextI18n") +public class CredentialsActivity extends AppCompatActivity implements View.OnClickListener { + + private boolean changesMade; + private boolean saveAuthkey, saveAuthkeyPrefSet; + + private TextInputLayout urlLayout, apiLayout; + private TextView emptyView; + private ViewGroup organisationView; + private ProgressBar progressBar; + + private Organisation myOrganisation; + private User myUser; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_credentials); + + initializeViews(); + loadPreferences(); + addSaveChangesListener(); + } + + private void initializeViews() { + + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayShowHomeEnabled(true); + + progressBar = findViewById(R.id.progressBar); + + urlLayout = findViewById(R.id.input_layout_server_url); + apiLayout = findViewById(R.id.input_layout_api_key); + + FloatingActionButton fab = findViewById(R.id.fab_download_own_org_info); + fab.setOnClickListener(this); + + emptyView = findViewById(R.id.empty); + organisationView = findViewById(R.id.myOrganisationView); + + } + + private void loadPreferences() { + + PreferenceManager preferenceManager = PreferenceManager.Instance(this); + + saveAuthkeyPrefSet = preferenceManager.saveAuthkeyEnabledExists(); + saveAuthkey = preferenceManager.saveAuthkeyEnabled(); + + urlLayout.getEditText().setText(preferenceManager.getMyServerUrl()); + apiLayout.getEditText().setText(preferenceManager.getMyServerApiKey()); + + myOrganisation = preferenceManager.getMyOrganisation(); + + if (myOrganisation == null) { + + emptyView.setVisibility(View.VISIBLE); + organisationView.setVisibility(View.GONE); + + } else { + + emptyView.setVisibility(View.GONE); + organisationView.setVisibility(View.VISIBLE); + + visualizeOrganisation(); + + } + } + + private void savePreferences() { + + PreferenceManager preferenceManager = PreferenceManager.Instance(this); + + preferenceManager.setMyServerUrl(urlLayout.getEditText().getText().toString()); + preferenceManager.setSaveAuthkeyEnabled(saveAuthkey); + + if (saveAuthkey) { + preferenceManager.setMyServerApiKey(apiLayout.getEditText().getText().toString()); + } else { + preferenceManager.setMyServerApiKey(""); + } + + if (myUser != null) { + preferenceManager.setMyUser(myUser); + } + + if (myOrganisation != null) { + preferenceManager.setMyOrganisation(myOrganisation); + } + + changesMade = false; + } + + private void addSaveChangesListener() { + urlLayout.getEditText().addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + changesMade = true; + } + }); + + apiLayout.getEditText().addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + changesMade = true; + } + }); + } + + private void tryDownloadOrgInfo() { + + if (myOrganisation != null) { + + DialogInterface.OnClickListener pos = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + downloadOrgInfo(); + } + }; + + new DialogFactory(this).createOverrideDialog(pos, null).show(); + + } else { + + downloadOrgInfo(); + + } + } + + private boolean validCredentials() { + boolean inputError = false; + String url = urlLayout.getEditText().getText().toString(); + String auth = apiLayout.getEditText().getText().toString(); + + if (url.equals("")) { + urlLayout.setError("Required"); + inputError = true; + } + + if (auth.equals("")) { + apiLayout.setError("Required"); + inputError = true; + } + + if (inputError) { + return false; + } + + urlLayout.setError(null); + apiLayout.setError(null); + + return true; + } + + private void downloadOrgInfo() { + + if (!validCredentials()) { + return; + } + + apiLayout.clearFocus(); + urlLayout.clearFocus(); + + progressBar.setVisibility(View.VISIBLE); + emptyView.setVisibility(View.GONE); + + final MispRequest mispRequest = MispRequest.Instance(this); + mispRequest.setServerCredentials(urlLayout.getEditText().getText().toString(), apiLayout.getEditText().getText().toString()); + + mispRequest.getMyUser(new MispRequest.UserCallback() { + @Override + public void onResult(JSONObject jsonUser) { + try { + myUser = new User(jsonUser); + } catch (JSONException e) { + makeSnackBar("Could not interpret user format"); + return; + } + + mispRequest.getOrganisation(myUser.getId(), new MispRequest.OrganisationCallback() { + @Override + public void onResult(JSONObject organisationInformation) { + try { + myOrganisation = new Organisation(organisationInformation); + changesMade = true; + } catch (JSONException e) { + makeSnackBar("Could not interpret organisation format"); + return; + } + + organisationView.setVisibility(View.VISIBLE); + emptyView.setVisibility(View.GONE); + + progressBar.setVisibility(View.GONE); + visualizeOrganisation(); + } + + @Override + public void onError(VolleyError volleyError) { + makeSnackBar(ReadableError.toReadable(volleyError)); + progressBar.setVisibility(View.GONE); + organisationView.setVisibility(View.GONE); + emptyView.setVisibility(View.VISIBLE); + } + }); + } + + @Override + public void onError(VolleyError volleyError) { + makeSnackBar(ReadableError.toReadable(volleyError)); + progressBar.setVisibility(View.GONE); + organisationView.setVisibility(View.GONE); + emptyView.setVisibility(View.VISIBLE); + } + }); + + } + + private void visualizeOrganisation() { + + TextView title = organisationView.findViewById(R.id.organisation_title); + title.setText(myOrganisation.getName()); + + TextView uuid = organisationView.findViewById(R.id.organisation_uuid); + uuid.setText(myOrganisation.getUuid()); + + TextView description = organisationView.findViewById(R.id.organisation_description); + description.setText(myOrganisation.getDescription()); + + TextView nationality = organisationView.findViewById(R.id.organisation_nationality); + nationality.setText(myOrganisation.getNationality()); + + TextView sector = findViewById(R.id.organisation_sector); + sector.setText(myOrganisation.getSector()); + + TextView users = findViewById(R.id.organisation_user_count); + + users.setText("" + myOrganisation.getUserCount()); + + } + + @Override + public void onClick(View v) { + int id = v.getId(); + + switch (id) { + case R.id.fab_download_own_org_info: + tryDownloadOrgInfo(); + break; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_credentials, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + int id = item.getItemId(); + + switch (id) { + case android.R.id.home: + exitSafely(); + return true; + +// case R.id.menu_item_deleteData: +// DialogInterface.OnClickListener pos = new DialogInterface.OnClickListener() { +// @Override +// public void onClick(DialogInterface dialog, int which) { +// PreferenceManager.Instance(getApplicationContext()).clearCredentialPreferences(); +// urlLayout.getEditText().setText(""); +// apiLayout.getEditText().setText(""); +// myOrganisation = null; +// myUser = null; +// emptyView.setVisibility(View.VISIBLE); +// organisationView.setVisibility(View.GONE); +// } +// }; +// +// new DialogFactory(this).createDeleteDialog(pos, null).show(); +// +// break; + + case R.id.load_config: + + // MOTOROLA + if (Build.VERSION.SDK_INT <= 25) { + urlLayout.getEditText().setText("http://192.168.178.200"); + apiLayout.getEditText().setText("dcfgDrNy3SyASmo9WRqyJ4LhsN1xWJ7phfTjklFa"); + } else { + urlLayout.getEditText().setText("http://192.168.178.201"); + apiLayout.getEditText().setText("5BGhMzdHIWvaxyrTUUVNk2NflDPzXJRZQvOa3CE2"); + } + break; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + exitSafely(); + } + + private void exitSafely() { + + if (changesMade || !saveAuthkeyPrefSet) { + saveDialog(new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + savePreferences(); + finish(); + } + }, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + } else { + finish(); + } + } + + private void makeSnackBar(String message) { + Snackbar.make(findViewById(R.id.coordinator), message, Snackbar.LENGTH_LONG).show(); + } + + private void saveDialog(DialogInterface.OnClickListener positive, DialogInterface.OnClickListener negative) { + AlertDialog.Builder adb = new AlertDialog.Builder(this); + + adb.setTitle(getResources().getString(R.string.unsaved_changes)); + adb.setMessage("\n" + getResources().getString(R.string.save_changes)); + + @SuppressLint("InflateParams") + View checkBoxView = getLayoutInflater().inflate(R.layout.dialog_save_authkey, null); + CheckBox c = checkBoxView.findViewById(R.id.checkbox); + c.setChecked(saveAuthkey); + + c.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + saveAuthkey = isChecked; + } + }); + + adb.setView(checkBoxView); + + adb.setPositiveButton(getResources().getString(R.string.save), positive); + adb.setNegativeButton(getResources().getString(R.string.discard), negative); + + Dialog d = adb.create(); + d.setCancelable(false); + d.getWindow().setWindowAnimations(R.style.DialogAnimation); + d.show(); + } + +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/MainActivity.java b/app/src/main/java/de/overview/wg/its/mispauth/MainActivity.java new file mode 100644 index 0000000..b788ee6 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/MainActivity.java @@ -0,0 +1,185 @@ +package de.overview.wg.its.mispauth; + +import android.annotation.SuppressLint; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; +import de.overview.wg.its.mispauth.adapter.SyncedPartnerAdapter; +import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; +import de.overview.wg.its.mispauth.model.SyncedPartner; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("ConstantConditions") +public class MainActivity extends AppCompatActivity { + + private List syncedPartnerList = new ArrayList<>(); + private SyncedPartnerAdapter syncedPartnerAdapter; + private TextView emptyPartnerListView; + private RecyclerView syncedPartnerRecyclerView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + initializeViews(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_main, menu); + + // Make icon white (compat limitation in xml) + Drawable drawable = menu.findItem(R.id.menu_item_credential_settings).getIcon(); + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable, ContextCompat.getColor(this, R.color.colorWhite)); + menu.findItem(R.id.menu_item_credential_settings).setIcon(drawable); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + switch (id) { + case R.id.menu_item_credential_settings: + startActivity(new Intent(this, CredentialsActivity.class)); + return true; + + case R.id.menu_item_delete_local_data: + createSelectDeleteDialog(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private void initializeViews() { + + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayShowTitleEnabled(true); + + FloatingActionButton fab = findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startSyncActivity(); + } + }); + + emptyPartnerListView = findViewById(R.id.empty); + syncedPartnerRecyclerView = findViewById(R.id.recyclerView); + + syncedPartnerAdapter = new SyncedPartnerAdapter(this, syncedPartnerList); + RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext()); + syncedPartnerRecyclerView.setLayoutManager(mLayoutManager); + syncedPartnerRecyclerView.setItemAnimator(new DefaultItemAnimator()); + syncedPartnerRecyclerView.setAdapter(syncedPartnerAdapter); + + refreshSyncedPartnerList(); + } + + private void createSelectDeleteDialog() { + + final PreferenceManager prefs = PreferenceManager.Instance(this); + + AlertDialog.Builder adb = new AlertDialog.Builder(this); + + adb.setTitle("Delete Local Data"); + adb.setMessage("(Checked items will be deleted)"); + + @SuppressLint("InflateParams") + View checkBoxView = getLayoutInflater().inflate(R.layout.dialog_select_delete_data, null); + + final CheckBox checkSyncedPartner = checkBoxView.findViewById(R.id.check_synced_partner_list); + final CheckBox checkCredentials = checkBoxView.findViewById(R.id.check_credentials); + final CheckBox checkUserData = checkBoxView.findViewById(R.id.check_user_preferences); + + adb.setView(checkBoxView); + + adb.setPositiveButton(getResources().getString(R.string.delete), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (checkSyncedPartner.isChecked()) { + prefs.clearSyncedInformationPreferences(); + } + + if (checkCredentials.isChecked()) { + prefs.clearCredentialPreferences(); + } + + if (checkUserData.isChecked()) { + prefs.clearUserPreferences(); + } + } + }); + + adb.setNegativeButton(getResources().getString(android.R.string.cancel), null); + + Dialog d = adb.create(); + d.getWindow().setWindowAnimations(R.style.DialogAnimation); + d.show(); + } + + private void refreshSyncedPartnerList() { + syncedPartnerList = PreferenceManager.Instance(this).getSyncedPartnerList(); + + if (syncedPartnerList == null) { + emptyPartnerListView.setVisibility(View.VISIBLE); + syncedPartnerRecyclerView.setVisibility(View.GONE); + } else { + syncedPartnerAdapter.setSyncedPartnerList(syncedPartnerList); + } + } + + private void startSyncActivity() { + + PreferenceManager preferenceManager = PreferenceManager.Instance(this); + + if (preferenceManager.getMyOrganisation() == null || preferenceManager.getMyUser() == null) { + + AlertDialog.Builder adb = new AlertDialog.Builder(this); + adb.setTitle(getResources().getString(R.string.missing_local_information_title)); + adb.setMessage(getResources().getString(R.string.missing_local_information_message)); + + adb.setPositiveButton(getResources().getString(R.string.enter_credentials), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + startCredentialsActivity(); + } + }); + + adb.setNegativeButton(android.R.string.cancel, null); + adb.show(); + + } else { + Intent intent = new Intent(this, SyncActivity.class); + startActivity(intent); + } + + } + + private void startCredentialsActivity() { + startActivity(new Intent(this, CredentialsActivity.class)); + } +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/SyncActivity.java b/app/src/main/java/de/overview/wg/its/mispauth/SyncActivity.java new file mode 100644 index 0000000..cf539f7 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/SyncActivity.java @@ -0,0 +1,281 @@ +package de.overview.wg.its.mispauth; + +import android.animation.Animator; +import android.content.Intent; +import android.graphics.Point; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.AppCompatActivity; +import android.view.Display; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.WindowManager; +import android.view.animation.DecelerateInterpolator; +import android.widget.*; +import com.google.gson.Gson; +import de.overview.wg.its.mispauth.auxiliary.AESSecurity; +import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; +import de.overview.wg.its.mispauth.auxiliary.RandomString; +import de.overview.wg.its.mispauth.auxiliary.TempAuth; +import de.overview.wg.its.mispauth.cam.CameraFragment; +import de.overview.wg.its.mispauth.model.*; +import net.glxn.qrgen.android.QRCode; + +public class SyncActivity extends AppCompatActivity implements View.OnClickListener { + + private static final String SCAN_PUB_KEY_FRAG_TAG = "scan_public_key_fragment_tag"; + private static final String SCAN_INFO_FRAG_TAG = "scan_info_fragment_tag"; + + private AESSecurity aesSecurity; + + private Fragment currentFragment; + private String currentFragmentTag; + + // Views for QR code + private LinearLayout qrBackground; + private ImageView qrImageView; + private Button forwardButton; + private TextView forwardDescription; + + private SyncInformationQr partnerInformation; + + private FragmentManager manager; + private FragmentTransaction transaction; + private PreferenceManager preferenceManager; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_public_key_exchange); + + manager = getSupportFragmentManager(); + preferenceManager = PreferenceManager.Instance(this); + + initializeViews(); + + aesSecurity = AESSecurity.getInstance(); + + setScanTypeFragment(0); + } + + private void initializeViews() { + + ImageButton closeButton = findViewById(R.id.close); + forwardButton = findViewById(R.id.forward); + + closeButton.setOnClickListener(this); + + forwardButton.setOnClickListener(this); + forwardButton.setEnabled(false); + forwardDescription = findViewById(R.id.forward_description); + + qrImageView = findViewById(R.id.qr_imageView); + qrBackground = findViewById(R.id.qr_background); + + setContinueScreenEnabled(false); + } + + private void setScanTypeFragment(int mode) { + + transaction = manager.beginTransaction(); + + User myUser = preferenceManager.getMyUser(); + Organisation myOrg = preferenceManager.getMyOrganisation(); + + switch (mode) { + + case 0: + + setContinueScreenEnabled(false); + + PublicKeyQr pkqr = new PublicKeyQr(myOrg.getName(), + myUser.getEmail(), + AESSecurity.publicKeyToString(aesSecurity.getPublicKey())); + + setQrContent(pkqr.toJSON().toString(), 0.6f); + + currentFragment = CameraFragment.newInstance(CameraFragment.ScanMode.PUBLIC_KEY); + currentFragmentTag = SCAN_PUB_KEY_FRAG_TAG; + + break; + + case 1: + + setContinueScreenEnabled(false); + + TempAuth.TMP_AUTH_KEY = new RandomString(40).nextString(); + + Server serverForMeOnOtherInstance = new Server(); + serverForMeOnOtherInstance.setAuthkey(TempAuth.TMP_AUTH_KEY); + serverForMeOnOtherInstance.setName("SyncServer for " + myOrg.getName()); + serverForMeOnOtherInstance.setUrl(preferenceManager.getMyServerUrl()); + + SyncInformationQr siqr = new SyncInformationQr( + preferenceManager.getMyOrganisation(), + serverForMeOnOtherInstance, + preferenceManager.getMyUser()); + + setQrContent(aesSecurity.encrypt(siqr.toJSON().toString()), 0.9f); + + currentFragment = CameraFragment.newInstance(CameraFragment.ScanMode.INFO); + currentFragmentTag = SCAN_INFO_FRAG_TAG; + + break; + + default: + + transaction.remove(currentFragment); + transaction.commit(); + + currentFragment = null; + + finish(); + + return; + } + + transaction.replace(R.id.fragment_container, currentFragment, currentFragmentTag); + transaction.commit(); + } + + private void setQrContent(String content, float qrToScreenRatio) { + + Display display = getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + + int width = (int) (size.x * qrToScreenRatio); + + //noinspection SuspiciousNameCombination + qrImageView.setImageBitmap(QRCode.from(content) + .withColor(0xFF000000, 0x00FFFFFF) + .withSize(width, width) + .bitmap()); + + circularReveal(qrBackground, true, 400); + } + + @Override + public void onClick(View v) { + + switch (v.getId()) { + + case R.id.close: + finish(); + break; + + case R.id.forward: + + if (currentFragmentTag.equals(SCAN_PUB_KEY_FRAG_TAG)) { + setScanTypeFragment(1); + } else if (currentFragmentTag.equals(SCAN_INFO_FRAG_TAG)) { + startUploadActivity(); + } + + break; + + } + } + + private void setContinueScreenEnabled(boolean enabled) { + if (enabled) { + forwardButton.setVisibility(View.VISIBLE); + forwardDescription.setVisibility(View.VISIBLE); + } else { + forwardButton.setVisibility(View.INVISIBLE); + forwardDescription.setVisibility(View.INVISIBLE); + } + + forwardButton.setEnabled(enabled); + } + + public void onPublicKeyResult(PublicKeyQr pkqr) { + aesSecurity.setForeignPublicKey(AESSecurity.publicKeyFromString(pkqr.getKey())); + + runOnUiThread(new Runnable() { + @Override + public void run() { + setContinueScreenEnabled(true); + } + }); + } + + public void onSyncInfoResult(SyncInformationQr siqr) { + partnerInformation = siqr; + + runOnUiThread(new Runnable() { + @Override + public void run() { + setContinueScreenEnabled(true); + } + }); + } + + private void startUploadActivity() { + + Intent i = new Intent(this, UploadActivity.class); + String partnerString = new Gson().toJson(partnerInformation); + i.putExtra(UploadActivity.PARTNER_INFO_BUNDLE_KEY, partnerString); + + startActivity(i); + finish(); + + } + + private void circularReveal(final View v, final boolean open, final long duration) { + + v.post(new Runnable() { + @Override + public void run() { + int cx = v.getWidth() / 2; + int cy = v.getHeight() / 2; + + float finalRadius = (float) Math.hypot(cx, cy); + + Animator anim; + + + if (open) { + anim = ViewAnimationUtils.createCircularReveal(v, cx, cy, 0, finalRadius); + v.setVisibility(View.VISIBLE); + } else { + anim = ViewAnimationUtils.createCircularReveal(v, cx, cy, finalRadius, 0); + } + + anim.setInterpolator(new DecelerateInterpolator()); + + anim.setDuration(duration); + anim.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!open) { + v.setVisibility(View.INVISIBLE); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + anim.start(); + } + }); + + } +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/UploadActivity.java b/app/src/main/java/de/overview/wg/its/mispauth/UploadActivity.java new file mode 100644 index 0000000..3219388 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/UploadActivity.java @@ -0,0 +1,240 @@ +package de.overview.wg.its.mispauth; + +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.View; +import com.android.volley.VolleyError; +import com.google.gson.Gson; +import de.overview.wg.its.mispauth.adapter.UploadStateAdapter; +import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; +import de.overview.wg.its.mispauth.auxiliary.ReadableError; +import de.overview.wg.its.mispauth.auxiliary.TempAuth; +import de.overview.wg.its.mispauth.model.*; +import de.overview.wg.its.mispauth.network.MispRequest; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("ConstantConditions") +public class UploadActivity extends AppCompatActivity implements View.OnClickListener { + + static final String PARTNER_INFO_BUNDLE_KEY = "partner_info"; + + private MispRequest mispRequest; + private SyncInformationQr partnerInformation; + + private Organisation partnerOrganisation; + private User partnerSyncUser; + private Server partnerServer; + + private UploadStateAdapter uploadStateAdapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_upload); + + Bundle b = getIntent().getExtras(); + assert b != null; + String info = b.getString(PARTNER_INFO_BUNDLE_KEY); + + partnerInformation = new Gson().fromJson(info, SyncInformationQr.class); + + mispRequest = MispRequest.Instance(this); + + initializeViews(); + + SyncUpload(); + } + + private void initializeViews() { + + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + getSupportActionBar().setDisplayShowHomeEnabled(false); + getSupportActionBar().setDisplayHomeAsUpEnabled(false); + getSupportActionBar().setDisplayShowTitleEnabled(true); + + FloatingActionButton fab = findViewById(R.id.fab); + fab.setOnClickListener(this); + + RecyclerView recyclerView = findViewById(R.id.recyclerView); + uploadStateAdapter = new UploadStateAdapter(); + RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this); + recyclerView.setLayoutManager(mLayoutManager); + recyclerView.setItemAnimator(new DefaultItemAnimator()); + recyclerView.setAdapter(uploadStateAdapter); + } + + @Override + public void onClick(View v) { + + int id = v.getId(); + + switch (id) { + case R.id.fab: + finish(); + break; + } + } + + private void setCurrentStateWrapper(int stateNumber, UploadState.State state) { + syncUploadStates.get(stateNumber).setCurrentState(state); + uploadStateAdapter.notifyItemChanged(stateNumber); + } + + private List syncUploadStates = new ArrayList<>(); + + private void SyncUpload() { + + partnerOrganisation = partnerInformation.getOrganisation(); + partnerSyncUser = partnerInformation.getUser(); + partnerServer = partnerInformation.getServer(); + + syncUploadStates.add(new UploadState("Add local organisation")); + syncUploadStates.add(new UploadState("Add sync user to organisation")); + syncUploadStates.add(new UploadState("Add external organisation")); + syncUploadStates.add(new UploadState("Add sync server")); + + uploadStateAdapter.setStateList(syncUploadStates); + + uploadSyncOrganisation(); + } + + private void uploadSyncOrganisation() { + setCurrentStateWrapper(0, UploadState.State.IN_PROGRESS); +// syncUploadStates.get(0).setCurrentState(UploadState.State.IN_PROGRESS); + mispRequest.addOrganisation(partnerOrganisation, new MispRequest.OrganisationCallback() { + @Override + public void onResult(JSONObject organisationInformation) { + try { + + Organisation retOrg = new Organisation(organisationInformation); + setCurrentStateWrapper(0, UploadState.State.DONE); +// syncUploadStates.get(0).setCurrentState(UploadState.State.DONE); + uploadSyncUser(retOrg.getId()); + + } catch (JSONException e) { + syncUploadStates.get(0).setError("Could not read server response"); + setCurrentStateWrapper(0, UploadState.State.ERROR); +// syncUploadStates.get(0).setCurrentState(UploadState.State.ERROR); + e.printStackTrace(); + } + + } + + @Override + public void onError(VolleyError volleyError) { + syncUploadStates.get(0).setError(ReadableError.toReadable(volleyError)); + setCurrentStateWrapper(0, UploadState.State.ERROR); +// syncUploadStates.get(0).setCurrentState(UploadState.State.ERROR); + } + }); + } + + private void uploadSyncUser(int orgID) { + + setCurrentStateWrapper(1, UploadState.State.IN_PROGRESS); +// syncUploadStates.get(1).setCurrentState(UploadState.State.IN_PROGRESS); + + partnerSyncUser.setOrgId(orgID); + partnerSyncUser.setAuthkey(TempAuth.TMP_AUTH_KEY); + partnerSyncUser.setRoleId(User.RoleId.SYNC_USER); + + mispRequest.addUser(partnerSyncUser, new MispRequest.UserCallback() { + @Override + public void onResult(JSONObject myUserInformation) { + setCurrentStateWrapper(1, UploadState.State.DONE); +// syncUploadStates.get(1).setCurrentState(UploadState.State.DONE); + uploadExternalSyncOrganisation(); + } + + @Override + public void onError(VolleyError volleyError) { + syncUploadStates.get(1).setError(ReadableError.toReadable(volleyError)); + setCurrentStateWrapper(1, UploadState.State.ERROR); +// syncUploadStates.get(1).setCurrentState(UploadState.State.ERROR); + } + }); + } + + private void uploadExternalSyncOrganisation() { + + setCurrentStateWrapper(2, UploadState.State.IN_PROGRESS); + + partnerOrganisation.setName(partnerOrganisation.getName() + " (Remote)"); + partnerOrganisation.setLocal(false); + + mispRequest.addOrganisation(partnerOrganisation, new MispRequest.OrganisationCallback() { + @Override + public void onResult(JSONObject organisationInformation) { + try { + + Organisation extOrg = new Organisation(organisationInformation); + setCurrentStateWrapper(2, UploadState.State.DONE); + uploadSyncServer(extOrg.getId()); + + } catch (JSONException e) { + syncUploadStates.get(2).setError("Could not read server response"); + setCurrentStateWrapper(2, UploadState.State.ERROR); + e.printStackTrace(); + } + } + + @Override + public void onError(VolleyError volleyError) { + syncUploadStates.get(2).setError(ReadableError.toReadable(volleyError)); + setCurrentStateWrapper(2, UploadState.State.ERROR); + } + }); + } + + private void uploadSyncServer(int remoteOrgId) { + + setCurrentStateWrapper(3, UploadState.State.IN_PROGRESS); + + partnerServer.setRemoteOrgId(remoteOrgId); + partnerServer.setPush(true); + + mispRequest.addServer(partnerServer, new MispRequest.ServerCallback() { + @Override + public void onResult(JSONObject servers) { + setCurrentStateWrapper(3, UploadState.State.DONE); + updateSyncedOrganisationList(); + } + + @Override + public void onError(VolleyError volleyError) { + syncUploadStates.get(3).setError(ReadableError.toReadable(volleyError)); + setCurrentStateWrapper(3, UploadState.State.ERROR); + } + }); + } + + private void updateSyncedOrganisationList() { + + PreferenceManager preferenceManager = PreferenceManager.Instance(this); + + List syncedPartnerList = preferenceManager.getSyncedPartnerList(); + + if (syncedPartnerList == null) { + syncedPartnerList = new ArrayList<>(); + } + + SyncedPartner sp = new SyncedPartner( + partnerInformation.getOrganisation().getName(), + partnerInformation.getServer().getUrl()); + + sp.generateTimeStamp(); + syncedPartnerList.add(sp); + preferenceManager.setSyncedPartnerList(syncedPartnerList); + } +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/activity/MainActivity.java b/app/src/main/java/de/overview/wg/its/mispauth/activity/MainActivity.java deleted file mode 100644 index 2ec5c12..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/activity/MainActivity.java +++ /dev/null @@ -1,125 +0,0 @@ -package de.overview.wg.its.mispauth.activity; - -import android.content.Intent; -import android.os.Bundle; -import android.support.design.widget.FloatingActionButton; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.DividerItemDecoration; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.Toast; -import com.android.volley.VolleyError; -import de.overview.wg.its.mispauth.R; -import de.overview.wg.its.mispauth.adapter.ExtOrgAdapter; -import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; -import de.overview.wg.its.mispauth.auxiliary.ReadableError; -import de.overview.wg.its.mispauth.model.Organisation; -import de.overview.wg.its.mispauth.network.MispRequest; -import org.json.JSONObject; - -public class MainActivity extends AppCompatActivity { - - private Organisation[] externalOrganisations; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - getExternalOrganisations(); - setUpRecyclerView(); - - FloatingActionButton fabAdd = findViewById(R.id.fab_add); - final FloatingActionButton fabSync = findViewById(R.id.fab_sync); - - fabAdd.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (fabSync.getVisibility() == View.GONE) { - fabSync.setVisibility(View.VISIBLE); - } else { - fabSync.setVisibility(View.GONE); - } - } - }); - - fabSync.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startSyncActivity(); - } - }); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - - if (id == R.id.menu_item_settings) { - startActivity(new Intent(this, SettingsActivity.class)); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private void setUpRecyclerView() { - RecyclerView orgRecyclerView = findViewById(R.id.orgRecyclerView); - orgRecyclerView.setHasFixedSize(true); - - RecyclerView.LayoutManager orgLayoutManager = new LinearLayoutManager(this); - orgRecyclerView.setLayoutManager(orgLayoutManager); - - DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(orgRecyclerView.getContext(), 1); - orgRecyclerView.addItemDecoration(dividerItemDecoration); - - RecyclerView.Adapter orgAdapter = new ExtOrgAdapter(this, externalOrganisations); - orgRecyclerView.setAdapter(orgAdapter); - - if (externalOrganisations.length == 0) { - orgRecyclerView.setVisibility(View.GONE); - findViewById(R.id.empty_view).setVisibility(View.VISIBLE); - } else { - orgRecyclerView.setVisibility(View.VISIBLE); - findViewById(R.id.empty_view).setVisibility(View.GONE); - } - - final SwipeRefreshLayout refreshLayout = findViewById(R.id.recycler_refresh); - refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - // TODO do stuff - // refreshLayout.setRefreshing(false); - } - }); - } - - private void getExternalOrganisations() { - Organisation a = new Organisation(); - a.setName("Ferrari"); - a.setDescription("Ferrari has nothing to share"); - a.setSector("Fast cars"); - a.setNationality("Italy"); - a.setLocal(true); - - externalOrganisations = new Organisation[]{a}; - } - - private void startSyncActivity() { - startActivity(new Intent(this, SyncActivity.class)); - } -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/activity/SettingsActivity.java b/app/src/main/java/de/overview/wg/its/mispauth/activity/SettingsActivity.java deleted file mode 100644 index 658f90f..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/activity/SettingsActivity.java +++ /dev/null @@ -1,211 +0,0 @@ -package de.overview.wg.its.mispauth.activity; - -import android.os.Bundle; -import android.support.design.widget.Snackbar; -import android.support.design.widget.TextInputLayout; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.ProgressBar; -import android.widget.TextView; -import com.android.volley.VolleyError; -import de.overview.wg.its.mispauth.R; -import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; -import de.overview.wg.its.mispauth.auxiliary.ReadableError; -import de.overview.wg.its.mispauth.model.Organisation; -import de.overview.wg.its.mispauth.model.User; -import de.overview.wg.its.mispauth.network.MispRequest; -import org.json.JSONObject; - -public class SettingsActivity extends AppCompatActivity { - - private static final String TAG = "DEBUG"; - - private PreferenceManager preferenceManager; - private ProgressBar progressBar; - private TextInputLayout serverUrlLayout, apiKeyLayout; - private EditText serverUrlText, apiKeyText; - - private Organisation org; - private User user; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_settings); - - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - getSupportActionBar().setDisplayShowHomeEnabled(true); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - serverUrlLayout = findViewById(R.id.input_layout_server_url); - apiKeyLayout = findViewById(R.id.input_layout_api_key); - serverUrlText = findViewById(R.id.edit_server_url); - apiKeyText = findViewById(R.id.edit_api_key); - progressBar = findViewById(R.id.progressBar); - - findViewById(R.id.fab_download_own_org_info).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - downloadMyOrgInfo(); - } - }); - - apiKeyText.setOnKeyListener(new View.OnKeyListener() { - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (keyCode == 66) { - hideKeyboard(v); - apiKeyText.clearFocus(); - return true; - } - return false; - } - }); - - restoreSavedValues(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_settings, menu); - return true; - } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - - if (id == R.id.menu_item_deleteData) { - serverUrlText.setText(""); - apiKeyText.setText(""); - preferenceManager.deleteAllLocalData(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private void setOrganisation(Organisation org) { - - if(org == null) { - return; - } - - TextView title = findViewById(R.id.organisation_title); - TextView uuid = findViewById(R.id.organisation_uuid); - TextView description = findViewById(R.id.organisation_description); - TextView nationality = findViewById(R.id.organisation_nationality); - TextView sector = findViewById(R.id.organisation_sector); - TextView userCount = findViewById(R.id.organisation_user_count); - - title.setText(org.getName()); - uuid.setText(org.getUuid()); - description.setText(org.getDescription()); - nationality.setText(org.getNationality()); - sector.setText(org.getSector()); - userCount.setText("" + org.getUserCount()); - } - - private void restoreSavedValues() { - preferenceManager = PreferenceManager.Instance(this); - - serverUrlText.setText(preferenceManager.getMyServerUrl()); - apiKeyText.setText(preferenceManager.getMyServerApiKey()); - - setOrganisation(preferenceManager.getMyOrganisation()); - } - - private void downloadMyOrgInfo(){ - user = new User(); - org = new Organisation(); - - boolean failed = false; - - String tmpServerUrl = serverUrlText.getText().toString(); - String tmpApiKey = apiKeyText.getText().toString(); - - if(tmpServerUrl.isEmpty()) { - serverUrlLayout.setError("Server URL is required"); - failed = true; - } - - if(tmpApiKey.isEmpty()) { - apiKeyLayout.setError("API Key is required"); - failed = true; - } - - if(failed) { - return; - } else { - serverUrlLayout.setError(null); - apiKeyLayout.setError(null); - } - - final MispRequest request = MispRequest.Instance(this); - request.setServerCredentials(tmpServerUrl, tmpApiKey); - - progressBar.setVisibility(View.VISIBLE); - - request.myUserInformation(new MispRequest.UserCallback() { - - @Override - public void onResult(JSONObject myUserInformation) { - - user.fromJSON(myUserInformation); - preferenceManager.setMyUser(user); - - int orgID = user.getOrgId(); - - request.getOrganisation(orgID, new MispRequest.OrganisationCallback() { - - @Override - public void onResult(JSONObject organisationInformation) { - progressBar.setVisibility(View.GONE); - - org.fromJSON(organisationInformation); - - preferenceManager.setMyOrganisation(org); - - setOrganisation(org); - } - - @Override - public void onError(VolleyError volleyError) { - progressBar.setVisibility(View.GONE); - MakeSnackbar(ReadableError.toReadable(volleyError)); - Log.e(TAG, "onError: " + volleyError.toString()); - } - }); - } - - @Override - public void onError(VolleyError volleyError) { - progressBar.setVisibility(View.GONE); - MakeSnackbar(ReadableError.toReadable(volleyError)); - } - }); - - // If auth was successful: save new credentials - preferenceManager.setMyServerUrl(tmpServerUrl); - preferenceManager.setMyServerApiKey(tmpApiKey); - } - - private void MakeSnackbar(String msg){ - View contextView = findViewById(R.id.coordinator); - Snackbar.make(contextView, msg, Snackbar.LENGTH_LONG).show(); - } - - private void hideKeyboard(View view) { - InputMethodManager manager = (InputMethodManager) view.getContext().getSystemService(INPUT_METHOD_SERVICE); - if (manager != null) { - manager.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - } -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/activity/SyncActivity.java b/app/src/main/java/de/overview/wg/its/mispauth/activity/SyncActivity.java deleted file mode 100644 index 0daec67..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/activity/SyncActivity.java +++ /dev/null @@ -1,218 +0,0 @@ -package de.overview.wg.its.mispauth.activity; - -import android.os.Bundle; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.View; -import android.widget.Button; -import de.overview.wg.its.mispauth.R; -import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; -import de.overview.wg.its.mispauth.fragment.*; - -public class SyncActivity extends AppCompatActivity { - - private static final String TAG = "DEBUG"; - - private PreferenceManager preferenceManager; - - private Button prevButton, nextButton; - private int partnerChoice = -1; - - private int currentFragmentPosition = 0; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_sync); - - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - preferenceManager = PreferenceManager.Instance(this); - - nextButton = findViewById(R.id.nextButton); - prevButton = findViewById(R.id.backButton); - - nextButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getFragment(currentFragmentPosition + 1, true); - } - }); - prevButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getFragment(currentFragmentPosition - 1, true); - } - }); - - getFragment(0, false); - } - - private void getNextFragment() { - getFragment(currentFragmentPosition + 1, true); - } - - private void getFragment(int position, boolean animate) { - - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - - if (animate) { - if (position > currentFragmentPosition) { - transaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right); - } else { - transaction.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left); - } - } - - currentFragmentPosition = position; - - switch (position) { - - case 0: - prevButton.setEnabled(false); - nextButton.setEnabled(false); - - prevButton.setText(R.string.back); - nextButton.setText(R.string.next); - - transaction.replace(R.id.fragmentContainer, new SyncStartFragment()); - break; - - case 1: - prevButton.setEnabled(true); - - prevButton.setText(R.string.back); - nextButton.setText(R.string.next); - - if (partnerChoice == 1) { - - nextButton.setEnabled(false); - transaction.replace(R.id.fragmentContainer, new ScanQrFragment(), "FRAGMENT_SCAN"); - - } else { - - nextButton.setEnabled(true); - transaction.replace(R.id.fragmentContainer, new ShowQrFragment(), "FRAGMENT_SHOW"); - - } - break; - - case 2: - if (partnerChoice == 1) { - - prevButton.setEnabled(true); - nextButton.setEnabled(true); - - prevButton.setText(R.string.reject); - nextButton.setText(R.string.accept); - - transaction.replace(R.id.fragmentContainer, new ReviewQrFragment()); - - } else { - - prevButton.setEnabled(true); - nextButton.setEnabled(false); - - prevButton.setText(R.string.back); - nextButton.setText(R.string.next); - - transaction.replace(R.id.fragmentContainer, new ScanQrFragment(), "FRAGMENT_SCAN"); - } - break; - - case 3: - - if (partnerChoice == 1) { - prevButton.setEnabled(true); - nextButton.setEnabled(true); - - prevButton.setText(R.string.back); - nextButton.setText(R.string.next); - - transaction.replace(R.id.fragmentContainer, new ShowQrFragment(), "FRAGMENT_SHOW"); - } else { - prevButton.setEnabled(true); - nextButton.setEnabled(true); - - prevButton.setText(R.string.reject); - nextButton.setText(R.string.accept); - - transaction.replace(R.id.fragmentContainer, new ReviewQrFragment()); - } - break; - - case 4: - - nextButton.setText(R.string.done); - nextButton.setEnabled(true); - - nextButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); - - transaction.replace(R.id.fragmentContainer, new UploadFragment()); - - break; - } - - transaction.commit(); - } - - public void setPartnerChoice(int choice) { - - partnerChoice = choice; - - if (choice != -1) { - nextButton.setEnabled(true); - } else { - nextButton.setEnabled(false); - } - } - - public void setScannedQr(String qr) { - - runOnUiThread(new Runnable() { - @Override - public void run() { - if (partnerChoice == 1) { - getFragment(2, true); - } else { - getFragment(3, true); - } - } - }); - -// try { -// OrganisationDialog d = new OrganisationDialog(this); -// Organisation o = new Organisation(); -// o.fromJSON(new JSONObject(qr)); -// d.createAcceptDialog(o, new OrganisationDialog.DialogCallback() { -// @Override -// public void onAccept() { -// nextButton.setEnabled(true); -// } -// -// @Override -// public void onReject() { -// scanFragment.setReadQr(true); -// scanFragment.openCamera(); -// } -// }); -// -// } catch (JSONException e) { -// e.printStackTrace(); -// } - - } - - public void uploadReady() { - nextButton.setEnabled(true); - } -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/adapter/ExtOrgAdapter.java b/app/src/main/java/de/overview/wg/its/mispauth/adapter/ExtOrgAdapter.java deleted file mode 100644 index 50ffb71..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/adapter/ExtOrgAdapter.java +++ /dev/null @@ -1,64 +0,0 @@ -package de.overview.wg.its.mispauth.adapter; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.RelativeLayout; -import android.widget.TextView; -import de.overview.wg.its.mispauth.R; -import de.overview.wg.its.mispauth.auxiliary.OrganisationDialog; -import de.overview.wg.its.mispauth.model.Organisation; - -public class ExtOrgAdapter extends RecyclerView.Adapter { - - private Context context; - private Organisation[] dataSet; - - public ExtOrgAdapter(Context context, Organisation[] dataSet) { - this.context = context; - this.dataSet = dataSet; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View extOrgView = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_holder_ext_org, parent, false); - return new ViewHolder(extOrgView); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, final int position) { - holder.orgTitle.setText(dataSet[position].getName()); - holder.subTitle.setText(dataSet[position].getDescription()); - - holder.parentLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - OrganisationDialog d = new OrganisationDialog(context); - d.createInfoDialog(dataSet[position]); - } - }); - } - - @Override - public int getItemCount() { - return dataSet.length; - } - - public static class ViewHolder extends RecyclerView.ViewHolder{ - - RelativeLayout parentLayout; - TextView orgTitle; - TextView subTitle; - - public ViewHolder(View v) { - super(v); - parentLayout = v.findViewById(R.id.parent_layout); - orgTitle = v.findViewById(R.id.ext_org_title); - subTitle = v.findViewById(R.id.ext_org_sub_title); - } - } -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/adapter/SyncedPartnerAdapter.java b/app/src/main/java/de/overview/wg/its/mispauth/adapter/SyncedPartnerAdapter.java new file mode 100644 index 0000000..b62e7ee --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/adapter/SyncedPartnerAdapter.java @@ -0,0 +1,58 @@ +package de.overview.wg.its.mispauth.adapter; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import de.overview.wg.its.mispauth.R; +import de.overview.wg.its.mispauth.model.SyncedPartner; + +import java.util.List; + +public class SyncedPartnerAdapter extends RecyclerView.Adapter { + + private List syncedPartnerList; + + public class MyViewHolder extends RecyclerView.ViewHolder { + public TextView title, dateAdded, url; + public MyViewHolder(View view) { + super(view); + title = view.findViewById(R.id.title); + dateAdded = view.findViewById(R.id.dateSynced); + url = view.findViewById(R.id.url); + } + } + + public SyncedPartnerAdapter(Context context, List syncedPartnerList) { + this.syncedPartnerList = syncedPartnerList; + } + + public void setSyncedPartnerList(List syncedPartnerList) { + this.syncedPartnerList = syncedPartnerList; + notifyDataSetChanged(); + } + + @NonNull + @Override + public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_synced_organisation, parent, false); + return new MyViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { + SyncedPartner syncedPartner = syncedPartnerList.get(position); + + holder.title.setText(syncedPartner.getName()); + holder.url.setText(syncedPartner.getUrl()); + holder.dateAdded.setText(syncedPartner.getSyncDate()); + } + + @Override + public int getItemCount() { + return syncedPartnerList.size(); + } +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/adapter/UploadStateAdapter.java b/app/src/main/java/de/overview/wg/its/mispauth/adapter/UploadStateAdapter.java new file mode 100644 index 0000000..4d3d9ce --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/adapter/UploadStateAdapter.java @@ -0,0 +1,93 @@ +package de.overview.wg.its.mispauth.adapter; + +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import de.overview.wg.its.mispauth.R; +import de.overview.wg.its.mispauth.model.UploadState; + +import java.util.ArrayList; +import java.util.List; + +public class UploadStateAdapter extends RecyclerView.Adapter { + + private List stateList = new ArrayList<>(); + + class MyViewHolder extends RecyclerView.ViewHolder { + + private TextView title, error; + private ImageView pendingIcon, errorIcon, doneIcon, inProgressIcon; + + private MyViewHolder(View view) { + + super(view); + + title = view.findViewById(R.id.title); + error = view.findViewById(R.id.state_error_text); + + pendingIcon = view.findViewById(R.id.state_pending); + errorIcon = view.findViewById(R.id.state_error); + doneIcon = view.findViewById(R.id.state_done); + inProgressIcon = view.findViewById(R.id.state_in_progress); + } + + private void setState(UploadState.State state) { + + error.setVisibility(View.GONE); + errorIcon.setVisibility(View.GONE); + pendingIcon.setVisibility(View.GONE); + doneIcon.setVisibility(View.GONE); + inProgressIcon.setVisibility(View.GONE); + + switch (state) { + case PENDING: + pendingIcon.setVisibility(View.VISIBLE); + break; + + case IN_PROGRESS: + inProgressIcon.setVisibility(View.VISIBLE); + break; + + case DONE: + doneIcon.setVisibility(View.VISIBLE); + break; + + case ERROR: + errorIcon.setVisibility(View.VISIBLE); + error.setVisibility(View.VISIBLE); + break; + } + } + } + + public void setStateList(List stateList) { + this.stateList = stateList; + notifyDataSetChanged(); + } + + @NonNull + @Override + public UploadStateAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_upload_state, parent, false); + return new UploadStateAdapter.MyViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull UploadStateAdapter.MyViewHolder holder, int position) { + UploadState state = stateList.get(position); + + holder.title.setText(state.getTitle()); + holder.error.setText(state.getError()); + holder.setState(stateList.get(position).getCurrentState()); + } + + @Override + public int getItemCount() { + return stateList.size(); + } + +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/AESSecurity.java b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/AESSecurity.java new file mode 100644 index 0000000..e116bc1 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/AESSecurity.java @@ -0,0 +1,119 @@ +package de.overview.wg.its.mispauth.auxiliary; + +import android.util.Base64; + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +public class AESSecurity { + + private static final String TAG = "MISP_LOGGING"; + private static final String ALGORITHM = "AES"; + + private static AESSecurity instance; + + private PublicKey publickey; + private KeyAgreement keyAgreement; + private byte[] sharedSecret; + + private AESSecurity() { + initialize(); + } + + private void initialize() { + KeyPairGenerator kpg = null; + + try { + kpg = KeyPairGenerator.getInstance("EC"); + kpg.initialize(256); + + KeyPair kp = kpg.generateKeyPair(); + + publickey = kp.getPublic(); + + keyAgreement = KeyAgreement.getInstance("ECDH"); + keyAgreement.init(kp.getPrivate()); + + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + e.printStackTrace(); + } + } + + public void setForeignPublicKey(PublicKey publickey) { + try { + keyAgreement.doPhase(publickey, true); + sharedSecret = keyAgreement.generateSecret(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } + } + + public String encrypt(String data) { + try { + Key key = generateKey(); + Cipher c = Cipher.getInstance(ALGORITHM); + c.init(Cipher.ENCRYPT_MODE, key); + + byte[] encVal = c.doFinal(data.getBytes()); + + return Base64.encodeToString(encVal, 0); + + } catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return data; + } + + public String decrypt(String data) { + try { + Key key = generateKey(); + Cipher c = Cipher.getInstance(ALGORITHM); + c.init(Cipher.DECRYPT_MODE, key); + + byte[] decoded = Base64.decode(data, 0); + byte[] decValue = c.doFinal(decoded); + return new String(decValue); + } catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return data; + } + + public PublicKey getPublicKey() { + return publickey; + } + + private Key generateKey() { + return new SecretKeySpec(sharedSecret, ALGORITHM); + } + + public static String publicKeyToString(PublicKey key) { + return Base64.encodeToString(key.getEncoded(), Base64.DEFAULT); + } + public static PublicKey publicKeyFromString(String key) { + + KeyFactory kf = null; + + byte[] input = Base64.decode(key, Base64.DEFAULT); + + try { + kf = KeyFactory.getInstance("EC"); // normal: DH + return kf.generatePublic(new X509EncodedKeySpec(input)); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + e.printStackTrace(); + } + + return null; + } + + public static AESSecurity getInstance() { + if(instance == null) { + instance = new AESSecurity(); + } + + return instance; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/OrganisationDialog.java b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/OrganisationDialog.java deleted file mode 100644 index 42b2abd..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/OrganisationDialog.java +++ /dev/null @@ -1,94 +0,0 @@ -package de.overview.wg.its.mispauth.auxiliary; - -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.support.v7.app.AlertDialog; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; -import de.overview.wg.its.mispauth.R; -import de.overview.wg.its.mispauth.model.Organisation; - -public class OrganisationDialog { - - private AlertDialog.Builder dialogBuilder; - private LayoutInflater inflater; - - public OrganisationDialog(Context context) { - dialogBuilder = new AlertDialog.Builder(context); - inflater = ((Activity)context).getLayoutInflater(); - } - - public void createInfoDialog(Organisation org) { - - View dialogContent = inflater.inflate(R.layout.view_holder_organisation, null); - dialogBuilder.setView(dialogContent); - - TextView title = dialogContent.findViewById(R.id.organisation_title); - title.setText(org.getName()); - - TextView uuid = dialogContent.findViewById(R.id.organisation_uuid); - uuid.setText(org.getUuid()); - - TextView description = dialogContent.findViewById(R.id.organisation_description); - description.setText(org.getDescription()); - - TextView sector = dialogContent.findViewById(R.id.organisation_sector); - sector.setText(org.getSector()); - - TextView nationality = dialogContent.findViewById(R.id.organisation_nationality); - nationality.setText(org.getNationality()); - - TextView userCount = dialogContent.findViewById(R.id.organisation_user_count); - userCount.setText("" + org.getUserCount()); - - dialogBuilder.setPositiveButton("OK", null); - dialogBuilder.setCancelable(true); - dialogBuilder.create().show(); - } - - public void createAcceptDialog(Organisation org, final DialogCallback callback) { - View dialogContent = inflater.inflate(R.layout.view_holder_organisation, null); - dialogBuilder.setView(dialogContent); - - TextView title = dialogContent.findViewById(R.id.organisation_title); - title.setText(org.getName()); - - TextView uuid = dialogContent.findViewById(R.id.organisation_uuid); - uuid.setText(org.getUuid()); - - TextView description = dialogContent.findViewById(R.id.organisation_description); - description.setText(org.getDescription()); - - TextView sector = dialogContent.findViewById(R.id.organisation_sector); - sector.setText(org.getSector()); - - TextView nationality = dialogContent.findViewById(R.id.organisation_nationality); - nationality.setText(org.getNationality()); - - TextView userCount = dialogContent.findViewById(R.id.organisation_user_count); - userCount.setText("" + org.getUserCount()); - - dialogBuilder.setNegativeButton("REJECT", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - callback.onReject(); - } - }); - dialogBuilder.setPositiveButton("ACCEPT", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - callback.onAccept(); - } - }); - - dialogBuilder.setCancelable(false); - dialogBuilder.create().show(); - } - - public interface DialogCallback { - void onAccept(); - void onReject(); - } -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/PreferenceManager.java b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/PreferenceManager.java index 9efd6b2..74d73a1 100644 --- a/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/PreferenceManager.java +++ b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/PreferenceManager.java @@ -2,31 +2,72 @@ package de.overview.wg.its.mispauth.auxiliary; import android.content.Context; import android.content.SharedPreferences; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import de.overview.wg.its.mispauth.model.Organisation; +import de.overview.wg.its.mispauth.model.SyncedPartner; import de.overview.wg.its.mispauth.model.User; import org.json.JSONException; import org.json.JSONObject; +import java.lang.reflect.Type; +import java.util.List; + public class PreferenceManager { private static PreferenceManager instance; - private SharedPreferences sharedPreferences; + + private SharedPreferences userPreferences; + private SharedPreferences credentialPreferences; + private SharedPreferences syncedInstancesPreferences; + + private static final String CREDENTIAL_PREFERENCE = "de.overview.wg.its.mispauth.credential_preference"; + private static final String SAVED_INSTANCES_PREFERENCE = "de.overview.wg.its.mispauth.saved_instances_preference"; + private static final String USER_PREFERENCE = "de.overview.wg.its.mispauth.user_preferences"; private static String PREF_KEY_SERVER_URL = "key_server_url"; private static String PREF_KEY_SERVER_API_KEY = "key_server_api_key"; private static String PREF_KEY_MY_ORGANISATION = "key_my_organisation"; private static String PREF_KEY_MY_USER = "key_my_user"; + private static String PREF_KEY_SAVE_AUTHKEY_ENABLED = "key_save_authkey_enabled"; + private static String PREF_KEY_SYNCED_ORGANISATIONS = "key_synced_organisations"; private PreferenceManager(Context context) { - sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(context); + credentialPreferences = context.getSharedPreferences(CREDENTIAL_PREFERENCE, Context.MODE_PRIVATE); + syncedInstancesPreferences = context.getSharedPreferences(SAVED_INSTANCES_PREFERENCE, Context.MODE_PRIVATE); + userPreferences = context.getSharedPreferences(USER_PREFERENCE, Context.MODE_PRIVATE); } + public List getSyncedPartnerList() { + String list = syncedInstancesPreferences.getString(PREF_KEY_SYNCED_ORGANISATIONS, ""); + Type type = new TypeToken>() {}.getType(); + return new Gson().fromJson(list, type); + } + public void setSyncedPartnerList(List syncedPartnerList) { + String json = new Gson().toJson(syncedPartnerList); + SharedPreferences.Editor editor = syncedInstancesPreferences.edit(); + editor.putString(PREF_KEY_SYNCED_ORGANISATIONS, json); + editor.apply(); + } + + public boolean saveAuthkeyEnabledExists() { + return userPreferences.contains(PREF_KEY_SAVE_AUTHKEY_ENABLED); + } + public boolean saveAuthkeyEnabled() { + return userPreferences.getBoolean(PREF_KEY_SAVE_AUTHKEY_ENABLED, false); + } + public void setSaveAuthkeyEnabled(boolean save) { + SharedPreferences.Editor editor = userPreferences.edit(); + editor.putBoolean(PREF_KEY_SAVE_AUTHKEY_ENABLED, save); + editor.apply(); + } + /** * @return own Organisation if available, else null */ public Organisation getMyOrganisation() { try { - JSONObject jsonObject = new JSONObject(sharedPreferences.getString(PREF_KEY_MY_ORGANISATION, "")); + JSONObject jsonObject = new JSONObject(credentialPreferences.getString(PREF_KEY_MY_ORGANISATION, "")); Organisation org = new Organisation(); org.fromJSON(jsonObject); return org; @@ -37,14 +78,14 @@ public class PreferenceManager { return null; } public void setMyOrganisation(Organisation org) { - SharedPreferences.Editor editor = sharedPreferences.edit(); + SharedPreferences.Editor editor = credentialPreferences.edit(); editor.putString(PREF_KEY_MY_ORGANISATION, org.toJSON().toString()); editor.apply(); } public User getMyUser() { try { - JSONObject jsonObject = new JSONObject(sharedPreferences.getString(PREF_KEY_MY_USER, "")); + JSONObject jsonObject = new JSONObject(credentialPreferences.getString(PREF_KEY_MY_USER, "")); User user = new User(); user.fromJSON(jsonObject); return user; @@ -55,34 +96,40 @@ public class PreferenceManager { return null; } public void setMyUser(User user) { - SharedPreferences.Editor editor = sharedPreferences.edit(); + SharedPreferences.Editor editor = credentialPreferences.edit(); editor.putString(PREF_KEY_MY_USER, user.toJSON().toString()); editor.apply(); } public String getMyServerUrl() { - return sharedPreferences.getString(PREF_KEY_SERVER_URL, ""); + return credentialPreferences.getString(PREF_KEY_SERVER_URL, ""); } public void setMyServerUrl(String serverUrl) { - SharedPreferences.Editor editor = sharedPreferences.edit(); + SharedPreferences.Editor editor = credentialPreferences.edit(); editor.putString(PREF_KEY_SERVER_URL, serverUrl); editor.apply(); } public String getMyServerApiKey() { - return sharedPreferences.getString(PREF_KEY_SERVER_API_KEY, ""); + return credentialPreferences.getString(PREF_KEY_SERVER_API_KEY, ""); } public void setMyServerApiKey(String apiKey) { - SharedPreferences.Editor editor = sharedPreferences.edit(); + SharedPreferences.Editor editor = credentialPreferences.edit(); editor.putString(PREF_KEY_SERVER_API_KEY, apiKey); editor.apply(); } - public void deleteAllLocalData() { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.clear(); - editor.apply(); + + public void clearUserPreferences() { + userPreferences.edit().clear().apply(); } + public void clearCredentialPreferences() { + credentialPreferences.edit().clear().apply(); + } + public void clearSyncedInformationPreferences() { + syncedInstancesPreferences.edit().clear().apply(); + } + public static PreferenceManager Instance(Context context) { if(instance == null) { diff --git a/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/RandomString.java b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/RandomString.java new file mode 100644 index 0000000..6a26fec --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/RandomString.java @@ -0,0 +1,51 @@ +package de.overview.wg.its.mispauth.auxiliary; + +import java.security.SecureRandom; +import java.util.Locale; +import java.util.Objects; +import java.util.Random; + +public class RandomString { + + private static final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String lower = upper.toLowerCase(Locale.ROOT); + private static final String digits = "0123456789"; + private static final String alphaNum = upper + lower + digits; + + private final Random random; + private final char[] symbols; + private final char[] buf; + + public RandomString(int length, Random random, String symbols) { + + if (length < 1) { + throw new IllegalArgumentException(); + } + if (symbols.length() < 2) { + throw new IllegalArgumentException(); + } + + this.random = Objects.requireNonNull(random); + this.symbols = symbols.toCharArray(); + this.buf = new char[length]; + + } + + public RandomString(int length, Random random) { + this(length, random, alphaNum); + } + + public RandomString(int length) { + this(length, new SecureRandom()); + } + + public String nextString() { + + for (int idx = 0; idx < buf.length; ++idx) { + buf[idx] = symbols[random.nextInt(symbols.length)]; + } + + return new String(buf); + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/TempAuth.java b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/TempAuth.java new file mode 100644 index 0000000..ed23b81 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/auxiliary/TempAuth.java @@ -0,0 +1,7 @@ +package de.overview.wg.its.mispauth.auxiliary; + +public class TempAuth { + + public static String TMP_AUTH_KEY; + +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/cam/AutoFitTextureView.java b/app/src/main/java/de/overview/wg/its/mispauth/cam/AutoFitTextureView.java new file mode 100644 index 0000000..d2305e8 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/cam/AutoFitTextureView.java @@ -0,0 +1,76 @@ +package de.overview.wg.its.mispauth.cam; + +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.content.Context; +import android.util.AttributeSet; +import android.view.TextureView; + +/** + * A {@link TextureView} that can be adjusted to a specified aspect ratio. + */ +public class AutoFitTextureView extends TextureView { + + private int mRatioWidth = 0; + private int mRatioHeight = 0; + + public AutoFitTextureView(Context context) { + this(context, null); + } + + public AutoFitTextureView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio + * calculated from the parameters. Note that the actual sizes of parameters don't matter, that + * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result. + * + * @param width Relative horizontal size + * @param height Relative vertical size + */ + public void setAspectRatio(int width, int height) { + if (width < 0 || height < 0) { + throw new IllegalArgumentException("Size cannot be negative."); + } + mRatioWidth = width; + mRatioHeight = height; + requestLayout(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + if (0 == mRatioWidth || 0 == mRatioHeight) { + setMeasuredDimension(width, height); + } else { + if (width < height * mRatioWidth / mRatioHeight) { + setMeasuredDimension(width, width * mRatioHeight / mRatioWidth); + } else { + setMeasuredDimension(height * mRatioWidth / mRatioHeight, height); + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/overview/wg/its/mispauth/cam/CameraFragment.java b/app/src/main/java/de/overview/wg/its/mispauth/cam/CameraFragment.java new file mode 100644 index 0000000..4d05141 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/cam/CameraFragment.java @@ -0,0 +1,929 @@ +package de.overview.wg.its.mispauth.cam; + +import android.Manifest; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.graphics.*; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.Image; +import android.media.ImageReader; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.renderscript.*; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.util.Size; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.view.*; +import android.widget.Toast; +import com.google.android.gms.vision.Frame; +import com.google.android.gms.vision.barcode.Barcode; +import com.google.android.gms.vision.barcode.BarcodeDetector; +import de.overview.wg.its.mispauth.SyncActivity; +import de.overview.wg.its.mispauth.R; +import de.overview.wg.its.mispauth.auxiliary.AESSecurity; +import de.overview.wg.its.mispauth.model.PublicKeyQr; +import de.overview.wg.its.mispauth.model.SyncInformationQr; +import org.json.JSONException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +public class CameraFragment extends Fragment implements ActivityCompat.OnRequestPermissionsResultCallback { + + private SyncActivity parentActivity; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + parentActivity = (SyncActivity) context; + } + + /** + * Conversion from screen rotation to JPEG orientation. + */ + private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); + private static final int REQUEST_CAMERA_PERMISSION = 1; + private static final String FRAGMENT_DIALOG = "dialog"; + + static { + ORIENTATIONS.append(Surface.ROTATION_0, 90); + ORIENTATIONS.append(Surface.ROTATION_90, 0); + ORIENTATIONS.append(Surface.ROTATION_180, 270); + ORIENTATIONS.append(Surface.ROTATION_270, 180); + } + + /** + * Tag for the {@link Log}. + */ + private static final String TAG = "MISP_LOGGING"; + + /** + * Max preview width that is guaranteed by Camera2 API + */ + private static final int MAX_PREVIEW_WIDTH = 1920; + + /** + * Max preview height that is guaranteed by Camera2 API + */ + private static final int MAX_PREVIEW_HEIGHT = 1080; + + /** + * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a + * {@link TextureView}. + */ + private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { + openCamera(width, height); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { + configureTransform(width, height); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture texture) { + } + + }; + + /** + * ID of the current {@link CameraDevice}. + */ + private String mCameraId; + + /** + * An {@link AutoFitTextureView} for camera preview. + */ + private AutoFitTextureView mTextureView; + + /** + * A {@link CameraCaptureSession } for camera preview. + */ + private CameraCaptureSession mCaptureSession; + + /** + * A reference to the opened {@link CameraDevice}. + */ + private CameraDevice mCameraDevice; + + /** + * The {@link android.util.Size} of camera preview. + */ + private Size mPreviewSize; + + /** + * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state. + */ + private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { + + @Override + public void onOpened(@NonNull CameraDevice cameraDevice) { + // This method is called when the camera is opened. We start camera preview here. + mCameraOpenCloseLock.release(); + mCameraDevice = cameraDevice; + createCameraPreviewSession(); + } + + @Override + public void onDisconnected(@NonNull CameraDevice cameraDevice) { + mCameraOpenCloseLock.release(); + cameraDevice.close(); + mCameraDevice = null; + } + + @Override + public void onError(@NonNull CameraDevice cameraDevice, int error) { + mCameraOpenCloseLock.release(); + cameraDevice.close(); + mCameraDevice = null; + Activity activity = getActivity(); + if (null != activity) { + activity.finish(); + } + } + + }; + + /** + * An additional thread for running tasks that shouldn't block the UI. + */ + private HandlerThread mBackgroundThread; + + /** + * A {@link Handler} for running tasks in the background. + */ + private Handler mBackgroundHandler; + + /** + * An {@link ImageReader} that handles still image capture. + */ + private ImageReader mImageReader; + + /** + * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a + * still image is ready to be saved. + */ + private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + + Image image = reader.acquireNextImage(); + Bitmap bitmap = YUV2Bitmap(image); + +// final Bitmap bitmap = invertBitmap(YUV2Bitmap(image)); + + if (bitmap != null) { + + if (readQrEnabled) { + + Frame frame = new Frame.Builder().setBitmap(bitmap).build(); + SparseArray barcodes = barcodeDetector.detect(frame); + + if (barcodes.size() > 0) { + if (currentScanMode == ScanMode.PUBLIC_KEY) { + returnPublicKey(barcodes.valueAt(0).rawValue); + } else { + returnSyncInformation(barcodes.valueAt(0).rawValue); + } + } + } + } + + if (image != null) { + image.close(); + } + } + + }; + + /** + * {@link CaptureRequest.Builder} for the camera preview + */ + private CaptureRequest.Builder mPreviewRequestBuilder; + + /** + * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder} + */ + private CaptureRequest mPreviewRequest; + + /** + * A {@link Semaphore} to prevent the app from exiting before closing the camera. + */ + private Semaphore mCameraOpenCloseLock = new Semaphore(1); + + /** + * Shows a {@link Toast} on the UI thread. + * + * @param text The message to show + */ + private void showToast(final String text) { + final Activity activity = getActivity(); + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(activity, text, Toast.LENGTH_SHORT).show(); + } + }); + } + } + + /** + * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that + * is at least as large as the respective texture view size, and that is at most as large as the + * respective max size, and whose aspect ratio matches with the specified value. If such size + * doesn't exist, choose the largest one that is at most as large as the respective max size, + * and whose aspect ratio matches with the specified value. + * + * @param choices The list of sizes that the camera supports for the intended output + * class + * @param textureViewWidth The width of the texture view relative to sensor coordinate + * @param textureViewHeight The height of the texture view relative to sensor coordinate + * @param maxWidth The maximum width that can be chosen + * @param maxHeight The maximum height that can be chosen + * @param aspectRatio The aspect ratio + * @return The optimal {@code Size}, or an arbitrary one if none were big enough + */ + private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { + + // Collect the supported resolutions that are at least as big as the preview Surface + List bigEnough = new ArrayList<>(); + // Collect the supported resolutions that are smaller than the preview Surface + List notBigEnough = new ArrayList<>(); + int w = aspectRatio.getWidth(); + int h = aspectRatio.getHeight(); + for (Size option : choices) { + if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && option.getHeight() == option.getWidth() * h / w) { + if (option.getWidth() >= textureViewWidth && option.getHeight() >= textureViewHeight) { + bigEnough.add(option); + } else { + notBigEnough.add(option); + } + } + } + + // Pick the smallest of those big enough. If there is no one big enough, pick the + // largest of those not big enough. + if (bigEnough.size() > 0) { + return Collections.min(bigEnough, new CompareSizesByArea()); + } else if (notBigEnough.size() > 0) { + return Collections.max(notBigEnough, new CompareSizesByArea()); + } else { + Log.e(TAG, "Couldn't find any suitable preview size"); + return choices[0]; + } + } + + public static CameraFragment newInstance(ScanMode mode) { + + CameraFragment f = new CameraFragment(); + + Bundle args = new Bundle(); + args.putSerializable(BUNDLE_MODE_KEY, mode); + f.setArguments(args); + + return f; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View v = inflater.inflate(R.layout.fragment_camera, container, false); + + initRenderScript(); + + setUpBarcodeDetector(); + checkBundle(); + + return v; + } + + @Override + public void onViewCreated(final View view, Bundle savedInstanceState) { + mTextureView = view.findViewById(R.id.texture); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } + + @Override + public void onResume() { + super.onResume(); + startBackgroundThread(); + + // When the screen is turned off and turned back on, the SurfaceTexture is already + // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open + // a camera and start preview from here (otherwise, we wait until the surface is ready in + // the SurfaceTextureListener). + if (mTextureView.isAvailable()) { + openCamera(mTextureView.getWidth(), mTextureView.getHeight()); + } else { + mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); + } + } + + @Override + public void onPause() { + closeCamera(); + stopBackgroundThread(); + super.onPause(); + } + + private void requestCameraPermission() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { + new ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); + } else { + requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == REQUEST_CAMERA_PERMISSION) { + if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { + ErrorDialog.newInstance("REQUEST PERMISSION").show(getChildFragmentManager(), FRAGMENT_DIALOG); + } + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + + /** + * Sets up member variables related to camera. + * + * @param width The width of available size for camera preview + * @param height The height of available size for camera preview + */ + @SuppressWarnings("SuspiciousNameCombination") + private void setUpCameraOutputs(int width, int height) { + Activity activity = getActivity(); + CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); + + try { + for (String cameraId : manager.getCameraIdList()) { + CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); + + // We don't use a front facing camera in this sample. + Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); + if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { + continue; + } + + StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + if (map == null) { + continue; + } + + // For still image captures, we use the largest available size. + Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)), new CompareSizesByArea()); + + mImageReader = ImageReader.newInstance(largest.getWidth() / 16, largest.getHeight() / 16, ImageFormat.YUV_420_888, 2); + mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); + + // Find out if we need to swap dimension to get the preview size relative to sensor coordinate. + int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + //noinspection ConstantConditions + int mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + boolean swappedDimensions = false; + switch (displayRotation) { + case Surface.ROTATION_0: + case Surface.ROTATION_180: + if (mSensorOrientation == 90 || mSensorOrientation == 270) { + swappedDimensions = true; + } + break; + case Surface.ROTATION_90: + case Surface.ROTATION_270: + if (mSensorOrientation == 0 || mSensorOrientation == 180) { + swappedDimensions = true; + } + break; + default: + Log.e(TAG, "Display rotation is invalid: " + displayRotation); + } + + Point displaySize = new Point(); + activity.getWindowManager().getDefaultDisplay().getSize(displaySize); + + int rotatedPreviewWidth = width; + int rotatedPreviewHeight = height; + int maxPreviewWidth = displaySize.x; + int maxPreviewHeight = displaySize.y; + + if (swappedDimensions) { + rotatedPreviewWidth = height; + rotatedPreviewHeight = width; + maxPreviewWidth = displaySize.y; + maxPreviewHeight = displaySize.x; + } + + if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { + maxPreviewWidth = MAX_PREVIEW_WIDTH; + } + + if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { + maxPreviewHeight = MAX_PREVIEW_HEIGHT; + } + + // Danger, W.R.! Attempting to use too large a preview size could exceed the camera + // bus' bandwidth limitation, resulting in gorgeous previews but the storage of + // garbage capture data. + mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), + rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, + maxPreviewHeight, largest); + + // We fit the aspect ratio of TextureView to the size of preview we picked. + int orientation = getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight()); + } else { + mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth()); + } + + mCameraId = cameraId; + return; + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } catch (NullPointerException e) { + // Currently an NPE is thrown when the Camera2API is used but not supported on the + // device this code runs. + ErrorDialog.newInstance("CAMERA ERROR").show(getChildFragmentManager(), FRAGMENT_DIALOG); + } + } + + /** + * Opens the camera specified by {@link CameraFragment#mCameraId}. + */ + private void openCamera(int width, int height) { + + if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + requestCameraPermission(); + return; + } + + setUpCameraOutputs(width, height); + configureTransform(width, height); + + Activity activity = getActivity(); + CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); + + try { + + if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { + throw new RuntimeException("Time out waiting to lock camera opening."); + } + + manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted while trying to lock camera opening.", e); + } + } + + /** + * Closes the current {@link CameraDevice}. + */ + private void closeCamera() { + try { + mCameraOpenCloseLock.acquire(); + + if (null != mCaptureSession) { + mCaptureSession.close(); + mCaptureSession = null; + } + + if (null != mCameraDevice) { + mCameraDevice.close(); + mCameraDevice = null; + } + + if (null != mImageReader) { + mImageReader.close(); + mImageReader = null; + } + + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted while trying to lock camera closing.", e); + } finally { + mCameraOpenCloseLock.release(); + } + } + + /** + * Starts a background thread and its {@link Handler}. + */ + private void startBackgroundThread() { + mBackgroundThread = new HandlerThread("CameraBackground"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + } + + /** + * Stops the background thread and its {@link Handler}. + */ + private void stopBackgroundThread() { + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + mBackgroundThread = null; + mBackgroundHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * Creates a new {@link CameraCaptureSession} for camera preview. + */ + private void createCameraPreviewSession() { + try { + SurfaceTexture texture = mTextureView.getSurfaceTexture(); + assert texture != null; + + // We configure the size of default buffer to be the size of camera preview we want. + texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); + + // This is the output Surface we need to start preview. + Surface surface = new Surface(texture); + Surface mImageSurface = mImageReader.getSurface(); + + // We set up a CaptureRequest.Builder with the output Surface. + mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mPreviewRequestBuilder.addTarget(surface); + mPreviewRequestBuilder.addTarget(mImageSurface); + + // Here, we create a CameraCaptureSession for camera preview. + mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), + new CameraCaptureSession.StateCallback() { + + @Override + public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { + // The camera is already closed + if (null == mCameraDevice) { + return; + } + + // When the session is ready, we start displaying the preview. + mCaptureSession = cameraCaptureSession; + try { + // Auto focus should be continuous for camera preview. + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + + // Finally, we start displaying the camera preview. + mPreviewRequest = mPreviewRequestBuilder.build(); + mCaptureSession.setRepeatingRequest(mPreviewRequest, null, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + @Override + public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { + showToast("Failed"); + } + }, null + ); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + /** + * Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`. + * This method should be called after the camera preview size is determined in + * setUpCameraOutputs and also the size of `mTextureView` is fixed. + * + * @param viewWidth The width of `mTextureView` + * @param viewHeight The height of `mTextureView` + */ + private void configureTransform(int viewWidth, int viewHeight) { + Activity activity = getActivity(); + + if (null == mTextureView || null == mPreviewSize || null == activity) { + return; + } + + int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + Matrix matrix = new Matrix(); + RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); + RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); + float centerX = viewRect.centerX(); + float centerY = viewRect.centerY(); + if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { + bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); + matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); + float scale = Math.max( + (float) viewHeight / mPreviewSize.getHeight(), + (float) viewWidth / mPreviewSize.getWidth()); + matrix.postScale(scale, scale, centerX, centerY); + matrix.postRotate(90 * (rotation - 2), centerX, centerY); + } else if (Surface.ROTATION_180 == rotation) { + matrix.postRotate(180, centerX, centerY); + } + mTextureView.setTransform(matrix); + } + + /** + * Compares two {@code Size}s based on their areas. + */ + static class CompareSizesByArea implements Comparator { + + @Override + public int compare(Size lhs, Size rhs) { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); + } + + } + + + /** + * Shows an error message dialog. + */ + public static class ErrorDialog extends DialogFragment { + + private static final String ARG_MESSAGE = "message"; + + public static ErrorDialog newInstance(String message) { + ErrorDialog dialog = new ErrorDialog(); + Bundle args = new Bundle(); + args.putString(ARG_MESSAGE, message); + dialog.setArguments(args); + return dialog; + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + return new AlertDialog.Builder(activity) + .setMessage(getArguments().getString(ARG_MESSAGE)) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + activity.finish(); + } + }) + .create(); + } + + } + + /** + * Shows OK/Cancel confirmation dialog about camera permission. + */ + public static class ConfirmationDialog extends DialogFragment { + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Fragment parent = getParentFragment(); + return new AlertDialog.Builder(getActivity()) + .setMessage("REQUEST PERMISSION") + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + parent.requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Activity activity = parent.getActivity(); + if (activity != null) { + activity.finish(); + } + } + }) + .create(); + } + } + + + private static final String BUNDLE_MODE_KEY = "mode"; + + public enum ScanMode { + PUBLIC_KEY, + INFO + } + + private ScanMode currentScanMode; + private boolean readQrEnabled = true; + private BarcodeDetector barcodeDetector; + + private RenderScript renderScript; + private static final Matrix4f TRANSFORMATION_MATRIX = new Matrix4f(new float[]{ + -0.33f, -0.33f, -0.33f, 1.0f, + -0.59f, -0.59f, -0.59f, 1.0f, + -0.11f, -0.11f, -0.11f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }); + + private void initRenderScript() { + renderScript = RenderScript.create(getActivity()); + } + + private Bitmap YUV2Bitmap(Image image) { + + if (image == null) { + return null; + } + + ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(renderScript, Element.U8_4(renderScript)); + + int W = image.getWidth(); + int H = image.getHeight(); + + Image.Plane Y = image.getPlanes()[0]; + Image.Plane U = image.getPlanes()[1]; + Image.Plane V = image.getPlanes()[2]; + + int Yb = Y.getBuffer().remaining(); + int Ub = U.getBuffer().remaining(); + int Vb = V.getBuffer().remaining(); + + byte[] data = new byte[Yb + Ub + Vb]; + + Y.getBuffer().get(data, 0, Yb); + V.getBuffer().get(data, Yb, Vb); + U.getBuffer().get(data, Yb + Vb, Ub); + + Type.Builder yuvType = new Type.Builder(renderScript, Element.U8(renderScript)).setX(data.length); + Allocation in = Allocation.createTyped(renderScript, yuvType.create(), Allocation.USAGE_SCRIPT); + + Type.Builder rgbaType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript)).setX(W).setY(H); + Allocation out = Allocation.createTyped(renderScript, rgbaType.create(), Allocation.USAGE_SCRIPT); + + final Bitmap bmpout = Bitmap.createBitmap(W, H, Bitmap.Config.ARGB_8888); + + in.copyFromUnchecked(data); + + yuvToRgbIntrinsic.setInput(in); + yuvToRgbIntrinsic.forEach(out); + + out.copyTo(bmpout); + image.close(); + + return bmpout; + } + + private Bitmap invertBitmap(Bitmap source) { + + final Bitmap result = source.copy(source.getConfig(), true); + Allocation input = Allocation.createFromBitmap(renderScript, source, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + Allocation output = Allocation.createTyped(renderScript, input.getType()); + + final ScriptIntrinsicColorMatrix inverter = ScriptIntrinsicColorMatrix.create(renderScript); + inverter.setColorMatrix(TRANSFORMATION_MATRIX); + inverter.forEach(input, output); + output.copyTo(result); + + source.recycle(); + renderScript.destroy(); + + return result; + } + + private void setReadQrEnabled(boolean enabled) { + readQrEnabled = enabled; + } + + private void checkBundle() { + Bundle args = getArguments(); + + if (args != null) { + currentScanMode = (ScanMode) args.getSerializable(BUNDLE_MODE_KEY); + } + } + + private void returnPublicKey(String qrData) { + + setReadQrEnabled(false); + + try { + final PublicKeyQr pkqr = new PublicKeyQr(qrData); +// parentActivity.qrVisible(false, 300); + + DialogInterface.OnClickListener positive = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + + parentActivity.onPublicKeyResult(pkqr); +// parentActivity.qrVisible(true, 800); + + closeCamera(); + + parentActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mTextureView.setVisibility(View.GONE); + } + }); + } + }; + + DialogInterface.OnClickListener negative = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { +// parentActivity.qrVisible(true, 800); + setReadQrEnabled(true); + } + }; + + DialogFactory diaFac = new DialogFactory(parentActivity); + diaFac.createKeyDialog(pkqr, positive, negative).show(); + + } catch (JSONException e) { + e.printStackTrace(); + } + } + + private void returnSyncInformation(String qrData) { + + setReadQrEnabled(false); + + AESSecurity aesSecurity = AESSecurity.getInstance(); + String decryptedQrData = aesSecurity.decrypt(qrData); + + final SyncInformationQr siqr; + + try { + siqr = new SyncInformationQr(decryptedQrData); + + DialogInterface.OnClickListener positive = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + parentActivity.onSyncInfoResult(siqr); + + closeCamera(); + + parentActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mTextureView.setVisibility(View.GONE); + } + }); + } + }; + + DialogInterface.OnClickListener negative = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + setReadQrEnabled(true); + } + }; + + DialogFactory diaFac = new DialogFactory(parentActivity); + diaFac.createInformationDialog(siqr, positive, negative).show(); + + } catch (JSONException e) { + e.printStackTrace(); + } + + } + + private void setUpBarcodeDetector() { + barcodeDetector = new BarcodeDetector.Builder(getActivity()) + .setBarcodeFormats(Barcode.QR_CODE) + .build(); + + if (!barcodeDetector.isOperational()) { + Toast.makeText(getActivity(), "Could not setup QR-Code scanner!", Toast.LENGTH_SHORT).show(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/overview/wg/its/mispauth/cam/DialogFactory.java b/app/src/main/java/de/overview/wg/its/mispauth/cam/DialogFactory.java new file mode 100644 index 0000000..9a944b9 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/cam/DialogFactory.java @@ -0,0 +1,136 @@ +package de.overview.wg.its.mispauth.cam; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.*; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; +import de.overview.wg.its.mispauth.R; +import de.overview.wg.its.mispauth.model.PublicKeyQr; +import de.overview.wg.its.mispauth.model.SyncInformationQr; + +public class DialogFactory { + + private Context context; + + private AlertDialog.Builder adb; + private LayoutInflater inflater; + + + public DialogFactory(Context context) { + this.context = context; + adb = new AlertDialog.Builder(context); + inflater = LayoutInflater.from(context); + } + + + public Dialog createKeyDialog(PublicKeyQr pkqr, + DialogInterface.OnClickListener positiveListener, + DialogInterface.OnClickListener negativeListener) { + + View title = inflater.inflate(R.layout.dialog_public_key, null); + adb.setCustomTitle(title); + + adb.setMessage("\nYou received a Public Key from " + pkqr.getOrganisation() + " (" + pkqr.getUser() + ")"); + + adb.setPositiveButton(context.getResources().getString(R.string.accept), positiveListener); + adb.setNegativeButton(context.getResources().getString(R.string.reject), negativeListener); + + adb.setCancelable(false); + + Dialog d = adb.create(); + d.getWindow().setWindowAnimations(R.style.DialogAnimation); + d.getWindow().setDimAmount(0.8f); + return d; + } + + + public Dialog createInformationDialog(SyncInformationQr siqr, + DialogInterface.OnClickListener positiv, + DialogInterface.OnClickListener negativ) { + + View title = inflater.inflate(R.layout.dialog_sync_info, null); + adb.setCustomTitle(title); + + View orgView = inflater.inflate(R.layout.view_organisation, null); + + TextView orgTitle = orgView.findViewById(R.id.organisation_title); + orgTitle.setText(siqr.getOrganisation().getName()); + + TextView orgUuid = orgView.findViewById(R.id.organisation_uuid); + orgUuid.setText(siqr.getOrganisation().getUuid()); + + TextView orgDesc = orgView.findViewById(R.id.organisation_description); + orgDesc.setText(siqr.getOrganisation().getDescription()); + + TextView orgNat = orgView.findViewById(R.id.organisation_nationality); + orgNat.setText(siqr.getOrganisation().getNationality()); + + TextView orgSec = orgView.findViewById(R.id.organisation_sector); + orgSec.setText(siqr.getOrganisation().getSector()); + + TextView orgUser = orgView.findViewById(R.id.organisation_user_count); + orgUser.setText("" + siqr.getOrganisation().getUserCount()); + + adb.setView(orgView); + + adb.setPositiveButton(context.getResources().getString(R.string.accept), positiv); + adb.setNegativeButton(context.getResources().getString(R.string.reject), negativ); + + Dialog d = adb.create(); + d.getWindow().setWindowAnimations(R.style.DialogAnimation); + d.getWindow().setDimAmount(0.8f); + return d; + } + + + public Dialog createDeleteDialog(DialogInterface.OnClickListener pos, + DialogInterface.OnClickListener neg) { + adb.setTitle(context.getResources().getString(R.string.delete_local_data)); + adb.setMessage(context.getResources().getString(R.string.delete_local_data_msg)); + + adb.setPositiveButton(context.getResources().getString(R.string.delete), pos); + adb.setNegativeButton(android.R.string.cancel, neg); + + adb.setCancelable(true); + Dialog d = adb.create(); + d.getWindow().setWindowAnimations(R.style.DialogAnimation); + return d; + } + + public Dialog createSelectDeleteDialog(DialogInterface.OnClickListener pos, + DialogInterface.OnClickListener neg) { + + adb.setTitle("Delete local data"); + adb.setMessage(""); + + adb.setPositiveButton(context.getResources().getString(R.string.delete), pos); + adb.setNegativeButton(android.R.string.cancel, neg); + + adb.setCancelable(true); + Dialog d = adb.create(); + d.getWindow().setWindowAnimations(R.style.DialogAnimation); + return d; + } + + + public Dialog createOverrideDialog(DialogInterface.OnClickListener pos, + DialogInterface.OnClickListener neg) { + + adb.setTitle(context.getResources().getString(R.string.override_local_data)); + adb.setMessage(context.getResources().getString(R.string.override_local_data_msg)); + + adb.setPositiveButton(context.getResources().getString(R.string.override), pos); + adb.setNegativeButton(android.R.string.cancel, null); + + Dialog d = adb.create(); + + d.setCancelable(false); + d.getWindow().setWindowAnimations(R.style.DialogAnimation); + + return d; + } + + +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/fragment/ReviewQrFragment.java b/app/src/main/java/de/overview/wg/its/mispauth/fragment/ReviewQrFragment.java deleted file mode 100644 index fe1cd04..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/fragment/ReviewQrFragment.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.overview.wg.its.mispauth.fragment; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import de.overview.wg.its.mispauth.R; - -public class ReviewQrFragment extends Fragment { - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_review_qr, null); - return v; - } -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/fragment/ScanQrFragment.java b/app/src/main/java/de/overview/wg/its/mispauth/fragment/ScanQrFragment.java deleted file mode 100644 index b7ce186..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/fragment/ScanQrFragment.java +++ /dev/null @@ -1,361 +0,0 @@ -package de.overview.wg.its.mispauth.fragment; - -import android.Manifest; -import android.content.Context; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.ImageFormat; -import android.graphics.SurfaceTexture; -import android.hardware.camera2.*; -import android.hardware.camera2.params.StreamConfigurationMap; -import android.media.Image; -import android.media.ImageReader; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.renderscript.*; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.Fragment; -import android.support.v4.content.ContextCompat; -import android.util.Size; -import android.util.SparseArray; -import android.view.*; -import android.widget.Toast; -import com.google.android.gms.vision.Frame; -import com.google.android.gms.vision.barcode.Barcode; -import com.google.android.gms.vision.barcode.BarcodeDetector; -import de.overview.wg.its.mispauth.R; -import de.overview.wg.its.mispauth.activity.SyncActivity; - -import java.util.Arrays; -import java.util.List; - -public class ScanQrFragment extends Fragment { - - private static final int CAMERA_REQUEST_CODE = 0; - - private HandlerThread backgroundThread; - private Handler backgroundHandler; - - private CameraManager cameraManager; - private CameraDevice cameraDevice; - private String cameraID; - private CameraCaptureSession cameraCaptureSession; - - private SurfaceTexture previewSurfaceTexture; - private Surface previewSurface, yuvSurface; - private Size[] yuvSizes; - - private BarcodeDetector barcodeDetector; - private TextureView previewView; - - private boolean readQr = true; - - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_sync_scan, null); - - previewView = v.findViewById(R.id.texture_scan_preview); - - if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - - if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.CAMERA)) { - - } else { - ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE); - } - - } else { - setUpBarcodeDetector(); - setUpPreviewTexture(); - } - - return v; - } - - @Override - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { - switch (requestCode) { - case CAMERA_REQUEST_CODE: { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - setUpBarcodeDetector(); - setUpPreviewTexture(); - } else { - Toast.makeText(getActivity(), "Camera permission needed!", Toast.LENGTH_SHORT).show(); - } - } - } - } - - @Override - public void onResume() { - super.onResume(); - openBackgroundThread(); - } - - @Override - public void onStop() { - super.onStop(); - closeCamera(); - closeBackgroundThread(); - } - - - private void returnResult(String qrData) { - ((SyncActivity) getActivity()).setScannedQr(qrData); - } - - private void setUpPreviewTexture() { - - previewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - surface.setDefaultBufferSize(width, height); - previewSurfaceTexture = surface; - setUpCamera(); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - }); - - } - - private void setUpCamera() { - cameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE); - - try { - for (String cameraId : cameraManager.getCameraIdList()) { - - CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId); - Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); - - if (facing == CameraCharacteristics.LENS_FACING_BACK) { - - cameraID = cameraId; - - StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - yuvSizes = streamConfigurationMap.getOutputSizes(ImageFormat.YUV_420_888); - - setUpImageReader(); - } - } - } catch (CameraAccessException e) { - e.printStackTrace(); - } - } - - private void setUpImageReader() { - - Size yuvSize = yuvSizes[yuvSizes.length - 6]; - - ImageReader yuvImageReader = ImageReader.newInstance(yuvSize.getWidth(), yuvSize.getHeight(), ImageFormat.YUV_420_888, 5); - ImageReader.OnImageAvailableListener yuvImageListener = new ImageReader.OnImageAvailableListener() { - - @Override - public void onImageAvailable(ImageReader reader) { - - if (!readQr) { - return; - } - - Image lastImage = reader.acquireLatestImage(); - Bitmap bitmap = YUV2Bitmap(lastImage); - - if (bitmap != null) { - - Frame frame = new Frame.Builder().setBitmap(bitmap).build(); - SparseArray barcodes = barcodeDetector.detect(frame); - - if (barcodes.size() > 0) { - returnResult(barcodes.valueAt(0).rawValue); - } - } - - if (lastImage != null) { - lastImage.close(); - } - } - }; - - yuvImageReader.setOnImageAvailableListener(yuvImageListener, backgroundHandler); - - previewSurface = new Surface(previewSurfaceTexture); - yuvSurface = yuvImageReader.getSurface(); - - openCamera(); - } - - public void openCamera() { - - CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { - @Override - public void onOpened(@NonNull CameraDevice camera) { - cameraDevice = camera; - createCaptureSession(); - } - - @Override - public void onDisconnected(@NonNull CameraDevice camera) { - - } - - @Override - public void onError(@NonNull CameraDevice camera, int error) { - - } - }; - - try { - if (ActivityCompat.checkSelfPermission(getActivity(), android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { - cameraManager.openCamera(cameraID, stateCallback, backgroundHandler); - } - } catch (CameraAccessException e) { - e.printStackTrace(); - } - } - - private void createCaptureSession() { - List surfaces = Arrays.asList(previewSurface, yuvSurface); - - CameraCaptureSession.StateCallback captureStateCallback = new CameraCaptureSession.StateCallback() { - @Override - public void onConfigured(@NonNull CameraCaptureSession session) { - cameraCaptureSession = session; - createCaptureRequest(); - } - - @Override - public void onConfigureFailed(@NonNull CameraCaptureSession session) { - - } - }; - - try { - cameraDevice.createCaptureSession(surfaces, captureStateCallback, backgroundHandler); - } catch (CameraAccessException e) { - e.printStackTrace(); - } - - } - - private void createCaptureRequest() { - try { - - CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); - requestBuilder.addTarget(previewSurface); - requestBuilder.addTarget(yuvSurface); - - CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { - @Override - public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { - super.onCaptureCompleted(session, request, result); - } - }; - - cameraCaptureSession.setRepeatingRequest(requestBuilder.build(), captureCallback, backgroundHandler); - - } catch (CameraAccessException e) { - e.printStackTrace(); - } - } - - private void openBackgroundThread() { - backgroundThread = new HandlerThread("raw_image_available_listener_thread"); - backgroundThread.start(); - backgroundHandler = new Handler(backgroundThread.getLooper()); - } - - private void closeBackgroundThread() { - backgroundThread.quitSafely(); - try { - backgroundThread.join(); - backgroundThread = null; - backgroundHandler = null; - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public void closeCamera() { - if (cameraCaptureSession != null) { - cameraCaptureSession.close(); - cameraCaptureSession = null; - } - - if (cameraDevice != null) { - cameraDevice.close(); - cameraDevice = null; - } - } - - private void setUpBarcodeDetector() { - barcodeDetector = new BarcodeDetector.Builder(getActivity()) - .setBarcodeFormats(Barcode.QR_CODE) - .build(); - - if (!barcodeDetector.isOperational()) { - Toast.makeText(getActivity(), "Could not setup QR-Code scanner!", Toast.LENGTH_SHORT).show(); - } - } - - private Bitmap YUV2Bitmap(Image image) { - - if (image == null) return null; - - int W = image.getWidth(); - int H = image.getHeight(); - - Image.Plane Y = image.getPlanes()[0]; - Image.Plane U = image.getPlanes()[1]; - Image.Plane V = image.getPlanes()[2]; - - int Yb = Y.getBuffer().remaining(); - int Ub = U.getBuffer().remaining(); - int Vb = V.getBuffer().remaining(); - - byte[] data = new byte[Yb + Ub + Vb]; - - Y.getBuffer().get(data, 0, Yb); - V.getBuffer().get(data, Yb, Vb); - U.getBuffer().get(data, Yb + Vb, Ub); - - RenderScript rs = RenderScript.create(getActivity().getApplicationContext()); - - ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs)); - - Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(data.length); - Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT); - - Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(W).setY(H); - Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT); - - - final Bitmap bmpout = Bitmap.createBitmap(W, H, Bitmap.Config.ARGB_8888); - - in.copyFromUnchecked(data); - - yuvToRgbIntrinsic.setInput(in); - yuvToRgbIntrinsic.forEach(out); - out.copyTo(bmpout); - image.close(); - return bmpout; - } - - public void setReadQr(boolean enabled) { - readQr = enabled; - } - -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/fragment/ShowQrFragment.java b/app/src/main/java/de/overview/wg/its/mispauth/fragment/ShowQrFragment.java deleted file mode 100644 index b4aba66..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/fragment/ShowQrFragment.java +++ /dev/null @@ -1,52 +0,0 @@ -package de.overview.wg.its.mispauth.fragment; - -import android.graphics.Point; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.util.DisplayMetrics; -import android.view.Display; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import de.overview.wg.its.mispauth.R; -import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; -import net.glxn.qrgen.android.QRCode; - -public class ShowQrFragment extends Fragment { - - private ImageView qrImageView; - private int screenWidth, screenHeight; - - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_sync_show, null); - -// DisplayMetrics metrics = new DisplayMetrics(); -// getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); -// -// screenWidth = (int)(metrics.widthPixels * metrics.density); -// screenHeight = (int)(metrics.heightPixels * metrics.density); - -// Display display = getActivity().getWindowManager().getDefaultDisplay(); -// Point size = new Point(); -// display.getSize(size); -// screenWidth = size.x; -// screenHeight = size.y; - - screenHeight = getResources().getDisplayMetrics().heightPixels; - screenWidth = getResources().getDisplayMetrics().widthPixels; - - qrImageView = v.findViewById(R.id.image_view_qr); - - PreferenceManager preferenceManager = PreferenceManager.Instance(getActivity()); - setQr(preferenceManager.getMyOrganisation().toJSON().toString()); - - return v; - } - - private void setQr(String msg) { - qrImageView.setImageBitmap(QRCode.from(msg) - .withSize(screenHeight, screenHeight) - .bitmap()); - } -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/fragment/SyncStartFragment.java b/app/src/main/java/de/overview/wg/its/mispauth/fragment/SyncStartFragment.java deleted file mode 100644 index 98409e9..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/fragment/SyncStartFragment.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.overview.wg.its.mispauth.fragment; - -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.RadioGroup; -import de.overview.wg.its.mispauth.R; -import de.overview.wg.its.mispauth.activity.SyncActivity; - - -public class SyncStartFragment extends Fragment { - - private static final String TAG = "DEBUG"; - private RadioGroup radioGroup; - - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_sync_start, null); - radioGroup = v.findViewById(R.id.radioGroup); - - radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - ((SyncActivity)getActivity()).setPartnerChoice(checkedId % 2); - } - }); - - return v; - } -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/fragment/UploadFragment.java b/app/src/main/java/de/overview/wg/its/mispauth/fragment/UploadFragment.java deleted file mode 100644 index f30e64d..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/fragment/UploadFragment.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.overview.wg.its.mispauth.fragment; - -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import de.overview.wg.its.mispauth.R; - -public class UploadFragment extends Fragment { - - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_sync_upload, null); - return v; - } - -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/interfaces/IProcessable.java b/app/src/main/java/de/overview/wg/its/mispauth/interfaces/IProcessable.java deleted file mode 100644 index 76d7e55..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/interfaces/IProcessable.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.overview.wg.its.mispauth.interfaces; - -public interface IProcessable { - boolean isDone(); -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/interfaces/ISyncCommunication.java b/app/src/main/java/de/overview/wg/its/mispauth/interfaces/ISyncCommunication.java deleted file mode 100644 index 1b8efb9..0000000 --- a/app/src/main/java/de/overview/wg/its/mispauth/interfaces/ISyncCommunication.java +++ /dev/null @@ -1,6 +0,0 @@ -package de.overview.wg.its.mispauth.interfaces; - -public interface ISyncCommunication { - void setPartnerChoice(int choice); - void setScannedQr(String qr); -} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/model/Organisation.java b/app/src/main/java/de/overview/wg/its/mispauth/model/Organisation.java index 50b1c98..af2d218 100644 --- a/app/src/main/java/de/overview/wg/its/mispauth/model/Organisation.java +++ b/app/src/main/java/de/overview/wg/its/mispauth/model/Organisation.java @@ -5,6 +5,8 @@ import org.json.JSONObject; public class Organisation { + public static final String ROOT_KEY = "Organisation"; + private static String ID_KEY = "id"; private static String NAME_KEY = "name"; private static String DATE_CREATED_KEY = "date_created"; @@ -34,48 +36,57 @@ public class Organisation { private int createdBy; private int userCount; - public Organisation() {} - - public void fromJSON(JSONObject org) { - - try { - id = org.getInt(ID_KEY); - dateCreated = org.getString(DATE_CREATED_KEY); - dateModified = org.getString(DATE_MODIFIED_KEY); - name = org.getString(NAME_KEY); - type = org.getString(TYPE_KEY); - nationality = org.getString(NATIONALITY_KEY); - sector = org.getString(SECTOR_KEY); - contacts = org.getString(CONTACTS_KEY); - description = org.getString(DESCRIPTION_KEY); - local = org.getBoolean(LOCAL_KEY); - uuid = org.getString(UUID_KEY); - restrictedToDomain = org.getString(RESTRICTED_TO_DOMAIN_KEY); - createdBy = org.getInt(CREATED_BY_KEY); - userCount = org.getInt(USER_COUNT_KEY); - - } catch (JSONException e) { - e.printStackTrace(); - } + public Organisation() { } + + public Organisation(JSONObject json) throws JSONException { + fromJSON(json); + } + + public void fromJSON(JSONObject org) throws JSONException { + + id = org.optInt(ID_KEY, -1); + dateCreated = org.optString(DATE_CREATED_KEY); + dateModified = org.optString(DATE_MODIFIED_KEY); + name = org.optString(NAME_KEY); + type = org.optString(TYPE_KEY); + nationality = org.optString(NATIONALITY_KEY); + sector = org.optString(SECTOR_KEY); + contacts = org.optString(CONTACTS_KEY); + description = org.optString(DESCRIPTION_KEY); + local = org.optBoolean(LOCAL_KEY, true); + uuid = org.optString(UUID_KEY); + restrictedToDomain = org.optString(RESTRICTED_TO_DOMAIN_KEY); + createdBy = org.optInt(CREATED_BY_KEY, -1); + userCount = org.optInt(USER_COUNT_KEY); + + } + public JSONObject toJSON() { + return toJSON(false); + } + + public JSONObject toJSON(boolean minimal) { JSONObject org = new JSONObject(); try { - org.put(ID_KEY, id); - org.put(NAME_KEY, name); - org.put(DATE_CREATED_KEY, dateCreated); - org.put(DATE_MODIFIED_KEY, dateModified); - org.put(TYPE_KEY, type); - org.put(NATIONALITY_KEY, nationality); - org.put(SECTOR_KEY, sector); - org.put(CONTACTS_KEY, contacts); - org.put(DESCRIPTION_KEY, description); - org.put(LOCAL_KEY, local); - org.put(UUID_KEY, uuid); - org.put(RESTRICTED_TO_DOMAIN_KEY, restrictedToDomain); - org.put(CREATED_BY_KEY, createdBy); - org.put(USER_COUNT_KEY, userCount); + org.putOpt(NAME_KEY, name); + org.putOpt(DESCRIPTION_KEY, description); + org.putOpt(NATIONALITY_KEY, nationality); + org.putOpt(SECTOR_KEY, sector); + org.putOpt(USER_COUNT_KEY, userCount); + + if (!minimal) { + org.putOpt(ID_KEY, id); + org.putOpt(UUID_KEY, uuid); + org.putOpt(TYPE_KEY, type); + org.putOpt(CONTACTS_KEY, contacts); + org.putOpt(DATE_CREATED_KEY, dateCreated); + org.putOpt(DATE_MODIFIED_KEY, dateModified); + org.putOpt(LOCAL_KEY, local); + org.putOpt(RESTRICTED_TO_DOMAIN_KEY, restrictedToDomain); + org.putOpt(CREATED_BY_KEY, createdBy); + } } catch (JSONException e) { e.printStackTrace(); @@ -88,13 +99,15 @@ public class Organisation { public void setName(String name) { this.name = name; } - public String getName(){ + + public String getName() { return name; } public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } @@ -102,6 +115,7 @@ public class Organisation { public String getSector() { return sector; } + public void setSector(String sector) { this.sector = sector; } @@ -109,6 +123,7 @@ public class Organisation { public String getNationality() { return nationality; } + public void setNationality(String nationality) { this.nationality = nationality; } @@ -116,6 +131,7 @@ public class Organisation { public int getId() { return id; } + public void setId(int id) { this.id = id; } @@ -123,6 +139,7 @@ public class Organisation { public String getDateCreated() { return dateCreated; } + public void setDateCreated(String dateCreated) { this.dateCreated = dateCreated; } @@ -130,6 +147,7 @@ public class Organisation { public String getDateModified() { return dateModified; } + public void setDateModified(String dateModified) { this.dateModified = dateModified; } @@ -137,6 +155,7 @@ public class Organisation { public String getType() { return type; } + public void setType(String type) { this.type = type; } @@ -144,6 +163,7 @@ public class Organisation { public String getContacts() { return contacts; } + public void setContacts(String contacts) { this.contacts = contacts; } @@ -151,6 +171,7 @@ public class Organisation { public boolean isLocal() { return local; } + public void setLocal(boolean local) { this.local = local; } @@ -158,6 +179,7 @@ public class Organisation { public String getUuid() { return uuid; } + public void setUuid(String uuid) { this.uuid = uuid; } @@ -165,6 +187,7 @@ public class Organisation { public String getRestrictedToDomain() { return restrictedToDomain; } + public void setRestrictedToDomain(String restrictedToDomain) { this.restrictedToDomain = restrictedToDomain; } @@ -172,6 +195,7 @@ public class Organisation { public int getCreatedBy() { return createdBy; } + public void setCreatedBy(int createdBy) { this.createdBy = createdBy; } @@ -179,6 +203,7 @@ public class Organisation { public int getUserCount() { return userCount; } + public void setUserCount(int userCount) { this.userCount = userCount; } diff --git a/app/src/main/java/de/overview/wg/its/mispauth/model/PublicKeyQr.java b/app/src/main/java/de/overview/wg/its/mispauth/model/PublicKeyQr.java new file mode 100644 index 0000000..358228b --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/model/PublicKeyQr.java @@ -0,0 +1,61 @@ +package de.overview.wg.its.mispauth.model; + +import org.json.JSONException; +import org.json.JSONObject; + +public class PublicKeyQr { + + private static final String KEY_ORG = "org"; + private static final String KEY_USER = "user"; + private static final String KEY_KEY = "key"; + + private String organisation, user, key; + + public PublicKeyQr(JSONObject qr) throws JSONException { + organisation = qr.getString(KEY_ORG); + user = qr.getString(KEY_USER); + key = qr.getString(KEY_KEY); + } + + public PublicKeyQr(String qr) throws JSONException{ + JSONObject json = new JSONObject(qr); + + organisation = json.getString(KEY_ORG); + user = json.getString(KEY_USER); + key = json.getString(KEY_KEY); + } + + public PublicKeyQr(String organisation, String user, String key) { + this.organisation = organisation; + this.user = user; + this.key = key; + } + + public JSONObject toJSON() { + try { + JSONObject json = new JSONObject(); + + json.put(KEY_ORG, organisation); + json.put(KEY_USER, user); + json.put(KEY_KEY, key); + + return json; + + } catch (JSONException e) { + + return null; + } + } + + public String getOrganisation() { + return organisation; + } + + public String getUser() { + return user; + } + + public String getKey() { + return key; + } +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/model/Server.java b/app/src/main/java/de/overview/wg/its/mispauth/model/Server.java index 32077ca..785e505 100644 --- a/app/src/main/java/de/overview/wg/its/mispauth/model/Server.java +++ b/app/src/main/java/de/overview/wg/its/mispauth/model/Server.java @@ -1,5 +1,105 @@ package de.overview.wg.its.mispauth.model; +import org.json.JSONException; +import org.json.JSONObject; + public class Server { + public static final String ROOT_KEY = "Server"; + + private static final String URL_KEY = "url"; + private static final String NAME_KEY = "name"; + private static final String REMOTE_ORG_ID_KEY = "remote_org_id"; + private static final String AUTHKEY_KEY = "authkey"; + private static final String PUSH_KEY = "push"; + private static final String PULL_KEY = "pull"; + + private String url; + private String name; + private int remoteOrgId; + private String authkey; + private boolean push, pull; + + public Server() { } + public Server(JSONObject json) throws JSONException { + fromJSON(json); + } + + public void fromJSON(JSONObject server) throws JSONException { + url = server.optString(URL_KEY); + name = server.optString(NAME_KEY); + remoteOrgId = server.optInt(REMOTE_ORG_ID_KEY, -1); + authkey = server.optString(AUTHKEY_KEY); + push = server.optBoolean(PUSH_KEY, false); + pull = server.optBoolean(PULL_KEY, false); + } + + public JSONObject toJSON() { + return toJSON(false); + } + public JSONObject toJSON(boolean minimal) { + + JSONObject jsonObject = new JSONObject(); + + try { + jsonObject.putOpt(URL_KEY, url); + jsonObject.putOpt(NAME_KEY, name); + jsonObject.putOpt(AUTHKEY_KEY, authkey); + + if (!minimal) { + jsonObject.putOpt(REMOTE_ORG_ID_KEY, remoteOrgId); + jsonObject.putOpt(PUSH_KEY, push); + jsonObject.putOpt(PULL_KEY, pull); + } + + } catch (JSONException e) { + e.printStackTrace(); + } + + return jsonObject; + } + + + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public int getRemoteOrgId() { + return remoteOrgId; + } + public void setRemoteOrgId(int remoteOrgId) { + this.remoteOrgId = remoteOrgId; + } + + public String getAuthkey() { + return authkey; + } + public void setAuthkey(String authkey) { + this.authkey = authkey; + } + + public boolean isPush() { + return push; + } + public void setPush(boolean push) { + this.push = push; + } + + public boolean isPull() { + return pull; + } + public void setPull(boolean pull) { + this.pull = pull; + } + } diff --git a/app/src/main/java/de/overview/wg/its/mispauth/model/SyncInformationQr.java b/app/src/main/java/de/overview/wg/its/mispauth/model/SyncInformationQr.java new file mode 100644 index 0000000..7dbcdd3 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/model/SyncInformationQr.java @@ -0,0 +1,50 @@ +package de.overview.wg.its.mispauth.model; + +import org.json.JSONArray; +import org.json.JSONException; + +public class SyncInformationQr { + + private Organisation organisation; + private Server server; + private User user; + + public SyncInformationQr(Organisation organisation, Server server, User user) { + this.organisation = organisation; + this.server = server; + this.user = user; + } + public SyncInformationQr(String stringArray) throws JSONException { + fromJSON(new JSONArray(stringArray)); + } + + private void fromJSON(JSONArray array) throws JSONException { + int length = array.length(); + + if (length == 3) { + organisation = new Organisation(array.getJSONObject(0)); + server = new Server(array.getJSONObject(1)); + user = new User(array.getJSONObject(2)); + } + } + + public JSONArray toJSON() { + JSONArray array = new JSONArray(); + + array.put(organisation.toJSON(true)); + array.put(server.toJSON(true)); + array.put(user.toJSON(true)); + + return array; + } + + public Organisation getOrganisation() { + return organisation; + } + public Server getServer() { + return server; + } + public User getUser() { + return user; + } +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/model/SyncedPartner.java b/app/src/main/java/de/overview/wg/its/mispauth/model/SyncedPartner.java new file mode 100644 index 0000000..6cd88ce --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/model/SyncedPartner.java @@ -0,0 +1,48 @@ +package de.overview.wg.its.mispauth.model; + +import android.annotation.SuppressLint; + +import java.sql.Timestamp; +import java.text.SimpleDateFormat; + +public class SyncedPartner { + + @SuppressLint("SimpleDateFormat") + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy"); + + private String name; + private String url; + private String syncDate; + + public SyncedPartner(String name, String url) { + this.name = name; + this.url = url; + } + + public void generateTimeStamp() { + syncDate = dateFormat.format(new Timestamp(System.currentTimeMillis())); + } + + // GETTER & SETTER + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + + public String getSyncDate() { + return syncDate; + } + public void setSyncDate(String syncDate) { + this.syncDate = syncDate; + } +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/model/UploadState.java b/app/src/main/java/de/overview/wg/its/mispauth/model/UploadState.java new file mode 100644 index 0000000..6eb71c4 --- /dev/null +++ b/app/src/main/java/de/overview/wg/its/mispauth/model/UploadState.java @@ -0,0 +1,41 @@ +package de.overview.wg.its.mispauth.model; + +public class UploadState { + + public enum State { + PENDING, + IN_PROGRESS, + DONE, + ERROR + } + private State currentState = State.PENDING; + private String title, error; + + + public UploadState(String title) { + this.title = title; + } + + + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + + public String getError() { + return error; + } + public void setError(String error) { + this.error = error; + } + + public State getCurrentState() { + return currentState; + } + public void setCurrentState(State currentState) { + this.currentState = currentState; + } + +} diff --git a/app/src/main/java/de/overview/wg/its/mispauth/model/User.java b/app/src/main/java/de/overview/wg/its/mispauth/model/User.java index bb06663..382ba16 100644 --- a/app/src/main/java/de/overview/wg/its/mispauth/model/User.java +++ b/app/src/main/java/de/overview/wg/its/mispauth/model/User.java @@ -5,6 +5,18 @@ import org.json.JSONObject; public class User { + // todo: must be configable? Roles can be edited on instance + public interface RoleId { + int ADMIN = 1; + int ORG_ADMIN = 2; + int USER = 3; + int PUBLISHER = 4; + int SYNC_USER = 5; + int READ_ONLY = 6; + } + + public static final String ROOT_KEY = "User"; + private static String ID_KEY = "id"; private static String PASSWORD_KEY = "password"; private static String ORG_ID_KEY = "org_id"; @@ -51,79 +63,85 @@ public class User { private String dateCreated; private String dateModified; - public User() {} - - public void fromJSON(JSONObject user) { - try { - - id = user.getInt(ID_KEY); - password = user.getString(PASSWORD_KEY); - orgId = user.getInt(ORG_ID_KEY); - email = user.getString(EMAIL_KEY); - autoAlert = user.getBoolean(AUTOALERT_KEY); - authkey = user.getString(AUTHKEY_KEY); - invitedBy = user.getInt(INVITED_BY_KEY); - gpgKey = user.getString(GPGKEY_KEY); - certifPublic = user.getString(CERTIF_PUBLIC); - nidsSid = user.getInt(NIDS_SID); - termsAccepted = user.getBoolean(TERMS_ACCEPTED_KEY); - newsRead = user.getInt(NEWSREAD_KEY); - roleId = user.getInt(ROLE_ID_KEY); - changePw = user.getString(CHANGE_PW_KEY); - contactAlert = user.getBoolean(CONTACT_ALERT_KEY); - disabled = user.getBoolean(DISABLED_KEY); - expiration = user.getString(EXPIRATION_KEY); - currentLogin = user.getString(CURRENT_LOGIN_KEY); - lastLogin = user.getString(LAST_LOGIN_KEY); - forceLogout = user.getBoolean(FORCE_LOGOUT_KEY); - dateCreated = user.getString(DATE_CREATED_KEY); - dateModified = user.getString(DATE_MODIFIED_KEY); - - } catch (JSONException e) { - e.printStackTrace(); - } - + public User() { + } + public User(JSONObject user) throws JSONException { + fromJSON(user); } + public void fromJSON(JSONObject user) throws JSONException { + + id = user.optInt(ID_KEY, -1); + password = user.optString(PASSWORD_KEY); + orgId = user.optInt(ORG_ID_KEY, -1); + email = user.optString(EMAIL_KEY); + autoAlert = user.optBoolean(AUTOALERT_KEY); + authkey = user.optString(AUTHKEY_KEY); + invitedBy = user.optInt(INVITED_BY_KEY, -1); + gpgKey = user.optString(GPGKEY_KEY); + certifPublic = user.optString(CERTIF_PUBLIC); + nidsSid = user.optInt(NIDS_SID); + termsAccepted = user.optBoolean(TERMS_ACCEPTED_KEY, false); + newsRead = user.optInt(NEWSREAD_KEY); + roleId = user.optInt(ROLE_ID_KEY, -1); + changePw = user.optString(CHANGE_PW_KEY); + contactAlert = user.optBoolean(CONTACT_ALERT_KEY, true); + disabled = user.optBoolean(DISABLED_KEY, false); + expiration = user.optString(EXPIRATION_KEY); + currentLogin = user.optString(CURRENT_LOGIN_KEY); + lastLogin = user.optString(LAST_LOGIN_KEY); + forceLogout = user.optBoolean(FORCE_LOGOUT_KEY); + dateCreated = user.optString(DATE_CREATED_KEY); + dateModified = user.optString(DATE_MODIFIED_KEY); + + } public JSONObject toJSON() { + return toJSON(false); + } + public JSONObject toJSON(boolean forSyncQR) { JSONObject user = new JSONObject(); try { - user.put(ID_KEY, id); - user.put(PASSWORD_KEY, password); - user.put(ORG_ID_KEY, orgId); - user.put(EMAIL_KEY, email); - user.put(AUTOALERT_KEY, autoAlert); - user.put(AUTHKEY_KEY, authkey); - user.put(INVITED_BY_KEY, invitedBy); - user.put(GPGKEY_KEY, gpgKey); - user.put(CERTIF_PUBLIC, certifPublic); - user.put(NIDS_SID, nidsSid); - user.put(TERMS_ACCEPTED_KEY, termsAccepted); - user.put(NEWSREAD_KEY, newsRead); - user.put(ROLE_ID_KEY, roleId); - user.put(CHANGE_PW_KEY, changePw); - user.put(CONTACT_ALERT_KEY, contactAlert); - user.put(DISABLED_KEY, disabled); - user.put(EXPIRATION_KEY, expiration); - user.put(CURRENT_LOGIN_KEY, currentLogin); - user.put(LAST_LOGIN_KEY, lastLogin); - user.put(FORCE_LOGOUT_KEY, forceLogout); - user.put(DATE_CREATED_KEY, dateCreated); - user.put(DATE_MODIFIED_KEY, dateModified); + user.putOpt(EMAIL_KEY, email); + + if (!forSyncQR) { + + user.putOpt(ID_KEY, id); + user.putOpt(ORG_ID_KEY, orgId); + user.putOpt(AUTHKEY_KEY, authkey); + user.putOpt(ROLE_ID_KEY, roleId); + user.putOpt(PASSWORD_KEY, password); + user.putOpt(CHANGE_PW_KEY, changePw); + user.putOpt(TERMS_ACCEPTED_KEY, termsAccepted); + user.putOpt(CERTIF_PUBLIC, certifPublic); + user.putOpt(GPGKEY_KEY, gpgKey); + user.putOpt(AUTOALERT_KEY, autoAlert); + user.putOpt(INVITED_BY_KEY, invitedBy); + user.putOpt(NIDS_SID, nidsSid); + user.putOpt(NEWSREAD_KEY, newsRead); + user.putOpt(CONTACT_ALERT_KEY, contactAlert); + user.putOpt(DISABLED_KEY, disabled); + user.putOpt(EXPIRATION_KEY, expiration); + user.putOpt(CURRENT_LOGIN_KEY, currentLogin); + user.putOpt(LAST_LOGIN_KEY, lastLogin); + user.putOpt(FORCE_LOGOUT_KEY, forceLogout); + user.putOpt(DATE_CREATED_KEY, dateCreated); + user.putOpt(DATE_MODIFIED_KEY, dateModified); + + } } catch (JSONException e) { e.printStackTrace(); } - return user; + return user; } + public int getId() { return id; } - public void setId(int id) { this.id = id; } @@ -131,7 +149,6 @@ public class User { public String getPassword() { return password; } - public void setPassword(String password) { this.password = password; } @@ -139,7 +156,6 @@ public class User { public int getOrgId() { return orgId; } - public void setOrgId(int orgId) { this.orgId = orgId; } @@ -147,7 +163,6 @@ public class User { public String getEmail() { return email; } - public void setEmail(String email) { this.email = email; } @@ -155,7 +170,6 @@ public class User { public boolean isAutoAlert() { return autoAlert; } - public void setAutoAlert(boolean autoAlert) { this.autoAlert = autoAlert; } @@ -163,7 +177,6 @@ public class User { public String getAuthkey() { return authkey; } - public void setAuthkey(String authkey) { this.authkey = authkey; } @@ -171,7 +184,6 @@ public class User { public int getInvitedBy() { return invitedBy; } - public void setInvitedBy(int invitedBy) { this.invitedBy = invitedBy; } @@ -179,7 +191,6 @@ public class User { public String getGpgKey() { return gpgKey; } - public void setGpgKey(String gpgKey) { this.gpgKey = gpgKey; } @@ -187,7 +198,6 @@ public class User { public String getCertifPublic() { return certifPublic; } - public void setCertifPublic(String certifPublic) { this.certifPublic = certifPublic; } @@ -195,7 +205,6 @@ public class User { public int getNidsSid() { return nidsSid; } - public void setNidsSid(int nidsSid) { this.nidsSid = nidsSid; } @@ -203,7 +212,6 @@ public class User { public boolean isTermsAccepted() { return termsAccepted; } - public void setTermsAccepted(boolean termsAccepted) { this.termsAccepted = termsAccepted; } @@ -211,7 +219,6 @@ public class User { public int getNewsRead() { return newsRead; } - public void setNewsRead(int newsRead) { this.newsRead = newsRead; } @@ -219,7 +226,6 @@ public class User { public int getRoleId() { return roleId; } - public void setRoleId(int roleId) { this.roleId = roleId; } @@ -227,7 +233,6 @@ public class User { public String getChangePw() { return changePw; } - public void setChangePw(String changePw) { this.changePw = changePw; } @@ -235,7 +240,6 @@ public class User { public boolean isContactAlert() { return contactAlert; } - public void setContactAlert(boolean contactAlert) { this.contactAlert = contactAlert; } @@ -243,7 +247,6 @@ public class User { public boolean isDisabled() { return disabled; } - public void setDisabled(boolean disabled) { this.disabled = disabled; } @@ -251,7 +254,6 @@ public class User { public String getExpiration() { return expiration; } - public void setExpiration(String expiration) { this.expiration = expiration; } @@ -259,7 +261,6 @@ public class User { public String getCurrentLogin() { return currentLogin; } - public void setCurrentLogin(String currentLogin) { this.currentLogin = currentLogin; } @@ -267,7 +268,6 @@ public class User { public String getLastLogin() { return lastLogin; } - public void setLastLogin(String lastLogin) { this.lastLogin = lastLogin; } @@ -275,7 +275,6 @@ public class User { public boolean isForceLogout() { return forceLogout; } - public void setForceLogout(boolean forceLogout) { this.forceLogout = forceLogout; } @@ -283,7 +282,6 @@ public class User { public String getDateCreated() { return dateCreated; } - public void setDateCreated(String dateCreated) { this.dateCreated = dateCreated; } @@ -291,7 +289,6 @@ public class User { public String getDateModified() { return dateModified; } - public void setDateModified(String dateModified) { this.dateModified = dateModified; } diff --git a/app/src/main/java/de/overview/wg/its/mispauth/network/MispRequest.java b/app/src/main/java/de/overview/wg/its/mispauth/network/MispRequest.java index 81c4a5b..9f3fa72 100644 --- a/app/src/main/java/de/overview/wg/its/mispauth/network/MispRequest.java +++ b/app/src/main/java/de/overview/wg/its/mispauth/network/MispRequest.java @@ -7,16 +7,31 @@ import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.Response; import com.android.volley.VolleyError; -import com.android.volley.toolbox.JsonArrayRequest; +import com.android.volley.toolbox.HurlStack; import com.android.volley.toolbox.JsonObjectRequest; import com.android.volley.toolbox.Volley; +import de.overview.wg.its.mispauth.R; import de.overview.wg.its.mispauth.auxiliary.PreferenceManager; import de.overview.wg.its.mispauth.auxiliary.ReadableError; import de.overview.wg.its.mispauth.model.Organisation; +import de.overview.wg.its.mispauth.model.Server; +import de.overview.wg.its.mispauth.model.User; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import javax.net.ssl.*; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; @@ -38,6 +53,7 @@ public class MispRequest { private MispRequest(Context context) { requestQueue = Volley.newRequestQueue(context); preferenceManager = PreferenceManager.Instance(context); + loadSavedCredentials(); } @@ -56,13 +72,10 @@ public class MispRequest { @Override public void onResponse(JSONObject response) { try { - callback.onResult(response.getJSONObject("Organisation")); - return; + callback.onResult(response.getJSONObject(Organisation.ROOT_KEY)); } catch (JSONException e) { e.printStackTrace(); } - - callback.onResult(response); } }; @@ -88,14 +101,14 @@ public class MispRequest { * * @param callback return user associated with this API-Key */ - public void myUserInformation(final UserCallback callback) { + public void getMyUser(final UserCallback callback) { Response.Listener listener = new Response.Listener() { @Override public void onResponse(JSONObject response) { try { - callback.onResult(response.getJSONObject("User")); + callback.onResult(response.getJSONObject(User.ROOT_KEY)); return; } catch (JSONException e) { e.printStackTrace(); @@ -114,7 +127,7 @@ public class MispRequest { }; if (serverUrl.isEmpty() || apiKey.isEmpty()) { - Log.e(TAG, "myUserInformation: server or api key is empty!"); + Log.e(TAG, "getMyUser: server or api key is empty!"); return; } @@ -137,13 +150,10 @@ public class MispRequest { @Override public void onResponse(JSONObject response) { try { - callback.onResult(response.getJSONObject("Organisation")); - return; + callback.onResult(response.getJSONObject(Organisation.ROOT_KEY)); } catch (JSONException e) { e.printStackTrace(); } - - callback.onResult(response); } }; @@ -204,6 +214,73 @@ public class MispRequest { requestQueue.add(r); } + public void addUser(User user, final UserCallback callback) { + Response.Listener listener = new Response.Listener() { + @Override + public void onResponse(JSONObject response) { + try { + callback.onResult(response.getJSONObject(User.ROOT_KEY)); + return; + } catch (JSONException e) { + e.printStackTrace(); + } + + callback.onResult(response); + } + }; + + Response.ErrorListener errorListener = new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + callback.onError(error); + } + }; + + Request r = objectRequest( + Request.Method.POST, + serverUrl + "/admin/users/add", + user.toJSON(), + listener, + errorListener + ); + + requestQueue.add(r); + } + + public void addServer(Server server, final ServerCallback callback) { + Response.Listener listener = new Response.Listener() { + @Override + public void onResponse(JSONObject response) { + try { + callback.onResult(response.getJSONObject(Server.ROOT_KEY)); + return; + } catch (JSONException e) { + e.printStackTrace(); + } + + callback.onResult(response); + } + }; + + Response.ErrorListener errorListener = new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + callback.onError(error); + } + }; + + Request r = objectRequest( + Request.Method.POST, + serverUrl + "/servers/add", + server.toJSON(), + listener, + errorListener + ); + + requestQueue.add(r); + } + + private JsonArrayRequestWithJsonObject arrayRequestWithJsonObject(int method, String url, @Nullable JSONObject body, Response.Listener listener, @@ -247,6 +324,72 @@ public class MispRequest { this.apiKey = apiKey; } +// private SSLSocketFactory getSocketFactory(Context context) { +// +// CertificateFactory cf = null; +// try { +// cf = CertificateFactory.getInstance("X.509"); +// InputStream caInput = context.getResources().openRawResource(R.raw.server); +// Certificate ca; +// try { +// ca = cf.generateCertificate(caInput); +// Log.e("CERT", "ca=" + ((X509Certificate) ca).getSubjectDN()); +// } finally { +// caInput.close(); +// } +// +// +// String keyStoreType = KeyStore.getDefaultType(); +// KeyStore keyStore = KeyStore.getInstance(keyStoreType); +// keyStore.load(null, null); +// keyStore.setCertificateEntry("ca", ca); +// +// +// String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); +// TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); +// tmf.init(keyStore); +// +// +// HostnameVerifier hostnameVerifier = new HostnameVerifier() { +// @Override +// public boolean verify(String hostname, SSLSession session) { +// +// Log.e("CipherUsed", session.getCipherSuite()); +// return hostname.compareTo("192.168.1.10")==0; //The Hostname of your server +// +// } +// }; +// +// +// HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); +// SSLContext context = null; +// context = SSLContext.getInstance("TLS"); +// +// context.init(null, tmf.getTrustManagers(), null); +// HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); +// +// SSLSocketFactory sf = context.getSocketFactory(); +// +// +// return sf; +// +// } catch (CertificateException e) { +// e.printStackTrace(); +// } catch (NoSuchAlgorithmException e) { +// e.printStackTrace(); +// } catch (KeyStoreException e) { +// e.printStackTrace(); +// } catch (FileNotFoundException e) { +// e.printStackTrace(); +// } catch (IOException e) { +// e.printStackTrace(); +// } catch (KeyManagementException e) { +// e.printStackTrace(); +// } +// +// return null; +// } + public static MispRequest Instance(Context context) { if (instance == null) { instance = new MispRequest(context); @@ -264,12 +407,12 @@ public class MispRequest { void onError(VolleyError volleyError); } public interface UserCallback { - void onResult(JSONObject myOrganisationInformation); + void onResult(JSONObject userInformation); void onError(VolleyError volleyError); } public interface ServerCallback { - void onResult(JSONObject servers); + void onResult(JSONObject server); void onError(VolleyError volleyError); } } diff --git a/app/src/main/res/anim/enter_from_left.xml b/app/src/main/res/anim/enter_from_left.xml deleted file mode 100644 index eb7fb78..0000000 --- a/app/src/main/res/anim/enter_from_left.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/anim/enter_from_right.xml b/app/src/main/res/anim/enter_from_right.xml deleted file mode 100644 index 0c7045c..0000000 --- a/app/src/main/res/anim/enter_from_right.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/anim/exit_to_left.xml b/app/src/main/res/anim/exit_to_left.xml deleted file mode 100644 index a9c41a6..0000000 --- a/app/src/main/res/anim/exit_to_left.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/anim/exit_to_right.xml b/app/src/main/res/anim/exit_to_right.xml deleted file mode 100644 index 7ef31a6..0000000 --- a/app/src/main/res/anim/exit_to_right.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/anim/slide_down.xml b/app/src/main/res/anim/slide_down.xml new file mode 100644 index 0000000..ad40deb --- /dev/null +++ b/app/src/main/res/anim/slide_down.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_up.xml b/app/src/main/res/anim/slide_up.xml new file mode 100644 index 0000000..a731689 --- /dev/null +++ b/app/src/main/res/anim/slide_up.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/animator/fade_in.xml b/app/src/main/res/animator/fade_in.xml deleted file mode 100644 index 2865a0c..0000000 --- a/app/src/main/res/animator/fade_in.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml index c7bd21d..789e157 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -1,34 +1,34 @@ - - - - - - - - - + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportHeight="108" + android:viewportWidth="108"> + + + + + + + + + diff --git a/app/src/main/res/drawable/background_rounded_main.xml b/app/src/main/res/drawable/background_rounded_main.xml deleted file mode 100644 index f7c1e47..0000000 --- a/app/src/main/res/drawable/background_rounded_main.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add_white.xml b/app/src/main/res/drawable/ic_add_white.xml deleted file mode 100644 index e3979cd..0000000 --- a/app/src/main/res/drawable/ic_add_white.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_arrow_back_white.xml b/app/src/main/res/drawable/ic_arrow_back_white.xml deleted file mode 100644 index 71d5bbd..0000000 --- a/app/src/main/res/drawable/ic_arrow_back_white.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml deleted file mode 100644 index 17aca2a..0000000 --- a/app/src/main/res/drawable/ic_check.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_cloud_download_white.xml b/app/src/main/res/drawable/ic_cloud_download_white.xml deleted file mode 100644 index 0feb270..0000000 --- a/app/src/main/res/drawable/ic_cloud_download_white.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_edit_white_24dp.xml b/app/src/main/res/drawable/ic_edit_white_24dp.xml deleted file mode 100644 index 46462b5..0000000 --- a/app/src/main/res/drawable/ic_edit_white_24dp.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_flag_white_24dp.xml b/app/src/main/res/drawable/ic_flag_white_24dp.xml deleted file mode 100644 index f2e075a..0000000 --- a/app/src/main/res/drawable/ic_flag_white_24dp.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_info_white.xml b/app/src/main/res/drawable/ic_info_white.xml deleted file mode 100644 index 7be0147..0000000 --- a/app/src/main/res/drawable/ic_info_white.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index d5fccc5..1fa43f1 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,74 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_perm_contact_calendar_white_24dp.xml b/app/src/main/res/drawable/ic_perm_contact_calendar_white_24dp.xml deleted file mode 100644 index b07fbed..0000000 --- a/app/src/main/res/drawable/ic_perm_contact_calendar_white_24dp.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_qr_aim.xml b/app/src/main/res/drawable/ic_qr_aim.xml deleted file mode 100644 index 3a353b0..0000000 --- a/app/src/main/res/drawable/ic_qr_aim.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_sector.xml b/app/src/main/res/drawable/ic_sector.xml deleted file mode 100644 index 7b7f112..0000000 --- a/app/src/main/res/drawable/ic_sector.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_settings_white.xml b/app/src/main/res/drawable/ic_settings_white.xml deleted file mode 100644 index 79af3ab..0000000 --- a/app/src/main/res/drawable/ic_settings_white.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_sync_alpha.xml b/app/src/main/res/drawable/ic_sync_alpha.xml deleted file mode 100644 index 65dac7d..0000000 --- a/app/src/main/res/drawable/ic_sync_alpha.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_sync_white.xml b/app/src/main/res/drawable/ic_sync_white.xml deleted file mode 100644 index 7ae478f..0000000 --- a/app/src/main/res/drawable/ic_sync_white.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/icon_add.xml b/app/src/main/res/drawable/icon_add.xml new file mode 100644 index 0000000..0258249 --- /dev/null +++ b/app/src/main/res/drawable/icon_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_check.xml b/app/src/main/res/drawable/icon_check.xml new file mode 100644 index 0000000..459aef9 --- /dev/null +++ b/app/src/main/res/drawable/icon_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_close.xml b/app/src/main/res/drawable/icon_close.xml new file mode 100644 index 0000000..ede4b71 --- /dev/null +++ b/app/src/main/res/drawable/icon_close.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_cloud_download.xml b/app/src/main/res/drawable/icon_cloud_download.xml new file mode 100644 index 0000000..261c312 --- /dev/null +++ b/app/src/main/res/drawable/icon_cloud_download.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_cloud_upload.xml b/app/src/main/res/drawable/icon_cloud_upload.xml new file mode 100644 index 0000000..7d0637d --- /dev/null +++ b/app/src/main/res/drawable/icon_cloud_upload.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_contact.xml b/app/src/main/res/drawable/icon_contact.xml new file mode 100644 index 0000000..0d6b3bb --- /dev/null +++ b/app/src/main/res/drawable/icon_contact.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_forward.xml b/app/src/main/res/drawable/icon_forward.xml new file mode 100644 index 0000000..cf9e208 --- /dev/null +++ b/app/src/main/res/drawable/icon_forward.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_hour_glass.xml b/app/src/main/res/drawable/icon_hour_glass.xml new file mode 100644 index 0000000..fa7abc6 --- /dev/null +++ b/app/src/main/res/drawable/icon_hour_glass.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_key.xml b/app/src/main/res/drawable/icon_key.xml new file mode 100644 index 0000000..2eddd16 --- /dev/null +++ b/app/src/main/res/drawable/icon_key.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_round_check.xml b/app/src/main/res/drawable/icon_round_check.xml new file mode 100644 index 0000000..529c91e --- /dev/null +++ b/app/src/main/res/drawable/icon_round_check.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/icon_round_error.xml b/app/src/main/res/drawable/icon_round_error.xml new file mode 100644 index 0000000..cb3b30a --- /dev/null +++ b/app/src/main/res/drawable/icon_round_error.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_settings.xml b/app/src/main/res/drawable/icon_settings.xml new file mode 100644 index 0000000..ace746c --- /dev/null +++ b/app/src/main/res/drawable/icon_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/rounded_square.xml b/app/src/main/res/drawable/rounded_square.xml new file mode 100644 index 0000000..f7f114d --- /dev/null +++ b/app/src/main/res/drawable/rounded_square.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_indicator_default.xml b/app/src/main/res/drawable/tab_indicator_default.xml deleted file mode 100644 index 60ae032..0000000 --- a/app/src/main/res/drawable/tab_indicator_default.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_indicator_selected.xml b/app/src/main/res/drawable/tab_indicator_selected.xml deleted file mode 100644 index f4ea196..0000000 --- a/app/src/main/res/drawable/tab_indicator_selected.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_selector.xml b/app/src/main/res/drawable/tab_selector.xml deleted file mode 100644 index 95bfb40..0000000 --- a/app/src/main/res/drawable/tab_selector.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_credentials.xml b/app/src/main/res/layout/activity_credentials.xml new file mode 100644 index 0000000..4ce7db7 --- /dev/null +++ b/app/src/main/res/layout/activity_credentials.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c446141..7feec8d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,92 +1,61 @@ + + - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" + - android:layout_width="match_parent" - android:layout_height="match_parent" + - tools:context=".activity.SettingsActivity" - android:background="@color/colorPrimary"> + - + - + - + - + - + - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_public_key_exchange.xml b/app/src/main/res/layout/activity_public_key_exchange.xml new file mode 100644 index 0000000..397d152 --- /dev/null +++ b/app/src/main/res/layout/activity_public_key_exchange.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + +