Create an initial intro screen on first launch
This commit is contained in:
parent
b610afdf93
commit
324f84b36c
9 changed files with 222 additions and 4 deletions
|
@ -25,6 +25,9 @@ android {
|
||||||
lintOptions {
|
lintOptions {
|
||||||
abortOnError false
|
abortOnError false
|
||||||
}
|
}
|
||||||
|
dataBinding {
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
@ -41,6 +44,7 @@ dependencies {
|
||||||
compile "com.android.support:recyclerview-v7:$supportLibVersion"
|
compile "com.android.support:recyclerview-v7:$supportLibVersion"
|
||||||
compile "com.android.support.constraint:constraint-layout:1.0.2"
|
compile "com.android.support.constraint:constraint-layout:1.0.2"
|
||||||
compile "com.github.aakira:expandable-layout:1.6.0"
|
compile "com.github.aakira:expandable-layout:1.6.0"
|
||||||
|
compile "com.heinrichreimersoftware:material-intro:1.6.2"
|
||||||
compile "com.journeyapps:zxing-android-embedded:3.5.0"
|
compile "com.journeyapps:zxing-android-embedded:3.5.0"
|
||||||
compile "com.vanniktech:vntnumberpickerpreference:1.0.0"
|
compile "com.vanniktech:vntnumberpickerpreference:1.0.0"
|
||||||
compile "de.psdev.licensesdialog:licensesdialog:1.8.3"
|
compile "de.psdev.licensesdialog:licensesdialog:1.8.3"
|
||||||
|
|
|
@ -35,6 +35,10 @@
|
||||||
android:name=".Activities.BackupActivity"
|
android:name=".Activities.BackupActivity"
|
||||||
android:parentActivityName=".Activities.MainActivity"
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
android:theme="@style/AppTheme.NoActionBar" />
|
android:theme="@style/AppTheme.NoActionBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".Activities.MainIntroActivity"
|
||||||
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
|
android:theme="@style/Theme.Intro" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".Activities.SettingsActivity"
|
android:name=".Activities.SettingsActivity"
|
||||||
android:parentActivityName=".Activities.MainActivity"
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
|
|
|
@ -103,7 +103,7 @@ public class MainActivity extends BaseActivity
|
||||||
.initiateScan();
|
.initiateScan();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showFirstTimeWarning() {
|
/*private void showFirstTimeWarning() {
|
||||||
ViewGroup container = findViewById(R.id.main_content);
|
ViewGroup container = findViewById(R.id.main_content);
|
||||||
View msgView = getLayoutInflater().inflate(R.layout.dialog_database_encryption, container, false);
|
View msgView = getLayoutInflater().inflate(R.layout.dialog_database_encryption, container, false);
|
||||||
|
|
||||||
|
@ -135,6 +135,11 @@ public class MainActivity extends BaseActivity
|
||||||
})
|
})
|
||||||
.create()
|
.create()
|
||||||
.show();
|
.show();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private void showFirstTimeWarning() {
|
||||||
|
Intent introIntent = new Intent(this, MainIntroActivity.class);
|
||||||
|
startActivityForResult(introIntent, Constants.INTENT_MAIN_INTRO);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void authenticate(int messageId) {
|
public void authenticate(int messageId) {
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
package org.shadowice.flocke.andotp.Activities;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
|
||||||
|
import com.heinrichreimersoftware.materialintro.app.SlideFragment;
|
||||||
|
import com.heinrichreimersoftware.materialintro.slide.FragmentSlide;
|
||||||
|
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
||||||
|
|
||||||
|
import org.shadowice.flocke.andotp.R;
|
||||||
|
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||||
|
import org.shadowice.flocke.andotp.Utilities.Settings;
|
||||||
|
|
||||||
|
public class MainIntroActivity extends IntroActivity {
|
||||||
|
private Settings settings;
|
||||||
|
|
||||||
|
private EncryptionFragment encryptionFragment;
|
||||||
|
|
||||||
|
@Override protected void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
settings = new Settings(this);
|
||||||
|
|
||||||
|
encryptionFragment = new EncryptionFragment();
|
||||||
|
|
||||||
|
setButtonBackFunction(BUTTON_BACK_FUNCTION_BACK);
|
||||||
|
|
||||||
|
addSlide(new SimpleSlide.Builder()
|
||||||
|
.title(R.string.intro_slide1_title)
|
||||||
|
.description(R.string.intro_slide1_desc)
|
||||||
|
.image(R.mipmap.ic_launcher)
|
||||||
|
.background(R.color.colorPrimary)
|
||||||
|
.backgroundDark(R.color.colorPrimaryDark)
|
||||||
|
.canGoBackward(false)
|
||||||
|
.scrollable(false)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
addSlide(new FragmentSlide.Builder()
|
||||||
|
.background(R.color.colorPrimary)
|
||||||
|
.backgroundDark(R.color.colorPrimaryDark)
|
||||||
|
.fragment(encryptionFragment)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Set authentication
|
||||||
|
addSlide(new SimpleSlide.Builder()
|
||||||
|
.title(R.string.intro_slide1_title)
|
||||||
|
.description(R.string.intro_slide1_desc)
|
||||||
|
.image(R.mipmap.ic_launcher)
|
||||||
|
.background(R.color.colorPrimary)
|
||||||
|
.backgroundDark(R.color.colorPrimaryDark)
|
||||||
|
.scrollable(false)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
if (position == 2) {
|
||||||
|
// TODO: Inter-page communication
|
||||||
|
settings.setEncryption(encryptionFragment.getSelectedEncryption());
|
||||||
|
Log.d("INTRO", "Encryption saved");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (getCurrentSlidePosition() != 0)
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EncryptionFragment extends SlideFragment {
|
||||||
|
private Spinner selection;
|
||||||
|
private TextView desc;
|
||||||
|
|
||||||
|
private SparseArray<Constants.EncryptionType> selectionMapping;
|
||||||
|
|
||||||
|
public EncryptionFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View root = inflater.inflate(R.layout.component_intro_encryption, container, false);
|
||||||
|
|
||||||
|
selection = root.findViewById(R.id.introEncryptionSelection);
|
||||||
|
desc = root.findViewById(R.id.introEncryptionDesc);
|
||||||
|
|
||||||
|
final String[] encValues = getResources().getStringArray(R.array.settings_values_encryption);
|
||||||
|
|
||||||
|
selectionMapping = new SparseArray<>();
|
||||||
|
for (int i = 0; i < encValues.length; i++)
|
||||||
|
selectionMapping.put(i, Constants.EncryptionType.valueOf(encValues[i].toUpperCase()));
|
||||||
|
|
||||||
|
selection.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
|
Constants.EncryptionType encryptionType = selectionMapping.get(i);
|
||||||
|
|
||||||
|
if (encryptionType == Constants.EncryptionType.PASSWORD)
|
||||||
|
desc.setText(R.string.intro_slide2_desc_password);
|
||||||
|
else if (encryptionType == Constants.EncryptionType.KEYSTORE)
|
||||||
|
desc.setText(R.string.intro_slide2_desc_keystore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Constants.EncryptionType getSelectedEncryption() {
|
||||||
|
return selectionMapping.get(selection.getSelectedItemPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,6 +52,7 @@ public class Constants {
|
||||||
public final static int INTENT_MAIN_AUTHENTICATE = 100;
|
public final static int INTENT_MAIN_AUTHENTICATE = 100;
|
||||||
public final static int INTENT_MAIN_SETTINGS = 101;
|
public final static int INTENT_MAIN_SETTINGS = 101;
|
||||||
public final static int INTENT_MAIN_BACKUP = 102;
|
public final static int INTENT_MAIN_BACKUP = 102;
|
||||||
|
public final static int INTENT_MAIN_INTRO = 103;
|
||||||
|
|
||||||
public final static int INTENT_BACKUP_OPEN_DOCUMENT_PLAIN = 200;
|
public final static int INTENT_BACKUP_OPEN_DOCUMENT_PLAIN = 200;
|
||||||
public final static int INTENT_BACKUP_SAVE_DOCUMENT_PLAIN = 201;
|
public final static int INTENT_BACKUP_SAVE_DOCUMENT_PLAIN = 201;
|
||||||
|
|
|
@ -290,6 +290,10 @@ public class Settings {
|
||||||
return EncryptionType.valueOf(encType.toUpperCase());
|
return EncryptionType.valueOf(encType.toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEncryption(EncryptionType encryptionType) {
|
||||||
|
setEncryption(encryptionType.name().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
public void setEncryption(String encryption) {
|
public void setEncryption(String encryption) {
|
||||||
setString(R.string.settings_key_encryption, encryption);
|
setString(R.string.settings_key_encryption, encryption);
|
||||||
}
|
}
|
||||||
|
|
40
app/src/main/res/layout/component_intro_encryption.xml
Normal file
40
app/src/main/res/layout/component_intro_encryption.xml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="@dimen/activity_margin_large" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/settings_title_encryption" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
|
android:text="@string/intro_slide2_desc"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/introEncryptionSelection"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin_large"
|
||||||
|
android:layout_marginStart="@dimen/activity_margin"
|
||||||
|
android:layout_marginEnd="@dimen/activity_margin"
|
||||||
|
android:entries="@array/settings_entries_encryption" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/introEncryptionDesc"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin_large"
|
||||||
|
android:text="@string/intro_slide2_desc_keystore"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
21
app/src/main/res/values/strings_intro.xml
Normal file
21
app/src/main/res/values/strings_intro.xml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="intro_slide1_title">Welcome to andOTP</string>
|
||||||
|
<string name="intro_slide1_desc">This wizard will guide you through the initial setup.</string>
|
||||||
|
|
||||||
|
<string name="intro_slide2_desc">To ensure the security of your accounts andOTP only stores them
|
||||||
|
in encrypted data files. Here you can choose which method of encryption will be used:</string>
|
||||||
|
<string name="intro_slide2_desc_keystore">The KeyStore is a system component of Android for
|
||||||
|
securely storing cryptographic keys. The advantage of this method is that the keys are
|
||||||
|
stored separated from the data files and can be backed by hardware cryptography (if your
|
||||||
|
device supports it). However, as the keys are not stored with the apps data <b>this method
|
||||||
|
prevents external backup solutions (like Titanium) from working</b>. If you choose this
|
||||||
|
method you will have to rely on the internal backup functions provided by andOTP.
|
||||||
|
\n\n<b>Warning</b>: The KeyStore is known to cause problems on some custom ROMs (and a few
|
||||||
|
stock ones as well). If you don\'t mind entering a password / PIN every time you start
|
||||||
|
andOTP it is highly recommended to use the password-based encryption.</string>
|
||||||
|
<string name="intro_slide2_desc_password">This method will encrypt your data with a key
|
||||||
|
generated from a password or PIN. The main advantage here is that this will work with
|
||||||
|
external backup solutions (like Titanium). However, you will have to enter your credentials
|
||||||
|
every time you start andOTP.</string>
|
||||||
|
</resources>
|
|
@ -18,9 +18,8 @@ buildscript {
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
maven { url "https://maven.google.com" }
|
||||||
url "https://maven.google.com"
|
maven { url "https://jitpack.io" }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue