Add per-app settings page

This commit is contained in:
Matthew Wong 2015-07-31 09:00:06 -04:00
parent 4b15ea3ae9
commit 2d7c37d379
9 changed files with 236 additions and 38 deletions

View file

@ -45,7 +45,7 @@
android:value="com.zeapo.pwdstore.PasswordStore" />
</activity>
<service android:name=".AutofillService"
<service android:name=".autofill.AutofillService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
@ -54,7 +54,7 @@
android:resource="@xml/autofill_config" />
</service>
<activity android:name=".AutofillActivity">
<activity android:name=".autofill.AutofillActivity">
</activity>

View file

@ -1,35 +0,0 @@
package com.zeapo.pwdstore;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
// blank activity started by service for calling startIntentSenderForResult
public class AutofillActivity extends AppCompatActivity {
public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913;
private boolean bound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
PendingIntent pi = intent.getExtras().getParcelable("pending_intent");
try {
startIntentSenderForResult(pi.getIntentSender()
, REQUEST_CODE_DECRYPT_AND_VERIFY, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(AutofillService.Constants.TAG, "SendIntentException", e);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
finish(); // go back to the password field app
if (resultCode == RESULT_OK) {
AutofillService.setUnlockOK(); // report the result to service
}
}
}

View file

@ -17,6 +17,7 @@ import android.widget.Toast;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import com.zeapo.pwdstore.autofill.AutofillActivity;
import com.zeapo.pwdstore.crypto.PgpHandler;
import com.zeapo.pwdstore.git.GitActivity;
import com.zeapo.pwdstore.utils.PasswordRepository;
@ -182,6 +183,15 @@ public class UserPreference extends AppCompatActivity {
findPreference("pref_select_external").setOnPreferenceChangeListener(resetRepo);
findPreference("git_external").setOnPreferenceChangeListener(resetRepo);
findPreference("autofill_apps").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(callingActivity, AutofillActivity.class);
startActivity(intent);
return false;
}
});
}
@Override

View file

@ -0,0 +1,84 @@
package com.zeapo.pwdstore.autofill;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import com.zeapo.pwdstore.R;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
// blank activity started by service for calling startIntentSenderForResult
public class AutofillActivity extends AppCompatActivity {
public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913;
private boolean bound = false;
private RecyclerView recyclerView;
private AutofillRecyclerAdapter recyclerAdapter;
private RecyclerView.LayoutManager layoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Bundle extras = intent.getExtras();
// if called by service just for startIntentSenderForResult
if (extras != null) {
try {
PendingIntent pi = intent.getExtras().getParcelable("pending_intent");
if (pi == null) {
return;
}
startIntentSenderForResult(pi.getIntentSender()
, REQUEST_CODE_DECRYPT_AND_VERIFY, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(AutofillService.Constants.TAG, "SendIntentException", e);
}
return;
}
// otherwise if called from settings
final PackageManager pm = getPackageManager();
List<ApplicationInfo> allApps = pm.getInstalledApplications(0);
SharedPreferences prefs
= getSharedPreferences("autofill", Context.MODE_PRIVATE);
Map<String, ?> prefApps = prefs.getAll();
ArrayList<ApplicationInfo> apps = new ArrayList<>();
for (ApplicationInfo applicationInfo : allApps) {
if (prefApps.containsKey(applicationInfo.packageName)) {
apps.add(applicationInfo);
}
}
setContentView(R.layout.autofill_recycler_view);
recyclerView = (RecyclerView) findViewById(R.id.autofill_recycler);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerAdapter = new AutofillRecyclerAdapter(apps, pm);
recyclerView.setAdapter(recyclerAdapter);
setTitle("Autofill Apps");
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
finish(); // go back to the password field app
if (resultCode == RESULT_OK) {
AutofillService.setUnlockOK(); // report the result to service
}
}
}

View file

@ -0,0 +1,62 @@
package com.zeapo.pwdstore.autofill;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
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 com.zeapo.pwdstore.R;
import java.util.ArrayList;
public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapter.ViewHolder> {
private ArrayList<ApplicationInfo> apps;
private PackageManager pm;
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public View view;
public TextView name;
public ImageView icon;
public ViewHolder(View view) {
super(view);
this.view = view;
name = (TextView) view.findViewById(R.id.app_name);
icon = (ImageView) view.findViewById(R.id.app_icon);
view.setOnClickListener(this);
}
@Override
public void onClick(View v) {
}
}
public AutofillRecyclerAdapter(ArrayList<ApplicationInfo> apps, PackageManager pm) {
this.apps = apps;
this.pm = pm;
}
@Override
public AutofillRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.autofill_row_layout, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(AutofillRecyclerAdapter.ViewHolder holder, int position) {
ApplicationInfo app = apps.get(position);
holder.name.setText(pm.getApplicationLabel(app));
holder.icon.setImageDrawable(pm.getApplicationIcon(app));
}
@Override
public int getItemCount() {
return apps.size();
}
}

View file

@ -1,4 +1,4 @@
package com.zeapo.pwdstore;
package com.zeapo.pwdstore.autofill;
import android.accessibilityservice.AccessibilityService;
import android.app.PendingIntent;
@ -20,6 +20,8 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
import com.zeapo.pwdstore.R;
import com.zeapo.pwdstore.UserPreference;
import com.zeapo.pwdstore.utils.PasswordItem;
import com.zeapo.pwdstore.utils.PasswordRepository;
@ -107,6 +109,12 @@ public class AutofillService extends AccessibilityService {
decryptAndVerify();
}
});
builder.setNeutralButton("Match", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog = builder.create();
dialog.setIcon(R.drawable.ic_launcher);
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

View file

@ -0,0 +1,12 @@
<?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.support.v7.widget.RecyclerView
android:id="@+id/autofill_recycler"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View file

@ -0,0 +1,49 @@
<?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="64dp"
android:background="@color/grey_white_1000">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:gravity="center_vertical">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/app_icon"
/>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_marginLeft="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="App"
android:id="@+id/app_name"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="The matched file.gpg/Don't ask"
android:id="@+id/secondary_text"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View file

@ -70,6 +70,14 @@
android:summary="@string/pref_recursive_filter_hint"
android:title="@string/pref_recursive_filter" />
</PreferenceCategory>
<PreferenceCategory android:title="Autofill">
<Preference
android:key="autofill_apps"
android:summary="Configure behaviour of autofill for specific apps."
android:title="Autofill Apps"/>
</PreferenceCategory>
<PreferenceCategory android:title="Misc">
<CheckBoxPreference
android:defaultValue="false"